mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-24 23:32:05 +01:00
Smack 4.3.3
-----BEGIN PGP SIGNATURE----- iQGTBAABCgB9FiEEl3UFnzoh3OFr5PuuIjmn6PWFIFIFAlyKV9tfFIAAAAAALgAo aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDk3 NzUwNTlGM0EyMURDRTE2QkU0RkJBRTIyMzlBN0U4RjU4NTIwNTIACgkQIjmn6PWF IFL4vQf/Qfg3VzNEnmk0+KjOtuvfAbhMfzE92gfo15vE0PPEIe9VA0Pzkvqhva4k Efw7BhD2zx8hWvo0d5FfkdII89hSYnOCiSmhiX1Ln9q/gUqFW0TDAKpsMfAl7jAK Fap8M7uUStP9T6fF/gq01djYCYoWA/4v1lGKv4J4b9gWCqzGIF2sK0M7of7VERnr pEXJSUM228rk6EYjVmX/9Ujo1Y+xaNMFFZZSbKYrirAqjP540v0OPBCRQyB8qFaI NTcNJ+qESe6Q80mw5V+y/kD6kX0LERSDB+pigzOOOlmfMtD1uADR84tAynuTKNAU 7/5K/YSQxceRY6RTgD17Al5lHIH5nw== =ToR+ -----END PGP SIGNATURE----- Merge tag '4.3.3' Smack 4.3.3
This commit is contained in:
commit
daab6039a1
15 changed files with 250 additions and 63 deletions
|
@ -141,6 +141,24 @@ hr {
|
||||||
|
|
||||||
<div id="pageBody">
|
<div id="pageBody">
|
||||||
|
|
||||||
|
<h2>4.3.3 -- <span style="font-weight: normal;">2019-03-14</span></h2>
|
||||||
|
|
||||||
|
<h2> Bug
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-856'>SMACK-856</a>] - Smack fails under JDK 11 because com.sun.jndi.dns.DnsContextFactory is not inaccessible
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2> Improvement
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-858'>SMACK-858</a>] - Dependency version specifier of jxmpp and MiniDNS include alpha/beta/... versions of the follow up version when Maven is used
|
||||||
|
</li>
|
||||||
|
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-859'>SMACK-859</a>] - MultiUserChat enter() should reset the timeout of the collector waiting for the final self presence to prevent timeouts for large MUCs
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>4.3.2 -- <span style="font-weight: normal;">2019-02-22</span></h2>
|
<h2>4.3.2 -- <span style="font-weight: normal;">2019-02-22</span></h2>
|
||||||
|
|
||||||
<h2> Bug
|
<h2> Bug
|
||||||
|
|
|
@ -266,6 +266,11 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
||||||
client = null;
|
client = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instantShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void instantShutdown() {
|
||||||
setWasAuthenticated();
|
setWasAuthenticated();
|
||||||
sessionID = null;
|
sessionID = null;
|
||||||
done = true;
|
done = true;
|
||||||
|
|
|
@ -111,6 +111,7 @@ import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.NonzaProvider;
|
import org.jivesoftware.smack.provider.NonzaProvider;
|
||||||
import org.jivesoftware.smack.provider.ProviderManager;
|
import org.jivesoftware.smack.provider.ProviderManager;
|
||||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||||
|
import org.jivesoftware.smack.util.Async;
|
||||||
import org.jivesoftware.smack.util.DNSUtil;
|
import org.jivesoftware.smack.util.DNSUtil;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
|
@ -357,6 +358,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
*/
|
*/
|
||||||
protected boolean authenticated = false;
|
protected boolean authenticated = false;
|
||||||
|
|
||||||
|
// TODO: Migrate to ZonedDateTime once Smack's minimum required Android SDK level is 26 (8.0, Oreo) or higher.
|
||||||
|
protected long authenticatedConnectionInitiallyEstablishedTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag that indicates if the user was authenticated with the server when the connection
|
* Flag that indicates if the user was authenticated with the server when the connection
|
||||||
* to the server was closed (abruptly or not).
|
* to the server was closed (abruptly or not).
|
||||||
|
@ -449,6 +453,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
@Override
|
@Override
|
||||||
public abstract boolean isUsingCompression();
|
public abstract boolean isUsingCompression();
|
||||||
|
|
||||||
|
protected void initState() {
|
||||||
|
saslFeatureReceived.init();
|
||||||
|
lastFeaturesReceived.init();
|
||||||
|
tlsHandled.init();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a connection to the XMPP server. It basically
|
* Establishes a connection to the XMPP server. It basically
|
||||||
* creates and maintains a connection to the server.
|
* creates and maintains a connection to the server.
|
||||||
|
@ -467,21 +477,23 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
throwAlreadyConnectedExceptionIfAppropriate();
|
throwAlreadyConnectedExceptionIfAppropriate();
|
||||||
|
|
||||||
// Reset the connection state
|
// Reset the connection state
|
||||||
|
initState();
|
||||||
saslAuthentication.init();
|
saslAuthentication.init();
|
||||||
saslFeatureReceived.init();
|
|
||||||
lastFeaturesReceived.init();
|
|
||||||
tlsHandled.init();
|
|
||||||
streamId = null;
|
streamId = null;
|
||||||
|
|
||||||
// Perform the actual connection to the XMPP service
|
try {
|
||||||
connectInternal();
|
// Perform the actual connection to the XMPP service
|
||||||
|
connectInternal();
|
||||||
|
|
||||||
// If TLS is required but the server doesn't offer it, disconnect
|
// If TLS is required but the server doesn't offer it, disconnect
|
||||||
// from the server and throw an error. First check if we've already negotiated TLS
|
// from the server and throw an error. First check if we've already negotiated TLS
|
||||||
// and are secure, however (features get parsed a second time after TLS is established).
|
// and are secure, however (features get parsed a second time after TLS is established).
|
||||||
if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
|
if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
|
||||||
shutdown();
|
throw new SecurityRequiredByClientException();
|
||||||
throw new SecurityRequiredByClientException();
|
}
|
||||||
|
} catch (SmackException | IOException | XMPPException | InterruptedException e) {
|
||||||
|
instantShutdown();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make note of the fact that we're now connected.
|
// Make note of the fact that we're now connected.
|
||||||
|
@ -652,6 +664,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException, InterruptedException {
|
protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException, InterruptedException {
|
||||||
|
if (!resumed) {
|
||||||
|
authenticatedConnectionInitiallyEstablishedTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
// Indicate that we're now authenticated.
|
// Indicate that we're now authenticated.
|
||||||
this.authenticated = true;
|
this.authenticated = true;
|
||||||
|
|
||||||
|
@ -834,36 +849,46 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
*
|
*
|
||||||
* @param exception the exception that causes the connection close event.
|
* @param exception the exception that causes the connection close event.
|
||||||
*/
|
*/
|
||||||
protected final synchronized void notifyConnectionError(Exception exception) {
|
protected final void notifyConnectionError(final Exception exception) {
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
LOGGER.log(Level.INFO, "Connection was already disconnected when attempting to handle " + exception,
|
LOGGER.log(Level.INFO, "Connection was already disconnected when attempting to handle " + exception,
|
||||||
exception);
|
exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentConnectionException = exception;
|
ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> {
|
||||||
notifyAll();
|
currentConnectionException = exception;
|
||||||
|
synchronized (AbstractXMPPConnection.this) {
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
for (StanzaCollector collector : collectors) {
|
for (StanzaCollector collector : collectors) {
|
||||||
collector.notifyConnectionError(exception);
|
collector.notifyConnectionError(exception);
|
||||||
}
|
}
|
||||||
SmackWrappedException smackWrappedException = new SmackWrappedException(exception);
|
SmackWrappedException smackWrappedException = new SmackWrappedException(exception);
|
||||||
tlsHandled.reportGenericFailure(smackWrappedException);
|
tlsHandled.reportGenericFailure(smackWrappedException);
|
||||||
saslFeatureReceived.reportGenericFailure(smackWrappedException);
|
saslFeatureReceived.reportGenericFailure(smackWrappedException);
|
||||||
lastFeaturesReceived.reportGenericFailure(smackWrappedException);
|
lastFeaturesReceived.reportGenericFailure(smackWrappedException);
|
||||||
|
// TODO From XMPPTCPConnection. Was called in Smack 4.3 where notifyConnectionError() was part of
|
||||||
|
// XMPPTCPConnection. Create delegation method?
|
||||||
|
// maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException);
|
||||||
|
|
||||||
// Closes the connection temporary. A if the connection supports stream management, then a reconnection is
|
// Closes the connection temporary. A if the connection supports stream management, then a reconnection is
|
||||||
// possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in
|
// possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in
|
||||||
// case the Exception is a StreamErrorException.
|
// case the Exception is a StreamErrorException.
|
||||||
instantShutdown();
|
instantShutdown();
|
||||||
|
|
||||||
callConnectionClosedOnErrorListener(exception);
|
Async.go(() -> {
|
||||||
|
// Notify connection listeners of the error.
|
||||||
|
callConnectionClosedOnErrorListener(exception);
|
||||||
|
}, AbstractXMPPConnection.this + " callConnectionClosedOnErrorListener()");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void instantShutdown() {
|
/**
|
||||||
// Default implementation simply calls shutdown(), subclasses may override this.
|
* Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza.
|
||||||
shutdown();
|
*/
|
||||||
}
|
public abstract void instantShutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts the current connection down.
|
* Shuts the current connection down.
|
||||||
|
@ -1811,6 +1836,18 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
return lastStanzaReceived;
|
return lastStanzaReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp when the connection was the first time authenticated, i.e., when the first successful login was
|
||||||
|
* performed. Note that this value is not reset on disconnect, so it represents the timestamp from the last
|
||||||
|
* authenticated connection. The value is also not reset on stream resumption.
|
||||||
|
*
|
||||||
|
* @return the timestamp or {@code null}.
|
||||||
|
* @since 4.3.3
|
||||||
|
*/
|
||||||
|
public final long getAuthenticatedConnectionInitiallyEstablishedTimestamp() {
|
||||||
|
return authenticatedConnectionInitiallyEstablishedTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a
|
* Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a
|
||||||
* stanza.
|
* stanza.
|
||||||
|
|
|
@ -93,17 +93,24 @@ public abstract class SmackException extends Exception {
|
||||||
return new NoResponseException(sb.toString());
|
return new NoResponseException(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NoResponseException newWith(XMPPConnection connection,
|
@Deprecated
|
||||||
|
// TODO: Remove in Smack 4.4.
|
||||||
|
public static NoResponseException newWith(long timeout,
|
||||||
|
StanzaCollector collector) {
|
||||||
|
return newWith(timeout, collector.getStanzaFilter(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NoResponseException newWith(long timeout,
|
||||||
StanzaCollector collector, boolean stanzaCollectorCancelled) {
|
StanzaCollector collector, boolean stanzaCollectorCancelled) {
|
||||||
return newWith(connection, collector.getStanzaFilter(), stanzaCollectorCancelled);
|
return newWith(timeout, collector.getStanzaFilter(), stanzaCollectorCancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
|
public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
|
||||||
return newWith(connection, filter, false);
|
return newWith(connection.getReplyTimeout(), filter, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter, boolean stanzaCollectorCancelled) {
|
public static NoResponseException newWith(long timeout, StanzaFilter filter, boolean stanzaCollectorCancelled) {
|
||||||
final StringBuilder sb = getWaitingFor(connection);
|
final StringBuilder sb = getWaitingFor(timeout);
|
||||||
if (stanzaCollectorCancelled) {
|
if (stanzaCollectorCancelled) {
|
||||||
sb.append(" StanzaCollector has been cancelled.");
|
sb.append(" StanzaCollector has been cancelled.");
|
||||||
}
|
}
|
||||||
|
@ -119,7 +126,10 @@ public abstract class SmackException extends Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StringBuilder getWaitingFor(XMPPConnection connection) {
|
private static StringBuilder getWaitingFor(XMPPConnection connection) {
|
||||||
final long replyTimeout = connection.getReplyTimeout();
|
return getWaitingFor(connection.getReplyTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringBuilder getWaitingFor(final long replyTimeout) {
|
||||||
final StringBuilder sb = new StringBuilder(256);
|
final StringBuilder sb = new StringBuilder(256);
|
||||||
sb.append("No response received within reply timeout. Timeout was "
|
sb.append("No response received within reply timeout. Timeout was "
|
||||||
+ replyTimeout + "ms (~"
|
+ replyTimeout + "ms (~"
|
||||||
|
|
|
@ -290,7 +290,7 @@ public class StanzaCollector implements AutoCloseable {
|
||||||
if (!connection.isConnected()) {
|
if (!connection.isConnected()) {
|
||||||
throw new NotConnectedException(connection, packetFilter);
|
throw new NotConnectedException(connection, packetFilter);
|
||||||
}
|
}
|
||||||
throw NoResponseException.newWith(connection, this, cancelled);
|
throw NoResponseException.newWith(timeout, this, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
XMPPErrorException.ifHasErrorThenThrow(result);
|
XMPPErrorException.ifHasErrorThenThrow(result);
|
||||||
|
|
|
@ -198,6 +198,12 @@ public class StanzaError extends AbstractError implements ExtensionElement {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder("XMPPError: ");
|
StringBuilder sb = new StringBuilder("XMPPError: ");
|
||||||
sb.append(condition.toString()).append(" - ").append(type.toString());
|
sb.append(condition.toString()).append(" - ").append(type.toString());
|
||||||
|
|
||||||
|
String descriptiveText = getDescriptiveText();
|
||||||
|
if (descriptiveText != null) {
|
||||||
|
sb.append(" [").append(descriptiveText).append(']');
|
||||||
|
}
|
||||||
|
|
||||||
if (errorGenerator != null) {
|
if (errorGenerator != null) {
|
||||||
sb.append(". Generated by ").append(errorGenerator);
|
sb.append(". Generated by ").append(errorGenerator);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,11 @@ public class DummyConnection extends AbstractXMPPConnection {
|
||||||
callConnectionClosedListener();
|
callConnectionClosedListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void instantShutdown() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSecureConnection() {
|
public boolean isSecureConnection() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -226,4 +231,5 @@ public class DummyConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||||
import org.jivesoftware.smack.filter.AndFilter;
|
import org.jivesoftware.smack.filter.AndFilter;
|
||||||
import org.jivesoftware.smack.filter.MessageTypeFilter;
|
import org.jivesoftware.smack.filter.MessageTypeFilter;
|
||||||
import org.jivesoftware.smack.filter.NotFilter;
|
import org.jivesoftware.smack.filter.NotFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
|
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
import org.jivesoftware.smack.filter.ToTypeFilter;
|
import org.jivesoftware.smack.filter.ToTypeFilter;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
@ -46,13 +45,12 @@ public final class StableUniqueStanzaIdManager extends Manager {
|
||||||
MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
|
MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
|
||||||
ToTypeFilter.ENTITY_FULL_OR_BARE_JID);
|
ToTypeFilter.ENTITY_FULL_OR_BARE_JID);
|
||||||
|
|
||||||
private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
|
|
||||||
|
|
||||||
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
|
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
|
||||||
private final StanzaListener stanzaListener = new StanzaListener() {
|
private static final StanzaListener ADD_ORIGIN_ID_INTERCEPTOR = new StanzaListener() {
|
||||||
@Override
|
@Override
|
||||||
public void processStanza(Stanza stanza) {
|
public void processStanza(Stanza stanza) {
|
||||||
OriginIdElement.addOriginId((Message) stanza);
|
Message message = (Message) stanza;
|
||||||
|
OriginIdElement.addOriginId(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,7 +93,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
|
||||||
public synchronized void enable() {
|
public synchronized void enable() {
|
||||||
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
|
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
|
||||||
StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER));
|
StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER));
|
||||||
connection().addStanzaInterceptor(stanzaListener, filter);
|
connection().addStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +101,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
|
||||||
*/
|
*/
|
||||||
public synchronized void disable() {
|
public synchronized void disable() {
|
||||||
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
|
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
|
||||||
connection().removeStanzaInterceptor(stanzaListener);
|
connection().removeStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,7 +24,11 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> {
|
public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> {
|
||||||
|
|
||||||
public static final OriginIdProvider TEST_INSTANCE = new OriginIdProvider();
|
public static final OriginIdProvider INSTANCE = new OriginIdProvider();
|
||||||
|
|
||||||
|
// TODO: Remove in Smack 4.4.
|
||||||
|
@Deprecated
|
||||||
|
public static final OriginIdProvider TEST_INSTANCE = INSTANCE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OriginIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {
|
public OriginIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {
|
||||||
|
|
|
@ -24,7 +24,11 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> {
|
public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> {
|
||||||
|
|
||||||
public static StanzaIdProvider TEST_INSTANCE = new StanzaIdProvider();
|
public static final StanzaIdProvider INSTANCE = new StanzaIdProvider();
|
||||||
|
|
||||||
|
// TODO: Remove in Smack 4.4.
|
||||||
|
@Deprecated
|
||||||
|
public static final StanzaIdProvider TEST_INSTANCE = INSTANCE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StanzaIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {
|
public StanzaIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smack.test.util.TestUtils;
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
||||||
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
|
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
|
||||||
import org.jivesoftware.smackx.sid.provider.OriginIdProvider;
|
import org.jivesoftware.smackx.sid.provider.OriginIdProvider;
|
||||||
|
@ -42,7 +43,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
|
||||||
assertEquals("alice@wonderland.lit", element.getBy());
|
assertEquals("alice@wonderland.lit", element.getBy());
|
||||||
assertXMLEqual(xml, element.toXML().toString());
|
assertXMLEqual(xml, element.toXML().toString());
|
||||||
|
|
||||||
StanzaIdElement parsed = StanzaIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
|
StanzaIdElement parsed = StanzaIdProvider.INSTANCE.parse(TestUtils.getParser(xml));
|
||||||
assertEquals(element.getId(), parsed.getId());
|
assertEquals(element.getId(), parsed.getId());
|
||||||
assertEquals(element.getBy(), parsed.getBy());
|
assertEquals(element.getBy(), parsed.getBy());
|
||||||
}
|
}
|
||||||
|
@ -54,7 +55,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
|
||||||
assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId());
|
assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId());
|
||||||
assertXMLEqual(xml, element.toXML().toString());
|
assertXMLEqual(xml, element.toXML().toString());
|
||||||
|
|
||||||
OriginIdElement parsed = OriginIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
|
OriginIdElement parsed = OriginIdProvider.INSTANCE.parse(TestUtils.getParser(xml));
|
||||||
assertEquals(element.getId(), parsed.getId());
|
assertEquals(element.getId(), parsed.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,4 +82,17 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
|
||||||
assertTrue(StanzaIdElement.hasStanzaId(message));
|
assertTrue(StanzaIdElement.hasStanzaId(message));
|
||||||
assertEquals(stanzaId, StanzaIdElement.getStanzaId(message));
|
assertEquals(stanzaId, StanzaIdElement.getStanzaId(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleUssidExtensions() throws Exception {
|
||||||
|
String message = "<message xmlns='jabber:client' from='e4aec989-3e20-4846-83bf-f50df89b5d07@muclight.example.com/user1@example.com' to='user1@example.com' id='6b71fe3a-3cb2-489c-9c8e-b6879761d15e' type='groupchat'>" +
|
||||||
|
"<body>Test message</body>" +
|
||||||
|
"<markable xmlns='urn:xmpp:chat-markers:0'/>" +
|
||||||
|
"<stanza-id by='e4aec989-3e20-4846-83bf-f50df89b5d07@muclight.example.com' id='B0KK24ETVC81' xmlns='urn:xmpp:sid:0'/>" +
|
||||||
|
"<stanza-id by='user1@example.com' id='B0KK24EV89G1' xmlns='urn:xmpp:sid:0'/>" +
|
||||||
|
"</message>";
|
||||||
|
Message messageStanza = PacketParserUtils.parseStanza(message);
|
||||||
|
|
||||||
|
assertTrue(StanzaIdElement.hasStanzaId(messageStanza));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,9 +341,10 @@ public class MultiUserChat {
|
||||||
|
|
||||||
// Setup the messageListeners and presenceListeners *before* the join presence is send.
|
// Setup the messageListeners and presenceListeners *before* the join presence is send.
|
||||||
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
|
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
|
||||||
connection.addSyncStanzaListener(presenceListener, new AndFilter(fromRoomFilter,
|
StanzaFilter presenceFromRoomFilter = new AndFilter(fromRoomFilter,
|
||||||
StanzaTypeFilter.PRESENCE,
|
StanzaTypeFilter.PRESENCE,
|
||||||
PossibleFromTypeFilter.ENTITY_FULL_JID));
|
PossibleFromTypeFilter.ENTITY_FULL_JID);
|
||||||
|
connection.addSyncStanzaListener(presenceListener, presenceFromRoomFilter);
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
connection.addSyncStanzaListener(subjectListener,
|
connection.addSyncStanzaListener(subjectListener,
|
||||||
new AndFilter(fromRoomFilter,
|
new AndFilter(fromRoomFilter,
|
||||||
|
@ -372,15 +373,27 @@ public class MultiUserChat {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
StanzaCollector presenceStanzaCollector = null;
|
||||||
Presence presence;
|
Presence presence;
|
||||||
try {
|
try {
|
||||||
presence = connection.createStanzaCollectorAndSend(responseFilter, joinPresence).nextResultOrThrow(conf.getTimeout());
|
// This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC.
|
||||||
|
StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence);
|
||||||
|
StanzaCollector.Configuration presenceStanzaCollectorConfguration = StanzaCollector.newConfiguration().setCollectorToReset(
|
||||||
|
selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter);
|
||||||
|
// This stanza collector is used to reset the timeout of the selfPresenceCollector.
|
||||||
|
presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration);
|
||||||
|
presence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout());
|
||||||
}
|
}
|
||||||
catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
|
catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
|
||||||
// Ensure that all callbacks are removed if there is an exception
|
// Ensure that all callbacks are removed if there is an exception
|
||||||
removeConnectionCallbacks();
|
removeConnectionCallbacks();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
if (presenceStanzaCollector != null) {
|
||||||
|
presenceStanzaCollector.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may
|
// This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may
|
||||||
// performed roomnick rewriting
|
// performed roomnick rewriting
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.jivesoftware.smack.util.dns.javax;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ import org.minidns.dnsname.DnsName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace.
|
* A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace.
|
||||||
|
* Note that using JavaxResovler requires applications using newer Java versions (at least 11) to declare a dependency on the "sun.jdk" module.
|
||||||
*
|
*
|
||||||
* @author Florian Schmaus
|
* @author Florian Schmaus
|
||||||
*
|
*
|
||||||
|
@ -51,7 +53,9 @@ public class JavaxResolver extends DNSResolver implements SmackInitializer {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
dirContext = new InitialDirContext();
|
Hashtable<String, String> env = new Hashtable<>();
|
||||||
|
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
|
||||||
|
dirContext = new InitialDirContext(env);
|
||||||
} catch (NamingException e) {
|
} catch (NamingException e) {
|
||||||
LOGGER.log(Level.SEVERE, "Could not construct InitialDirContext", e);
|
LOGGER.log(Level.SEVERE, "Could not construct InitialDirContext", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,6 +257,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
*/
|
*/
|
||||||
private final Collection<StanzaListener> stanzaAcknowledgedListeners = new ConcurrentLinkedQueue<>();
|
private final Collection<StanzaListener> stanzaAcknowledgedListeners = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These listeners are invoked for every stanza that got dropped.
|
||||||
|
* <p>
|
||||||
|
* We use a {@link ConcurrentLinkedQueue} here in order to allow the listeners to remove
|
||||||
|
* themselves after they have been invoked.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private final Collection<StanzaListener> stanzaDroppedListeners = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This listeners are invoked for a acknowledged stanza that has the given stanza ID. They will
|
* This listeners are invoked for a acknowledged stanza that has the given stanza ID. They will
|
||||||
* only be invoked once and automatically removed after that.
|
* only be invoked once and automatically removed after that.
|
||||||
|
@ -421,9 +430,24 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// (Re-)send the stanzas *after* we tried to enable SM
|
// Inform client about failed resumption if possible, resend stanzas otherwise
|
||||||
for (Stanza stanza : previouslyUnackedStanzas) {
|
// Process the stanzas synchronously so a client can re-queue them for transmission
|
||||||
sendStanzaInternal(stanza);
|
// before it is informed about connection success
|
||||||
|
if (!stanzaDroppedListeners.isEmpty()) {
|
||||||
|
for (Stanza stanza : previouslyUnackedStanzas) {
|
||||||
|
for (StanzaListener listener : stanzaDroppedListeners) {
|
||||||
|
try {
|
||||||
|
listener.processStanza(stanza);
|
||||||
|
}
|
||||||
|
catch (InterruptedException | NotConnectedException | NotLoggedInException e) {
|
||||||
|
LOGGER.log(Level.FINER, "StanzaDroppedListener received exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Stanza stanza : previouslyUnackedStanzas) {
|
||||||
|
sendStanzaInternal(stanza);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
afterSuccessfulLogin(false);
|
afterSuccessfulLogin(false);
|
||||||
|
@ -452,9 +476,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
shutdown(false);
|
shutdown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void instantShutdown() {
|
public synchronized void instantShutdown() {
|
||||||
shutdown(true);
|
shutdown(true);
|
||||||
|
@ -492,6 +513,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
// Reset the stream management session id to null, since if the stream is cleanly closed, i.e. sending a closing
|
// Reset the stream management session id to null, since if the stream is cleanly closed, i.e. sending a closing
|
||||||
// stream tag, there is no longer a stream to resume.
|
// stream tag, there is no longer a stream to resume.
|
||||||
smSessionId = null;
|
smSessionId = null;
|
||||||
|
// Note that we deliberately do not reset authenticatedConnectionInitiallyEstablishedTimestamp here, so that the
|
||||||
|
// information is available in the connectionClosedOnError() listeners.
|
||||||
}
|
}
|
||||||
authenticated = false;
|
authenticated = false;
|
||||||
connected = false;
|
connected = false;
|
||||||
|
@ -499,6 +522,16 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
reader = null;
|
reader = null;
|
||||||
writer = null;
|
writer = null;
|
||||||
|
|
||||||
|
initState();
|
||||||
|
|
||||||
|
// Wait for reader and writer threads to be terminated.
|
||||||
|
readerWriterSemaphore.acquireUninterruptibly(2);
|
||||||
|
readerWriterSemaphore.release(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initState() {
|
||||||
|
super.initState();
|
||||||
maybeCompressFeaturesReceived.init();
|
maybeCompressFeaturesReceived.init();
|
||||||
compressSyncPoint.init();
|
compressSyncPoint.init();
|
||||||
smResumedSyncPoint.init();
|
smResumedSyncPoint.init();
|
||||||
|
@ -784,14 +817,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException {
|
protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException, SecurityRequiredByServerException {
|
||||||
StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
|
StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
|
||||||
if (startTlsFeature != null) {
|
if (startTlsFeature != null) {
|
||||||
if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
|
if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
|
||||||
SmackException smackException = new SecurityRequiredByServerException();
|
SecurityRequiredByServerException smackException = new SecurityRequiredByServerException();
|
||||||
tlsHandled.reportFailure(smackException);
|
tlsHandled.reportFailure(smackException);
|
||||||
notifyConnectionError(smackException);
|
throw smackException;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
|
if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
|
||||||
|
@ -1062,7 +1094,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
LOGGER.info(XMPPTCPConnection.this
|
LOGGER.info(XMPPTCPConnection.this
|
||||||
+ " received closing </stream> element."
|
+ " received closing </stream> element."
|
||||||
+ " Server wants to terminate the connection, calling disconnect()");
|
+ " Server wants to terminate the connection, calling disconnect()");
|
||||||
disconnect();
|
ASYNC_BUT_ORDERED.performAsyncButOrdered(XMPPTCPConnection.this, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
disconnect();
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1567,6 +1603,32 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
stanzaAcknowledgedListeners.clear();
|
stanzaAcknowledgedListeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Stanza dropped listener.
|
||||||
|
* <p>
|
||||||
|
* Those listeners will be invoked every time a Stanza has been dropped due to a failed SM resume. They will not get
|
||||||
|
* automatically removed. If at least one StanzaDroppedListener is configured, no attempt will be made to retransmit
|
||||||
|
* the Stanzas.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param listener the listener to add.
|
||||||
|
* @since 4.3.3
|
||||||
|
*/
|
||||||
|
public void addStanzaDroppedListener(StanzaListener listener) {
|
||||||
|
stanzaDroppedListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given Stanza dropped listener.
|
||||||
|
*
|
||||||
|
* @param listener the listener.
|
||||||
|
* @return true if the listener was removed.
|
||||||
|
* @since 4.3.3
|
||||||
|
*/
|
||||||
|
public boolean removeStanzaDroppedListener(StanzaListener listener) {
|
||||||
|
return stanzaDroppedListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new Stanza ID acknowledged listener for the given ID.
|
* Add a new Stanza ID acknowledged listener for the given ID.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -2,6 +2,12 @@ allprojects {
|
||||||
ext {
|
ext {
|
||||||
shortVersion = '4.4.0-alpha2'
|
shortVersion = '4.4.0-alpha2'
|
||||||
isSnapshot = true
|
isSnapshot = true
|
||||||
|
// When using dynamic versions for those, do *not* use [1.0,
|
||||||
|
// 2.0), since this will also pull in 2.0-alpha1. Instead use
|
||||||
|
// [1.0, 1.0.99].
|
||||||
|
// See also:
|
||||||
|
// - https://issues.apache.org/jira/browse/MNG-6232
|
||||||
|
// - https://issues.igniterealtime.org/browse/SMACK-858
|
||||||
jxmppVersion = '0.7.0-alpha5'
|
jxmppVersion = '0.7.0-alpha5'
|
||||||
miniDnsVersion = '0.4.0-alpha3'
|
miniDnsVersion = '0.4.0-alpha3'
|
||||||
smackMinAndroidSdk = 19
|
smackMinAndroidSdk = 19
|
||||||
|
|
Loading…
Reference in a new issue