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-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java
index dfcc1f790..ddf67f054 100644
--- a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java
@@ -17,6 +17,7 @@
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;
@@ -25,8 +26,10 @@ 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;
@@ -34,25 +37,34 @@ 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;
+
/**
- * HTTP File Upload manager class.
- * XEP version 0.2.5
+ * 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 {
@@ -65,8 +77,10 @@ public final class HttpFileUploadManager extends Manager {
}
private static final Map INSTANCES = new WeakHashMap<>();
- private DomainBareJid defaultUploadService;
- private Long maxFileSize;
+
+ private UploadService defaultUploadService;
+
+ private SSLSocketFactory tlsSocketFactory;
/**
* Obtain the HttpFileUploadManager responsible for a connection.
@@ -106,6 +120,40 @@ public final class HttpFileUploadManager extends Manager {
});
}
+ 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.
*
@@ -122,39 +170,20 @@ public final class HttpFileUploadManager extends Manager {
*/
public boolean discoverUploadService() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException,
InterruptedException, SmackException.NoResponseException {
- defaultUploadService = null;
- maxFileSize = null;
-
- List servicesDiscoverInfo = ServiceDiscoveryManager.getInstanceFor(connection())
- .findServicesDiscoverInfo(SlotRequest.NAMESPACE, true, false);
+ ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
+ List servicesDiscoverInfo = sdm
+ .findServicesDiscoverInfo(NAMESPACE, true, true);
if (servicesDiscoverInfo.isEmpty()) {
- return false;
+ servicesDiscoverInfo = sdm.findServicesDiscoverInfo(NAMESPACE_0_2, true, true);
+ if (servicesDiscoverInfo.isEmpty()) {
+ return false;
+ }
}
DiscoverInfo discoverInfo = servicesDiscoverInfo.get(0);
- defaultUploadService = discoverInfo.getFrom().asDomainBareJid();
-
- if (defaultUploadService == null) {
- return false;
- }
-
- DataForm dataForm = DataForm.from(discoverInfo);
- if (dataForm == null) {
- return true;
- }
-
- FormField field = dataForm.getField("max-file-size");
- if (field == null) {
- return true;
- }
-
- List values = field.getValues();
- if (!values.isEmpty()) {
- maxFileSize = Long.valueOf(values.get(0));
- }
-
+ defaultUploadService = uploadServiceFrom(discoverInfo);
return true;
}
@@ -172,19 +201,10 @@ public final class HttpFileUploadManager extends Manager {
*
* @return upload service JID or null if not available
*/
- public DomainBareJid getDefaultUploadService() {
+ public UploadService getDefaultUploadService() {
return defaultUploadService;
}
- /**
- * Get max file size allowed by upload service.
- *
- * @return max file size in bytes or null if not available
- */
- public Long getMaxFileSize() {
- return maxFileSize;
- }
-
/**
* Request slot and uploaded file to HTTP file upload service.
*
@@ -203,19 +223,6 @@ public final class HttpFileUploadManager extends Manager {
return uploadFile(file, null);
}
- /**
- * Callback interface to get upload progress.
- */
- public interface UploadProgressListener {
- /**
- * Callback for displaying upload progress.
- *
- * @param uploadedBytes - number of bytes uploaded at the moment
- * @param totalBytes - total number of bytes to be uploaded
- */
- void onUploadProgress(long uploadedBytes, long totalBytes);
- }
-
/**
* Request slot and uploaded file to HTTP file upload service with progress callback.
*
@@ -233,27 +240,26 @@ public final class HttpFileUploadManager extends Manager {
*/
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.getPutUrl(), listener);
+ 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.
+ * 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 -- must be less or equal
- * to {@link HttpFileUploadManager#getMaxFileSize()} (if available)
+ * @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 {@link HttpFileUploadManager#getMaxFileSize()}
+ * @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
@@ -271,13 +277,12 @@ public final class HttpFileUploadManager extends Manager {
* 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 -- must be less or equal
- * to {@link HttpFileUploadManager#getMaxFileSize()} (if available)
+ * @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 {@link HttpFileUploadManager#getMaxFileSize()}
+ * @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
@@ -295,81 +300,164 @@ public final class HttpFileUploadManager extends Manager {
* 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 -- must be less or equal
- * to {@link HttpFileUploadManager#getMaxFileSize()} (if available)
+ * @param fileSize file size in bytes.
* @param contentType file content-type or null
- * @param uploadService upload service to use or null for default one
+ * @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 {@link HttpFileUploadManager#getMaxFileSize()}
+ * @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 uploadService)
+ public Slot requestSlot(String filename, long fileSize, String contentType, DomainBareJid uploadServiceAddress)
throws SmackException, InterruptedException, XMPPException.XMPPErrorException {
- if (defaultUploadService == null && uploadService == null) {
- throw new SmackException("No upload service specified or discovered.");
- }
+ final XMPPConnection connection = connection();
+ final UploadService defaultUploadService = this.defaultUploadService;
+
+ // The upload service we are going to use.
+ UploadService uploadService;
- if (uploadService == null && maxFileSize != null) {
- if (fileSize > maxFileSize) {
- throw new IllegalArgumentException("Requested file size " + fileSize
- + " is greater than max allowed size " + maxFileSize);
+ 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);
}
}
- SlotRequest slotRequest = new SlotRequest(filename, fileSize, contentType);
- if (uploadService != null) {
- slotRequest.setTo(uploadService);
- } else {
- slotRequest.setTo(defaultUploadService);
+ 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();
+ }
- return connection().createStanzaCollectorAndSend(slotRequest).nextResultOrThrow();
+ public void useTlsSettingsFrom(ConnectionConfiguration connectionConfiguration) {
+ SSLContext sslContext = connectionConfiguration.getCustomSSLContext();
+ setTlsContext(sslContext);
}
- private void uploadFile(File file, URL putUrl, UploadProgressListener listener) throws IOException {
+ 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;");
- OutputStream outputStream = urlConnection.getOutputStream();
-
- long bytesSend = 0;
+ for (Entry header : slot.getHeaders().entrySet()) {
+ urlConnection.setRequestProperty(header.getKey(), header.getValue());
+ }
- long fileSize = file.length();
- if (listener != null) {
- listener.onUploadProgress(0, fileSize);
+ final SSLSocketFactory tlsSocketFactory = this.tlsSocketFactory;
+ if (tlsSocketFactory != null && urlConnection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
+ httpsUrlConnection.setSSLSocketFactory(tlsSocketFactory);
}
- BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
+ try {
+ OutputStream outputStream = urlConnection.getOutputStream();
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytesRead);
- outputStream.flush();
- bytesSend += bytesRead;
+ long bytesSend = 0;
if (listener != null) {
- listener.onUploadProgress(bytesSend, fileSize);
+ listener.onUploadProgress(0, fileSize);
}
- }
+ BufferedInputStream inputStream = new BufferedInputStream(fis);
- inputStream.close();
- outputStream.close();
+ // 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();
- if (status != HttpURLConnection.HTTP_CREATED) {
- throw new IOException("Error response from server during file upload:"
- + " " + urlConnection.getResponseCode()
- + " " + urlConnection.getResponseMessage()
- + ", file size: " + fileSize
- + ", put URL: " + putUrl);
+ 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
index 57eec5d62..a0fcb7d04 100644
--- 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
@@ -32,9 +32,15 @@ public class FileTooLargeError implements ExtensionElement {
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() {
@@ -48,15 +54,15 @@ public class FileTooLargeError implements ExtensionElement {
@Override
public String getNamespace() {
- return NAMESPACE;
+ return namespace;
}
@Override
- public CharSequence toXML() {
+ public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.rightAngleBracket();
xml.element("max-file-size", String.valueOf(maxFileSize));
- xml.closeElement(ELEMENT);
+ xml.closeElement(this);
return xml;
}
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
index 470987dc4..e341bca46 100644
--- 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
@@ -19,6 +19,8 @@ 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.
@@ -33,12 +35,26 @@ public class Slot extends IQ {
private final URL putUrl;
private final URL getUrl;
+ private final Map headers;
public Slot(URL putUrl, URL getUrl) {
- super(ELEMENT, NAMESPACE);
+ 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() {
@@ -49,6 +65,9 @@ public class Slot extends IQ {
return getUrl;
}
+ public Map getHeaders() {
+ return headers;
+ }
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
@@ -56,6 +75,9 @@ public class Slot extends IQ {
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
index bcdb823e6..a776ccf32 100644
--- 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
@@ -17,6 +17,8 @@
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.
@@ -26,23 +28,31 @@ import org.jivesoftware.smack.packet.IQ;
*/
public class SlotRequest extends IQ {
public static final String ELEMENT = "request";
- public static final String NAMESPACE = "urn:xmpp:http:upload";
+ 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.
*
- * @throws IllegalArgumentException if size is less than or equal to zero
+ * @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(String filename, long size, String contentType) {
- super(ELEMENT, NAMESPACE);
+ 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.");
@@ -53,10 +63,7 @@ public class SlotRequest extends IQ {
this.contentType = contentType;
setType(Type.get);
- }
-
- public SlotRequest(String filename, long size) {
- this(filename, size, null);
+ setTo(uploadServiceAddress);
}
public String getFilename() {
@@ -76,9 +83,7 @@ public class SlotRequest extends IQ {
xml.rightAngleBracket();
xml.element("filename", filename);
xml.element("size", String.valueOf(size));
- if (contentType != null) {
- xml.element("content-type", contentType);
- }
+ 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/provider/FileTooLargeErrorProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProvider.java
index 3f1accb7c..77ea7fda4 100644
--- 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
@@ -18,6 +18,7 @@ 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;
/**
@@ -30,6 +31,7 @@ public class FileTooLargeErrorProvider extends ExtensionElementProvider {
@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();
@@ -51,6 +57,14 @@ public class SlotProvider extends IQProvider {
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:
@@ -61,6 +75,13 @@ public class SlotProvider extends IQProvider {
}
}
- return new Slot(putUrl, getUrl);
+ 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/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers
index 0b9f7cf8a..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
@@ -234,6 +234,16 @@
+
+ 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
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
index 40c79f335..d2abf74f6 100644
--- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorCreateTest.java
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorCreateTest.java
@@ -23,7 +23,7 @@ import org.junit.Test;
public class FileTooLargeErrorCreateTest {
String fileTooLargeErrorExtensionExample
- = ""
+ = ""
+ "20000"
+ "";
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
index 838de9466..ebb5170cd 100644
--- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java
@@ -26,7 +26,7 @@ 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"
+ "";
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
index e7ef98bb1..43c6fe80d 100644
--- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java
@@ -19,27 +19,28 @@ 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("my_juliet.png", 23456, "image/jpeg");
+ 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());
@@ -50,7 +51,7 @@ public class SlotRequestCreateTest {
@Test
public void checkSlotRequestCreationWithoutContentType() throws XmppStringprepException {
- SlotRequest slotRequest = new SlotRequest("my_romeo.png", 52523);
+ 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());
@@ -61,11 +62,11 @@ public class SlotRequestCreateTest {
@Test(expected = IllegalArgumentException.class)
public void checkSlotRequestCreationNegativeSize() {
- new SlotRequest("my_juliet.png", -23456, "image/jpeg");
+ new SlotRequest(JidTestUtil.DOMAIN_BARE_JID_1, "my_juliet.png", -23456, "image/jpeg");
}
@Test(expected = IllegalArgumentException.class)
public void checkSlotRequestCreationZeroSize() {
- new SlotRequest("my_juliet.png", 0, "image/jpeg");
+ 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/FileTooLargeErrorProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProviderTest.java
similarity index 94%
rename from smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorProviderTest.java
rename to smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProviderTest.java
index 10573d938..564b142d6 100644
--- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorProviderTest.java
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProviderTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.httpfileupload;
+package org.jivesoftware.smackx.httpfileupload.provider;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.util.PacketParserUtils;
@@ -33,14 +33,14 @@ public class FileTooLargeErrorProviderTest {
+ "id='step_03' "
+ "to='romeo@montague.tld/garden' "
+ "type='error'>"
- + ""
+ + ""
+ "my_juliet.png"
+ "23456"
+ ""
+ ""
+ ""
+ "File too large. The maximum file size is 20000 bytes"
- + ""
+ + ""
+ "20000"
+ ""
+ ""
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java
similarity index 85%
rename from smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotProviderTest.java
rename to smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java
index 13b830b67..4cef3c550 100644
--- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotProviderTest.java
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java
@@ -14,15 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jivesoftware.smackx.httpfileupload;
+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.jivesoftware.smackx.httpfileupload.provider.SlotProvider;
import org.junit.Assert;
import org.junit.Test;
-import org.xmlpull.v1.XmlPullParser;
import java.net.URL;
@@ -38,7 +36,7 @@ public class SlotProviderTest {
+ "id='step_03' "
+ "to='romeo@montague.tld/garden' "
+ "type='result'>"
- + ""
+ + ""
+ "https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"
+ "https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"
+ ""
@@ -46,8 +44,7 @@ public class SlotProviderTest {
@Test
public void checkSlotProvider() throws Exception {
- XmlPullParser parser = PacketParserUtils.getParserFor(slotExample);
- Slot slot = new SlotProvider().parse(parser);
+ 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"),
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/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/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)