Merge branch '4.2'

This commit is contained in:
Florian Schmaus 2017-11-22 08:37:47 +01:00
commit 81002c4fbd
63 changed files with 391 additions and 266 deletions

View File

@ -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',

View File

@ -60,6 +60,10 @@
<property name="format" value="^\s*[\S&amp;&amp;[^\*/]]+\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"/>

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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:

View File

@ -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]);

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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,
}

View File

@ -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);

View File

@ -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.
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -2,7 +2,7 @@ allprojects {
ext {
shortVersion = '4.3.0-alpha1'
isSnapshot = true
jxmppVersion = '0.5.0'
jxmppVersion = '0.6.0'
smackMinAndroidSdk = 8
}
}