mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 06:12:05 +01:00
Smack 4.2.0
-----BEGIN PGP SIGNATURE----- iQGTBAABCgB9FiEEl3UFnzoh3OFr5PuuIjmn6PWFIFIFAljDKBtfFIAAAAAALgAo aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDk3 NzUwNTlGM0EyMURDRTE2QkU0RkJBRTIyMzlBN0U4RjU4NTIwNTIACgkQIjmn6PWF IFJB7Qf6AlkwpzMqq1g18jzEBFVX/3Sk2QWivEY7t3EhGuSguan2VIfd1fL0P85Q vLBm6Pw93haIxHXKRUKc8DINwP9yuRMvUotCN2hYVgfqfByHGhDCJLTNZ9atncL5 JToptfhdRy6kgljVZPtpXOMXKBvaO3QOuTuC5cmz8PlidsYw0yUnliPLQ36uPRWX eaEXXbgmkjJh35WjsaafD/uM86OCqZahfvEf3e8bkPzdAayd0OKU67+v0ArA9P2E CiRU5vfco/vt2Qo41aLLIEOjSFfVX6Xh/pXxfQvInMAxies0KRLi5vonOmfrWRmi uIblzcYRXCSaZSgVN2yF8KzmF4pzcw== =qETn -----END PGP SIGNATURE----- Merge tag '4.2.0' Smack 4.2.0
This commit is contained in:
commit
1a93b448db
51 changed files with 1996 additions and 218 deletions
|
@ -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
|
||||
|
|
14
README.md
14
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
|
||||
------
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 |
|
||||
|
||||
|
|
|
@ -141,6 +141,140 @@ hr {
|
|||
|
||||
<div id="pageBody">
|
||||
|
||||
<h2>4.2.0 -- <span style="font-weight: normal;">2017-03-10</span></h2>
|
||||
|
||||
<h2> Sub-task
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-639'>SMACK-639</a>] - Add support for pre-approved subscription requests (RFC 6121 § 3.4)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-306'>SMACK-306</a>] - loadRosterOnLogin has non-trivial side effect on getRoster
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-416'>SMACK-416</a>] - Refactor PEP to make it use the existing pubsub API.
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-674'>SMACK-674</a>] - PubSub Affiliation extension element is missing 'jid' attribute, and is using wrong element name 'subscription'
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-682'>SMACK-682</a>] - Add support for "XEP-0360: Nonzas (are not Stanzas)"
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-683'>SMACK-683</a>] - Using a Proxy with XMPPTCPConnection failes with "SocketException: Unconnected sockets not implemented"
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-691'>SMACK-691</a>] - Add support for MUCItem's Actor 'nick'
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-705'>SMACK-705</a>] - PubSub's Affiliation.getElementName() returns wrong name
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-722'>SMACK-722</a>] - SASL X-OAUTH2 implementation incorrectly performs Base64 encoding twice
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-723'>SMACK-723</a>] - Support "Caps Optimizations" (XEP-0115 § 8.4)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-724'>SMACK-724</a>] - Do not re-use the Socket after connect() failed.
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-725'>SMACK-725</a>] - ReconnectionManager should handle AlreadyConnectedException and AlreadyLoggedInException not as failure
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-741'>SMACK-741</a>] - Ad-hoc command 'note' element 'type' attribute should be treated as optional
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-745'>SMACK-745</a>] - Memory leak in MultiUserChat
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2> New Feature
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-366'>SMACK-366</a>] - Add support for DNSSEC.
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-610'>SMACK-610</a>] - Add support for XEP-0080: User Location
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-619'>SMACK-619</a>] - Add roomDestroyed to MUC UserStatusListener
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-625'>SMACK-625</a>] - Add support for XEP-313: Message Archive Management
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-675'>SMACK-675</a>] - Add support for PubSub affiliation actions as owner
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-677'>SMACK-677</a>] - Add support for SASL 'authzid' (Authorization Identity)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-690'>SMACK-690</a>] - Add support for DNS-Based Authentication of Named Entities (DANE, RFC 6698)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-731'>SMACK-731</a>] - Add support for XEP-0191: Blocking Command
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-732'>SMACK-732</a>] - Smack should be able to handle "single equals sign" SASL responses
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-740'>SMACK-740</a>] - Add support for Multi-User Chat Light
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-742'>SMACK-742</a>] - Add support for XEP-0133: Service Administration
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-747'>SMACK-747</a>] - Add support for XEP-0363: HTTP File Upload
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2> Task
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-638'>SMACK-638</a>] - Call connection creation listeners from within AbstractXMPPConnection's constructor
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-644'>SMACK-644</a>] - Throw exception if account creation or password change is performed over insecure connections
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-655'>SMACK-655</a>] - Enable StreamManagement by default
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2> Improvement
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-372'>SMACK-372</a>] - Make package protected methods in PEPItem public
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-572'>SMACK-572</a>] - Rejoin MUC rooms after reconnect
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-628'>SMACK-628</a>] - Rework Roster handling with anonymous connections
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-629'>SMACK-629</a>] - Rework how Smack handles anonymous connections
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-631'>SMACK-631</a>] - Improve ParsingExceptionCallback, allow it to be a functional interface
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-632'>SMACK-632</a>] - Make Smack interruptible
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-633'>SMACK-633</a>] - Allow clean and graceful disconnects (stream closing)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-634'>SMACK-634</a>] - Use jxmpp-jid, add Jid class to replace String's being used as JIDs
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-646'>SMACK-646</a>] - Add support for MUC roomnick rewrite
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-647'>SMACK-647</a>] - Don't automatically call login() on connect() if the connection was authenticated before
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-648'>SMACK-648</a>] - Improve MultiUserChat API
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-657'>SMACK-657</a>] - Rename RosterEntry.getStatus and RosterPacket.ItemStatus to ItemAskStatus
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-663'>SMACK-663</a>] - Roster should be fully loaded when Roster.getInstanceFor(XMPPConnection) is called with a authenticated connection
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-665'>SMACK-665</a>] - Rename 'serviceName' to 'xmppServiceDomain'
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-666'>SMACK-666</a>] - Typo in 'RosterEntries.rosterEntires()', change to 'RosterEntries.rosterEntries()'
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-703'>SMACK-703</a>] - Limit the stored presences of entities not in Roster
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-704'>SMACK-704</a>] - Pass down Message stanza in ChatStateListener
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-711'>SMACK-711</a>] - Improve the logging of TCP connection attempts.
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-720'>SMACK-720</a>] - Improve support for Tor and Hidden Services.
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-721'>SMACK-721</a>] - Report illegal Stream Management states to avoid OOM Exception
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-727'>SMACK-727</a>] - Add partial support for the IoT XEPs (XEP-0323, -0324, -0325, -0347)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-733'>SMACK-733</a>] - Handle outgoing 'unavailable' Presences in Roster
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-736'>SMACK-736</a>] - Add support for Chat Markers (XEP-0333)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-737'>SMACK-737</a>] - Add support for Bits of Binary (XEP-0231)
|
||||
</li>
|
||||
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-738'>SMACK-738</a>] - Add support for Push Notifications (XEP-0357)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>4.1.9 -- <span style="font-weight: normal;">2016-11-19</span></h2>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<SecureRandom> SECURE_RANDOM = new ThreadLocal<SecureRandom>() {
|
||||
@Override
|
||||
protected SecureRandom initialValue() {
|
||||
return new SecureRandom();
|
||||
}
|
||||
};
|
||||
|
||||
private static final Cache<String, Keys> CACHE = new LruCache<String, Keys>(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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Random> randGen = new ThreadLocal<Random>() {
|
||||
@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<randBuffer.length; i++) {
|
||||
randBuffer[i] = numbersAndLetters[randGen.nextInt(numbersAndLetters.length)];
|
||||
randBuffer[i] = numbersAndLetters[random.nextInt(numbersAndLetters.length)];
|
||||
}
|
||||
return new String(randBuffer);
|
||||
}
|
||||
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
private static final ThreadLocal<SecureRandom> SECURE_RANDOM = new ThreadLocal<SecureRandom>() {
|
||||
@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]);
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
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<XMPPConnection, HttpFileUploadManager> 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<String> 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<DiscoverInfo> 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<String, String> 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
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<String, String> headers;
|
||||
|
||||
public Slot(URL putUrl, URL getUrl) {
|
||||
this(putUrl, getUrl, null);
|
||||
}
|
||||
|
||||
public Slot(URL putUrl, URL getUrl, Map<String, String> headers) {
|
||||
this(putUrl, getUrl, headers, NAMESPACE);
|
||||
}
|
||||
|
||||
protected Slot(URL putUrl, URL getUrl, Map<String, String> 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<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
||||
xml.rightAngleBracket();
|
||||
|
||||
xml.element("put", putUrl.toString());
|
||||
xml.element("get", getUrl.toString());
|
||||
for (Map.Entry<String, String> entry : getHeaders().entrySet()) {
|
||||
xml.openElement("header").attribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
package org.jivesoftware.smackx.httpfileupload.element;
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
package org.jivesoftware.smackx.httpfileupload;
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
public class FileTooLargeErrorProvider extends ExtensionElementProvider<FileTooLargeError> {
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
public class SlotProvider extends IQProvider<Slot> {
|
||||
|
||||
@Override
|
||||
public Slot parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException {
|
||||
final String namespace = parser.getNamespace();
|
||||
URL putUrl = null;
|
||||
URL getUrl = null;
|
||||
Map<String, String> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html">XEP-0363: HTTP File Upload</a>
|
||||
*/
|
||||
package org.jivesoftware.smackx.httpfileupload.provider;
|
|
@ -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<XMPPConnection, MamManager> INSTANCES = new WeakHashMap<>();
|
||||
private static final Map<XMPPConnection, Map<Jid, MamManager>> 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<Jid, MamManager> 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<FormField> 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<FormField> 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<FormField> 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).
|
||||
* <p>
|
||||
* Note that the messageUid is the XEP-0313 UID and <b>not</> the stanza ID of the message.
|
||||
* </p>
|
||||
*
|
||||
* @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).
|
||||
* <p>
|
||||
* Note that the messageUid is the XEP-0313 UID and <b>not</> the stanza ID of the message.
|
||||
* </p>
|
||||
*
|
||||
* @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<FormField> 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<FormField> 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<Forwarded> forwardedMessages;
|
||||
public final MamFinIQ mamFin;
|
||||
private final String node;
|
||||
private final DataForm form;
|
||||
|
||||
private MamQueryResult(List<Forwarded> forwardedMessages, MamFinIQ mamFin, DataForm form) {
|
||||
private MamQueryResult(List<Forwarded> 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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -233,4 +233,26 @@
|
|||
<className>org.jivesoftware.smackx.chat_markers.provider.AcknowledgedProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0363: HTTP File Upload -->
|
||||
<iqProvider>
|
||||
<elementName>slot</elementName>
|
||||
<namespace>urn:xmpp:http:upload:0</namespace>
|
||||
<className>org.jivesoftware.smackx.httpfileupload.provider.SlotProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>file-too-large</elementName>
|
||||
<namespace>urn:xmpp:http:upload:0</namespace>
|
||||
<className>org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider</className>
|
||||
</extensionProvider>
|
||||
<iqProvider>
|
||||
<elementName>slot</elementName>
|
||||
<namespace>urn:xmpp:http:upload</namespace>
|
||||
<className>org.jivesoftware.smackx.httpfileupload.provider.SlotProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>file-too-large</elementName>
|
||||
<namespace>urn:xmpp:http:upload</namespace>
|
||||
<className>org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
</smackProviders>
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
<className>org.jivesoftware.smackx.iot.data.IoTDataManager</className>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager</className>
|
||||
<className>org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager</className>
|
||||
<className>org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager</className>
|
||||
</startupClasses>
|
||||
</smack>
|
||||
|
|
|
@ -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
|
||||
= "<file-too-large xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<max-file-size>20000</max-file-size>"
|
||||
+ "</file-too-large>";
|
||||
|
||||
@Test
|
||||
public void checkFileTooLargeErrorExtensionCreation() {
|
||||
FileTooLargeError fileTooLargeError = new FileTooLargeError(20000);
|
||||
|
||||
Assert.assertEquals(20000, fileTooLargeError.getMaxFileSize());
|
||||
Assert.assertEquals(fileTooLargeErrorExtensionExample, fileTooLargeError.toXML().toString());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
= "<slot xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<put>https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png</put>"
|
||||
+ "<get>https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png</get>"
|
||||
+ "</slot>";
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
|
@ -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
|
||||
= "<request xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<filename>my_juliet.png</filename>"
|
||||
+ "<size>23456</size>"
|
||||
+ "<content-type>image/jpeg</content-type>"
|
||||
+ "</request>";
|
||||
|
||||
String testRequestWithoutContentType
|
||||
= "<request xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<filename>my_romeo.png</filename>"
|
||||
+ "<size>52523</size>"
|
||||
+ "</request>";
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html#errors">XEP-0363: HTTP File Upload 5. Error conditions</a>
|
||||
*/
|
||||
String slotErrorFileToLarge
|
||||
= "<iq from='upload.montague.tld' "
|
||||
+ "id='step_03' "
|
||||
+ "to='romeo@montague.tld/garden' "
|
||||
+ "type='error'>"
|
||||
+ "<request xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<filename>my_juliet.png</filename>"
|
||||
+ "<size>23456</size>"
|
||||
+ "</request>"
|
||||
+ "<error type='modify'>"
|
||||
+ "<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />"
|
||||
+ "<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>File too large. The maximum file size is 20000 bytes</text>"
|
||||
+ "<file-too-large xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<max-file-size>20000</max-file-size>"
|
||||
+ "</file-too-large>"
|
||||
+ "</error>"
|
||||
+ "</iq>";
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
|
@ -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 <a href="http://xmpp.org/extensions/xep-0363.html#request">XEP-0363: HTTP File Upload 4. Requesting a slot</a>
|
||||
*/
|
||||
String slotExample
|
||||
= "<iq from='upload.montague.tld' "
|
||||
+ "id='step_03' "
|
||||
+ "to='romeo@montague.tld/garden' "
|
||||
+ "type='result'>"
|
||||
+ "<slot xmlns='urn:xmpp:http:upload:0'>"
|
||||
+ "<put>https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png</put>"
|
||||
+ "<get>https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png</get>"
|
||||
+ "</slot>"
|
||||
+ "</iq>";
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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<PingFailedListener> pingFailedListeners = Collections
|
||||
.synchronizedSet(new HashSet<PingFailedListener>());
|
||||
private final Set<PingFailedListener> pingFailedListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private final ScheduledExecutorService executorService;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<String> enabledTests, Set<String> disabledTests,
|
||||
Set<String> testPackages, String adminAccountUsername, String adminAccountPassword) {
|
||||
Set<String> 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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/iot/package-info.java
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/muc/package-info.java
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/package-info.java
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/ping/package-info.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)
|
||||
|
|
Loading…
Reference in a new issue