mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-22 03:52:06 +01:00
Merge branch '4.2'
This commit is contained in:
commit
81002c4fbd
63 changed files with 391 additions and 266 deletions
|
@ -79,6 +79,14 @@ allprojects {
|
|||
':smack-android',
|
||||
':smack-android-extensions',
|
||||
].collect{ project(it) }
|
||||
androidOptionalProjects = [
|
||||
':smack-tcp',
|
||||
':smack-extensions',
|
||||
':smack-experimental',
|
||||
':smack-bosh',
|
||||
':smack-omemo',
|
||||
':smack-omemo-signal',
|
||||
].collect{ project(it) }
|
||||
gplLicensedProjects = [
|
||||
':smack-omemo-signal',
|
||||
':smack-omemo-signal-integration-test',
|
||||
|
|
|
@ -60,6 +60,10 @@
|
|||
<property name="format" value="^\s*[\S&&[^\*/]]+\s+$"/>
|
||||
<property name="message" value="Line containing trailing whitespace character(s)"/>
|
||||
</module>
|
||||
<module name="RegexpSingleline">
|
||||
<property name="format" value="^\s*//[^\s]"/>
|
||||
<property name="message" value="Comment start ('//') followed by non-space character. You would not continue after a punctuation without a space, would you?"/>
|
||||
</module>
|
||||
<module name="JavadocPackage"/>
|
||||
<module name="TreeWalker">
|
||||
<module name="FinalClass"/>
|
||||
|
|
|
@ -6,24 +6,24 @@ Smack: Getting Started
|
|||
This document will introduce you to the Smack API and provide an overview of
|
||||
important classes and concepts.
|
||||
|
||||
JAR Files and Requirements
|
||||
--------------------------
|
||||
Smack Modules and Requirements
|
||||
-------------------------------
|
||||
|
||||
Smack is meant to be easily embedded into any existing Java application. The
|
||||
library ships as several JAR files to provide more flexibility over which
|
||||
library ships as several modulesto provide more flexibility over which
|
||||
features applications require:
|
||||
|
||||
* `smack-core.jar` -- provides core XMPP functionality. All XMPP features that are part of the XMPP RFCs are included.
|
||||
* `smack-im.jar` -- provides functinoality defined in RFC 6121 (XMPP-IM), like the Roster.
|
||||
* `smack-tcp.jar` -- support for XMPP over TCP. Includes XMPPTCPConnection class, which you usually want to use
|
||||
* `smack-extensions.jar` -- support for many of the extensions (XEPs) defined by the XMPP Standards Foundation, including multi-user chat, file transfer, user search, etc. The extensions are documented in the [extensions manual](extensions/index.md).
|
||||
* `smack-experimental.jar` -- support for experimental extensions (XEPs) defined by the XMPP Standards Foundation. The API and functionality of those extensions should be considered as unstable.
|
||||
* `smack-legacy.jar` -- support for legacy extensions (XEPs) defined by the XMPP Standards Foundation.
|
||||
* `smack-bosh.jar` -- support for BOSH (XEP-0124). This code should be considered as beta.
|
||||
* `smack-jingle.jar` -- support for Jingle. This code is old and currenlty unmaintained.
|
||||
* `smack-resolver-dnsjava.jar` -- support for resolving DNS SRV records with the help of dnsjava. Ideal for platforms that do not support the javax.naming API.
|
||||
* `smack-resolver-javax.jar` -- support for resolving DNS SRV records with the javax namespace API.
|
||||
* `smack-debug.jar` -- an enhanced GUI debugger for protocol traffic. It will automatically be used when found in the classpath and when [debugging](debugging.md) is enabled.
|
||||
* `smack-core` -- provides core XMPP functionality. All XMPP features that are part of the XMPP RFCs are included.
|
||||
* `smack-im` -- provides functinoality defined in RFC 6121 (XMPP-IM), like the Roster.
|
||||
* `smack-tcp` -- support for XMPP over TCP. Includes XMPPTCPConnection class, which you usually want to use
|
||||
* `smack-extensions` -- support for many of the extensions (XEPs) defined by the XMPP Standards Foundation, including multi-user chat, file transfer, user search, etc. The extensions are documented in the [extensions manual](extensions/index.md).
|
||||
* `smack-experimental` -- support for experimental extensions (XEPs) defined by the XMPP Standards Foundation. The API and functionality of those extensions should be considered as unstable.
|
||||
* `smack-legacy` -- support for legacy extensions (XEPs) defined by the XMPP Standards Foundation.
|
||||
* `smack-bosh` -- support for BOSH (XEP-0124). This code should be considered as beta.
|
||||
* `smack-resolver-minidns` -- support for resolving DNS SRV records with the help of MiniDNS. Ideal for platforms that do not support the javax.naming API. Also supports [DNSSEC](dnssec.md).
|
||||
* `smack-resolver-dnsjava` -- support for resolving DNS SRV records with the help of dnsjava.
|
||||
* `smack-resolver-javax` -- support for resolving DNS SRV records with the javax namespace API.
|
||||
* `smack-debug` -- an enhanced GUI debugger for protocol traffic. It will automatically be used when found in the classpath and when [debugging](debugging.md) is enabled.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
@ -46,10 +46,14 @@ The `XMPPTCPConnection` class is used to create a connection to an XMPP
|
|||
server. Below are code examples for making a connection:
|
||||
|
||||
```
|
||||
// Create a connection to the jabber.org server.
|
||||
AbstractXMPPConnection conn1 = **new** XMPPTCPConnection("username", "password", "jabber.org");
|
||||
conn1.connect();
|
||||
// Create a connection and login to the example.org XMPP service.
|
||||
AbstractXMPPConnection connection = new XMPPTCPConnection("username", "password" "example.org");
|
||||
conn1.connect().login();
|
||||
```
|
||||
|
||||
Further connection parameters can be configured by using a configuration builder:
|
||||
|
||||
```
|
||||
// Create a connection to the jabber.org server on a specific port.
|
||||
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
|
||||
.setUsernameAndPassword("username", "password")
|
||||
|
@ -59,7 +63,7 @@ XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
|
|||
.build();
|
||||
|
||||
AbstractXMPPConnection conn2 = **new** XMPPTCPConnection(config);
|
||||
conn2.connect();
|
||||
conn2.connect().login();
|
||||
```
|
||||
|
||||
Note that maximum security will be used when connecting to the server by
|
||||
|
|
|
@ -9,9 +9,8 @@ smack-extensions and smack-experimental."""
|
|||
dependencies {
|
||||
// androidProjects lists all projects that are checked to compile against android.jar
|
||||
// Filter out the optional Smack dependencies from androidProjects
|
||||
androidProjects.findAll {
|
||||
![':smack-tcp', ':smack-extensions', ':smack-experimental', ':smack-bosh'].contains(it.getPath())
|
||||
}.each { project ->
|
||||
(androidProjects - androidOptionalProjects)
|
||||
.each { project ->
|
||||
compile project
|
||||
}
|
||||
|
||||
|
|
|
@ -524,7 +524,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
break;
|
||||
case "error":
|
||||
//Some bosh error isn't stream error.
|
||||
// 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 {
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
|
|||
import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
|
||||
|
@ -1541,7 +1542,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
|
||||
final StanzaListener packetListener = new StanzaListener() {
|
||||
@Override
|
||||
public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException {
|
||||
public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException, NotLoggedInException {
|
||||
boolean removed = removeAsyncStanzaListener(this);
|
||||
if (!removed) {
|
||||
// We lost a race against the "no response" handling runnable. Avoid calling the callback, as the
|
||||
|
@ -1609,7 +1610,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
public void addOneTimeSyncCallback(final StanzaListener callback, final StanzaFilter packetFilter) {
|
||||
final StanzaListener packetListener = new StanzaListener() {
|
||||
@Override
|
||||
public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException {
|
||||
public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException, NotLoggedInException {
|
||||
try {
|
||||
callback.processStanza(packet);
|
||||
} finally {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
|
||||
/**
|
||||
|
@ -49,7 +50,8 @@ public interface StanzaListener {
|
|||
* @param packet the stanza(/packet) to process.
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @throws NotLoggedInException
|
||||
*/
|
||||
public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException;
|
||||
public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException, NotLoggedInException;
|
||||
|
||||
}
|
||||
|
|
|
@ -66,7 +66,15 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
|
|||
|
||||
private Type type = Type.available;
|
||||
private String status = null;
|
||||
|
||||
/**
|
||||
* The priority of the presence. The magic value {@link Integer#MIN_VALUE} is used to indicate that the original
|
||||
* presence stanza did not had an explicit priority set. In which case the priority defaults to 0.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3.</a>
|
||||
*/
|
||||
private int priority = Integer.MIN_VALUE;
|
||||
|
||||
private Mode mode = null;
|
||||
|
||||
/**
|
||||
|
@ -201,6 +209,9 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
|
|||
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
|
||||
*/
|
||||
public int getPriority() {
|
||||
if (priority == Integer.MIN_VALUE) {
|
||||
return 0;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
|
|
|
@ -226,9 +226,13 @@ public class XMPPError extends AbstractError {
|
|||
}
|
||||
|
||||
public static XMPPError.Builder from(Condition condition, String descriptiveText) {
|
||||
Map<String, String> descriptiveTexts = new HashMap<String, String>();
|
||||
XMPPError.Builder builder = getBuilder().setCondition(condition);
|
||||
if (descriptiveText != null) {
|
||||
Map<String, String> descriptiveTexts = new HashMap<>();
|
||||
descriptiveTexts.put("en", descriptiveText);
|
||||
return getBuilder().setCondition(condition).setDescriptiveTexts(descriptiveTexts);
|
||||
builder.setDescriptiveTexts(descriptiveTexts);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Builder getBuilder() {
|
||||
|
|
|
@ -97,7 +97,6 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
| 1 | 1 |
|
||||
+----+--------+
|
||||
*/
|
||||
//in.read(buf, 0, 2);
|
||||
fill(in, buf, 2);
|
||||
|
||||
boolean check = false;
|
||||
|
@ -160,7 +159,6 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
`failure' (STATUS value other than X'00') status, it MUST close the
|
||||
connection.
|
||||
*/
|
||||
//in.read(buf, 0, 2);
|
||||
fill(in, buf, 2);
|
||||
if (buf[1] == 0)
|
||||
{
|
||||
|
@ -260,7 +258,6 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
o BND.PORT server bound port in network octet order
|
||||
*/
|
||||
|
||||
//in.read(buf, 0, 4);
|
||||
fill(in, buf, 4);
|
||||
|
||||
if (buf[1] != 0)
|
||||
|
@ -279,17 +276,13 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
|
|||
switch (buf[3] & 0xff)
|
||||
{
|
||||
case 1:
|
||||
//in.read(buf, 0, 6);
|
||||
fill(in, buf, 6);
|
||||
break;
|
||||
case 3:
|
||||
//in.read(buf, 0, 1);
|
||||
fill(in, buf, 1);
|
||||
//in.read(buf, 0, buf[0]+2);
|
||||
fill(in, buf, (buf[0] & 0xff) + 2);
|
||||
break;
|
||||
case 4:
|
||||
//in.read(buf, 0, 18);
|
||||
fill(in, buf, 18);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -272,7 +272,7 @@ public class StringUtils {
|
|||
* array index.
|
||||
*/
|
||||
private static final char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" +
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
|
||||
|
||||
/**
|
||||
* Returns a random String of numbers and letters (lower and upper case)
|
||||
|
@ -288,17 +288,7 @@ public class StringUtils {
|
|||
* @return a random String of numbers and letters of the specified length.
|
||||
*/
|
||||
public static String insecureRandomString(int length) {
|
||||
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[random.nextInt(numbersAndLetters.length)];
|
||||
}
|
||||
return new String(randBuffer);
|
||||
return randomString(length, randGen.get());
|
||||
}
|
||||
|
||||
private static final ThreadLocal<SecureRandom> SECURE_RANDOM = new ThreadLocal<SecureRandom>() {
|
||||
|
@ -309,12 +299,16 @@ public class StringUtils {
|
|||
};
|
||||
|
||||
public static String randomString(final int length) {
|
||||
return randomString(length, SECURE_RANDOM.get());
|
||||
}
|
||||
|
||||
private static String randomString(final int length, Random random) {
|
||||
if (length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] randomBytes = new byte[length];
|
||||
SECURE_RANDOM.get().nextBytes(randomBytes);
|
||||
random.nextBytes(randomBytes);
|
||||
char[] randomChars = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
randomChars[i] = getPrintableChar(randomBytes[i]);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.jivesoftware.smackx.bytestreams.ibb;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
|
@ -65,7 +66,7 @@ class DataListener extends AbstractIqRequestHandler {
|
|||
ibbSession.processIQPacket(data);
|
||||
}
|
||||
}
|
||||
catch (NotConnectedException | InterruptedException e) {
|
||||
catch (NotConnectedException | InterruptedException | NotLoggedInException e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.StanzaListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.filter.AndFilter;
|
||||
|
@ -847,8 +848,9 @@ public class InBandBytestreamSession implements BytestreamSession {
|
|||
* @param data
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @throws NotLoggedInException
|
||||
*/
|
||||
public void processIQPacket(Data data) throws NotConnectedException, InterruptedException {
|
||||
public void processIQPacket(Data data) throws NotConnectedException, InterruptedException, NotLoggedInException {
|
||||
inputStream.dataPacketListener.processStanza(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||
|
@ -766,6 +765,25 @@ public final class ServiceDiscoveryManager extends Manager {
|
|||
*/
|
||||
public List<DiscoverInfo> findServicesDiscoverInfo(String feature, boolean stopOnFirst, boolean useCache)
|
||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||
return findServicesDiscoverInfo(feature, stopOnFirst, useCache, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all services under the users service that provide a given feature.
|
||||
*
|
||||
* @param feature the feature to search for
|
||||
* @param stopOnFirst if true, stop searching after the first service was found
|
||||
* @param useCache if true, query a cache first to avoid network I/O
|
||||
* @param encounteredExceptions an optional map which will be filled with the exceptions encountered
|
||||
* @return a possible empty list of services providing the given feature
|
||||
* @throws NoResponseException
|
||||
* @throws XMPPErrorException
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @since 4.2.2
|
||||
*/
|
||||
public List<DiscoverInfo> findServicesDiscoverInfo(String feature, boolean stopOnFirst, boolean useCache, Map<? super Jid, Exception> encounteredExceptions)
|
||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||
List<DiscoverInfo> serviceDiscoInfo = null;
|
||||
DomainBareJid serviceName = connection().getXMPPServiceDomain();
|
||||
if (useCache) {
|
||||
|
@ -780,8 +798,9 @@ public final class ServiceDiscoveryManager extends Manager {
|
|||
try {
|
||||
info = discoverInfo(serviceName);
|
||||
} catch (XMPPErrorException e) {
|
||||
// Be extra robust here: Return the empty linked list and log this situation
|
||||
LOGGER.log(Level.WARNING, "Could not discover information about service", e);
|
||||
if (encounteredExceptions != null) {
|
||||
encounteredExceptions.put(serviceName, e);
|
||||
}
|
||||
return serviceDiscoInfo;
|
||||
}
|
||||
// Check if the server supports the feature
|
||||
|
@ -800,25 +819,27 @@ public final class ServiceDiscoveryManager extends Manager {
|
|||
// Get the disco items and send the disco packet to each server item
|
||||
items = discoverItems(serviceName);
|
||||
} catch (XMPPErrorException e) {
|
||||
LOGGER.log(Level.WARNING, "Could not discover items about service", e);
|
||||
if (encounteredExceptions != null) {
|
||||
encounteredExceptions.put(serviceName, e);
|
||||
}
|
||||
return serviceDiscoInfo;
|
||||
}
|
||||
for (DiscoverItems.Item item : items.getItems()) {
|
||||
Jid address = item.getEntityID();
|
||||
try {
|
||||
// TODO is it OK here in all cases to query without the node attribute?
|
||||
// MultipleRecipientManager queried initially also with the node attribute, but this
|
||||
// could be simply a fault instead of intentional.
|
||||
info = discoverInfo(item.getEntityID());
|
||||
info = discoverInfo(address);
|
||||
}
|
||||
catch (XMPPErrorException | NoResponseException e) {
|
||||
// Don't throw this exceptions if one of the server's items fail
|
||||
LOGGER.log(Level.WARNING, "Exception while discovering info for feature " + feature
|
||||
+ " of " + item.getEntityID() + " node: " + item.getNode(), e);
|
||||
if (encounteredExceptions != null) {
|
||||
encounteredExceptions.put(address, e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (info.containsFeature(feature)) {
|
||||
serviceDiscoInfo.add(info);
|
||||
//serviceAddresses.add(item.getEntityID().asDomainBareJid());
|
||||
if (stopOnFirst) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ public class OutgoingFileTransfer extends FileTransfer {
|
|||
transferThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//Create packet filter
|
||||
// Create packet filter.
|
||||
try {
|
||||
outputStream = negotiateStream(fileName, fileSize, description);
|
||||
} catch (XMPPErrorException e) {
|
||||
|
|
|
@ -333,7 +333,7 @@ public class MultiUserChat {
|
|||
new NotFilter(MessageWithThreadFilter.INSTANCE))
|
||||
);
|
||||
// @formatter:on
|
||||
connection.addSyncStanzaListener(declinesListener, DECLINE_FILTER);
|
||||
connection.addSyncStanzaListener(declinesListener, new AndFilter(fromRoomFilter, DECLINE_FILTER));
|
||||
connection.addPacketInterceptor(presenceInterceptor, new AndFilter(ToMatchesFilter.create(room),
|
||||
StanzaTypeFilter.PRESENCE));
|
||||
messageCollector = connection.createStanzaCollector(fromRoomGroupchatFilter);
|
||||
|
@ -727,20 +727,19 @@ public class MultiUserChat {
|
|||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized void leave() throws NotConnectedException, InterruptedException {
|
||||
// If not joined already, do nothing.
|
||||
if (!joined) {
|
||||
return;
|
||||
}
|
||||
// Note that this method is intentionally not guarded by
|
||||
// "if (!joined) return" because it should be always be possible to leave the room in case the instance's
|
||||
// state does not reflect the actual state.
|
||||
|
||||
// Reset occupant information first so that we are assume that we left the room even if sendStanza() would
|
||||
// throw.
|
||||
userHasLeft();
|
||||
|
||||
// We leave a room by sending a presence packet where the "to"
|
||||
// field is in the form "roomName@service/nickname"
|
||||
Presence leavePresence = new Presence(Presence.Type.unavailable);
|
||||
leavePresence.setTo(JidCreate.fullFrom(room, nickname));
|
||||
connection.sendStanza(leavePresence);
|
||||
// Reset occupant information.
|
||||
occupantsMap.clear();
|
||||
nickname = null;
|
||||
joined = false;
|
||||
userHasLeft();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -878,12 +877,21 @@ public class MultiUserChat {
|
|||
Destroy destroy = new Destroy(alternateJID, reason);
|
||||
iq.setDestroy(destroy);
|
||||
|
||||
try {
|
||||
connection.createStanzaCollectorAndSend(iq).nextResultOrThrow();
|
||||
}
|
||||
catch (XMPPErrorException e) {
|
||||
// Note that we do not call userHasLeft() here because an XMPPErrorException would usually indicate that the
|
||||
// room was not destroyed and we therefore we also did not leave the room.
|
||||
throw e;
|
||||
}
|
||||
catch (NoResponseException | NotConnectedException | InterruptedException e) {
|
||||
// Reset occupant information.
|
||||
userHasLeft();
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Reset occupant information.
|
||||
occupantsMap.clear();
|
||||
nickname = null;
|
||||
joined = false;
|
||||
userHasLeft();
|
||||
}
|
||||
|
||||
|
@ -2011,6 +2019,7 @@ public class MultiUserChat {
|
|||
private void removeConnectionCallbacks() {
|
||||
connection.removeSyncStanzaListener(messageListener);
|
||||
connection.removeSyncStanzaListener(presenceListener);
|
||||
connection.removeSyncStanzaListener(subjectListener);
|
||||
connection.removeSyncStanzaListener(declinesListener);
|
||||
connection.removePacketInterceptor(presenceInterceptor);
|
||||
if (messageCollector != null) {
|
||||
|
@ -2023,6 +2032,11 @@ public class MultiUserChat {
|
|||
* Remove all callbacks and resources necessary when the user has left the room for some reason.
|
||||
*/
|
||||
private synchronized void userHasLeft() {
|
||||
// We do not reset nickname here, in case this method has been called erroneously, it should still be possible
|
||||
// to call leave() in order to resync the state. And leave() requires the nickname to send the unsubscribe
|
||||
// presence.
|
||||
occupantsMap.clear();
|
||||
joined = false;
|
||||
// Update the list of joined rooms
|
||||
multiUserChatManager.removeJoinedRoom(room);
|
||||
removeConnectionCallbacks();
|
||||
|
@ -2340,15 +2354,12 @@ public class MultiUserChat {
|
|||
if (statusCodes.contains(Status.KICKED_307)) {
|
||||
// Check if this occupant was kicked
|
||||
if (isUserModification) {
|
||||
joined = false;
|
||||
// Reset occupant information.
|
||||
userHasLeft();
|
||||
|
||||
for (UserStatusListener listener : userStatusListeners) {
|
||||
listener.kicked(mucUser.getItem().getActor(), mucUser.getItem().getReason());
|
||||
}
|
||||
|
||||
// Reset occupant information.
|
||||
occupantsMap.clear();
|
||||
nickname = null;
|
||||
userHasLeft();
|
||||
}
|
||||
else {
|
||||
for (ParticipantStatusListener listener : participantStatusListeners) {
|
||||
|
@ -2397,7 +2408,7 @@ public class MultiUserChat {
|
|||
listener.nicknameChanged(from, mucUser.getItem().getNick());
|
||||
}
|
||||
}
|
||||
//The room has been destroyed
|
||||
// The room has been destroyed.
|
||||
if (mucUser.getDestroy() != null) {
|
||||
MultiUserChat alternateMUC = multiUserChatManager.getMultiUserChat(mucUser.getDestroy().getJid());
|
||||
for (UserStatusListener listener : userStatusListeners) {
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.jivesoftware.smackx.offline;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
|
@ -58,6 +60,8 @@ import org.jivesoftware.smackx.xdata.Form;
|
|||
*/
|
||||
public class OfflineMessageManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(OfflineMessageManager.class.getName());
|
||||
|
||||
private final static String namespace = "http://jabber.org/protocol/offline";
|
||||
|
||||
private final XMPPConnection connection;
|
||||
|
@ -144,7 +148,7 @@ public class OfflineMessageManager {
|
|||
* @throws InterruptedException
|
||||
*/
|
||||
public List<Message> getMessages(final List<String> nodes) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
List<Message> messages = new ArrayList<Message>(nodes.size());
|
||||
OfflineMessageRequest request = new OfflineMessageRequest();
|
||||
for (String node : nodes) {
|
||||
OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
|
||||
|
@ -165,12 +169,17 @@ public class OfflineMessageManager {
|
|||
try {
|
||||
connection.createStanzaCollectorAndSend(request).nextResultOrThrow();
|
||||
// Collect the received offline messages
|
||||
Message message = messageCollector.nextResult();
|
||||
while (message != null && pendingNodes > 0) {
|
||||
pendingNodes--;
|
||||
messages.add(message);
|
||||
Message message;
|
||||
do {
|
||||
message = messageCollector.nextResult();
|
||||
if (message != null) {
|
||||
messages.add(message);
|
||||
pendingNodes--;
|
||||
} else if (message == null && pendingNodes > 0) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Did not receive all expected offline messages. " + pendingNodes + " are missing.");
|
||||
}
|
||||
} while (message != null && pendingNodes > 0);
|
||||
}
|
||||
finally {
|
||||
// Stop queuing offline messages
|
||||
|
|
|
@ -50,7 +50,7 @@ public class DataFormTest {
|
|||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
//Build a Form
|
||||
// Build a Form.
|
||||
DataForm df = new DataForm(DataForm.Type.submit);
|
||||
String instruction = "InstructionTest1";
|
||||
df.addInstruction(instruction);
|
||||
|
@ -77,7 +77,7 @@ public class DataFormTest {
|
|||
|
||||
@Test
|
||||
public void testLayout() throws Exception {
|
||||
//Build a Form
|
||||
// Build a Form.
|
||||
DataForm df = new DataForm(DataForm.Type.submit);
|
||||
String instruction = "InstructionTest1";
|
||||
df.addInstruction(instruction);
|
||||
|
@ -119,7 +119,7 @@ public class DataFormTest {
|
|||
|
||||
@Test
|
||||
public void testValidation() throws Exception {
|
||||
//Build a Form
|
||||
// Build a Form.
|
||||
DataForm df = new DataForm(DataForm.Type.submit);
|
||||
String instruction = "InstructionTest1";
|
||||
df.addInstruction(instruction);
|
||||
|
|
|
@ -251,9 +251,10 @@ public final class Roster extends Manager {
|
|||
connection.addSyncStanzaListener(presencePacketListener, PRESENCE_PACKET_FILTER);
|
||||
|
||||
connection.addAsyncStanzaListener(new StanzaListener() {
|
||||
@SuppressWarnings("fallthrough")
|
||||
@Override
|
||||
public void processStanza(Stanza stanza) throws NotConnectedException,
|
||||
InterruptedException {
|
||||
InterruptedException, NotLoggedInException {
|
||||
Presence presence = (Presence) stanza;
|
||||
Jid from = presence.getFrom();
|
||||
SubscribeAnswer subscribeAnswer = null;
|
||||
|
@ -279,13 +280,26 @@ public final class Roster extends Manager {
|
|||
break;
|
||||
}
|
||||
|
||||
if (subscribeAnswer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Presence response;
|
||||
if (subscribeAnswer == SubscribeAnswer.Approve) {
|
||||
switch (subscribeAnswer) {
|
||||
case ApproveAndAlsoRequestIfRequired:
|
||||
BareJid bareFrom = from.asBareJid();
|
||||
RosterUtil.askForSubscriptionIfRequired(Roster.this, bareFrom);
|
||||
// The fall through is intended.
|
||||
case Approve:
|
||||
response = new Presence(Presence.Type.subscribed);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
case Deny:
|
||||
response = new Presence(Presence.Type.unsubscribed);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
response.setTo(presence.getFrom());
|
||||
connection.sendStanza(response);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.concurrent.locks.Condition;
|
|||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
|
@ -86,6 +87,33 @@ public class RosterUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-approve the subscription if it is required and possible.
|
||||
*
|
||||
* @param roster The roster which should be used for the pre-approval.
|
||||
* @param jid The XMPP address which should be pre-approved.
|
||||
* @throws NotLoggedInException
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @since 4.2.2
|
||||
*/
|
||||
public static void preApproveSubscriptionIfRequiredAndPossible(Roster roster, BareJid jid)
|
||||
throws NotLoggedInException, NotConnectedException, InterruptedException {
|
||||
if (!roster.isSubscriptionPreApprovalSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RosterEntry entry = roster.getEntry(jid);
|
||||
if (entry == null || (!entry.canSeeMyPresence() && !entry.isApproved())) {
|
||||
try {
|
||||
roster.preApprove(jid);
|
||||
} catch (FeatureNotSupportedException e) {
|
||||
// Should never happen since we checked for the feature above.
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void askForSubscriptionIfRequired(Roster roster, BareJid jid)
|
||||
throws NotLoggedInException, NotConnectedException, InterruptedException {
|
||||
RosterEntry entry = roster.getEntry(jid);
|
||||
|
|
|
@ -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.
|
||||
|
@ -23,13 +23,29 @@ import org.jxmpp.jid.Jid;
|
|||
|
||||
|
||||
/**
|
||||
* Handle incoming requests to subscribe to our presence.
|
||||
* Handle incoming requests to subscribe to our presence. The
|
||||
* {@link #processSubscribe(Jid, Presence)} method may return a subscribe
|
||||
* answer. If no subscribe answer is returned, the next listener will be
|
||||
* notified and asked. If no listener returns an answer, then nothing happens.
|
||||
*
|
||||
*/
|
||||
public interface SubscribeListener {
|
||||
|
||||
public enum SubscribeAnswer {
|
||||
/**
|
||||
* Approve the subscription request.
|
||||
*/
|
||||
Approve,
|
||||
|
||||
/**
|
||||
* Approve the subscription request and also request subscription from the other party if not already subscribed.
|
||||
* @since 4.2.2
|
||||
*/
|
||||
ApproveAndAlsoRequestIfRequired,
|
||||
|
||||
/**
|
||||
* Deny the subscription request.
|
||||
*/
|
||||
Deny,
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
EntityBareJid userOne = conOne.getUser().asEntityBareJid();
|
||||
EntityBareJid userTwo = conTwo.getUser().asEntityBareJid();
|
||||
|
||||
//Make sure MAM is archiving messages
|
||||
// Make sure MAM is archiving messages.
|
||||
mamManagerConTwo.updateArchivingPreferences(null, null, MamPrefsIQ.DefaultBehavior.always);
|
||||
|
||||
Message message = new Message(userTwo);
|
||||
|
|
|
@ -117,7 +117,7 @@ public class JMFInit extends Frame implements Runnable {
|
|||
throw td;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
//Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
Class<?> jsauto;
|
||||
|
@ -226,7 +226,7 @@ public class JMFInit extends Frame implements Runnable {
|
|||
}
|
||||
}
|
||||
catch (Throwable tt) {
|
||||
//Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ public class JMFInit extends Frame implements Runnable {
|
|||
}
|
||||
}
|
||||
catch (Throwable tt) {
|
||||
//Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -359,7 +359,7 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
if (candidate instanceof ICECandidate) {
|
||||
ICECandidate iceCandidate = (ICECandidate) candidate;
|
||||
if (iceCandidate.getType().equals(Type.relay)) {
|
||||
//TODO Check if the relay is reacheable
|
||||
// TODO Check if the relay is reacheable.
|
||||
addValidRemoteCandidate(iceCandidate);
|
||||
foundRemoteRelay = true;
|
||||
}
|
||||
|
|
|
@ -317,7 +317,7 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
|
|||
preKeys.put(Integer.parseInt(f.getName()), p);
|
||||
|
||||
} catch (IOException e) {
|
||||
//Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
return preKeys;
|
||||
|
@ -355,7 +355,7 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
|
|||
signedPreKeys.put(Integer.parseInt(f.getName()), p);
|
||||
|
||||
} catch (IOException e) {
|
||||
//Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
return signedPreKeys;
|
||||
|
@ -414,7 +414,7 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
|
|||
sessions.put(id, s);
|
||||
|
||||
} catch (IOException e) {
|
||||
//Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
return sessions;
|
||||
|
|
|
@ -81,6 +81,7 @@ import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
|
|||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException;
|
||||
import org.jivesoftware.smack.StanzaListener;
|
||||
import org.jivesoftware.smack.SynchronizationPoint;
|
||||
|
@ -309,7 +310,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
addConnectionListener(new AbstractConnectionListener() {
|
||||
@Override
|
||||
public void connectionClosedOnError(Exception e) {
|
||||
if (e instanceof XMPPException.StreamErrorException) {
|
||||
if (e instanceof XMPPException.StreamErrorException || e instanceof StreamManagementException) {
|
||||
dropSmState();
|
||||
}
|
||||
}
|
||||
|
@ -1146,8 +1147,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
if (!smSessionId.equals(resumed.getPrevId())) {
|
||||
throw new StreamIdDoesNotMatchException(smSessionId, resumed.getPrevId());
|
||||
}
|
||||
// Mark SM as enabled and resumption as successful.
|
||||
smResumedSyncPoint.reportSuccess();
|
||||
// Mark SM as enabled
|
||||
smEnabledSyncPoint.reportSuccess();
|
||||
// First, drop the stanzas already handled by the server
|
||||
processHandledCount(resumed.getHandledCount());
|
||||
|
@ -1163,6 +1163,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
if (!stanzasToResend.isEmpty()) {
|
||||
requestSmAcknowledgementInternal();
|
||||
}
|
||||
// Mark SM resumption as successful
|
||||
smResumedSyncPoint.reportSuccess();
|
||||
LOGGER.fine("Stream Management (XEP-198): Stream resumed");
|
||||
break;
|
||||
case AckAnswer.ELEMENT:
|
||||
|
@ -1183,7 +1185,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
if (parser.getName().equals("stream")) {
|
||||
final String endTagName = parser.getName();
|
||||
if ("stream".equals(endTagName)) {
|
||||
if (!parser.getNamespace().equals("http://etherx.jabber.org/streams")) {
|
||||
LOGGER.warning(XMPPTCPConnection.this + " </stream> but different namespace " + parser.getNamespace());
|
||||
break;
|
||||
|
@ -1879,7 +1882,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
try {
|
||||
listener.processStanza(ackedStanza);
|
||||
}
|
||||
catch (InterruptedException | NotConnectedException e) {
|
||||
catch (InterruptedException | NotConnectedException | NotLoggedInException e) {
|
||||
LOGGER.log(Level.FINER, "Received exception", e);
|
||||
}
|
||||
}
|
||||
|
@ -1892,7 +1895,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
try {
|
||||
listener.processStanza(ackedStanza);
|
||||
}
|
||||
catch (InterruptedException | NotConnectedException e) {
|
||||
catch (InterruptedException | NotConnectedException | NotLoggedInException e) {
|
||||
LOGGER.log(Level.FINER, "Received exception", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ allprojects {
|
|||
ext {
|
||||
shortVersion = '4.3.0-alpha1'
|
||||
isSnapshot = true
|
||||
jxmppVersion = '0.5.0'
|
||||
jxmppVersion = '0.6.0'
|
||||
smackMinAndroidSdk = 8
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue