diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html index 184c15f70..688d8bd6f 100644 --- a/resources/releasedocs/changelog.html +++ b/resources/releasedocs/changelog.html @@ -141,6 +141,35 @@ hr {
+ +

4.1.4 -- 2015-09-14

+ +

Bug +

+ + +

Improvement +

+ +

4.1.3 -- 2015-07-15

Bug diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index de3226d6a..d99cbf563 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -459,7 +459,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { if (!config.allowNullOrEmptyUsername) { StringUtils.requireNotNullOrEmpty(username, "Username must not be null or empty"); } - throwNotConnectedExceptionIfAppropriate(); + throwNotConnectedExceptionIfAppropriate("Did you call connect() before login()?"); throwAlreadyLoggedInExceptionIfAppropriate(); usedUsername = username != null ? username.toString() : null; usedPassword = password; @@ -604,8 +604,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } protected void throwNotConnectedExceptionIfAppropriate() throws NotConnectedException { + throwNotConnectedExceptionIfAppropriate(null); + } + + protected void throwNotConnectedExceptionIfAppropriate(String optionalHint) throws NotConnectedException { if (!isConnected()) { - throw new NotConnectedException(); + throw new NotConnectedException(optionalHint); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java index 7a52710db..9c33de47a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java @@ -164,7 +164,12 @@ public class SmackException extends Exception { private static final long serialVersionUID = 9197980400776001173L; public NotConnectedException() { - super("Client is not, or no longer, connected"); + this(null); + } + + public NotConnectedException(String optionalHint) { + super("Client is not, or no longer, connected." + + (optionalHint != null ? ' ' + optionalHint : "")); } public NotConnectedException(XMPPConnection connection, String details) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java index b7bd7f699..172d47b57 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java @@ -297,7 +297,8 @@ public class PacketParserUtils { } /** - * Returns the textual content of an element as String. + * Returns the textual content of an element as String. After this method returns the parser + * position will be END_TAG, following the established pull parser calling convention. *

* The parser must be positioned on a START_TAG of an element which MUST NOT contain Mixed * Content (as defined in XML 3.2.2), or else an XmlPullParserException will be thrown. diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java index 8a19c8852..764a34b52 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.WeakHashMap; import org.jivesoftware.smack.ExceptionCallback; +import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; @@ -66,6 +67,22 @@ public final class CarbonManager extends Manager { super(connection); ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); sdm.addFeature(CarbonExtension.NAMESPACE); + connection.addConnectionListener(new AbstractConnectionListener() { + @Override + public void connectionClosed() { + // Reset the state if the connection was cleanly closed. Note that this is not strictly necessary, + // because we also reset in authenticated() if the stream got not resumed, but for maximum correctness, + // also reset here. + enabled_state = false; + } + @Override + public void authenticated(XMPPConnection connection, boolean resumed) { + if (!resumed) { + // Non-resumed XMPP sessions always start with disabled carbons + enabled_state = false; + } + } + }); } /** diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/json/provider/AbstractJsonExtensionProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/json/provider/AbstractJsonExtensionProvider.java index 821557017..ab96a7d0b 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/json/provider/AbstractJsonExtensionProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/json/provider/AbstractJsonExtensionProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright © 2014-2015 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,6 @@ public abstract class AbstractJsonExtensionProvider extends ExtensionElementProv public AbstractJsonPacketExtension parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException { String json = PacketParserUtils.parseElementText(parser); - parser.next(); return from(json); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java index e0c620b47..88a8e4612 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -192,7 +192,7 @@ public class MultiUserChat { Presence oldPresence = occupantsMap.put(from, presence); if (oldPresence != null) { // Get the previous occupant's affiliation & role - MUCUser mucExtension = MUCUser.from(packet); + MUCUser mucExtension = MUCUser.from(oldPresence); MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation(); MUCRole oldRole = mucExtension.getItem().getRole(); // Get the new occupant's affiliation & role diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java index 2d967df0c..b7631816b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java @@ -41,6 +41,7 @@ import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.privacy.filter.SetActiveListFilter; import org.jivesoftware.smackx.privacy.filter.SetDefaultListFilter; @@ -270,7 +271,7 @@ public final class PrivacyListManager extends Manager { } /** - * Answer the active privacy list. + * Answer the active privacy list. Returns null if there is no active list. * * @return the privacy list of the active list. * @throws XMPPErrorException @@ -281,6 +282,9 @@ public final class PrivacyListManager extends Manager { public PrivacyList getActiveList() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { Privacy privacyAnswer = this.getPrivacyWithListNames(); String listName = privacyAnswer.getActiveName(); + if (StringUtils.isNullOrEmpty(listName)) { + return null; + } boolean isDefaultAndActive = listName != null && listName.equals(privacyAnswer.getDefaultName()); return new PrivacyList(true, isDefaultAndActive, listName, getPrivacyListItems(listName)); } @@ -303,7 +307,7 @@ public final class PrivacyListManager extends Manager { } /** - * Answer the default privacy list. + * Answer the default privacy list. Returns null if there is no default list. * * @return the privacy list of the default list. * @throws XMPPErrorException @@ -314,7 +318,10 @@ public final class PrivacyListManager extends Manager { public PrivacyList getDefaultList() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { Privacy privacyAnswer = this.getPrivacyWithListNames(); String listName = privacyAnswer.getDefaultName(); - boolean isDefaultAndActive = listName != null && listName.equals(privacyAnswer.getActiveName()); + if (StringUtils.isNullOrEmpty(listName)) { + return null; + } + boolean isDefaultAndActive = listName.equals(privacyAnswer.getActiveName()); return new PrivacyList(isDefaultAndActive, true, listName, getPrivacyListItems(listName)); } @@ -368,6 +375,7 @@ public final class PrivacyListManager extends Manager { * @throws InterruptedException */ private List getPrivacyListItems(String listName) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + assert StringUtils.isNotEmpty(listName); // The request of the list is an privacy message with an empty list Privacy request = new Privacy(); request.setPrivacyList(listName, new ArrayList()); @@ -389,6 +397,7 @@ public final class PrivacyListManager extends Manager { * @throws InterruptedException */ public PrivacyList getPrivacyList(String listName) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + listName = StringUtils.requireNotNullOrEmpty(listName, "List name must not be null"); return new PrivacyList(false, false, listName, getPrivacyListItems(listName)); } diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index a26d34024..9078300b9 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -1290,7 +1290,7 @@ public final class Roster extends Manager { } } - if (!isLoaded()) { + if (!isLoaded() && rosterLoadedAtLogin) { LOGGER.warning("Roster not loaded while processing presence stanza"); } Presence presence = (Presence) packet; 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 81bd7c08a..664ce777f 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 @@ -16,6 +16,7 @@ */ package org.jivesoftware.smack.tcp; +import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; @@ -299,6 +300,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { public XMPPTCPConnection(XMPPTCPConnectionConfiguration config) { super(config); this.config = config; + addConnectionListener(new AbstractConnectionListener() { + @Override + public void connectionClosedOnError(Exception e) { + if (e instanceof XMPPException.StreamErrorException) { + dropSmState(); + } + } + }); } /** @@ -398,7 +407,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // connection instance though). This is used in writePackets to decide if stanzas should // be added to the unacknowledged stanzas queue, because they have to be added right // after the 'enable' stream element has been sent. - unacknowledgedStanzas = null; + dropSmState(); } if (isSmAvailable() && useSm) { // Remove what is maybe left from previously stream managed sessions @@ -1715,6 +1724,17 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } } + /** + * Drop the stream management state. Sets {@link #smSessionId} and + * {@link #unacknowledgedStanzas} to null. + */ + private void dropSmState() { + // clientHandledCount and serverHandledCount will be reset on and + // respective. No need to reset them here. + smSessionId = null; + unacknowledgedStanzas = null; + } + /** * Get the maximum resumption time in seconds after which a managed stream can be resumed. *