diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html
index 7ffbe2f85..7e189a858 100644
--- a/resources/releasedocs/changelog.html
+++ b/resources/releasedocs/changelog.html
@@ -141,6 +141,25 @@ hr {
+
4.1.6 -- 2016-01-23
+
+
Bug
+
+
+- [SMACK-705] - PubSub's Affiliation.getElementName() returns wrong name
+
+- [SMACK-706] - Smack may sends <bind/> and <session/> twice if Stream Management is used and a previous SM state exists
+
+- [SMACK-707] - Infinite loop of NullPointerExceptions in Socks5Proxy
+
+- [SMACK-708] - DeliveryReceipt(Manager) should ensure that receipts (and requests) have an ID set
+
+- [SMACK-709] - Don't request delivery receipts for messages without a body
+
+- [SMACK-710] - SASL DIGEST-MD5 backslash must be quoted
+
+
+
4.1.5 -- 2015-11-22
Bug
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java
index fd1b64996..54f9dacfa 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java
@@ -387,7 +387,7 @@ public final class Socks5Proxy {
try {
- if (Socks5Proxy.this.serverSocket.isClosed()
+ if (Socks5Proxy.this.serverSocket == null || Socks5Proxy.this.serverSocket.isClosed()
|| Thread.currentThread().isInterrupted()) {
return;
}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java
index 87169dbe0..4b8dee7f2 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2013-2014 the original author or authors
+ * Copyright 2013-2015 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.Map;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
/**
@@ -42,7 +43,7 @@ public class DeliveryReceipt implements ExtensionElement
public DeliveryReceipt(String id)
{
- this.id = id;
+ this.id = StringUtils.requireNotNullOrEmpty(id, "id must not be null");
}
public String getId()
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java
index d65258dd9..a6ff3680d 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java
@@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
@@ -31,6 +32,7 @@ import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter;
+import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
@@ -38,6 +40,7 @@ import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.roster.Roster;
+import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jxmpp.jid.Jid;
@@ -74,6 +77,8 @@ public final class DeliveryReceiptManager extends Manager {
private static final StanzaFilter MESSAGES_WITH_DELIVERY_RECEIPT = new AndFilter(StanzaTypeFilter.MESSAGE,
new StanzaExtensionFilter(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE));
+ private static final Logger LOGGER = Logger.getLogger(DeliveryReceiptManager.class.getName());
+
private static Map instances = new WeakHashMap();
static {
@@ -160,6 +165,11 @@ public final class DeliveryReceiptManager extends Manager {
final Message messageWithReceiptRequest = (Message) packet;
Message ack = receiptMessageFor(messageWithReceiptRequest);
+ if (ack == null) {
+ LOGGER.warning("Received message stanza with receipt request from '" + from
+ + "' without a stanza ID set. Message: " + messageWithReceiptRequest);
+ return;
+ }
connection.sendStanza(ack);
}
}, MESSAGES_WITH_DEVLIERY_RECEIPT_REQUEST);
@@ -237,13 +247,17 @@ public final class DeliveryReceiptManager extends Manager {
/**
* A filter for stanzas to request delivery receipts for. Notably those are message stanzas of type normal, chat or
- * headline, which do notcontain a delivery receipt, i.e. are ack messages.
+ * headline, which do notcontain a delivery receipt, i.e. are ack messages, and have a body extension.
*
* @see XEP-184 § 5.4 Ack Messages
*/
private static final StanzaFilter MESSAGES_TO_REQUEST_RECEIPTS_FOR = new AndFilter(
- MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE, new NotFilter(new StanzaExtensionFilter(
- DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE)));
+ // @formatter:off
+ MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
+ new NotFilter(new StanzaExtensionFilter(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE)),
+ MessageWithBodiesFilter.INSTANCE
+ );
+ // @formatter:on
private static final StanzaListener AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER = new StanzaListener() {
@Override
@@ -254,7 +268,9 @@ public final class DeliveryReceiptManager extends Manager {
};
/**
- * Enables automatic requests of delivery receipts for outgoing messages of type 'normal', 'chat' or 'headline.
+ * Enables automatic requests of delivery receipts for outgoing messages of
+ * {@link Message.Type#normal}, {@link Message.Type#chat} or {@link Message.Type#headline}, and
+ * with a {@link Message.Body} extension.
*
* @since 4.1
* @see #dontAutoAddDeliveryReceiptRequests()
@@ -302,14 +318,21 @@ public final class DeliveryReceiptManager extends Manager {
/**
* Create and return a new message including a delivery receipt extension for the given message.
+ *
+ * If {@code messageWithReceiptRequest} does not have a Stanza ID set, then {@code null} will be returned.
+ *
*
* @param messageWithReceiptRequest the given message with a receipt request extension.
- * @return a new message with a receipt.
+ * @return a new message with a receipt or null
.
* @since 4.1
*/
public static Message receiptMessageFor(Message messageWithReceiptRequest) {
+ String stanzaId = messageWithReceiptRequest.getStanzaId();
+ if (StringUtils.isNullOrEmpty(stanzaId)) {
+ return null;
+ }
Message message = new Message(messageWithReceiptRequest.getFrom(), messageWithReceiptRequest.getType());
- message.addExtension(new DeliveryReceipt(messageWithReceiptRequest.getStanzaId()));
+ message.addExtension(new DeliveryReceipt(stanzaId));
return message;
}
}
diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java
index e72ba840e..47d0513a0 100644
--- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java
+++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java
@@ -152,7 +152,7 @@ public class SASLDigestMD5Mechanism extends SASLMechanism {
} else {
authzid = ",authzid=\"" + authorizationId + '"';
}
- String saslString = "username=\"" + authenticationId + '"'
+ String saslString = "username=\"" + quoteBackslash(authenticationId) + '"'
+ authzid
+ ",realm=\"" + serviceName + '"'
+ ",nonce=\"" + nonce + '"'
@@ -228,4 +228,18 @@ public class SASLDigestMD5Mechanism extends SASLMechanism {
return responseValue;
}
+ /**
+ * Quote the backslash in the given String. Replaces all occurrences of "\" with "\\".
+ *
+ * According to RFC 2831 § 7.2 a quoted-string consists either of qdtext or quoted-pair. And since quoted-pair is a
+ * backslash followed by a char, every backslash in qdtext must be quoted, since it otherwise would be treated as
+ * qdtext.
+ *
+ *
+ * @param string the input string.
+ * @return the input string where the every backslash is quoted.
+ */
+ public static String quoteBackslash(String string) {
+ return string.replace("\\", "\\\\");
+ }
}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
index d8472011a..4883dcf64 100644
--- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
@@ -396,8 +396,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
LOGGER.fine("Stream resumption failed, continuing with normal stream establishment process");
}
- bindResourceAndEstablishSession(resource);
-
List previouslyUnackedStanzas = new LinkedList();
if (unacknowledgedStanzas != null) {
// There was a previous connection with SM enabled but that was either not resumable or
@@ -410,6 +408,12 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
// after the 'enable' stream element has been sent.
dropSmState();
}
+
+ // Now bind the resource. It is important to do this *after* we dropped an eventually
+ // existing Stream Management state. As otherwise and may end up in
+ // unacknowledgedStanzas and become duplicated on reconnect. See SMACK-706.
+ bindResourceAndEstablishSession(resource);
+
if (isSmAvailable() && useSm) {
// Remove what is maybe left from previously stream managed sessions
serverHandledStanzasCount = 0;