From 5e203086b3a9549719d983613449c029abb004d8 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Fri, 25 Jun 2021 15:54:10 +0200 Subject: [PATCH 01/91] [sint] increase stability Some roster-based tests depend on there not being any prior subscription state beteween entities. The utility method that tries to guarantee that, acts on the state of the roster that's cached in memory, but acts on the one that's stored on the server. This occasionally causes issues, as both representations might be different. Stability is added in this commit by: - refreshing the roster from the server prior to evaluating it - ignoring an 'item-not-found' as returned by the server, when the code tries to remove that item. --- .../inttest/util/IntegrationTestRosterUtil.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java index c03ba1850..4a6569dcf 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java @@ -24,6 +24,7 @@ import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.roster.AbstractPresenceEventListener; import org.jivesoftware.smack.roster.PresenceEventListener; import org.jivesoftware.smack.roster.Roster; @@ -99,7 +100,16 @@ public class IntegrationTestRosterUtil { if (c2Entry == null) { return; } - roster.removeEntry(c2Entry); + try { + roster.removeEntry(c2Entry); + } catch (XMPPErrorException e) { + // Account for race conditions: server-sided, the item might already have been removed. + if (e.getStanzaError().getCondition() == StanzaError.Condition.item_not_found) { + // Trying to remove non-existing item. As it needs to be gone, this is fine. + return; + } + throw e; + } } } From 00249f3a676002c7248f6052d81c4d08a40e37d9 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 6 Jul 2021 14:51:00 +0200 Subject: [PATCH 02/91] Smack 4.4.4-SNAPSHOT --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 9e3a93350..fb1df371a 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.3 +4.4.4-SNAPSHOT From 58774ad05b33da76a7f73f226d189563382641a1 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Thu, 8 Jul 2021 10:32:19 +0200 Subject: [PATCH 03/91] SMACK-908: Don't use components to count tabs in Debugger Although the amount of components in a JTabbedPane apparently is often equal to the amount of tabs in it, that need not be the case. --- .../smackx/debugger/EnhancedDebuggerWindow.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java index 9822b59f9..5755207d8 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java @@ -140,12 +140,12 @@ public final class EnhancedDebuggerWindow { if (frame == null) { createDebug(); } - debugger.tabbedPane.setName("XMPPConnection_" + tabbedPane.getComponentCount()); - tabbedPane.add(debugger.tabbedPane, tabbedPane.getComponentCount() - 1); + debugger.tabbedPane.setName("XMPPConnection_" + tabbedPane.getTabCount()); + tabbedPane.add(debugger.tabbedPane, -1); tabbedPane.setIconAt(tabbedPane.indexOfComponent(debugger.tabbedPane), connectionCreatedIcon); tabbedPane.setSelectedIndex(tabbedPane.indexOfComponent(debugger.tabbedPane)); frame.setTitle( - "Smack Debug Window -- Total connections: " + (tabbedPane.getComponentCount() - 1)); + "Smack Debug Window -- Total connections: " + (tabbedPane.getTabCount() - 1)); // Keep the added debugger for later access debuggers.add(debugger); } @@ -286,7 +286,7 @@ public final class EnhancedDebuggerWindow { @Override public void actionPerformed(ActionEvent e) { // Remove the selected tab pane if it's not the Smack info pane - if (tabbedPane.getSelectedIndex() < tabbedPane.getComponentCount() - 1) { + if (tabbedPane.getSelectedIndex() < tabbedPane.getTabCount() - 1) { int index = tabbedPane.getSelectedIndex(); // Notify to the debugger to stop debugging EnhancedDebugger debugger = debuggers.get(index); @@ -297,7 +297,7 @@ public final class EnhancedDebuggerWindow { // Update the root window title frame.setTitle( "Smack Debug Window -- Total connections: " - + (tabbedPane.getComponentCount() - 1)); + + (tabbedPane.getTabCount() - 1)); } } }); @@ -309,7 +309,7 @@ public final class EnhancedDebuggerWindow { public void actionPerformed(ActionEvent e) { ArrayList debuggersToRemove = new ArrayList<>(); // Remove all the debuggers of which their connections are no longer valid - for (int index = 0; index < tabbedPane.getComponentCount() - 1; index++) { + for (int index = 0; index < tabbedPane.getTabCount() - 1; index++) { EnhancedDebugger debugger = debuggers.get(index); if (!debugger.isConnectionActive()) { // Notify to the debugger to stop debugging @@ -325,7 +325,7 @@ public final class EnhancedDebuggerWindow { // Update the root window title frame.setTitle( "Smack Debug Window -- Total connections: " - + (tabbedPane.getComponentCount() - 1)); + + (tabbedPane.getTabCount() - 1)); } }); menu.add(menuItem); From e626580f682bc1b924014fbb09adefffc89f33c6 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 18 Jul 2021 17:21:50 +0200 Subject: [PATCH 04/91] [xdata] Safe the raw character data of form field values Related to SMACK-909. --- .../smack/util/CollectionUtil.java | 9 +++- .../jivesoftware/smack/util/StringUtils.java | 12 ++++- .../smackx/xdata/AbstractMultiFormField.java | 21 ++++---- .../AbstractSingleStringValueFormField.java | 18 ++++--- .../smackx/xdata/BooleanFormField.java | 17 +++++-- .../jivesoftware/smackx/xdata/FormField.java | 36 +++++++++++--- .../smackx/xdata/JidMultiFormField.java | 39 ++++++++++++--- .../smackx/xdata/JidSingleFormField.java | 14 +++++- .../smackx/xdata/SingleValueFormField.java | 48 +++++++++++++++---- .../smackx/xdata/form/FormReader.java | 7 ++- .../xdata/provider/DataFormProvider.java | 13 ++--- 11 files changed, 178 insertions(+), 56 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java index b35dfb4a0..24b73b8b0 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,4 +82,11 @@ public class CollectionUtil { } return new HashSet<>(collection); } + + public static List emptyOrSingletonListFrom(T element) { + if (element == null) { + return Collections.emptyList(); + } + return Collections.singletonList(element); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index 77bc402c3..5ebe4c116 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2016-2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2016-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package org.jivesoftware.smack.util; import java.io.IOException; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; @@ -605,4 +606,13 @@ public class StringUtils { String[] lines = input.split(PORTABLE_NEWLINE_REGEX); return Arrays.asList(lines); } + + public static List toStrings(Collection charSequences) { + List res = new ArrayList<>(charSequences.size()); + for (CharSequence cs : charSequences) { + String string = cs.toString(); + res.add(string); + } + return res; + } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractMultiFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractMultiFormField.java index 31c778a87..16ccc3c7f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractMultiFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractMultiFormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus + * Copyright 2020-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import org.jxmpp.util.XmppDateTime; public class AbstractMultiFormField extends FormField { - private final List values; + private final List values; protected AbstractMultiFormField(Builder builder) { super(builder); @@ -35,19 +35,18 @@ public class AbstractMultiFormField extends FormField { } @Override - public final List getValues() { + public final List getRawValues() { return values; } - - public abstract static class Builder> + public abstract static class Builder> extends FormField.Builder { - private List values; + private List values; protected Builder(AbstractMultiFormField formField) { super(formField); - values = CollectionUtil.newListWith(formField.getValues()); + values = CollectionUtil.newListWith(formField.getRawValues()); } protected Builder(String fieldName, FormField.Type type) { @@ -68,9 +67,13 @@ public class AbstractMultiFormField extends FormField { public abstract B addValue(CharSequence value); public B addValueVerbatim(CharSequence value) { + return addValueVerbatim(new Value(value)); + } + + public B addValueVerbatim(Value value) { ensureValuesAreInitialized(); - values.add(value.toString()); + values.add(value); return getThis(); } @@ -83,7 +86,7 @@ public class AbstractMultiFormField extends FormField { ensureValuesAreInitialized(); for (CharSequence value : values) { - this.values.add(value.toString()); + addValueVerbatim(value); } return getThis(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractSingleStringValueFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractSingleStringValueFormField.java index 27fca4cde..9e57a294e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractSingleStringValueFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/AbstractSingleStringValueFormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus. + * Copyright 2020-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,8 @@ public class AbstractSingleStringValueFormField extends SingleValueFormField { return Integer.valueOf(value); } - public abstract static class Builder> extends FormField.Builder { + public abstract static class Builder> + extends SingleValueFormField.Builder { private String value; @@ -73,19 +74,24 @@ public class AbstractSingleStringValueFormField extends SingleValueFormField { return setValue(value); } + public B setValue(Value value) { + this.value = value.getValue().toString(); + this.rawValue = value; + return getThis(); + } + public B setValue(CharSequence value) { this.value = value.toString(); + rawValue = new Value(this.value); return getThis(); } public B setValue(Enum value) { - this.value = value.toString(); - return getThis(); + return setValue(value.toString()); } public B setValue(int value) { - this.value = Integer.toString(value); - return getThis(); + return setValue(Integer.toString(value)); } public B setValue(URL value) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java index 2b036b162..85e351649 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus. + * Copyright 2020-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ public class BooleanFormField extends SingleValueFormField { return new Builder(this); } - public static final class Builder extends FormField.Builder { + public static final class Builder extends SingleValueFormField.Builder { private Boolean value; private Builder(BooleanFormField booleanFormField) { @@ -57,6 +57,7 @@ public class BooleanFormField extends SingleValueFormField { @Override protected void resetInternal() { + super.resetInternal(); value = null; } @@ -70,15 +71,21 @@ public class BooleanFormField extends SingleValueFormField { @Deprecated // TODO: Remove in Smack 4.6. public Builder addValue(CharSequence value) { - return setValue(value); + return setValue(new Value(value)); } public Builder setValue(CharSequence value) { - boolean valueBoolean = ParserUtils.parseXmlBoolean(value.toString()); - return setValue(valueBoolean); + return setValue(new Value(value)); + } + + public Builder setValue(Value value) { + this.value = ParserUtils.parseXmlBoolean(value.getValue().toString()); + rawValue = value; + return getThis(); } public Builder setValue(boolean value) { + rawValue = new Value(Boolean.toString(value)); this.value = value; return this; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java index 4bc28a7ef..e87d2e5c6 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2019-2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2019-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -269,7 +269,25 @@ public abstract class FormField implements FullyQualifiedElement { * * @return a List of the default values or answered values of the question. */ - public abstract List getValues(); + public List getValues() { + return getRawValueCharSequences(); + } + + public abstract List getRawValues(); + + private transient List rawValueCharSequences; + + public final List getRawValueCharSequences() { + if (rawValueCharSequences == null) { + List rawValues = getRawValues(); + rawValueCharSequences = new ArrayList<>(rawValues.size()); + for (Value value : rawValues) { + rawValueCharSequences.add(value.value); + } + } + + return rawValueCharSequences; + } public boolean hasValueSet() { List values = getValues(); @@ -383,12 +401,15 @@ public abstract class FormField implements FullyQualifiedElement { protected transient List extraXmlChildElements; + /** + * Populate @{link {@link #extraXmlChildElements}}. Note that this method may be overridden by subclasses. + */ protected void populateExtraXmlChildElements() { - List values = getValues(); + List values = getRawValues(); + // Note that we need to create a new ArrayList here, since subclasses may add to it by overriding + // populateExtraXmlChildElements. extraXmlChildElements = new ArrayList<>(values.size()); - for (CharSequence value : values) { - extraXmlChildElements.add(new Value(value)); - } + extraXmlChildElements.addAll(values); } @Override @@ -412,7 +433,8 @@ public abstract class FormField implements FullyQualifiedElement { populateExtraXmlChildElements(); } - if (formFieldChildElements.isEmpty() && extraXmlChildElements == null) { + if (formFieldChildElements.isEmpty() + && (extraXmlChildElements == null || extraXmlChildElements.isEmpty())) { buf.closeEmptyElement(); } else { buf.rightAngleBracket(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidMultiFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidMultiFormField.java index e1e93c0d3..e720e816d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidMultiFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidMultiFormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus. + * Copyright 2020-2021 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,14 +23,19 @@ import java.util.List; import org.jivesoftware.smack.util.CollectionUtil; import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; -public class JidMultiFormField extends FormField { +public final class JidMultiFormField extends FormField { private final List values; + private final List rawValues; + protected JidMultiFormField(Builder builder) { super(builder); values = CollectionUtil.cloneAndSeal(builder.values); + rawValues = CollectionUtil.cloneAndSeal(builder.rawValues); } @Override @@ -38,6 +43,11 @@ public class JidMultiFormField extends FormField { return values; } + @Override + public List getRawValues() { + return rawValues; + } + public Builder asBuilder() { return new Builder(this); } @@ -45,6 +55,8 @@ public class JidMultiFormField extends FormField { public static final class Builder extends FormField.Builder { private List values; + private List rawValues; + private Builder(JidMultiFormField jidMultiFormField) { super(jidMultiFormField); values = CollectionUtil.newListWith(jidMultiFormField.getValues()); @@ -57,25 +69,40 @@ public class JidMultiFormField extends FormField { private void ensureValuesAreInitialized() { if (values == null) { values = new ArrayList<>(); + rawValues = new ArrayList<>(); } } @Override protected void resetInternal() { values = null; + rawValues = null; } public Builder addValue(Jid jid) { - ensureValuesAreInitialized(); + Value value = new Value(jid); + ensureValuesAreInitialized(); values.add(jid); + rawValues.add(value); + + return getThis(); + } + + public Builder addValue(Value value) throws XmppStringprepException { + Jid jid = JidCreate.from(value.getValue()); + + ensureValuesAreInitialized(); + values.add(jid); + rawValues.add(value); + return this; } public Builder addValues(Collection jids) { - ensureValuesAreInitialized(); - - values.addAll(jids); + for (Jid jid : jids) { + addValue(jid); + } return this; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidSingleFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidSingleFormField.java index d1b0237de..07e2e158b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidSingleFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/JidSingleFormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus. + * Copyright 2020-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.jivesoftware.smackx.xdata; import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; public class JidSingleFormField extends SingleValueFormField { @@ -36,7 +38,7 @@ public class JidSingleFormField extends SingleValueFormField { return new Builder(this); } - public static final class Builder extends FormField.Builder { + public static final class Builder extends SingleValueFormField.Builder { private Jid value; private Builder(JidSingleFormField jidSingleFormField) { @@ -50,11 +52,19 @@ public class JidSingleFormField extends SingleValueFormField { @Override protected void resetInternal() { + super.resetInternal(); value = null; } public Builder setValue(Jid value) { this.value = value; + this.rawValue = new Value(value); + return getThis(); + } + + public Builder setValue(Value value) throws XmppStringprepException { + this.value = JidCreate.from(value.getValue()); + this.rawValue = value; return this; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/SingleValueFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/SingleValueFormField.java index 2c039f1ab..d4b389a96 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/SingleValueFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/SingleValueFormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus. + * Copyright 2020-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,30 +19,62 @@ package org.jivesoftware.smackx.xdata; import java.util.Collections; import java.util.List; +import org.jivesoftware.smack.util.CollectionUtil; + public abstract class SingleValueFormField extends FormField { + private final Value rawValue; + protected SingleValueFormField(Builder builder) { super(builder); + rawValue = builder.rawValue; } @Override public final List getValues() { CharSequence value = getValue(); - if (value == null) { - return Collections.emptyList(); - } - return Collections.singletonList(value); + return CollectionUtil.emptyOrSingletonListFrom(value); } public abstract CharSequence getValue(); + public final Value getRawValue() { + return rawValue; + } + + @Override + public final List getRawValues() { + Value rawValue = getRawValue(); + return CollectionUtil.emptyOrSingletonListFrom(rawValue); + } + @Override protected void populateExtraXmlChildElements() { - CharSequence value = getValue(); - if (value == null) { + if (rawValue == null) { return; } - extraXmlChildElements = Collections.singletonList(new Value(value)); + extraXmlChildElements = Collections.singletonList(rawValue); + } + + public abstract static class Builder> + extends FormField.Builder { + + protected Builder(String fieldName, Type type) { + super(fieldName, type); + } + + protected Builder(SingleValueFormField formField) { + super(formField); + rawValue = formField.getRawValue(); + } + + protected Value rawValue; + + @Override + protected void resetInternal() { + rawValue = null; + }; + } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FormReader.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FormReader.java index 493abd25a..043e9a1fe 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FormReader.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FormReader.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus + * Copyright 2020-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import org.jivesoftware.smack.util.StringUtils; + import org.jivesoftware.smackx.xdata.AbstractMultiFormField; import org.jivesoftware.smackx.xdata.AbstractSingleStringValueFormField; import org.jivesoftware.smackx.xdata.BooleanFormField; @@ -54,7 +56,8 @@ public interface FormReader { return Collections.emptyList(); } AbstractMultiFormField multiFormField = formField.ifPossibleAs(AbstractMultiFormField.class); - return multiFormField.getValues(); + List charSequences = multiFormField.getValues(); + return StringUtils.toStrings(charSequences); } default Boolean readBoolean(String fieldName) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java index d858ee666..9fc8db616 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java @@ -49,9 +49,6 @@ import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jivesoftware.smackx.xdatalayout.packet.DataLayout; import org.jivesoftware.smackx.xdatalayout.provider.DataLayoutProvider; -import org.jxmpp.jid.Jid; -import org.jxmpp.jid.impl.JidCreate; - /** * The DataFormProvider parses DataForm packets. * @@ -237,8 +234,7 @@ public class DataFormProvider extends ExtensionElementProvider { case jid_multi: JidMultiFormField.Builder jidMultiBuilder = FormField.jidMultiBuilder(fieldName); for (FormField.Value value : values) { - Jid jid = JidCreate.from(value.getValue()); - jidMultiBuilder.addValue(jid); + jidMultiBuilder.addValue(value); } builder = jidMultiBuilder; break; @@ -246,9 +242,8 @@ public class DataFormProvider extends ExtensionElementProvider { ensureAtMostSingleValue(type, values); JidSingleFormField.Builder jidSingleBuilder = FormField.jidSingleBuilder(fieldName); if (!values.isEmpty()) { - CharSequence jidCharSequence = values.get(0).getValue(); - Jid jid = JidCreate.from(jidCharSequence); - jidSingleBuilder.setValue(jid); + FormField.Value value = values.get(0); + jidSingleBuilder.setValue(value); } builder = jidSingleBuilder; break; @@ -302,7 +297,7 @@ public class DataFormProvider extends ExtensionElementProvider { BooleanFormField.Builder builder = FormField.booleanBuilder(fieldName); ensureAtMostSingleValue(builder.getType(), values); if (values.size() == 1) { - String value = values.get(0).getValue().toString(); + FormField.Value value = values.get(0); builder.setValue(value); } return builder; From 8f760eaeb3190ee88d66e153592f91e46b96b0a8 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 18 Jul 2021 17:22:06 +0200 Subject: [PATCH 05/91] [caps] Use the raw character data of form fields when caclulating the hash Fixes SMACK-909. --- .../java/org/jivesoftware/smackx/caps/EntityCapsManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index 523213513..ff3eb0c47 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2009 Jonas Ådahl, 2011-2020 Florian Schmaus + * Copyright © 2009 Jonas Ådahl, 2011-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -700,7 +700,7 @@ public final class EntityCapsManager extends Manager { for (FormField f : fs) { sb.append(f.getFieldName()); sb.append('<'); - formFieldValuesToCaps(f.getValues(), sb); + formFieldValuesToCaps(f.getRawValueCharSequences(), sb); } } From 9c5b0a2a167fe7ed7514059ba55f5b716f8b1ee6 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 10 Aug 2021 14:51:52 +0200 Subject: [PATCH 06/91] Bump Bouncycastle to 1.69 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a85f9370f..3d04098c9 100644 --- a/build.gradle +++ b/build.gradle @@ -147,7 +147,7 @@ allprojects { smackMinAndroidSdk = 19 junitVersion = '5.7.1' commonsIoVersion = '2.6' - bouncyCastleVersion = '1.68' + bouncyCastleVersion = '1.69' guavaVersion = '30.1-jre' mockitoVersion = '3.7.7' orgReflectionsVersion = '0.9.11' From 9e33fc56e15bdb5e5857d249cf89f8da3f355d3e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 10 Aug 2021 15:23:49 +0200 Subject: [PATCH 07/91] Bump PGPainless to 0.2.8 --- smack-openpgp/build.gradle | 2 +- .../smackx/ox/crypto/PainlessOpenPgpProvider.java | 9 +++++---- .../smackx/ox/util/SecretKeyBackupHelper.java | 6 +++--- .../smackx/ox/PainlessOpenPgpProviderTest.java | 8 ++++---- .../smackx/ox_im/OXInstantMessagingManagerTest.java | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/smack-openpgp/build.gradle b/smack-openpgp/build.gradle index 08d104afa..112a2c4d8 100644 --- a/smack-openpgp/build.gradle +++ b/smack-openpgp/build.gradle @@ -8,7 +8,7 @@ dependencies { api project(':smack-extensions') api project(':smack-experimental') - api 'org.pgpainless:pgpainless-core:0.2.0' + api 'org.pgpainless:pgpainless-core:0.2.8' testImplementation "org.bouncycastle:bcprov-jdk15on:${bouncyCastleVersion}" diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java index e8b99e3ef..110bafe43 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java @@ -47,6 +47,7 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.util.io.Streams; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.DocumentSignatureType; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.MissingPublicKeyCallback; import org.pgpainless.decryption_verification.OpenPgpMetadata; @@ -209,10 +210,10 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider { DecryptionStream cipherStream = PGPainless.decryptAndOrVerify() .onInputStream(cipherText) - .decryptWith(getStore().getKeyRingProtector(), self.getSecretKeys()) - .verifyWith(announcedPublicKeys) - .handleMissingPublicKeysWith(missingPublicKeyCallback) - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKeys(self.getSecretKeys(), getStore().getKeyRingProtector()) + .addVerificationCerts(announcedPublicKeys) + .setMissingCertificateCallback(missingPublicKeyCallback)); Streams.pipeAll(cipherStream, plainText); diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java index 3c8483dfe..b1ad533fb 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/SecretKeyBackupHelper.java @@ -37,6 +37,7 @@ import org.bouncycastle.util.io.Streams; import org.jxmpp.jid.BareJid; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.encryption_signing.EncryptionStream; @@ -153,9 +154,8 @@ public class SecretKeyBackupHelper { try { DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(encryptedIn) - .decryptWith(Passphrase.fromPassword(backupCode.toString())) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionPassphrase(Passphrase.fromPassword(backupCode.toString()))); Streams.pipeAll(decryptionStream, plaintextOut); decryptionStream.close(); diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java index f6b78493a..c3fdb45e0 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java @@ -142,7 +142,7 @@ public class PainlessOpenPgpProviderTest extends SmackTestSuite { // Decrypt and Verify decrypted = bobProvider.decryptAndOrVerify(bobConnection, encrypted.getElement(), bobSelf, aliceForBob); - OpenPgpV4Fingerprint decryptionFingerprint = decrypted.getMetadata().getDecryptionFingerprint(); + OpenPgpV4Fingerprint decryptionFingerprint = decrypted.getMetadata().getDecryptionKey().getFingerprint(); assertTrue(bobSelf.getSecretKeys().contains(decryptionFingerprint.getKeyId())); assertTrue(decrypted.getMetadata().containsVerifiedSignatureFrom(alicePubKeys)); @@ -162,9 +162,9 @@ public class PainlessOpenPgpProviderTest extends SmackTestSuite { decrypted = bobProvider.decryptAndOrVerify(bobConnection, encrypted.getElement(), bobSelf, aliceForBob); - decryptionFingerprint = decrypted.getMetadata().getDecryptionFingerprint(); + decryptionFingerprint = decrypted.getMetadata().getDecryptionKey().getFingerprint(); assertTrue(bobSelf.getSecretKeys().contains(decryptionFingerprint.getKeyId())); - assertTrue(decrypted.getMetadata().getVerifiedSignatureKeyFingerprints().isEmpty()); + assertTrue(decrypted.getMetadata().getVerifiedSignatures().isEmpty()); assertEquals(OpenPgpMessage.State.crypt, decrypted.getState()); CryptElement decryptedCrypt = (CryptElement) decrypted.getOpenPgpContentElement(); @@ -182,7 +182,7 @@ public class PainlessOpenPgpProviderTest extends SmackTestSuite { decrypted = bobProvider.decryptAndOrVerify(bobConnection, encrypted.getElement(), bobSelf, aliceForBob); - assertNull(decrypted.getMetadata().getDecryptionFingerprint()); + assertNull(decrypted.getMetadata().getDecryptionKey()); assertTrue(decrypted.getMetadata().containsVerifiedSignatureFrom(alicePubKeys)); assertEquals(OpenPgpMessage.State.sign, decrypted.getState()); diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java index 13363cd58..7854aedce 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java @@ -155,7 +155,7 @@ public class OXInstantMessagingManagerTest extends SmackTestSuite { assertTrue(metadata.isSigned() && metadata.isEncrypted()); // Check, if one of Bobs keys was used for decryption - assertNotNull(bobSelf.getSigningKeyRing().getPublicKey(metadata.getDecryptionFingerprint().getKeyId())); + assertNotNull(bobSelf.getSigningKeyRing().getPublicKey(metadata.getDecryptionKey().getKeyId())); // TODO: I observed this assertTrue() to fail sporadically. As a first attempt to diagnose this, a message was // added to the assertion. However since most (all?) objects used in the message do not implement a proper From 48c057ab10e7f82a6af718c942863188c0b18437 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Wed, 16 Jun 2021 11:46:16 +0100 Subject: [PATCH 08/91] [sinttest] Additional tests for s5 of XEP-0045 Modified-by: Florian Schmaus --- .../AbstractMultiUserChatIntegrationTest.java | 40 ++- ...AffiliationsPrivilegesIntegrationTest.java | 292 +++++++++++++++++- 2 files changed, 327 insertions(+), 5 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java index 597b767e6..d20a51f52 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java @@ -90,15 +90,47 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati muc.destroy("test fixture teardown", null); } - static void createMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException { - MultiUserChat.MucCreateConfigFormHandle handle = muc.create(Resourcepart.from(resourceName)); + static void createMuc(MultiUserChat muc, Resourcepart resourceName) throws + SmackException.NoResponseException, XMPPException.XMPPErrorException, + InterruptedException, MultiUserChatException.MucAlreadyJoinedException, + SmackException.NotConnectedException, + MultiUserChatException.MissingMucCreationAcknowledgeException, + MultiUserChatException.NotAMucServiceException { + MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); if (handle != null) { handle.makeInstant(); } } - static void createModeratedMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException { - muc.create(Resourcepart.from(resourceName)); + static void createMuc(MultiUserChat muc, String nickname) throws + XmppStringprepException, MultiUserChatException.MucAlreadyJoinedException, + XMPPException.XMPPErrorException, SmackException.NotConnectedException, + MultiUserChatException.MissingMucCreationAcknowledgeException, + SmackException.NoResponseException, InterruptedException, + MultiUserChatException.NotAMucServiceException { + createMuc(muc, Resourcepart.from(nickname)); + } + + static void createMembersOnlyMuc(MultiUserChat muc, Resourcepart resourceName) throws + SmackException.NoResponseException, XMPPException.XMPPErrorException, + InterruptedException, MultiUserChatException.MucAlreadyJoinedException, + SmackException.NotConnectedException, + MultiUserChatException.MissingMucCreationAcknowledgeException, + MultiUserChatException.MucConfigurationNotSupportedException, + MultiUserChatException.NotAMucServiceException { + MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); + if (handle != null) { + handle.getConfigFormManager().makeMembersOnly().submitConfigurationForm(); + } + } + + static void createModeratedMuc(MultiUserChat muc, Resourcepart resourceName) throws + SmackException.NoResponseException, XMPPException.XMPPErrorException, + InterruptedException, MultiUserChatException.MucAlreadyJoinedException, + SmackException.NotConnectedException, + MultiUserChatException.MissingMucCreationAcknowledgeException, + MultiUserChatException.NotAMucServiceException { + muc.create(resourceName); Form configForm = muc.getConfigurationForm(); FillableForm answerForm = configForm.getFillableForm(); answerForm.setAnswer("muc#roomconfig_moderatedroom", true); //TODO Add this to the MucConfigFormManager? diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java index 966ec3e97..db96c7f1e 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2021 Florian Schmaus + * Copyright 2021 Florian Schmaus, Dan Caseley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.jivesoftware.smack.SmackException; @@ -35,6 +36,7 @@ import org.igniterealtime.smack.inttest.util.ResultSyncPoint; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; @@ -619,4 +621,292 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs } + /** + * Asserts that an affiliation is persistent between visits to the room. + * + *

From XEP-0045 § 5.2:

+ *
+ * These affiliations are long-lived in that they persist across a user's visits to the room and are not affected + * by happenings in the room...Affiliations are granted, revoked, and maintained based on the user's bare JID, not + * the nick as with roles. + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestPersistentAffiliation() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + + try { + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.grantOwnership(conTwo.getUser().asBareJid()); + mucAsSeenByOne.grantAdmin(conThree.getUser().asBareJid()); + + mucAsSeenByTwo.leave(); + mucAsSeenByThree.leave(); + Presence p2 = mucAsSeenByTwo.join(nicknameTwo); + Presence p3 = mucAsSeenByThree.join(nicknameThree); + assertEquals(MUCAffiliation.owner, MUCUser.from(p2).getItem().getAffiliation()); + assertEquals(MUCAffiliation.admin, MUCUser.from(p3).getItem().getAffiliation()); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that a moderator cannot revoke voice from an owner + * + *

From XEP-0045 § 5.1.1:

+ *
+ * A moderator MUST NOT be able to revoke voice privileges from an admin or owner + *
+ * + *

From XEP-0045 § 8.4:

+ *
+ * A moderator MUST NOT be able to revoke voice from a user whose affiliation is at or above the moderator's level. + * In addition, a service MUST NOT allow the voice privileges of an admin or owner to be removed by anyone. If a + * moderator attempts to revoke voice privileges from such a user, the service MUST deny the request and return a + * <not-allowed/> error to the sender along with the offending item(s) + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestModeratorCannotRevokeVoiceFromOwner() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createModeratedMuc(mucAsSeenByOne, nicknameOne); + + try { + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByOne.grantModerator(nicknameTwo); + XMPPException.XMPPErrorException xe = assertThrows(XMPPException.XMPPErrorException.class, + () -> mucAsSeenByTwo.revokeVoice(nicknameOne)); + assertEquals(xe.getStanzaError().getCondition().toString(), "not-allowed"); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that a moderator cannot revoke moderator privileges from a moderator with a higher affiliation + * than themselves. + * + *

From XEP-0045 § 5.1.3 and §5.2.1:

+ *
+ * A moderator SHOULD NOT be allowed to revoke moderation privileges from someone with a higher affiliation than + * themselves (i.e., an unaffiliated moderator SHOULD NOT be allowed to revoke moderation privileges from an admin + * or an owner, and an admin SHOULD NOT be allowed to revoke moderation privileges from an owner) + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestModeratorCannotBeRevokedFromHigherAffiliation() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createModeratedMuc(mucAsSeenByOne, nicknameOne); + + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + mucAsSeenByOne.grantModerator(nicknameThree); + + try { + // Admin cannot revoke from Owner + XMPPException.XMPPErrorException xe1 = assertThrows(XMPPException.XMPPErrorException.class, + () -> mucAsSeenByTwo.revokeModerator(nicknameOne)); + // Moderator cannot revoke from Admin + XMPPException.XMPPErrorException xe2 = assertThrows(XMPPException.XMPPErrorException.class, + () -> mucAsSeenByThree.revokeModerator(nicknameOne)); + // Moderator cannot revoke from Owner + XMPPException.XMPPErrorException xe3 = assertThrows(XMPPException.XMPPErrorException.class, + () -> mucAsSeenByThree.revokeModerator(nicknameTwo)); + assertEquals(xe1.getStanzaError().getCondition().toString(), "not-allowed"); + assertEquals(xe2.getStanzaError().getCondition().toString(), "not-allowed"); + assertEquals(xe3.getStanzaError().getCondition().toString(), "not-allowed"); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that an unmoderated room assigns the correct default roles for a given affiliation + * + *

From XEP-0045 § 5.1.2:

+ *
+ * ...the initial default roles that a service SHOULD set based on the user's affiliation... + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestDefaultRoleForAffiliationInUnmoderatedRoom() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-unmoderatedroles"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void adminGranted(EntityFullJid participant) { + resultSyncPoint.signal("done"); + } + }); + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + resultSyncPoint.waitForResult(timeout); + + try { + assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( + JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole()); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( + JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole()); + assertEquals(MUCRole.participant, mucAsSeenByOne.getOccupant( + JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole()); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that a moderated room assigns the correct default roles for a given affiliation + * + *

From XEP-0045 § 5.1.2:

+ *
+ * ...the initial default roles that a service SHOULD set based on the user's affiliation... + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestDefaultRoleForAffiliationInModeratedRoom() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-moderatedroles"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void adminGranted(EntityFullJid participant) { + resultSyncPoint.signal("done"); + } + }); + + createModeratedMuc(mucAsSeenByOne, nicknameOne); + + try { + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + resultSyncPoint.waitForResult(timeout); + + assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( + JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole()); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( + JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole()); + assertEquals(MUCRole.visitor, mucAsSeenByOne.getOccupant( + JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole()); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that a members-only room assigns the correct default roles for a given affiliation + * + *

From XEP-0045 § 5.1.2:

+ *
+ * ...the initial default roles that a service SHOULD set based on the user's affiliation... + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestDefaultRoleForAffiliationInMembersOnlyRoom() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-membersonlyroles"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + final EntityFullJid jidOne = JidCreate.entityFullFrom(mucAddress, nicknameOne); + final EntityFullJid jidTwo = JidCreate.entityFullFrom(mucAddress, nicknameTwo); + final EntityFullJid jidThree = JidCreate.entityFullFrom(mucAddress, nicknameThree); + + createMembersOnlyMuc(mucAsSeenByOne, nicknameOne); + + final ResultSyncPoint adminResultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void adminGranted(EntityFullJid participant) { + adminResultSyncPoint.signal("done"); + } + }); + + try { + mucAsSeenByOne.grantMembership(conTwo.getUser().asBareJid()); + mucAsSeenByOne.grantMembership(conThree.getUser().asBareJid()); + + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + adminResultSyncPoint.waitForResult(timeout); + assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(jidOne).getRole()); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(jidTwo).getRole()); + assertEquals(MUCRole.participant, mucAsSeenByOne.getOccupant(jidThree).getRole()); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + } From a0b9279441d82747181e38847fab156f50868982 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 22 Aug 2021 16:16:03 +0200 Subject: [PATCH 09/91] [sinttest] Add "compatibility mode" setting --- .../smack/inttest/Configuration.java | 24 +++++++++++++++++++ ...AffiliationsPrivilegesIntegrationTest.java | 12 +++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index 8135e3deb..b624cf074 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -117,6 +117,13 @@ public final class Configuration { public final DnsResolver dnsResolver; + public enum CompatibilityMode { + standardsCompliant, + ejabberd, + } + + public final CompatibilityMode compatibilityMode; + private Configuration(Configuration.Builder builder) throws KeyManagementException, NoSuchAlgorithmException { service = Objects.requireNonNull(builder.service, "'service' must be set. Either via 'properties' files or via system property 'sinttest.service'."); @@ -192,6 +199,7 @@ public final class Configuration { this.verbose = builder.verbose; this.dnsResolver = builder.dnsResolver; + this.compatibilityMode = builder.compatibilityMode; } public boolean isAccountRegistrationPossible() { @@ -246,6 +254,8 @@ public final class Configuration { private DnsResolver dnsResolver = DnsResolver.minidns; + private CompatibilityMode compatibilityMode = CompatibilityMode.standardsCompliant; + private Builder() { } @@ -427,6 +437,20 @@ public final class Configuration { return setDnsResolver(dnsResolver); } + public Builder setCompatibilityMode(CompatibilityMode compatibilityMode) { + this.compatibilityMode = compatibilityMode; + return this; + } + + public Builder setCompatibilityMode(String compatibilityModeString) { + if (compatibilityModeString == null) { + return this; + } + + CompatibilityMode compatibilityMode = CompatibilityMode.valueOf(compatibilityModeString); + return setCompatibilityMode(compatibilityMode); + } + public Configuration build() throws KeyManagementException, NoSuchAlgorithmException { return new Configuration(this); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java index db96c7f1e..2010ab146 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java @@ -838,6 +838,16 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs createModeratedMuc(mucAsSeenByOne, nicknameOne); + final MUCRole threeRole; + switch (sinttestConfiguration.compatibilityMode) { + default: + threeRole = MUCRole.visitor; + break; + case ejabberd: + threeRole = MUCRole.participant; + break; + } + try { mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByThree.join(nicknameThree); @@ -849,7 +859,7 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole()); assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole()); - assertEquals(MUCRole.visitor, mucAsSeenByOne.getOccupant( + assertEquals(threeRole, mucAsSeenByOne.getOccupant( JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole()); } finally { tryDestroy(mucAsSeenByOne); From 32a38c4e7776d009f89f6567a2dc9436f06fb767 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 22 Aug 2021 16:16:57 +0200 Subject: [PATCH 10/91] [core] Check if the successor vertex exists in StateDescriptorGraph If the successor's module is disabled then the vertex may be null. In this case, we can simple continue with the next successor in the list. Previously, due to d33a5a23c32a ("[core] Introduce Builder.failOnUnknownStates() and unit tests") this would trigger an assert in addOutgoingEdge(). Fixes: d33a5a23c32a ("[core] Introduce Builder.failOnUnknownStates() and unit tests") --- .../java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java index 35144f73a..41bb29f59 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java @@ -186,6 +186,10 @@ public class StateDescriptorGraph { for (GraphVertex> successor : sortedSuccessors) { GraphVertex successorVertex = successorStateDescriptors.get(successor.element); + if (successorVertex == null) { + // The successor does not exist, probably because its module was not enabled. + continue; + } node.addOutgoingEdge(successorVertex); // Recurse further. From 0d73c21945710134c500867ab413082ec3e4404f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 23 Aug 2021 17:39:59 +0200 Subject: [PATCH 11/91] [pubsub] FormNode(Provider) should not fail if there is no DataForm Error IQ respones may not contain a data form, e.g. Also FormNode's toXML() already handled the case where submitForm was 'null'. Only the constructor threw a IAE if submitForm was 'null'. Fixes SMACK-910. Closes: https://github.com/igniterealtime/Smack/pull/471 --- .../java/org/jivesoftware/smackx/pubsub/FormNode.java | 9 +-------- .../smackx/pubsub/provider/FormNodeProvider.java | 7 ++++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java index 3a6945f0f..ef8f3373c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java @@ -38,11 +38,7 @@ public class FormNode extends NodeExtension { * @param submitForm The form */ public FormNode(FormNodeType formType, DataForm submitForm) { - super(formType.getNodeElement()); - - if (submitForm == null) - throw new IllegalArgumentException("Submit form cannot be null"); - configForm = submitForm; + this(formType, null, submitForm); } /** @@ -55,9 +51,6 @@ public class FormNode extends NodeExtension { */ public FormNode(FormNodeType formType, String nodeId, DataForm submitForm) { super(formType.getNodeElement(), nodeId); - - if (submitForm == null) - throw new IllegalArgumentException("Submit form cannot be null"); configForm = submitForm; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java index 176d71544..329d5791a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java @@ -35,6 +35,11 @@ import org.jivesoftware.smackx.xdata.packet.DataForm; public class FormNodeProvider extends EmbeddedExtensionProvider { @Override protected FormNode createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) { - return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), (DataForm) content.iterator().next()); + DataForm dataForm = null; + if (!content.isEmpty()) { + dataForm = (DataForm) content.get(0); + } + + return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), dataForm); } } From 613f1afcab2eb33dcef3973a9af8dd4f8743b867 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 25 Aug 2021 22:48:31 +0200 Subject: [PATCH 12/91] [mam] Delete unused local variable 'iqType' in MamPrefsIQProvider --- .../jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java index 2b4795036..664b3e657 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java @@ -43,17 +43,12 @@ public class MamPrefsIQProvider extends IQProvider { @Override public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { - String iqType = parser.getAttributeValue("", "type"); String defaultBehaviorString = parser.getAttributeValue("", "default"); DefaultBehavior defaultBehavior = null; if (defaultBehaviorString != null) { defaultBehavior = DefaultBehavior.valueOf(defaultBehaviorString); } - if (iqType == null) { - iqType = "result"; - } - List alwaysJids = null; List neverJids = null; From c13f4ccac35f16278b658fa40b997ffa4ddd5320 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 25 Aug 2021 22:52:26 +0200 Subject: [PATCH 13/91] [mam] Fix MamPrfsIQProviderTest Parsers handed over to IQ providers should be positioned at the IQ child element when being invoked. Therefore we remove the wrapping in some test XML. Also make checkMamPrefsIQProvider() a paramterized test. --- .../mam/provider/MamPrefsIQProvider.java | 2 ++ .../smackx/mam/MamPrefIQProviderTest.java | 36 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java index 664b3e657..604b644ca 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java @@ -41,6 +41,8 @@ import org.jxmpp.jid.impl.JidCreate; */ public class MamPrefsIQProvider extends IQProvider { + public static final MamPrefsIQProvider INSTANCE = new MamPrefsIQProvider(); + @Override public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { String defaultBehaviorString = parser.getAttributeValue("", "default"); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java index cd5d4c0ca..956d2057b 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java @@ -19,54 +19,62 @@ package org.jivesoftware.smackx.mam; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import java.util.List; import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.SmackTestUtil; +import org.jivesoftware.smack.test.util.SmackTestUtil.XmlPullParserKind; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; import org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.jxmpp.jid.Jid; public class MamPrefIQProviderTest extends MamTest { - private static final String exampleMamPrefsIQ1 = "" + "" + private static final String exampleMamPrefsIQ1 = "" + "" + "romeo@montague.lit" + "" + "" - + "montague@montague.lit" + "" + "" + ""; + + "montague@montague.lit" + "" + ""; - private static final String exampleMamPrefsIQ2 = "" + "" + private static final String exampleMamPrefsIQ2 = "" + "" + "romeo@montague.lit" + "montague@montague.lit" + "" - + "" + "" + "" + ""; + + "" + "" + ""; - private static final String exampleMamPrefsIQ3 = "" + "" + "" - + ""; + private static final String exampleMamPrefsIQ3 = "" + ""; private static final String exampleMamPrefsResultIQ = "" + "" + "" + "romeo@montague.lit" + "" + "" + "sarasa@montague.lit" + "montague@montague.lit" + "" + "" + ""; - @Test - public void checkMamPrefsIQProvider() throws Exception { - XmlPullParser parser1 = PacketParserUtils.getParserFor(exampleMamPrefsIQ1); - MamPrefsIQ mamPrefIQ1 = new MamPrefsIQProvider().parse(parser1); + @ParameterizedTest + @EnumSource(value = SmackTestUtil.XmlPullParserKind.class) + public void checkMamPrefsIQProvider(XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { + XmlPullParser parser1 = SmackTestUtil.getParserFor(exampleMamPrefsIQ1, parserKind); + MamPrefsIQ mamPrefIQ1 = MamPrefsIQProvider.INSTANCE.parse(parser1); assertEquals(IQ.Type.set, mamPrefIQ1.getType()); assertEquals(mamPrefIQ1.getAlwaysJids().get(0).toString(), "romeo@montague.lit"); assertEquals(mamPrefIQ1.getNeverJids().get(0).toString(), "montague@montague.lit"); - XmlPullParser parser2 = PacketParserUtils.getParserFor(exampleMamPrefsIQ2); - MamPrefsIQ mamPrefIQ2 = new MamPrefsIQProvider().parse(parser2); + XmlPullParser parser2 = SmackTestUtil.getParserFor(exampleMamPrefsIQ2, parserKind); + MamPrefsIQ mamPrefIQ2 = MamPrefsIQProvider.INSTANCE.parse(parser2); assertEquals(IQ.Type.set, mamPrefIQ2.getType()); assertEquals(mamPrefIQ2.getAlwaysJids().get(0).toString(), "romeo@montague.lit"); assertEquals(mamPrefIQ2.getAlwaysJids().get(1).toString(), "montague@montague.lit"); assertTrue(mamPrefIQ2.getNeverJids().isEmpty()); - XmlPullParser parser3 = PacketParserUtils.getParserFor(exampleMamPrefsIQ3); - MamPrefsIQ mamPrefIQ3 = new MamPrefsIQProvider().parse(parser3); + XmlPullParser parser3 = SmackTestUtil.getParserFor(exampleMamPrefsIQ3, parserKind); + MamPrefsIQ mamPrefIQ3 = MamPrefsIQProvider.INSTANCE.parse(parser3); assertEquals(IQ.Type.set, mamPrefIQ3.getType()); } From 3f2418dec744c39915b742a18942f383a0814479 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 25 Aug 2021 22:54:23 +0200 Subject: [PATCH 14/91] [core] Fix spelling errors in code comment within PacketParserUtils --- .../java/org/jivesoftware/smack/util/PacketParserUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7729bea9d..9b08d6572 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 @@ -549,8 +549,8 @@ public class PacketParserUtils { if (provider != null) { iqPacket = provider.parse(parser, iqData, outerXmlEnvironment); } - // Note that if we reach this code, it is guranteed that the result IQ contained a child element - // (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_ELEMENT first. + // Note that if we reach this code, it is guaranteed that the result IQ contained a child element + // (RFC 6120 § 8.2.3 6) because otherwise we would have reached the END_ELEMENT first. else { // No Provider found for the IQ stanza, parse it to an UnparsedIQ instance // so that the content of the IQ can be examined later on From 5b857a1b828b24f5b9888db41ade2afb0741c731 Mon Sep 17 00:00:00 2001 From: Frank Matheron Date: Thu, 26 Aug 2021 16:43:34 +0200 Subject: [PATCH 15/91] [mam] Support multiple versions of MAM Support multiple version of MAM and do discovery of the version of MAM supported by the archive. Currently supported MAM versions are v1 and v2. Fixes SMACK-911 See https://discourse.igniterealtime.org/t/mam-support-of-multiple-mam-versions/90136 --- .../jivesoftware/smackx/mam/MamManager.java | 78 ++++++++++++--- .../smackx/mam/element/MamElementFactory.java | 94 +++++++++++++++++++ .../smackx/mam/element/MamElements.java | 32 ++++--- .../smackx/mam/element/MamFinIQ.java | 10 +- .../smackx/mam/element/MamPrefsIQ.java | 16 ++-- .../smackx/mam/element/MamQueryIQ.java | 29 +++--- .../mam/element/MamV1ElementFactory.java | 66 +++++++++++++ .../mam/element/MamV2ElementFactory.java | 66 +++++++++++++ .../smackx/mam/element/MamVersion.java | 69 ++++++++++++++ .../smackx/mam/provider/MamFinIQProvider.java | 4 +- .../mam/provider/MamPrefsIQProvider.java | 4 +- .../mam/provider/MamQueryIQProvider.java | 4 +- .../mam/provider/MamResultProvider.java | 4 +- .../experimental.providers | 20 ++++ .../jivesoftware/smackx/mam/FiltersTest.java | 10 +- .../org/jivesoftware/smackx/mam/MamTest.java | 5 +- .../jivesoftware/smackx/mam/PagingTest.java | 3 +- .../smackx/mam/PreferencesTest.java | 10 +- .../smackx/mam/QueryArchiveTest.java | 8 +- .../smackx/mam/ResultsLimitTest.java | 6 +- .../smackx/mam/RetrieveFormFieldsTest.java | 10 +- 21 files changed, 460 insertions(+), 88 deletions(-) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElementFactory.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV1ElementFactory.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV2ElementFactory.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamVersion.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java index b0d43e117..546e0d79c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java @@ -47,15 +47,17 @@ import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.commands.AdHocCommandManager; import org.jivesoftware.smackx.commands.RemoteCommand; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverItems; import org.jivesoftware.smackx.forward.packet.Forwarded; -import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs; +import org.jivesoftware.smackx.mam.element.MamElementFactory; import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; import org.jivesoftware.smackx.mam.element.MamFinIQ; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.mam.filter.MamResultFilter; import org.jivesoftware.smackx.muc.MultiUserChat; import org.jivesoftware.smackx.rsm.packet.RSMSet; @@ -225,6 +227,8 @@ public final class MamManager extends Manager { private final AdHocCommandManager adHocCommandManager; + private MamVersion mamVersion = null; + private MamManager(XMPPConnection connection, Jid archiveAddress) { super(connection); this.archiveAddress = archiveAddress; @@ -250,6 +254,52 @@ public final class MamManager extends Manager { return archiveAddress; } + /** + * Returns the MAM namespace used by this {@link MamManager}. If the archive does not support any MAM namespace + * supported by Smack, null is returned. + * + * @return the MAM namespace used by this manager, null if MAM is not supported + * @throws NoResponseException if there was no response from the remote entity. + * @throws XMPPErrorException if there was an XMPP error returned. + * @throws NotConnectedException if the XMPP connection is not connected. + * @throws InterruptedException if the calling thread was interrupted. + */ + public String getMamNamespace() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException { + MamVersion mamVersion = getSupportedMamVersionOrNull(); + return mamVersion == null ? null : mamVersion.getNamespace(); + } + + private MamVersion getSupportedMamVersionOrNull() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException { + if (mamVersion != null) { + return mamVersion; + } + + DiscoverInfo info = serviceDiscoveryManager.discoverInfo(getArchiveAddress()); + + // Enum values are always returned the order they are declared (see https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3). + // We pick the first version supported by the server. + for (MamVersion v : MamVersion.values()) { + if (info.containsFeature(v.getNamespace())) { + mamVersion = v; + break; + } + } + + return mamVersion; + } + + private MamVersion getSupportedMamVersionOrThrow() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException { + MamVersion mamVersion = getSupportedMamVersionOrNull(); + if (mamVersion == null) { + throw new UnsupportedOperationException("Message Archive Management is not supported by " + getArchiveAddress()); + } + return mamVersion; + } + + private MamElementFactory getElementFactory() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException { + return getSupportedMamVersionOrThrow().newElementFactory(); + } + public static final class MamQueryArgs { private final String node; @@ -275,11 +325,11 @@ public final class MamManager extends Manager { private DataForm dataForm; - DataForm getDataForm() { + DataForm getDataForm(MamVersion version) { if (dataForm != null) { return dataForm; } - DataForm.Builder dataFormBuilder = getNewMamForm(); + DataForm.Builder dataFormBuilder = getNewMamForm(version); dataFormBuilder.addFields(formFields.values()); dataForm = dataFormBuilder.build(); return dataForm; @@ -472,9 +522,9 @@ public final class MamManager extends Manager { NotConnectedException, NotLoggedInException, InterruptedException { String queryId = StringUtils.secureUniqueRandomString(); String node = mamQueryArgs.node; - DataForm dataForm = mamQueryArgs.getDataForm(); + DataForm dataForm = mamQueryArgs.getDataForm(mamVersion); - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, dataForm); + MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, dataForm); mamQueryIQ.setType(IQ.Type.set); mamQueryIQ.setTo(archiveAddress); @@ -530,7 +580,7 @@ public final class MamManager extends Manager { throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { String queryId = StringUtils.secureUniqueRandomString(); - MamQueryIQ mamQueryIq = new MamQueryIQ(queryId, node, null); + MamQueryIQ mamQueryIq = getElementFactory().newQueryIQ(queryId, node, null); mamQueryIq.setTo(archiveAddress); MamQueryIQ mamResponseQueryIq = connection().sendIqRequestAndWaitForResponse(mamQueryIq); @@ -592,7 +642,7 @@ public final class MamManager extends Manager { private List page(RSMSet requestRsmSet) throws NoResponseException, XMPPErrorException, NotConnectedException, NotLoggedInException, InterruptedException { String queryId = StringUtils.secureUniqueRandomString(); - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, form); + MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, form); mamQueryIQ.setType(IQ.Type.set); mamQueryIQ.setTo(archiveAddress); mamQueryIQ.addExtension(requestRsmSet); @@ -696,9 +746,7 @@ public final class MamManager extends Manager { * @see XEP-0313 § 7. Determining support */ public boolean isSupported() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - // Note that this may return 'null' but SDM's supportsFeature() does the right thing™ then. - Jid archiveAddress = getArchiveAddress(); - return serviceDiscoveryManager.supportsFeature(archiveAddress, MamElements.NAMESPACE); + return getSupportedMamVersionOrNull() != null; } public boolean isAdvancedConfigurationSupported() throws InterruptedException, XMPPException, SmackException { @@ -720,8 +768,8 @@ public final class MamManager extends Manager { throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress); } - private static DataForm.Builder getNewMamForm() { - FormField field = FormField.buildHiddenFormType(MamElements.NAMESPACE); + private static DataForm.Builder getNewMamForm(MamVersion version) { + FormField field = FormField.buildHiddenFormType(version.getNamespace()); DataForm.Builder form = DataForm.builder(); form.addField(field); return form; @@ -765,7 +813,7 @@ public final class MamManager extends Manager { */ public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { - MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); + MamPrefsIQ mamPrefIQ = getElementFactory().newPrefsIQ(); return queryMamPrefs(mamPrefIQ); } @@ -830,6 +878,7 @@ public final class MamManager extends Manager { public static final class MamPrefs { private final List alwaysJids; private final List neverJids; + private final MamVersion mamVersion; private DefaultBehavior defaultBehavior; private MamPrefs(MamPrefsResult mamPrefsResult) { @@ -837,6 +886,7 @@ public final class MamManager extends Manager { this.alwaysJids = new ArrayList<>(mamPrefsIq.getAlwaysJids()); this.neverJids = new ArrayList<>(mamPrefsIq.getNeverJids()); this.defaultBehavior = mamPrefsIq.getDefault(); + this.mamVersion = MamVersion.fromNamespace(mamPrefsIq.getNamespace()); } public void setDefaultBehavior(DefaultBehavior defaultBehavior) { @@ -856,7 +906,7 @@ public final class MamManager extends Manager { } private MamPrefsIQ constructMamPrefsIq() { - return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); + return mamVersion.newElementFactory().newPrefsIQ(alwaysJids, neverJids, defaultBehavior); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElementFactory.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElementFactory.java new file mode 100644 index 000000000..6b0017bbc --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElementFactory.java @@ -0,0 +1,94 @@ +/** + * + * Copyright © 2016-2021 Florian Schmaus and Frank Matheron + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.mam.element; + +import java.util.List; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +import org.jxmpp.jid.Jid; + +/** + * Factory that creates MAM objects. + * + * @since 4.5.0 + */ +public interface MamElementFactory { + + /** + * Creates a new {@link MamElementFactory} for the parser based on the namespace of the parser. + * @param parser the XML parser to retrieve the MAM namespace from + * @return the factory suitable for the MAM namespace + */ + static MamElementFactory forParser(XmlPullParser parser) { + String namespace = parser.getNamespace(); + return MamVersion.fromNamespace(namespace).newElementFactory(); + } + + /** + * Create a MAM result extension class. + * + * @param queryId id of the query + * @param id the message's archive UID + * @param forwarded the original message as it was received + * @return the result extension + */ + MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded forwarded); + + /** + * Create a MAM fin IQ class. + * + * @param queryId id of the query + * @param rsmSet the RSM set included in the {@code } + * @param complete true if the results returned by the server are complete (no further paging in needed) + * @param stable false if the results returned by the sever are unstable (e.g. they might later change in sequence or content) + * @return the fin IQ + */ + MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable); + + /** + * Create a new MAM preferences IQ. + * + * @param alwaysJids JIDs for which all messages are archived by default + * @param neverJids JIDs for which messages are never archived + * @param defaultBehavior default archive behavior + * @return the prefs IQ + */ + MamPrefsIQ newPrefsIQ(List alwaysJids, List neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior); + + /** + * Construct a new MAM {@code } IQ retrieval request (IQ type 'get'). + * + * @return the prefs IQ + */ + MamPrefsIQ newPrefsIQ(); + + /** + * Create a new MAM Query IQ. + * + * @param queryId id of the query + * @param node pubsub node id when querying a pubsub node, null when not querying a pubsub node + * @param dataForm the dataform containing the query parameters + * @return the query IQ + */ + MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm); + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java index 24c0753a6..6e3d7e982 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java @@ -18,15 +18,13 @@ package org.jivesoftware.smackx.mam.element; import java.util.List; -import javax.xml.namespace.QName; - import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.MessageView; +import org.jivesoftware.smack.packet.XmlElement; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; - import org.jivesoftware.smackx.forward.packet.Forwarded; import org.jxmpp.jid.Jid; @@ -41,8 +39,6 @@ import org.jxmpp.jid.Jid; */ public class MamElements { - public static final String NAMESPACE = "urn:xmpp:mam:2"; - /** * MAM result extension class. * @@ -50,18 +46,13 @@ public class MamElements { * Archive Management * */ - public static class MamResultExtension implements ExtensionElement { + public abstract static class MamResultExtension implements ExtensionElement { /** * result element. */ public static final String ELEMENT = "result"; - /** - * The qualified name of the MAM result extension element. - */ - public static final QName QNAME = new QName(NAMESPACE, ELEMENT); - /** * id of the result. */ @@ -77,20 +68,27 @@ public class MamElements { */ private String queryId; + protected final MamVersion version; + /** * MAM result extension constructor. * + * @param version TODO javadoc me please * @param queryId TODO javadoc me please * @param id TODO javadoc me please * @param forwarded TODO javadoc me please */ - public MamResultExtension(String queryId, String id, Forwarded forwarded) { + public MamResultExtension(MamVersion version, String queryId, String id, Forwarded forwarded) { if (StringUtils.isEmpty(id)) { throw new IllegalArgumentException("id must not be null or empty"); } if (forwarded == null) { throw new IllegalArgumentException("forwarded must no be null"); } + if (version == null) { + throw new IllegalArgumentException("version must not be null"); + } + this.version = version; this.id = id; this.forwarded = forwarded; this.queryId = queryId; @@ -130,7 +128,7 @@ public class MamElements { @Override public final String getNamespace() { - return NAMESPACE; + return version.getNamespace(); } @Override @@ -148,7 +146,13 @@ public class MamElements { } public static MamResultExtension from(MessageView message) { - return message.getExtension(MamResultExtension.class); + for (XmlElement extension : message.getExtensions()) { + if (extension instanceof MamResultExtension) { + return (MamResultExtension) extension; + } + } + + return null; } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java index cd5b92669..91b2defbc 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java @@ -35,11 +35,6 @@ public class MamFinIQ extends IQ { */ public static final String ELEMENT = "fin"; - /** - * the IQ NAMESPACE. - */ - public static final String NAMESPACE = MamElements.NAMESPACE; - /** * RSM set. */ @@ -63,13 +58,14 @@ public class MamFinIQ extends IQ { /** * MamFinIQ constructor. * + * @param version TODO javadoc me please * @param queryId TODO javadoc me please * @param rsmSet TODO javadoc me please * @param complete TODO javadoc me please * @param stable TODO javadoc me please */ - public MamFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) { - super(ELEMENT, NAMESPACE); + public MamFinIQ(MamVersion version, String queryId, RSMSet rsmSet, boolean complete, boolean stable) { + super(ELEMENT, version.getNamespace()); if (rsmSet == null) { throw new IllegalArgumentException("rsmSet must not be null"); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java index 743847914..e5898f4c3 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java @@ -46,11 +46,6 @@ public class MamPrefsIQ extends IQ { */ public static final String ELEMENT = "prefs"; - /** - * the IQ NAMESPACE. - */ - public static final String NAMESPACE = MamElements.NAMESPACE; - /** * list of always. */ @@ -68,9 +63,11 @@ public class MamPrefsIQ extends IQ { /** * Construct a new MAM {@code } IQ retrieval request (IQ type 'get'). + * + * @param version TODO javadoc me please * */ - public MamPrefsIQ() { - super(ELEMENT, NAMESPACE); + public MamPrefsIQ(MamVersion version) { + super(ELEMENT, version.getNamespace()); alwaysJids = null; neverJids = null; defaultBehavior = null; @@ -79,12 +76,13 @@ public class MamPrefsIQ extends IQ { /** * MAM preferences IQ constructor. * + * @param version TODO javadoc me please * @param alwaysJids TODO javadoc me please * @param neverJids TODO javadoc me please * @param defaultBehavior TODO javadoc me please */ - public MamPrefsIQ(List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) { - super(ELEMENT, NAMESPACE); + public MamPrefsIQ(MamVersion version, List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) { + super(ELEMENT, version.getNamespace()); setType(Type.set); this.alwaysJids = alwaysJids; this.neverJids = neverJids; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java index b41400505..3909b077c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java @@ -35,11 +35,6 @@ public class MamQueryIQ extends IQ { */ public static final String ELEMENT = QUERY_ELEMENT; - /** - * the MAM query IQ NAMESPACE. - */ - public static final String NAMESPACE = MamElements.NAMESPACE; - private final String queryId; private final String node; private final DataForm dataForm; @@ -47,41 +42,45 @@ public class MamQueryIQ extends IQ { /** * MAM query IQ constructor. * + * @param version TODO javadoc me please * @param queryId TODO javadoc me please */ - public MamQueryIQ(String queryId) { - this(queryId, null, null); + public MamQueryIQ(MamVersion version, String queryId) { + this(version, queryId, null, null); setType(IQ.Type.get); } /** * MAM query IQ constructor. * + * @param version TODO javadoc me please * @param form TODO javadoc me please */ - public MamQueryIQ(DataForm form) { - this(null, null, form); + public MamQueryIQ(MamVersion version, DataForm form) { + this(version, null, null, form); } /** * MAM query IQ constructor. * + * @param version TODO javadoc me please * @param queryId TODO javadoc me please * @param form TODO javadoc me please */ - public MamQueryIQ(String queryId, DataForm form) { - this(queryId, null, form); + public MamQueryIQ(MamVersion version, String queryId, DataForm form) { + this(version, queryId, null, form); } /** * MAM query IQ constructor. * + * @param version TODO javadoc me please * @param queryId TODO javadoc me please * @param node TODO javadoc me please * @param dataForm TODO javadoc me please */ - public MamQueryIQ(String queryId, String node, DataForm dataForm) { - super(ELEMENT, NAMESPACE); + public MamQueryIQ(MamVersion version, String queryId, String node, DataForm dataForm) { + super(ELEMENT, version.getNamespace()); this.queryId = queryId; this.node = node; this.dataForm = dataForm; @@ -91,9 +90,9 @@ public class MamQueryIQ extends IQ { if (formType == null) { throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field"); } - if (!formType.equals(MamElements.NAMESPACE)) { + if (!formType.equals(version.getNamespace())) { throw new IllegalArgumentException( - "Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'"); + "Value of the hidden form type field must be '" + version.getNamespace() + "'"); } addExtension(dataForm); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV1ElementFactory.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV1ElementFactory.java new file mode 100644 index 000000000..e87f79ca7 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV1ElementFactory.java @@ -0,0 +1,66 @@ +/** + * + * Copyright © 2016-2021 Florian Schmaus and Frank Matheron + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.mam.element; + +import java.util.List; +import javax.xml.namespace.QName; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +import org.jxmpp.jid.Jid; + +class MamV1ElementFactory implements MamElementFactory { + + @Override + public MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded forwarded) { + return new MamV1ResultExtension(queryId, id, forwarded); + } + + @Override + public MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) { + return new MamFinIQ(MamVersion.MAM1, queryId, rsmSet, complete, stable); + } + + @Override + public MamPrefsIQ newPrefsIQ(List alwaysJids, List neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior) { + return new MamPrefsIQ(MamVersion.MAM1, alwaysJids, neverJids, defaultBehavior); + } + + @Override + public MamPrefsIQ newPrefsIQ() { + return new MamPrefsIQ(MamVersion.MAM1); + } + + @Override + public MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm) { + return new MamQueryIQ(MamVersion.MAM1, queryId, node, dataForm); + } + + public static class MamV1ResultExtension extends MamElements.MamResultExtension { + /** + * The qualified name of the MAM result extension element. + */ + public static final QName QNAME = new QName(MamVersion.MAM1.getNamespace(), ELEMENT); + + MamV1ResultExtension(String queryId, String id, Forwarded forwarded) { + super(MamVersion.MAM1, queryId, id, forwarded); + } + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV2ElementFactory.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV2ElementFactory.java new file mode 100644 index 000000000..a706f8632 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamV2ElementFactory.java @@ -0,0 +1,66 @@ +/** + * + * Copyright © 2016-2021 Florian Schmaus and Frank Matheron + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.mam.element; + +import java.util.List; +import javax.xml.namespace.QName; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +import org.jxmpp.jid.Jid; + +class MamV2ElementFactory implements MamElementFactory { + + @Override + public MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded forwarded) { + return new MamV2ResultExtension(queryId, id, forwarded); + } + + @Override + public MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) { + return new MamFinIQ(MamVersion.MAM2, queryId, rsmSet, complete, stable); + } + + @Override + public MamPrefsIQ newPrefsIQ(List alwaysJids, List neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior) { + return new MamPrefsIQ(MamVersion.MAM2, alwaysJids, neverJids, defaultBehavior); + } + + @Override + public MamPrefsIQ newPrefsIQ() { + return new MamPrefsIQ(MamVersion.MAM2); + } + + @Override + public MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm) { + return new MamQueryIQ(MamVersion.MAM2, queryId, node, dataForm); + } + + public static class MamV2ResultExtension extends MamElements.MamResultExtension { + /** + * The qualified name of the MAM result extension element. + */ + public static final QName QNAME = new QName(MamVersion.MAM2.getNamespace(), ELEMENT); + + MamV2ResultExtension(String queryId, String id, Forwarded forwarded) { + super(MamVersion.MAM2, queryId, id, forwarded); + } + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamVersion.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamVersion.java new file mode 100644 index 000000000..50a7a211d --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamVersion.java @@ -0,0 +1,69 @@ +/** + * + * Copyright © 2016-2021 Florian Schmaus and Frank Matheron + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.mam.element; + +/** + * MAM versions supported by Smack. + * + * @since 4.5.0 + */ +public enum MamVersion { + // Note that the order in which the enum values are defined, is also the order in which we attempt to find a + // supported version. The versions should therefore be listed in order of newest to oldest, so that Smack prefers + // using a newer version over an older version. + MAM2("urn:xmpp:mam:2") { + @Override + public MamElementFactory newElementFactory() { + return new MamV2ElementFactory(); + } + }, + MAM1("urn:xmpp:mam:1") { + @Override + public MamElementFactory newElementFactory() { + return new MamV1ElementFactory(); + } + }; + + private final String namespace; + + MamVersion(String namespace) { + this.namespace = namespace; + } + + /** + * Each MAM version is identified by its namespace. Returns the namespace for this MAM version. + * @return the namespace of the MAM version + */ + public String getNamespace() { + return namespace; + } + + /** + * Creates a new factory that creates IQ's and extension objects for this MAM version. + * @return the factory + */ + public abstract MamElementFactory newElementFactory(); + + public static MamVersion fromNamespace(String namespace) { + for (MamVersion v : MamVersion.values()) { + if (v.namespace.equals(namespace)) { + return v; + } + } + throw new IllegalArgumentException("Unsupported namespace: " + namespace); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java index 1af384165..92be55c0b 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java @@ -25,6 +25,7 @@ import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.mam.element.MamElementFactory; import org.jivesoftware.smackx.mam.element.MamFinIQ; import org.jivesoftware.smackx.rsm.packet.RSMSet; import org.jivesoftware.smackx.rsm.provider.RSMSetProvider; @@ -41,6 +42,7 @@ public class MamFinIQProvider extends IQProvider { @Override public MamFinIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { + MamElementFactory elementFactory = MamElementFactory.forParser(parser); String queryId = parser.getAttributeValue("", "queryid"); boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false); boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true); @@ -65,7 +67,7 @@ public class MamFinIQProvider extends IQProvider { } } - return new MamFinIQ(queryId, rsmSet, complete, stable); + return elementFactory.newFinIQ(queryId, rsmSet, complete, stable); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java index 604b644ca..d4d0b7c46 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java @@ -25,6 +25,7 @@ import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.mam.element.MamElementFactory; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; @@ -45,6 +46,7 @@ public class MamPrefsIQProvider extends IQProvider { @Override public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { + MamElementFactory elementFactory = MamElementFactory.forParser(parser); String defaultBehaviorString = parser.getAttributeValue("", "default"); DefaultBehavior defaultBehavior = null; if (defaultBehaviorString != null) { @@ -79,7 +81,7 @@ public class MamPrefsIQProvider extends IQProvider { } } - return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); + return elementFactory.newPrefsIQ(alwaysJids, neverJids, defaultBehavior); } private static List iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException { diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java index d0971c5de..e6280a52d 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java @@ -24,6 +24,7 @@ import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.mam.element.MamElementFactory; import org.jivesoftware.smackx.mam.element.MamQueryIQ; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jivesoftware.smackx.xdata.provider.DataFormProvider; @@ -41,6 +42,7 @@ public class MamQueryIQProvider extends IQProvider { @Override public MamQueryIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { + MamElementFactory elementFactory = MamElementFactory.forParser(parser); DataForm dataForm = null; String queryId = parser.getAttributeValue("", "queryid"); String node = parser.getAttributeValue("", "node"); @@ -68,7 +70,7 @@ public class MamQueryIQProvider extends IQProvider { } } - return new MamQueryIQ(queryId, node, dataForm); + return elementFactory.newQueryIQ(queryId, node, dataForm); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java index 21012f873..c870e5c87 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java @@ -28,6 +28,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.forward.packet.Forwarded; import org.jivesoftware.smackx.forward.provider.ForwardedProvider; +import org.jivesoftware.smackx.mam.element.MamElementFactory; import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; /** @@ -43,6 +44,7 @@ public class MamResultProvider extends ExtensionElementProvider forwarded = null; String queryId = parser.getAttributeValue("", "queryid"); String id = parser.getAttributeValue("", "id"); @@ -69,7 +71,7 @@ public class MamResultProvider extends ExtensionElementProviderurn:xmpp:mam:2 org.jivesoftware.smackx.mam.provider.MamResultProvider + + prefs + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider + + + query + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamQueryIQProvider + + + fin + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamFinIQProvider + + + result + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamResultProvider + diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java index 212c37e89..b5c974dce 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java @@ -23,7 +23,7 @@ import java.util.Date; import java.util.List; import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs; -import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.junit.jupiter.api.Test; @@ -35,7 +35,7 @@ public class FiltersTest extends MamTest { private static String getMamXMemberWith(List fieldsNames, List fieldsValues) { String xml = "" + "" + "" - + MamElements.NAMESPACE + "" + ""; + + MamVersion.MAM2.getNamespace() + "
" + ""; for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) { xml += "" + "" + fieldsValues.get(i) + "" @@ -51,7 +51,7 @@ public class FiltersTest extends MamTest { Date date = new Date(); MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsSince(date).build(); - DataForm dataForm = mamQueryArgs.getDataForm(); + DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2); List fields = new ArrayList<>(); fields.add("start"); @@ -66,7 +66,7 @@ public class FiltersTest extends MamTest { Date date = new Date(); MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsBefore(date).build(); - DataForm dataForm = mamQueryArgs.getDataForm(); + DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2); List fields = new ArrayList<>(); fields.add("end"); @@ -81,7 +81,7 @@ public class FiltersTest extends MamTest { Jid jid = JidTestUtil.BARE_JID_1; MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsToJid(jid).build(); - DataForm dataForm = mamQueryArgs.getDataForm(); + DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2); List fields = new ArrayList<>(); fields.add("with"); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java index 33e9df1a3..b8e6727d5 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java @@ -23,6 +23,7 @@ import org.jivesoftware.smack.DummyConnection; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.junit.jupiter.api.BeforeAll; @@ -47,9 +48,9 @@ public class MamTest extends SmackTestSuite { protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm"); + Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm", MamVersion.class); methodGetNewMamForm.setAccessible(true); - DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager); + DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager, MamVersion.MAM2); return dataFormBuilder.build(); } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java index 9a3a4e61a..7d7a26966 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java @@ -22,6 +22,7 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.rsm.packet.RSMSet; import org.jivesoftware.smackx.xdata.packet.DataForm; @@ -40,7 +41,7 @@ public class PagingTest extends MamTest { int max = 10; RSMSet rsmSet = new RSMSet(max); - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm); mamQueryIQ.setStanzaId("sarasa"); mamQueryIQ.setType(IQ.Type.set); mamQueryIQ.addExtension(rsmSet); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java index 2e80871bc..5b7aa81a7 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java @@ -23,9 +23,9 @@ import java.util.List; import org.jivesoftware.smack.packet.StreamOpen; -import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.junit.jupiter.api.Test; import org.jxmpp.jid.Jid; @@ -33,16 +33,16 @@ import org.jxmpp.jid.impl.JidCreate; public class PreferencesTest { - private static final String retrievePrefsStanzaExample = "" + "" + "" + ""; - private static final String updatePrefsStanzaExample = "" + "" + "" + "" + "romeo@montague.lit" + "other@montague.lit" + "" + "" + "montague@montague.lit" + "" + "" + ""; @Test public void checkRetrievePrefsStanza() throws Exception { - MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); + MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ(); mamPrefIQ.setStanzaId("sarasa"); assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), retrievePrefsStanzaExample); } @@ -56,7 +56,7 @@ public class PreferencesTest { List neverJids = new ArrayList<>(); neverJids.add(JidCreate.from("montague@montague.lit")); - MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster); + MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster); mamPrefIQ.setStanzaId("sarasa"); assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), updatePrefsStanzaExample); } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java index 3b378a516..85ac63a8f 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java @@ -29,9 +29,9 @@ import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smackx.delay.packet.DelayInformation; import org.jivesoftware.smackx.forward.packet.Forwarded; -import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.junit.jupiter.api.Test; @@ -41,7 +41,7 @@ public class QueryArchiveTest extends MamTest { private static final String mamSimpleQueryIQ = "" + "" + "" + "" + "" - + MamElements.NAMESPACE + "" + "" + "" + "" + ""; + + MamVersion.MAM2.getNamespace() + "
" + "" + "" + "" + ""; private static final String mamQueryResultExample = "" + "" @@ -54,7 +54,7 @@ public class QueryArchiveTest extends MamTest { @Test public void checkMamQueryIQ() throws Exception { DataForm dataForm = getNewMamForm(); - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm); mamQueryIQ.setType(IQ.Type.set); mamQueryIQ.setStanzaId("sarasa"); assertEquals(mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), mamSimpleQueryIQ); @@ -80,7 +80,7 @@ public class QueryArchiveTest extends MamTest { Forwarded forwarded = new Forwarded<>(forwardedMessage, delay); - message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded)); + message.addExtension(MamVersion.MAM2.newElementFactory().newResultExtension("g27", "34482-21985-73620", forwarded)); assertEquals(mamQueryResultExample, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java index 893b78a5f..4089a3296 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java @@ -22,8 +22,8 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs; -import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.junit.jupiter.api.Test; @@ -32,13 +32,13 @@ public class ResultsLimitTest extends MamTest { private static final String resultsLimitStanza = "" + "" + "" + "" + "" - + MamElements.NAMESPACE + "" + "" + "" + "" + + MamVersion.MAM2.getNamespace() + "
" + "" + "" + "" + "10" + "" + "" + ""; @Test public void checkResultsLimit() throws Exception { DataForm dataForm = getNewMamForm(); - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm); mamQueryIQ.setType(IQ.Type.set); mamQueryIQ.setStanzaId("sarasa"); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java index f973fa719..3fb2c3928 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java @@ -22,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs; -import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.element.MamVersion; import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.packet.DataForm; @@ -32,18 +32,18 @@ import org.jxmpp.jid.JidTestUtil; public class RetrieveFormFieldsTest extends MamTest { - private static final String retrieveFormFieldStanza = "" + "" + "" + ""; private static final String additionalFieldsStanza = "" + "" - + "" + MamElements.NAMESPACE + "" + "" + + "" + MamVersion.MAM2.getNamespace() + "" + "" + "" + "Hi" + "" + "" + "one@exampleone.org" + "" + ""; @Test public void checkRetrieveFormFieldsStanza() throws Exception { - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId); + MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId); mamQueryIQ.setStanzaId("sarasa"); assertEquals(retrieveFormFieldStanza, mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); @@ -63,7 +63,7 @@ public class RetrieveFormFieldsTest extends MamTest { .withAdditionalFormField(field1) .withAdditionalFormField(field2) .build(); - DataForm dataForm = mamQueryArgs.getDataForm(); + DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2); String dataFormResult = dataForm.toXML().toString(); From f0a0796d339f37d546f971cc879e8bd87db2b461 Mon Sep 17 00:00:00 2001 From: Jonathan Lennox Date: Thu, 9 Sep 2021 17:28:25 +0000 Subject: [PATCH 16/91] Update documentation of default SecurityMode. --- .../java/org/jivesoftware/smack/ConnectionConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 23e664e94..2a0165009 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -405,7 +405,7 @@ public abstract class ConnectionConfiguration { /** * Returns the TLS security mode used when making the connection. By default, - * the mode is {@link SecurityMode#ifpossible}. + * the mode is {@link SecurityMode#required}. * * @return the security mode. */ @@ -960,7 +960,7 @@ public abstract class ConnectionConfiguration { /** * Sets the TLS security mode used when making the connection. By default, - * the mode is {@link SecurityMode#ifpossible}. + * the mode is {@link SecurityMode#required}. * * @param securityMode the security mode. * @return a reference to this builder. From 8ae5ef1f513d4b3b24c291991215d04f96016453 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Sep 2021 09:55:15 +0200 Subject: [PATCH 17/91] [socks5] Ensure that the local SOCKS5 proxy is running (if enabled) In 9352225f444b ("Rework SOCKS5 unit tests so that they can be run in parallel") the call to getSocks5Proxy() in Socks5BytestreamManager.getLocalStreamHost() was removed. Since getSocks5Proxy() does also start the local proxy, if it is not already running, this caused Smack to no longer automatically start the local proxy. This commit re-adds the call to getSocks5Proxy() and fixes SMACK-912. --- .../smackx/bytestreams/socks5/Socks5BytestreamManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index f257efa6e..46b438d72 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -658,6 +658,9 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream * is not running */ public List getLocalStreamHost() { + // Ensure that the local SOCKS5 proxy is running (if enabled). + Socks5Proxy.getSocks5Proxy(); + List streamHosts = new ArrayList<>(); XMPPConnection connection = connection(); From 55299fb7e7c4e3636beba180868cab62f5e06cfe Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Sep 2021 10:10:55 +0200 Subject: [PATCH 18/91] [core] Assert that 'event' is not END_DOCUMENT in forwardToEndTagOfDepth() --- .../src/main/java/org/jivesoftware/smack/util/ParserUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java index 8f67c5ccf..be526aaba 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java @@ -79,6 +79,7 @@ public class ParserUtils { throws XmlPullParserException, IOException { XmlPullParser.Event event = parser.getEventType(); while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) { + assert event != XmlPullParser.Event.END_DOCUMENT; event = parser.next(); } } From 09710b3203ef7110d1566f1cc8d58b70678a8ae0 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Sep 2021 18:23:59 +0200 Subject: [PATCH 19/91] [socks5] Fix javadoc of getLocalStreamHost() The method does no longer return null. Reported-by: Simon Abykov --- .../smackx/bytestreams/socks5/Socks5BytestreamManager.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index 46b438d72..573a7cb42 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -652,10 +652,9 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream /** * Returns the stream host information of the local SOCKS5 proxy containing the IP address and - * the port or null if local SOCKS5 proxy is not running. + * the port. The returned list may be empty if the local SOCKS5 proxy is not running. * - * @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy - * is not running + * @return the stream host information of the local SOCKS5 proxy */ public List getLocalStreamHost() { // Ensure that the local SOCKS5 proxy is running (if enabled). From b57cf8375f38d609ec6c6d9597585d1b49d3f806 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Sep 2021 18:25:44 +0200 Subject: [PATCH 20/91] [socks5] Remove stale null check The method getLocalStreamHost() does no longer return 'null', hence the null check is unnecessary. --- .../smackx/bytestreams/socks5/Socks5BytestreamManager.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index 573a7cb42..7d8b1a12b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -615,9 +615,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream if (annouceLocalStreamHost) { // add local proxy on first position if exists List localProxies = getLocalStreamHost(); - if (localProxies != null) { - streamHosts.addAll(localProxies); - } + streamHosts.addAll(localProxies); } // query SOCKS5 proxies for network settings From 49ad8c0954e31c757f3d5c14f967f7617191135d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 Sep 2021 13:55:52 +0200 Subject: [PATCH 21/91] [disco] Add DisocverInfo.nullSafeContainsFuture(DiscoverInfo, CharSequence) --- .../jivesoftware/smackx/disco/packet/DiscoverInfo.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java index b4d3f7554..abe18f630 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java @@ -258,6 +258,14 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { return features.contains(new Feature(feature)); } + public static boolean nullSafeContainsFeature(DiscoverInfo discoverInfo, CharSequence feature) { + if (discoverInfo == null) { + return false; + } + + return discoverInfo.containsFeature(feature); + } + @Override protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { xml.optAttribute("node", getNode()); From ae4ff244a3bd0203cde772107cf2d0ffabaf42d6 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 Sep 2021 13:56:15 +0200 Subject: [PATCH 22/91] [muc] Check mucServicedDiscoInfo for null in serviceSupportsStableIds() Fixes SMACK-913. --- .../main/java/org/jivesoftware/smackx/muc/MultiUserChat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7e0c0359b..b80f3582d 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 @@ -2558,7 +2558,7 @@ public class MultiUserChat { } public boolean serviceSupportsStableIds() { - return mucServiceDiscoInfo.containsFeature(MultiUserChatConstants.STABLE_ID_FEATURE); + return DiscoverInfo.nullSafeContainsFeature(mucServiceDiscoInfo, MultiUserChatConstants.STABLE_ID_FEATURE); } @Override From ec456399b584968ec22892a3c03e8c80e0d0d06f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 Sep 2021 14:14:01 +0200 Subject: [PATCH 23/91] [carbons] Remove erroneous assert statement in connectionClosed() The assert statement in CarbonManager's connectionClosed() connection listener callback was erroneous. A connection may be connected, but never was authenticated. If now the connection is closed, then carbonsListener was never setup (via the authenticated() callback), causing the assert to throw an exception. --- .../java/org/jivesoftware/smackx/carbons/CarbonManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 196484c97..f5b231d1e 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 @@ -154,8 +154,7 @@ public final class CarbonManager extends Manager { // because we also reset in authenticated() if the stream got not resumed, but for maximum correctness, // also reset here. enabled_state = false; - boolean removed = connection().removeSyncStanzaListener(carbonsListener); - assert removed; + connection().removeSyncStanzaListener(carbonsListener); } @Override public void authenticated(XMPPConnection connection, boolean resumed) { From 52a49769f9a825a8c0252efcad0d96635fb257a6 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 5 Oct 2021 20:42:42 +0200 Subject: [PATCH 24/91] [muc] Check for self-presence first in presence listener Fixes SMACK-914 --- .../org/jivesoftware/smackx/muc/MultiUserChat.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 b80f3582d..800b50555 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 @@ -212,7 +212,12 @@ public class MultiUserChat { switch (presence.getType()) { case available: Presence oldPresence = occupantsMap.put(from, presence); - if (oldPresence != null) { + if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) { + processedReflectedSelfPresence = true; + synchronized (this) { + notify(); + } + } else if (oldPresence != null) { // Get the previous occupant's affiliation & role MUCUser mucExtension = MUCUser.from(oldPresence); MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation(); @@ -228,11 +233,6 @@ public class MultiUserChat { newAffiliation, isUserStatusModification, from); - } else if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) { - processedReflectedSelfPresence = true; - synchronized (this) { - notify(); - } } else { // A new occupant has joined the room for (ParticipantStatusListener listener : participantStatusListeners) { From 105c74b22bce25ee98a8f68ef65f5c8885abddfc Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 5 Oct 2021 20:47:35 +0200 Subject: [PATCH 25/91] [muc] Call userHasLeft() *after* the leave presence was sent Calling userHasLeft before sending the leave presence may result in invalid state as the MUC presence lister may modify the MUCs local state, e.g., occupantsMap, conurrently with the leave operation. If we reset it after the leave presence was send and acknowledged, then this can not happen as the server will not longer send any MUC related presences to us. Also fixes SMACK-914. In theory 52a49769f9a8 ("[muc] Check for self-presence first in presence listener") alone would fix SMACK-914, but this also fixes it indepentendly of 52a49769f9a8. Both commits are sensible, so both are applied. --- .../org/jivesoftware/smackx/muc/MultiUserChat.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 800b50555..6e10a66ea 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 @@ -789,11 +789,14 @@ public class MultiUserChat { StanzaFilter reflectedLeavePresenceFilter = new AndFilter(reflectedLeavePresenceFilters); - // Reset occupant information first so that we are assume that we left the room even if sendStanza() would - // throw. - userHasLeft(); - - Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow(); + Presence reflectedLeavePresence; + try { + reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow(); + } finally { + // Reset occupant information after we send the leave presence. This ensures that we only call userHasLeft() + // and reset the local MUC state after we successfully left the MUC (or if an exception occurred). + userHasLeft(); + } return reflectedLeavePresence; } From 820adf88653e6fe3d6b1e59489379966dfbf5dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Tue, 12 Oct 2021 13:06:05 -0700 Subject: [PATCH 26/91] [muc] Also process destory message if it contains Fixes SMACK-915 --- .../smackx/muc/MultiUserChat.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) 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 b80f3582d..1565b2544 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 @@ -259,23 +259,24 @@ public class MultiUserChat { listener.left(from); } } + } - Destroy destroy = mucUser.getDestroy(); - // The room has been destroyed. - if (destroy != null) { - EntityBareJid alternateMucJid = destroy.getJid(); - final MultiUserChat alternateMuc; - if (alternateMucJid == null) { - alternateMuc = null; - } else { - alternateMuc = multiUserChatManager.getMultiUserChat(alternateMucJid); - } + Destroy destroy = mucUser.getDestroy(); + // The room has been destroyed. + if (destroy != null) { + EntityBareJid alternateMucJid = destroy.getJid(); + final MultiUserChat alternateMuc; + if (alternateMucJid == null) { + alternateMuc = null; + } else { + alternateMuc = multiUserChatManager.getMultiUserChat(alternateMucJid); + } - for (UserStatusListener listener : userStatusListeners) { - listener.roomDestroyed(alternateMuc, destroy.getReason()); - } + for (UserStatusListener listener : userStatusListeners) { + listener.roomDestroyed(alternateMuc, destroy.getReason()); } } + if (isUserStatusModification) { for (UserStatusListener listener : userStatusListeners) { listener.removed(mucUser, presence); From 9b339efbc1cff33326f0284c23a3860074ae9457 Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Sat, 16 Oct 2021 21:09:01 +0200 Subject: [PATCH 27/91] Prevent password enforcement for SASL anonymous requirePassword from 92f4aadfdc45c144763c2e2a1921c2fa9a6db90b already excludes SASL external, but missed SASL anonymous. --- .../java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java index fd99732fe..79b9eff48 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java @@ -60,4 +60,8 @@ public class SASLAnonymous extends SASLMechanism { // SASL Anonymous is always successful :) } + @Override + public boolean requiresPassword() { + return false; + } } From 8074ddd60a0abe77ea257af4a113e558e69a8c8f Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Sat, 16 Oct 2021 21:22:42 +0200 Subject: [PATCH 28/91] Fix BOSH connection establishment AbstractXMPPConnection waits for the flag lastFeaturesReceived since 57961a8cc1f2df6ecc1afa8c4f8460794d8d2dce, but it is never set from BOSH connections. Use parseFeaturesAndNotify instead of parseFeatures to set the signal. Similarly the XmlEnvironment is not set from bosh, but required in ParserUtils.getXmlLang. --- .../jivesoftware/smack/bosh/XMPPBOSHConnection.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index c3d7109f8..a88507719 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -43,6 +43,7 @@ import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; import org.igniterealtime.jbosh.AbstractBody; import org.igniterealtime.jbosh.BOSHClient; @@ -200,6 +201,13 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { + getHost() + ":" + getPort() + "."; throw new SmackException.SmackMessageException(errorMessage); } + + try { + XmlPullParser parser = PacketParserUtils.getParserFor(""); + onStreamOpen(parser); + } catch (XmlPullParserException | IOException e) { + throw new AssertionError("Failed to setup stream environment", e); + } } @Override @@ -511,7 +519,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { parseAndProcessStanza(parser); break; case "features": - parseFeatures(parser); + parseFeaturesAndNotify(parser); break; case "error": // Some BOSH error isn't stream error. From b675aac81b6d4a78b744dbd744baab938d782fdc Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Sun, 17 Oct 2021 15:57:48 +0200 Subject: [PATCH 29/91] Make Smack jars OSGi bundles Fixes SMACK-343 by using bnd instead of the deprecated Gradle plugin that was previously used and removed in commit d06f533bb975f41a5e86c990e1bb830b7e7dc923. --- build.gradle | 14 +++++++++++--- smack-core/build.gradle | 13 +++++++++++++ smack-xmlparser-stax/build.gradle | 15 +++++++++++++++ smack-xmlparser-xpp3/build.gradle | 15 +++++++++++++++ smack-xmlparser/build.gradle | 14 ++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d80fbb28c..3991e96ed 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ buildscript { dependencies { classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2' classpath "org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.3.1" + classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.0.0" } } @@ -413,6 +414,7 @@ subprojects { apply plugin: 'signing' apply plugin: 'checkstyle' apply plugin: 'org.kordamp.gradle.clirr' + apply plugin: 'biz.aQute.bnd.builder' checkstyle { toolVersion = '8.27' @@ -575,9 +577,15 @@ project(':smack-omemo').clirr.enabled = false project(':smack-omemo-signal').clirr.enabled = false subprojects*.jar { - manifest { - from sharedManifest - } + manifest { + from sharedManifest + } + bundle { + bnd( + '-removeheaders': 'Tool, Bnd-*', + '-exportcontents': '*', + ) + } } configure(subprojects - gplLicensedProjects) { diff --git a/smack-core/build.gradle b/smack-core/build.gradle index b0cd79641..6b35345e8 100644 --- a/smack-core/build.gradle +++ b/smack-core/build.gradle @@ -1,3 +1,8 @@ +// Note that this is also declared in the main build.gradle for +// subprojects, but since evaluationDependsOnChildren is enabled we +// need to declare it here too to have bundle{bnd{...}} available +apply plugin: 'biz.aQute.bnd.builder' + description = """\ Smack core components.""" @@ -55,3 +60,11 @@ task createVersionResource(type: CreateFileTask) { } compileJava.dependsOn(createVersionResource) + +jar { + bundle { + bnd( + 'DynamicImport-Package': '*', + ) + } +} diff --git a/smack-xmlparser-stax/build.gradle b/smack-xmlparser-stax/build.gradle index d4e21ba8f..5012907c1 100644 --- a/smack-xmlparser-stax/build.gradle +++ b/smack-xmlparser-stax/build.gradle @@ -1,3 +1,8 @@ +// Note that this is also declared in the main build.gradle for +// subprojects, but since evaluationDependsOnChildren is enabled we +// need to declare it here too to have bundle{bnd{...}} available +apply plugin: 'biz.aQute.bnd.builder' + description = """\ Smack XML parser using Stax.""" @@ -5,3 +10,13 @@ dependencies { compile project(':smack-xmlparser') //testCompile project(path: ":smack-xmlparser", configuration: "testRuntime") } + +jar { + bundle { + bnd( + // see http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.loader.html + 'Require-Capability': 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)"', + 'Provide-Capability': "osgi.serviceloader;osgi.serviceloader=org.jivesoftware.smack.xml.XmlPullParserFactory;register:=org.jivesoftware.smack.xml.stax.StaxXmlPullParserFactory", + ) + } +} diff --git a/smack-xmlparser-xpp3/build.gradle b/smack-xmlparser-xpp3/build.gradle index a0afd7c4a..f0a9f56c6 100644 --- a/smack-xmlparser-xpp3/build.gradle +++ b/smack-xmlparser-xpp3/build.gradle @@ -1,3 +1,8 @@ +// Note that this is also declared in the main build.gradle for +// subprojects, but since evaluationDependsOnChildren is enabled we +// need to declare it here too to have bundle{bnd{...}} available +apply plugin: 'biz.aQute.bnd.builder' + description = """\ Smack XML parser using XPP3.""" @@ -11,3 +16,13 @@ dependencies { api project(':smack-xmlparser') //testCompile project(path: ":smack-xmlparser", configuration: "testRuntime") } + +jar { + bundle { + bnd( + // see http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.loader.html + 'Require-Capability': 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)"', + 'Provide-Capability': "osgi.serviceloader;osgi.serviceloader=org.jivesoftware.smack.xml.XmlPullParserFactory;register:=org.jivesoftware.smack.xml.xpp3.Xpp3XmlPullParserFactory", + ) + } +} diff --git a/smack-xmlparser/build.gradle b/smack-xmlparser/build.gradle index 46318a504..513a2b153 100644 --- a/smack-xmlparser/build.gradle +++ b/smack-xmlparser/build.gradle @@ -1,2 +1,16 @@ +// Note that this is also declared in the main build.gradle for +// subprojects, but since evaluationDependsOnChildren is enabled we +// need to declare it here too to have bundle{bnd{...}} available +apply plugin: 'biz.aQute.bnd.builder' + description = """\ Smack XML parser fundamentals""" + +jar { + bundle { + bnd( + // see http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.loader.html + 'Require-Capability': 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)"', + ) + } +} From 27ff37fa98d82203b9bf2a9e7404b5967990bf9d Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Sun, 17 Oct 2021 16:06:41 +0200 Subject: [PATCH 30/91] Add getter for the stanza associated with the exception Fixes SMACK-916 --- .../java/org/jivesoftware/smack/XMPPException.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java index ce4c0a4f0..f7235fb48 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java @@ -120,6 +120,16 @@ public abstract class XMPPException extends Exception { return error; } + /** + * Gets the stanza associated with this exception. + * + * @return the stanza from which this exception was created or {@code null} if the exception is not from a + * stanza. + */ + public Stanza getStanza() { + return stanza; + } + /** * Get the request which triggered the error response causing this exception. * From 585bcb4dc8d6a2164945ff3dd9b55cad9472f872 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 19 Oct 2021 11:16:35 +0200 Subject: [PATCH 31/91] [jingle] Add empty element optimization for --- .../jivesoftware/smackx/jingle/element/JingleContent.java | 5 +++++ .../org/jivesoftware/smackx/jingle/JingleContentTest.java | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java index 171231a9b..4a73db0a0 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java @@ -145,6 +145,11 @@ public final class JingleContent implements FullyQualifiedElement { xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition); xml.attribute(NAME_ATTRIBUTE_NAME, name); xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders); + + if (description == null && transport == null) { + return xml.closeEmptyElement(); + } + xml.rightAngleBracket(); xml.optAppend(description); diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java index 45904af4e..946ae95e8 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java @@ -75,8 +75,7 @@ public class JingleContentTest extends SmackTestSuite { assertEquals(content1.toXML().toString(), builder.build().toXML().toString()); String xml = - "" + - ""; + ""; assertEquals(xml, content1.toXML().toString()); } } From b243a40e26ebb1e0f33c80c91372dc1124a3fa0f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 19 Oct 2021 11:29:23 +0200 Subject: [PATCH 32/91] [core] Pass down the XML environment in IQChildElementXmlStringBuilder This allows to avoid redundant XML namespaces within IQs, like for example here: Fixes SMACK-917 Reported-by: Jonathan Lennox --- .../org/jivesoftware/smack/packet/IQ.java | 19 +++++++++---------- .../smack/util/XmlStringBuilder.java | 12 +++++++----- .../ibb/packet/DataPacketExtension.java | 2 +- .../workgroup/packet/RoomInvitation.java | 2 +- .../smackx/workgroup/packet/RoomTransfer.java | 2 +- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java index 0f412b602..6af350a66 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java @@ -202,7 +202,7 @@ public abstract class IQ extends Stanza implements IqView { // Add the query section if there is one. IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder( - new IQChildElementXmlStringBuilder(this)); + new IQChildElementXmlStringBuilder(getChildElementName(), getChildElementNamespace(), null, xml.getXmlEnvironment())); // TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change // the logic. if (iqChildElement == null) { @@ -399,17 +399,16 @@ public abstract class IQ extends Stanza implements IqView { private boolean isEmptyElement; - private IQChildElementXmlStringBuilder(IQ iq) { - this(iq.getChildElementName(), iq.getChildElementNamespace()); + public IQChildElementXmlStringBuilder(ExtensionElement extensionElement, + XmlEnvironment enclosingXmlEnvironment) { + this(extensionElement.getElementName(), extensionElement.getNamespace(), extensionElement.getLanguage(), + enclosingXmlEnvironment); } - public IQChildElementXmlStringBuilder(ExtensionElement pe) { - this(pe.getElementName(), pe.getNamespace()); - } - - private IQChildElementXmlStringBuilder(String element, String namespace) { - prelude(element, namespace); - this.element = element; + private IQChildElementXmlStringBuilder(String elementName, String xmlNs, String xmlLang, + XmlEnvironment enclosingXmlEnvironment) { + super(elementName, xmlNs, xmlLang, enclosingXmlEnvironment); + this.element = elementName; } public void setEmptyElement() { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java index a494307e7..5a1fae11f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2020 Florian Schmaus + * Copyright 2014-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,11 +53,13 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element { } public XmlStringBuilder(FullyQualifiedElement element, XmlEnvironment enclosingXmlEnvironment) { - sb = new LazyStringBuilder(); - halfOpenElement(element); + this(element.getElementName(), element.getNamespace(), element.getLanguage(), enclosingXmlEnvironment); + } + + public XmlStringBuilder(String elementName, String xmlNs, String xmlLang, XmlEnvironment enclosingXmlEnvironment) { + sb = new LazyStringBuilder(); + halfOpenElement(elementName); - String xmlNs = element.getNamespace(); - String xmlLang = element.getLanguage(); if (enclosingXmlEnvironment == null) { xmlnsAttribute(xmlNs); xmllangAttribute(xmlLang); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java index 532ac9a11..008c941c6 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java @@ -153,7 +153,7 @@ public class DataPacketExtension implements ExtensionElement { @Override public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); + XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace)); xml.closeElement(this); return xml; } diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java index d2e56b29d..c7577e727 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java @@ -114,7 +114,7 @@ public class RoomInvitation implements ExtensionElement { @Override public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); + XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace)); xml.closeElement(this); return xml; } diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java index 0e8012aae..09b09f657 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java @@ -109,7 +109,7 @@ public class RoomTransfer implements ExtensionElement { @Override public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); + XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace)); xml.closeElement(this); return xml; } From a3840659aa626d921ab05beabd627a36a283e3d8 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 19 Oct 2021 11:31:08 +0200 Subject: [PATCH 33/91] [jingle] Mimic Manager.connection() in JingleTransportManager Eventually JingleTransportManager should be a subclass of Manager (or be replaced by Manager), as JingleTransportManager holds a strong reference to the XMPPConnection. This could cause memory leaks. But for now, we mimic the Manager API in JingleTransportManger to make a future transition to Manager easier. --- .../smackx/jingle/transports/JingleTransportManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java index 5b348c5d3..8c91c8976 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java @@ -36,6 +36,10 @@ public abstract class JingleTransportManager i } public XMPPConnection getConnection() { + return connection(); + } + + public XMPPConnection connection() { return connection; } From 453ca6aeb06d97ffaa9e9937e17877ccc83958df Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 19 Oct 2021 11:32:51 +0200 Subject: [PATCH 34/91] [jingle] Make Jingle.Builder extend IqBuilder This makes Jingle.Builder to follow the new IqBuilder pattern, allowing to construct Jingle IQs with a given stanza ID (mostly useful for unit tests). --- .../smackx/jingle/JingleUtil.java | 16 +++--- .../smackx/jingle/element/Jingle.java | 53 ++++++++++++++++--- .../jingle/provider/JingleProvider.java | 11 ++-- .../jingle_s5b/JingleS5BTransportManager.java | 8 +-- .../smackx/jingle/JingleTest.java | 6 +-- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java index 1ac93c448..1cac1f095 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java @@ -51,7 +51,7 @@ public class JingleUtil { JingleContentDescription description, JingleContentTransport transport) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setAction(JingleAction.session_initiate) .setSessionId(sessionId) .setInitiator(connection.getUser()); @@ -116,7 +116,7 @@ public class JingleUtil { JingleContentDescription description, JingleContentTransport transport) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setResponder(connection.getUser()) .setAction(JingleAction.session_accept) .setSessionId(sessionId); @@ -151,7 +151,7 @@ public class JingleUtil { } public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setAction(JingleAction.session_terminate) .setSessionId(sessionId) .setReason(reason); @@ -230,7 +230,7 @@ public class JingleUtil { public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId, JingleContent.Creator contentCreator, String contentName) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setAction(JingleAction.session_terminate) .setSessionId(sessionId); @@ -312,7 +312,7 @@ public class JingleUtil { } public Jingle createSessionPing(FullJid recipient, String sessionId) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setSessionId(sessionId) .setAction(JingleAction.session_info); @@ -341,7 +341,7 @@ public class JingleUtil { public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Creator contentCreator, String contentName, JingleContentTransport transport) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setInitiator(initiator) .setSessionId(sessionId) .setAction(JingleAction.transport_replace); @@ -368,7 +368,7 @@ public class JingleUtil { public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Creator contentCreator, String contentName, JingleContentTransport transport) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setAction(JingleAction.transport_accept) .setInitiator(initiator) .setSessionId(sessionId); @@ -395,7 +395,7 @@ public class JingleUtil { public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Creator contentCreator, String contentName, JingleContentTransport transport) { - Jingle.Builder jb = Jingle.getBuilder(); + Jingle.Builder jb = Jingle.builder(connection); jb.setAction(JingleAction.transport_reject) .setInitiator(initiator) .setSessionId(sessionId); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java index 40acf187a..7598d1894 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2014-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IqBuilder; +import org.jivesoftware.smack.packet.IqData; +import org.jivesoftware.smack.packet.id.StandardStanzaIdSource; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; @@ -65,9 +69,9 @@ public final class Jingle extends IQ { private final List contents; - private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason, + private Jingle(Builder builder, String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason, List contents) { - super(ELEMENT, NAMESPACE); + super(builder, ELEMENT, NAMESPACE); this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null"); this.action = Objects.requireNonNull(action, "Jingle action must not be null"); this.initiator = initiator; @@ -169,11 +173,31 @@ public final class Jingle extends IQ { return xml; } + /** + * Deprecated, do not use. + * + * @return a builder. + * @deprecated use {@link #builder(XMPPConnection)} instead. + */ + @Deprecated + // TODO: Remove in Smack 4.6. public static Builder getBuilder() { - return new Builder(); + return builder(StandardStanzaIdSource.DEFAULT.getNewStanzaId()); } - public static final class Builder { + public static Builder builder(XMPPConnection connection) { + return new Builder(connection); + } + + public static Builder builder(IqData iqData) { + return new Builder(iqData); + } + + public static Builder builder(String stanzaId) { + return new Builder(stanzaId); + } + + public static final class Builder extends IqBuilder { private String sid; private JingleAction action; @@ -186,7 +210,16 @@ public final class Jingle extends IQ { private List contents; - private Builder() { + Builder(IqData iqCommon) { + super(iqCommon); + } + + Builder(XMPPConnection connection) { + super(connection); + } + + Builder(String stanzaId) { + super(stanzaId); } public Builder setSessionId(String sessionId) { @@ -228,8 +261,14 @@ public final class Jingle extends IQ { return this; } + @Override public Jingle build() { - return new Jingle(sid, action, initiator, responder, reason, contents); + return new Jingle(this, sid, action, initiator, responder, reason, contents); + } + + @Override + public Builder getThis() { + return this; } } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java index 8ff1e269b..f6c352a75 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus + * Copyright 2017-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,12 @@ package org.jivesoftware.smackx.jingle.provider; import java.io.IOException; import java.util.logging.Logger; +import org.jivesoftware.smack.packet.IqData; import org.jivesoftware.smack.packet.StandardExtensionElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.StandardExtensionElementProvider; -import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.IqProvider; import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -40,13 +41,13 @@ import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport; import org.jxmpp.jid.FullJid; -public class JingleProvider extends IQProvider { +public class JingleProvider extends IqProvider { private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName()); @Override - public Jingle parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { - Jingle.Builder builder = Jingle.getBuilder(); + public Jingle parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { + Jingle.Builder builder = Jingle.builder(iqData); String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME); if (actionString != null) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java index 60b1e9f88..cae0f8e11 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java @@ -148,7 +148,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager { builder.build(); }); @@ -48,7 +48,7 @@ public class JingleTest extends SmackTestSuite { public void onlySessionIdBuilderTest() { String sessionId = "testSessionId"; - Jingle.Builder builder = Jingle.getBuilder(); + Jingle.Builder builder = Jingle.builder("id"); builder.setSessionId(sessionId); assertThrows(IllegalArgumentException.class, () -> { builder.build(); @@ -59,7 +59,7 @@ public class JingleTest extends SmackTestSuite { public void parserTest() throws XmppStringprepException { String sessionId = "testSessionId"; - Jingle.Builder builder = Jingle.getBuilder(); + Jingle.Builder builder = Jingle.builder("id"); builder.setSessionId(sessionId); builder.setAction(JingleAction.session_initiate); From 6f67553fcf4d8340f15e47f8a6bf711efcf98971 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 19 Oct 2021 11:34:07 +0200 Subject: [PATCH 35/91] [jingle] Add unit test to check that there are no redundant namespaces Related to SMACK-917. --- .../smackx/jingle/element/JingleTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/element/JingleTest.java diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/element/JingleTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/element/JingleTest.java new file mode 100644 index 000000000..ae198617f --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/element/JingleTest.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2021 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.element; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.jivesoftware.smack.packet.StreamOpen; + +import org.junit.jupiter.api.Test; + +public class JingleTest { + + @Test + public void noRedundantNamespaceTest() { + Jingle.Builder jingleBuilder = Jingle.builder("test-id"); + jingleBuilder.setSessionId("MySession"); + jingleBuilder.setAction(JingleAction.content_accept); + + JingleContent.Builder jingleContentBuilder = JingleContent.getBuilder(); + jingleContentBuilder.setName("Hello world"); + jingleContentBuilder.setCreator(JingleContent.Creator.initiator); + + jingleBuilder.addJingleContent(jingleContentBuilder.build()); + Jingle iq = jingleBuilder.build(); + + String actualXml = iq.toXML(StreamOpen.CLIENT_NAMESPACE).toString(); + String expectedXml + = "" + + "" + + "" + + ""; + assertEquals(expectedXml, actualXml); + } +} From 1956048811ebd41e90dec5dd3756305b907928e5 Mon Sep 17 00:00:00 2001 From: Simon Abykov Date: Tue, 19 Oct 2021 17:34:13 +0100 Subject: [PATCH 36/91] Accept an empty string as the label value --- .../jivesoftware/smackx/xdata/FormField.java | 3 +- .../smackx/xdata/FormFieldTest.java | 16 ++++++++++ .../xdata/provider/DataFormProviderTest.java | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java index acf4f276d..a2ad8cb76 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java @@ -33,6 +33,7 @@ import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.HashCode; import org.jivesoftware.smack.util.MultiMap; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -600,7 +601,7 @@ public abstract class FormField implements XmlElement { * @return a reference to this builder. */ public B setLabel(String label) { - this.label = StringUtils.requireNotNullNorEmpty(label, "label must not be null or empty"); + this.label = Objects.requireNonNull(label, "label must not be null"); return getThis(); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java index 67cd5dd89..d6d0232c9 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java @@ -17,6 +17,8 @@ package org.jivesoftware.smackx.xdata; import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; import org.jxmpp.jid.JidTestUtil; @@ -35,4 +37,18 @@ class FormFieldTest { assertXmlSimilar(expectedXml, xml); } + @Test + public void testEmptyLabel() { + TextSingleFormField.Builder builder = FormField.textSingleBuilder("type"); + builder.setLabel(""); + TextSingleFormField formField = builder.build(); + + assertEquals("", formField.getLabel()); + } + + @Test + public void testThrowExceptionWhenNullLabel() { + TextSingleFormField.Builder builder = FormField.textSingleBuilder("type"); + assertThrows(IllegalArgumentException.class, () -> builder.setLabel(null)); + } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java index 90093023c..3d901a8fc 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java @@ -115,4 +115,33 @@ public class DataFormProviderTest { assertEquals(2, items.size()); } + @Test + public void testRetrieveFieldWithEmptyLabel() throws XmlPullParserException, IOException, SmackParsingException { + + String form = + "" + + " Advanced User Search" + + " The following fields are available for searching. Wildcard (*) characters are allowed as part of the query." + + " " + + " jabber:iq:search" + + " " + + " " + + " " + + " " + + " " + + " true" + + " " + + " " + + " true" + + " " + + " " + + " true" + + " " + + ""; + XmlPullParser parser = PacketParserUtils.getParserFor(form); + DataForm dataForm = DataFormProvider.INSTANCE.parse(parser); + FormField usernameFormField = dataForm.getField("FORM_TYPE"); + assertEquals(FormField.Type.hidden, usernameFormField.getType()); + assertEquals("", usernameFormField.getLabel()); + } } From 19270a2eca979af371102bd0745ce91920638dcc Mon Sep 17 00:00:00 2001 From: Simon Abykov Date: Tue, 19 Oct 2021 17:34:13 +0100 Subject: [PATCH 37/91] Accept an empty string as the label value --- .../jivesoftware/smackx/xdata/FormField.java | 3 +- .../smackx/xdata/FormFieldTest.java | 16 ++++++++++ .../xdata/provider/DataFormProviderTest.java | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java index e87d2e5c6..667bdf869 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java @@ -33,6 +33,7 @@ import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.HashCode; import org.jivesoftware.smack.util.MultiMap; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -600,7 +601,7 @@ public abstract class FormField implements FullyQualifiedElement { * @return a reference to this builder. */ public B setLabel(String label) { - this.label = StringUtils.requireNotNullNorEmpty(label, "label must not be null or empty"); + this.label = Objects.requireNonNull(label, "label must not be null"); return getThis(); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java index 67cd5dd89..d6d0232c9 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java @@ -17,6 +17,8 @@ package org.jivesoftware.smackx.xdata; import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; import org.jxmpp.jid.JidTestUtil; @@ -35,4 +37,18 @@ class FormFieldTest { assertXmlSimilar(expectedXml, xml); } + @Test + public void testEmptyLabel() { + TextSingleFormField.Builder builder = FormField.textSingleBuilder("type"); + builder.setLabel(""); + TextSingleFormField formField = builder.build(); + + assertEquals("", formField.getLabel()); + } + + @Test + public void testThrowExceptionWhenNullLabel() { + TextSingleFormField.Builder builder = FormField.textSingleBuilder("type"); + assertThrows(IllegalArgumentException.class, () -> builder.setLabel(null)); + } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java index f25eb67d3..c6be83817 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java @@ -114,4 +114,33 @@ public class DataFormProviderTest { assertEquals(2, items.size()); } + @Test + public void testRetrieveFieldWithEmptyLabel() throws XmlPullParserException, IOException, SmackParsingException { + + String form = + "" + + " Advanced User Search" + + " The following fields are available for searching. Wildcard (*) characters are allowed as part of the query." + + " " + + " jabber:iq:search" + + " " + + " " + + " " + + " " + + " " + + " true" + + " " + + " " + + " true" + + " " + + " " + + " true" + + " " + + ""; + XmlPullParser parser = PacketParserUtils.getParserFor(form); + DataForm dataForm = DataFormProvider.INSTANCE.parse(parser); + FormField usernameFormField = dataForm.getField("FORM_TYPE"); + assertEquals(FormField.Type.hidden, usernameFormField.getType()); + assertEquals("", usernameFormField.getLabel()); + } } From d0be6a6216bc6ac95f02b946e20895370157f111 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 20 Oct 2021 19:02:19 +0200 Subject: [PATCH 38/91] [build] Remove OSS Sonatype Snapshot repository Having the OSS Sonatype Snapshot repository searched causes build failures if there snapshot dependencies are resolved due the javadoc linking: javadoc: error - Error fetching URL: https://jxmpp.org/releases/1.0.3-SNAPSHOT/javadoc/ > Task :javadocAll javadoc: error - Error fetching URL: https://minidns.org/releases/1.0.2-SNAPSHOT/javadoc/ 2 errors --- build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle b/build.gradle index 3991e96ed..98782ec68 100644 --- a/build.gradle +++ b/build.gradle @@ -172,10 +172,6 @@ allprojects { repositories { mavenLocal() mavenCentral() - // Add OSS Sonatype Snapshot repository - maven { - url 'https://oss.sonatype.org/content/repositories/snapshots' - } } tasks.withType(JavaCompile) { From d8ce2d335bc50d2738b6b4759b54cdedb6aa61bc Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Wed, 20 Oct 2021 22:24:41 +0200 Subject: [PATCH 39/91] Add missing stream namespace to xml declaration Fixes 7199003f98a924f11a3cea550cbde852f215c764 --- .../java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index a88507719..76d8271ec 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -203,7 +203,8 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { } try { - XmlPullParser parser = PacketParserUtils.getParserFor(""); + XmlPullParser parser = PacketParserUtils.getParserFor( + ""); onStreamOpen(parser); } catch (XmlPullParserException | IOException e) { throw new AssertionError("Failed to setup stream environment", e); From 4ae3fbb073d221536a10806e5d950a7fe111bb27 Mon Sep 17 00:00:00 2001 From: Jonathan Lennox Date: Fri, 22 Oct 2021 19:55:25 +0000 Subject: [PATCH 40/91] Add removeExtension methods to StanzaBuilder. --- .../jivesoftware/smack/packet/StanzaBuilder.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java index d37ce41b2..0dca1296f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java @@ -184,6 +184,19 @@ public abstract class StanzaBuilder> implements Stanz return getThis(); } + public final B removeExtension(String elementName, String namespace) { + QName key = new QName(namespace, elementName); + extensionElements.remove(key); + return getThis(); + } + + public final B removeExtension(ExtensionElement extension) { + QName key = extension.getQName(); + List list = extensionElements.getAll(key); + list.remove(extension); + return getThis(); + } + public abstract Stanza build(); public abstract B getThis(); From 22b2efc6a675d8f25e4434b704e2696484c25468 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 25 Oct 2021 17:00:14 +0200 Subject: [PATCH 41/91] Update NOTICE file --- NOTICE | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/NOTICE b/NOTICE index 2c4a2fd15..495ab3409 100644 --- a/NOTICE +++ b/NOTICE @@ -29,6 +29,7 @@ Chris Deering Christoph Fiehe Craig Hesling Damian Minkov +Dan Caseley Daniele Ricci Daniel Henninger Daniel Hintze @@ -44,6 +45,7 @@ Fernando Ramirez Florian Kimmann Florian Schmaus Francisco Vives +Frank Matheron Gaston Dombiak Georg Lukas Gilles Cornu @@ -64,6 +66,7 @@ Jay Kline Jeff Williams Jesus Fuentes John Haubrich +Jonathan Lennox Júlio Cesar Bueno Cotta Lars Noschinski Luca Stucchi @@ -82,6 +85,7 @@ Pete Matern Piotr Nosek Rajat Kumar Gupta Robin Collier +Simon Abykov Simon Schuster Son Goku Tairs Rzajevs @@ -98,4 +102,4 @@ V Lau Vyacheslav Blinov Wolf Posdorfer Xiaowei YAN -Yash Thakkar \ No newline at end of file +Yash Thakkar From 53d385ab93d263268f4561a48389ea21ab4def42 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 29 Oct 2021 21:44:32 +0200 Subject: [PATCH 42/91] Bump PGPainless to 0.2.19 --- smack-openpgp/build.gradle | 3 ++- .../ox/crypto/PainlessOpenPgpProvider.java | 3 +-- .../ox/PainlessOpenPgpProviderTest.java | 18 +++++++--------- .../src/test/resources/logback-test.xml | 21 +++++++++++++++++++ 4 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 smack-openpgp/src/test/resources/logback-test.xml diff --git a/smack-openpgp/build.gradle b/smack-openpgp/build.gradle index 112a2c4d8..24212ccf1 100644 --- a/smack-openpgp/build.gradle +++ b/smack-openpgp/build.gradle @@ -8,9 +8,10 @@ dependencies { api project(':smack-extensions') api project(':smack-experimental') - api 'org.pgpainless:pgpainless-core:0.2.8' + api 'org.pgpainless:pgpainless-core:0.2.19' testImplementation "org.bouncycastle:bcprov-jdk15on:${bouncyCastleVersion}" + testImplementation 'ch.qos.logback:logback-classic:1.2.6' testFixturesApi(testFixtures(project(":smack-core"))) testImplementation group: 'commons-io', name: 'commons-io', version: "$commonsIoVersion" diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java index 110bafe43..2fa3bbe77 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java @@ -29,7 +29,6 @@ import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.stringencoder.Base64; - import org.jivesoftware.smackx.ox.OpenPgpContact; import org.jivesoftware.smackx.ox.OpenPgpMessage; import org.jivesoftware.smackx.ox.OpenPgpSelf; @@ -90,7 +89,7 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider { SigningOptions signOpts = new SigningOptions(); signOpts.addInlineSignature(getStore().getKeyRingProtector(), self.getSigningKeyRing(), - "xmpp:" + self.getJid().toString(), DocumentSignatureType.BINARY_DOCUMENT); + DocumentSignatureType.BINARY_DOCUMENT); EncryptionStream cipherStream = PGPainless.encryptAndOrSign() .onOutputStream(cipherText) diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java index c3fdb45e0..5f1210367 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java @@ -47,29 +47,27 @@ import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.jxmpp.jid.BareJid; import org.jxmpp.jid.Jid; import org.jxmpp.jid.JidTestUtil; +import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.protection.UnprotectedKeysProtector; import org.pgpainless.key.util.KeyRingUtils; public class PainlessOpenPgpProviderTest extends SmackTestSuite { - private static final File storagePath; + private static File storagePath; private static final BareJid alice = JidTestUtil.BARE_JID_1; private static final BareJid bob = JidTestUtil.BARE_JID_2; - static { + @BeforeEach + @AfterEach + public void deletePath() throws IOException { storagePath = new File(org.apache.commons.io.FileUtils.getTempDirectory(), "smack-painlessprovidertest"); - } - - @BeforeClass - @AfterClass - public static void deletePath() throws IOException { org.apache.commons.io.FileUtils.deleteDirectory(storagePath); } @@ -142,7 +140,7 @@ public class PainlessOpenPgpProviderTest extends SmackTestSuite { // Decrypt and Verify decrypted = bobProvider.decryptAndOrVerify(bobConnection, encrypted.getElement(), bobSelf, aliceForBob); - OpenPgpV4Fingerprint decryptionFingerprint = decrypted.getMetadata().getDecryptionKey().getFingerprint(); + OpenPgpFingerprint decryptionFingerprint = decrypted.getMetadata().getDecryptionKey().getFingerprint(); assertTrue(bobSelf.getSecretKeys().contains(decryptionFingerprint.getKeyId())); assertTrue(decrypted.getMetadata().containsVerifiedSignatureFrom(alicePubKeys)); diff --git a/smack-openpgp/src/test/resources/logback-test.xml b/smack-openpgp/src/test/resources/logback-test.xml new file mode 100644 index 000000000..c77b1b2f7 --- /dev/null +++ b/smack-openpgp/src/test/resources/logback-test.xml @@ -0,0 +1,21 @@ + + + + System.out + + %blue(%-5level) %green(%logger{35}) - %msg %n + + + + + System.err + + %blue(%-5level) %green(%logger{35}) - %msg %n + + + + + + + + \ No newline at end of file From 22cf7bace8f4d0dc9f0bfd74bbe4b76ff1013c2a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 1 Nov 2021 17:53:01 +0100 Subject: [PATCH 43/91] [resources] Rename get-contributors.sh to generate-notice-file --- resources/generate-notice-file | 38 ++++++++++++++++++++++++++++++++++ resources/get-contributors.sh | 7 ------- 2 files changed, 38 insertions(+), 7 deletions(-) create mode 100755 resources/generate-notice-file delete mode 100755 resources/get-contributors.sh diff --git a/resources/generate-notice-file b/resources/generate-notice-file new file mode 100755 index 000000000..60c66bbce --- /dev/null +++ b/resources/generate-notice-file @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +SMACK_DIR=$(realpath "${SCRIPT_DIR}/..") + +cd "${SMACK_DIR}" + +TEMPFILE=$(mktemp) + +cleanup() { + rm "${TEMPFILE}" +} +trap cleanup EXIT + +git shortlog -s |\ + cut -f2- |\ + grep -v '(no author)' |\ + grep '\w \w.*' |\ + sort \ + > "${TEMPFILE}" + +readonly NOTICE_FILE="${SMACK_DIR}/NOTICE" + +cat < "${NOTICE_FILE}" + Smack + + An open-source XMPP library + maintained by Florian Schmaus + + https://igniterealtime.org/projects/smack + + +Authors: + +EOF + +cat "${TEMPFILE}" >> "${NOTICE_FILE}" diff --git a/resources/get-contributors.sh b/resources/get-contributors.sh deleted file mode 100755 index c31236464..000000000 --- a/resources/get-contributors.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -git shortlog -s |\ - cut -f2- |\ - grep -v '(no author)' |\ - grep '\w \w.*' |\ - sort From 5731e61782e33a0ae5496ce8fe03b64bb86807a0 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 1 Nov 2021 17:54:01 +0100 Subject: [PATCH 44/91] Smack 4.4.4 --- resources/releasedocs/changelog.html | 21 +++++++++++++++++++++ version | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html index 8d8e20366..92b5b2ae9 100644 --- a/resources/releasedocs/changelog.html +++ b/resources/releasedocs/changelog.html @@ -141,6 +141,27 @@ hr {
+

4.4.4 -- 2021-11-01

+ +

Bug +

+
    +
  • [SMACK-916] - XMPPErrorException.stanza is missing a getter method +
  • +
  • [SMACK-915] - Smack does not process MUC destroy message if they contain 'status' +
  • +
  • [SMACK-914] - MultiUserChat may be become unjoinable due to a race condition +
  • +
  • [SMACK-913] - MultiUserChat.serviceSupportsStableIds\(\) may throws a NullPointerException +
  • +
  • [SMACK-912] - Smack does not start the local SOCKS5 proxy automatically +
  • +
  • [SMACK-910] - FormNode and FormNodeProvide should handle non-existent DataForm +
  • +
  • [SMACK-909] - Must use the raw character data of a form field in entity caps hash calculation +
  • +
+

4.4.3 -- 2021-07-06

Bug diff --git a/version b/version index fb1df371a..cbe06cdbf 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.4-SNAPSHOT +4.4.4 From 403890d9884fac25073413aa7d8ce5238e0f7770 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 1 Nov 2021 18:10:35 +0100 Subject: [PATCH 45/91] Smack 4.4.5-SNAPSHOT --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index cbe06cdbf..dd9c2fc4f 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.4 +4.4.5-SNAPSHOT From 7dc8cc176e06ca7b8c8e73b90e7ac13f40294160 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 1 Nov 2021 19:27:09 +0100 Subject: [PATCH 46/91] [core] Add DoOnce utility --- .../org/jivesoftware/smack/util/DoOnce.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/DoOnce.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/DoOnce.java b/smack-core/src/main/java/org/jivesoftware/smack/util/DoOnce.java new file mode 100644 index 000000000..2037f8802 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/DoOnce.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2021 Florian Schmaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.util; + +public final class DoOnce { + + private boolean done; + + public void once(Runnable run) { + if (done) return; + + done = true; + run.run(); + } +} From f2c2f45f18563e8c5382e8cb385683f70ecedb8d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 1 Nov 2021 19:27:22 +0100 Subject: [PATCH 47/91] [jiveproperties] Use DoOnce utility to log "JavaObject not enabled" --- .../provider/JivePropertiesExtensionProvider.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java index 402e296d5..6d99c1637 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java @@ -26,6 +26,7 @@ import java.util.logging.Logger; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smack.util.DoOnce; import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -35,6 +36,8 @@ import org.jivesoftware.smackx.jiveproperties.packet.JivePropertiesExtension; public class JivePropertiesExtensionProvider extends ExtensionElementProvider { + private static final DoOnce LOG_OBJECT_NOT_ENABLED = new DoOnce(); + private static final Logger LOGGER = Logger.getLogger(JivePropertiesExtensionProvider.class.getName()); /** @@ -113,7 +116,10 @@ public class JivePropertiesExtensionProvider extends ExtensionElementProvider LOGGER.severe( + "JavaObject is not enabled. Enable with JivePropertiesManager.setJavaObjectEnabled(true)") + ); } } if (name != null && value != null) { From fdffd58d259f64e0457cd027c719150444b203df Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Wed, 14 Oct 2020 16:21:22 +0200 Subject: [PATCH 48/91] Add method to remove registered CAPS listener ServiceDiscoveryManager allows a listener for CAPS changes to be registered. It should also allow to remove a previously registered listener. --- .../jivesoftware/smackx/disco/ServiceDiscoveryManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index b37209ad4..20714db77 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -921,6 +921,10 @@ public final class ServiceDiscoveryManager extends Manager { return entityCapabilitiesChangedListeners.add(entityCapabilitiesChangedListener); } + public boolean removeEntityCapabilitiesChangedListener(EntityCapabilitiesChangedListener entityCapabilitiesChangedListener) { + return entityCapabilitiesChangedListeners.remove(entityCapabilitiesChangedListener); + } + private static final int RENEW_ENTITY_CAPS_DELAY_MILLIS = 25; private ScheduledAction renewEntityCapsScheduledAction; From 8736e3e4345ad49e2e922e999770cdd31c10a1e5 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 5 Nov 2021 10:19:26 +0100 Subject: [PATCH 49/91] [core] Remove erroneous "assert !connected" in connect() Connections like XMPPTCPConnection may still reported connected, if they are, for example disconnected but resumable. This is already accounted for in throwAlreadyConnectedExceptionIfAppropriate(), hence the assert is unnecessary and leads to false negatives for XMPPTCPConnection. For the sake of completeness, the right condition, assuming XMPPTCPConnection is used, for the assert would be: assert !connected || disconnectedButResumable; --- .../java/org/jivesoftware/smack/AbstractXMPPConnection.java | 3 --- 1 file changed, 3 deletions(-) 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 0c2c59a14..3f913120e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -521,9 +521,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { closingStreamReceived = false; streamId = null; - // The connection should not be connected nor marked as such prior calling connectInternal(). - assert !connected; - try { // Perform the actual connection to the XMPP service connectInternal(); From 11cc2d8d771c34c10b54891369fb132322f873c1 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 30 Nov 2021 10:49:55 +0100 Subject: [PATCH 50/91] [core] Align behavior of getParserFor(InputStream) and getParserFor(Reader) Reported-by: Ingo Bauersachs --- .../java/org/jivesoftware/smack/util/PacketParserUtils.java | 6 +++--- .../java/org/jivesoftware/smackx/amp/AMPExtensionTest.java | 2 -- .../smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) 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 9b08d6572..ddcb5a8ac 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 @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2019 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2019-2021 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,9 +76,9 @@ public class PacketParserUtils { return getParserFor(new StringReader(stanza)); } - public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException { + public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException, IOException { InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - return SmackXmlParser.newXmlParser(inputStreamReader); + return getParserFor(inputStreamReader); } public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException { diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/amp/AMPExtensionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/amp/AMPExtensionTest.java index af8426029..f8df83f83 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/amp/AMPExtensionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/amp/AMPExtensionTest.java @@ -67,7 +67,6 @@ public class AMPExtensionTest { AMPExtensionProvider ampProvider = new AMPExtensionProvider(); XmlPullParser parser = PacketParserUtils.getParserFor(INCORRECT_RECEIVING_STANZA_STREAM); - assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next()); assertEquals(AMPExtension.ELEMENT, parser.getName()); ExtensionElement extension = ampProvider.parse(parser); @@ -85,7 +84,6 @@ public class AMPExtensionTest { AMPExtensionProvider ampProvider = new AMPExtensionProvider(); XmlPullParser parser = PacketParserUtils.getParserFor(CORRECT_SENDING_STANZA_STREAM); - assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next()); assertEquals(AMPExtension.ELEMENT, parser.getName()); ExtensionElement extension = ampProvider.parse(parser); assertTrue(extension instanceof AMPExtension); diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java index 67557c553..5505e4dda 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java @@ -39,7 +39,6 @@ public class XHTMLExtensionProviderTest { public void parsesWell() throws IOException, XmlPullParserException { InputStream inputStream = getClass().getResourceAsStream(XHTML_EXTENSION_SAMPLE_RESOURCE_NAME); XmlPullParser parser = PacketParserUtils.getParserFor(inputStream); - parser.next(); XHTMLExtensionProvider provider = new XHTMLExtensionProvider(); ExtensionElement extension = provider.parse(parser, parser.getDepth(), null); From e3c50aeeb75bb4fc98d499b6c6912690f163586a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 7 Dec 2021 19:29:08 +0100 Subject: [PATCH 51/91] Bump PGPainless to 1.0.0-rc6 --- smack-openpgp/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smack-openpgp/build.gradle b/smack-openpgp/build.gradle index 24212ccf1..81c6accbd 100644 --- a/smack-openpgp/build.gradle +++ b/smack-openpgp/build.gradle @@ -8,10 +8,9 @@ dependencies { api project(':smack-extensions') api project(':smack-experimental') - api 'org.pgpainless:pgpainless-core:0.2.19' + api 'org.pgpainless:pgpainless-core:1.0.0-rc6' testImplementation "org.bouncycastle:bcprov-jdk15on:${bouncyCastleVersion}" - testImplementation 'ch.qos.logback:logback-classic:1.2.6' testFixturesApi(testFixtures(project(":smack-core"))) testImplementation group: 'commons-io', name: 'commons-io', version: "$commonsIoVersion" From 819ed87a178e71c6c40b70d6a5f079751337b51f Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Mon, 12 Oct 2020 16:58:33 +0200 Subject: [PATCH 52/91] [sinttest] Wait for notification filter to propagate The UserTuneIntegrationTest, in rapid succession: - add a listener for PEP-published usertune data - publishes a usertune - waits for a notification to arrive Implicit to adding the listener is the publication of a change in Pubsub notification filtering. This can involve a stanza handshake, as CAPS is involved. A race condition exists where the usertune data can be published before the notification filter has been properly applied. The changes in this commit add a synchronzation point that ensures that the notification filter is in place, before the usertune data is published. Co-authored-by: Paul Schaub --- .../GeolocationIntegrationTest.java | 186 ++++++++++++++-- .../smackx/mood/MoodIntegrationTest.java | 172 +++++++++++++-- .../usertune/UserTuneIntegrationTest.java | 208 ++++++++++++++---- 3 files changed, 485 insertions(+), 81 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java index 32f1d32b3..7e149ff3b 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java @@ -23,8 +23,9 @@ import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; -import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.disco.EntityCapabilitiesChangedListener; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.geoloc.GeoLocationManager; import org.jivesoftware.smackx.geoloc.packet.GeoLocation; import org.jivesoftware.smackx.pep.PepEventListener; @@ -35,7 +36,7 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; -import org.jxmpp.jid.EntityBareJid; +import org.junit.jupiter.api.Assertions; import org.jxmpp.util.XmppDateTime; public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest { @@ -49,10 +50,21 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest { glm2 = GeoLocationManager.getInstanceFor(conTwo); } + @AfterClass + public void unsubscribe() throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + } + + /** + * Verifies that a notification is sent when a publication is received, assuming that notification filtering + * has been adjusted to allow for the notification to be delivered. + * + * @throws Exception if the test fails + */ @SmackIntegrationTest - public void test() throws TimeoutException, Exception { + public void testNotification() throws Exception { GeoLocation.Builder builder = GeoLocation.builder(); - GeoLocation geoLocation1 = builder.setAccuracy(23d) + GeoLocation data = builder.setAccuracy(23d) .setAlt(1000d) .setAltAccuracy(10d) .setArea("Delhi") @@ -77,31 +89,163 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest { .build(); IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); - final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint(); - final PepEventListener geoLocationListener = new PepEventListener() { - @Override - public void onPepEvent(EntityBareJid jid, GeoLocation geoLocation, String id, Message message) { - if (geoLocation.equals(geoLocation1)) { - geoLocationReceived.signal(); - } else { - geoLocationReceived.signalFailure("Received non matching GeoLocation"); - } + final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint(); + + final PepEventListener geoLocationListener = (jid, geoLocation, id, message) -> { + if (geoLocation.equals(data)) { + geoLocationReceived.signal(); } }; - glm2.addGeoLocationListener(geoLocationListener); - try { - glm1.publishGeoLocation(geoLocation1); - geoLocationReceived.waitForResult(timeout); + // Register ConTwo's interest in receiving geolocation notifications, and wait for that interest to have been propagated. + registerListenerAndWait(glm2, ServiceDiscoveryManager.getInstanceFor(conTwo), geoLocationListener); + + // Publish the data. + glm1.publishGeoLocation(data); // for the purpose of this test, this needs not be blocking/use publishAndWait(); + + // Wait for the data to be received. + try { + Object result = geoLocationReceived.waitForResult(timeout); + + // Explicitly assert the success case. + Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); + } catch (TimeoutException e) { + Assertions.fail("Expected to receive a PEP notification, but did not."); + } } finally { - glm2.removeGeoLocationListener(geoLocationListener); + unregisterListener(glm2, geoLocationListener); } } - @AfterClass - public void unsubscribe() throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + /** + * Verifies that a notification for a previously sent publication is received as soon as notification filtering + * has been adjusted to allow for the notification to be delivered. + * + * @throws Exception if the test fails + */ + @SmackIntegrationTest + public void testNotificationAfterFilterChange() throws Exception { + GeoLocation.Builder builder = GeoLocation.builder(); + GeoLocation data = builder.setAccuracy(12d) + .setAlt(999d) + .setAltAccuracy(9d) + .setArea("Amsterdam") + .setBearing(9d) + .setBuilding("Test Building") + .setCountry("Netherlands") + .setCountryCode("NL") + .setDescription("My Description") + .setFloor("middle") + .setLat(25.098345d) + .setLocality("brilliant") + .setLon(77.992034) + .setPostalcode("110085") + .setRegion("North") + .setRoom("small") + .setSpeed(250.0d) + .setStreet("Wall Street") + .setText("Unit Testing GeoLocation 2") + .setTimestamp(XmppDateTime.parseDate("2007-02-19")) + .setTzo("+5:30") + .setUri(new URI("http://xmpp.org")) + .build(); + + IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); + + final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint(); + + final PepEventListener geoLocationListener = (jid, geoLocation, id, message) -> { + if (geoLocation.equals(data)) { + geoLocationReceived.signal(); + } + }; + + // TODO Ensure that pre-existing filtering notification excludes geolocation. + try { + // Publish the data + publishAndWait(glm1, ServiceDiscoveryManager.getInstanceFor(conOne), data); + + // Adds listener, which implicitly publishes a disco/info filter for geolocation notification. + registerListenerAndWait(glm2, ServiceDiscoveryManager.getInstanceFor(conTwo), geoLocationListener); + + // Wait for the data to be received. + try { + Object result = geoLocationReceived.waitForResult(timeout); + + // Explicitly assert the success case. + Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); + } catch (TimeoutException e) { + Assertions.fail("Expected to receive a PEP notification, but did not."); + } + } finally { + unregisterListener(glm2, geoLocationListener); + } + } + + /** + * Registers a listener for GeoLocation data. This implicitly publishes a CAPS update to include a notification + * filter for the geolocation node. This method blocks until the server has indicated that this update has been + * received. + * + * @param geoManager The GeoLocationManager instance for the connection that is expected to receive data. + * @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data. + * @param listener A listener instance for GeoLocation data that is to be registered. + * + * @throws Exception if the test fails + */ + public void registerListenerAndWait(GeoLocationManager geoManager, ServiceDiscoveryManager discoManager, PepEventListener listener) throws Exception { + final SimpleResultSyncPoint notificationFilterReceived = new SimpleResultSyncPoint(); + final EntityCapabilitiesChangedListener notificationFilterReceivedListener = info -> { + if (info.containsFeature(GeoLocationManager.GEOLOCATION_NODE + "+notify")) { + notificationFilterReceived.signal(); + } + }; + + discoManager.addEntityCapabilitiesChangedListener(notificationFilterReceivedListener); + try { + geoManager.addGeoLocationListener(listener); + notificationFilterReceived.waitForResult(timeout); + } finally { + discoManager.removeEntityCapabilitiesChangedListener(notificationFilterReceivedListener); + } + } + + /** + * The functionally reverse of {@link #registerListenerAndWait(GeoLocationManager, ServiceDiscoveryManager, PepEventListener)} + * with the difference of not being a blocking operation. + * + * @param geoManager The GeoLocationManager instance for the connection that was expected to receive data. + * @param listener A listener instance for GeoLocation data that is to be removed. + */ + public void unregisterListener(GeoLocationManager geoManager, PepEventListener listener) { + // Does it make sense to have a method implementation that's one line? This is provided to allow for symmetry in the API. + geoManager.removeGeoLocationListener(listener); + } + + /** + * Publish data using PEP, and block until the server has echoed the publication back to the publishing user. + * + * @param geoManager The GeoLocationManager instance for the connection that is expected to publish data. + * @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data. + * @param data The data to be published. + * + * @throws Exception if the test fails + */ + public void publishAndWait(GeoLocationManager geoManager, ServiceDiscoveryManager discoManager, GeoLocation data) throws Exception { + final SimpleResultSyncPoint publicationEchoReceived = new SimpleResultSyncPoint(); + final PepEventListener publicationEchoListener = (jid, geoLocation, id, message) -> { + if (geoLocation.equals(data)) { + publicationEchoReceived.signal(); + } + }; + try { + registerListenerAndWait(geoManager, discoManager, publicationEchoListener); + geoManager.addGeoLocationListener(publicationEchoListener); + geoManager.publishGeoLocation(data); + } finally { + geoManager.removeGeoLocationListener(publicationEchoListener); + } } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java index d2eff1080..1a83085dc 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java @@ -16,9 +16,13 @@ */ package org.jivesoftware.smackx.mood; +import java.util.concurrent.TimeoutException; + import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.disco.EntityCapabilitiesChangedListener; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.mood.element.MoodElement; import org.jivesoftware.smackx.pep.PepEventListener; @@ -28,6 +32,7 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +import org.junit.jupiter.api.Assertions; public class MoodIntegrationTest extends AbstractSmackIntegrationTest { @@ -40,32 +45,155 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest { mm2 = MoodManager.getInstanceFor(conTwo); } - @SmackIntegrationTest - public void test() throws Exception { - IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); - - final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint(); - - final PepEventListener moodListener = (jid, moodElement, id, message) -> { - if (moodElement.getMood() == Mood.satisfied) { - moodReceived.signal(); - } - }; - mm2.addMoodListener(moodListener); - - try { - mm1.setMood(Mood.satisfied); - - moodReceived.waitForResult(timeout); - } finally { - mm2.removeMoodListener(moodListener); - } - } - @AfterClass public void unsubscribe() throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } + + /** + * Verifies that a notification is sent when a publication is received, assuming that notification filtering + * has been adjusted to allow for the notification to be delivered. + * + * @throws Exception if the test fails + */ + @SmackIntegrationTest + public void testNotification() throws Exception { + Mood data = Mood.satisfied; + + IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); + + final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint(); + + final PepEventListener moodListener = (jid, moodElement, id, message) -> { + if (moodElement.getMood().equals(data)) { + moodReceived.signal(); + } + }; + + try { + // Register ConTwo's interest in receiving mood notifications, and wait for that interest to have been propagated. + registerListenerAndWait(mm2, ServiceDiscoveryManager.getInstanceFor(conTwo), moodListener); + + // Publish the data. + mm1.setMood(data); // for the purpose of this test, this needs not be blocking/use publishAndWait(); + + // Wait for the data to be received. + try { + moodReceived.waitForResult(timeout); + } catch (TimeoutException e) { + Assertions.fail("Expected to receive a PEP notification, but did not."); + } + } finally { + unregisterListener(mm2, moodListener); + } + } + + /** + * Verifies that a notification for a previously sent publication is received as soon as notification filtering + * has been adjusted to allow for the notification to be delivered. + * + * @throws Exception if the test fails + */ + @SmackIntegrationTest + public void testNotificationAfterFilterChange() throws Exception { + Mood data = Mood.cautious; + + IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); + + final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint(); + + final PepEventListener moodListener = (jid, moodElement, id, message) -> { + if (moodElement.getMood().equals(data)) { + moodReceived.signal(); + } + }; + + // TODO Ensure that pre-existing filtering notification excludes mood. + try { + // Publish the data + publishAndWait(mm1, ServiceDiscoveryManager.getInstanceFor(conOne), data); + + // Adds listener, which implicitly publishes a disco/info filter for mood notification. + registerListenerAndWait(mm2, ServiceDiscoveryManager.getInstanceFor(conTwo), moodListener); + + // Wait for the data to be received. + try { + Object result = moodReceived.waitForResult(timeout); + + // Explicitly assert the success case. + Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); + } catch (TimeoutException e) { + Assertions.fail("Expected to receive a PEP notification, but did not."); + } + } finally { + unregisterListener(mm2, moodListener); + } + } + + /** + * Registers a listener for User Tune data. This implicitly publishes a CAPS update to include a notification + * filter for the mood node. This method blocks until the server has indicated that this update has been + * received. + * + * @param moodManager The MoodManager instance for the connection that is expected to receive data. + * @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data. + * @param listener A listener instance for Mood data that is to be registered. + * + * @throws Exception if the test fails + */ + public void registerListenerAndWait(MoodManager moodManager, ServiceDiscoveryManager discoManager, PepEventListener listener) throws Exception { + final SimpleResultSyncPoint notificationFilterReceived = new SimpleResultSyncPoint(); + final EntityCapabilitiesChangedListener notificationFilterReceivedListener = info -> { + if (info.containsFeature(MoodManager.MOOD_NODE + "+notify")) { + notificationFilterReceived.signal(); + } + }; + + discoManager.addEntityCapabilitiesChangedListener(notificationFilterReceivedListener); + try { + moodManager.addMoodListener(listener); + notificationFilterReceived.waitForResult(timeout); + } finally { + discoManager.removeEntityCapabilitiesChangedListener(notificationFilterReceivedListener); + } + } + + /** + * The functionally reverse of {@link #registerListenerAndWait(MoodManager, ServiceDiscoveryManager, PepEventListener)} + * with the difference of not being a blocking operation. + * + * @param moodManager The MoodManager instance for the connection that was expected to receive data. + * @param listener A listener instance for Mood data that is to be removed. + */ + public void unregisterListener(MoodManager moodManager, PepEventListener listener) { + // Does it make sense to have a method implementation that's one line? This is provided to allow for symmetry in the API. + moodManager.removeMoodListener(listener); + } + + /** + * Publish data using PEP, and block until the server has echoed the publication back to the publishing user. + * + * @param moodManager The MoodManager instance for the connection that is expected to publish data. + * @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data. + * @param data The data to be published. + * + * @throws Exception if the test fails + */ + public void publishAndWait(MoodManager moodManager, ServiceDiscoveryManager discoManager, Mood data) throws Exception { + final SimpleResultSyncPoint publicationEchoReceived = new SimpleResultSyncPoint(); + final PepEventListener publicationEchoListener = (jid, moodElement, id, message) -> { + if (moodElement.getMood().equals(data)) { + publicationEchoReceived.signal(); + } + }; + try { + registerListenerAndWait(moodManager, discoManager, publicationEchoListener); + moodManager.addMoodListener(publicationEchoListener); + moodManager.setMood(data); + } finally { + moodManager.removeMoodListener(publicationEchoListener); + } + } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java index 0309f9af0..ac3fa0ac0 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java @@ -17,12 +17,14 @@ package org.jivesoftware.smackx.usertune; import java.net.URI; +import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.disco.EntityCapabilitiesChangedListener; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.pep.PepEventListener; import org.jivesoftware.smackx.usertune.element.UserTuneElement; @@ -32,7 +34,7 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; -import org.jxmpp.jid.EntityBareJid; +import org.junit.jupiter.api.Assertions; public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { @@ -45,46 +47,176 @@ public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { utm2 = UserTuneManager.getInstanceFor(conTwo); } - @SmackIntegrationTest - public void test() throws Exception { - URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9"); - UserTuneElement.Builder builder = UserTuneElement.getBuilder(); - UserTuneElement userTuneElement1 = builder.setArtist("Yes") - .setLength(686) - .setRating(8) - .setSource("Yessongs") - .setTitle("Heart of the Sunrise") - .setTrack("3") - .setUri(uri) - .build(); - - IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); - - final SimpleResultSyncPoint userTuneReceived = new SimpleResultSyncPoint(); - - final PepEventListener userTuneListener = new PepEventListener() { - @Override - public void onPepEvent(EntityBareJid jid, UserTuneElement userTuneElement, String id, Message message) { - if (userTuneElement.equals(userTuneElement1)) { - userTuneReceived.signal(); - } - } - }; - - utm2.addUserTuneListener(userTuneListener); - - try { - utm1.publishUserTune(userTuneElement1); - userTuneReceived.waitForResult(timeout); - } finally { - utm2.removeUserTuneListener(userTuneListener); - } - } - @AfterClass public void unsubscribe() throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } + + /** + * Verifies that a notification is sent when a publication is received, assuming that notification filtering + * has been adjusted to allow for the notification to be delivered. + * + * @throws Exception if the test fails + */ + @SmackIntegrationTest + public void testNotification() throws Exception { + URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9"); + UserTuneElement.Builder builder = UserTuneElement.getBuilder(); + UserTuneElement data = builder.setArtist("Yes") + .setLength(686) + .setRating(8) + .setSource("Yessongs") + .setTitle("Heart of the Sunrise") + .setTrack("3") + .setUri(uri) + .build(); + + IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); + + final SimpleResultSyncPoint userTuneReceived = new SimpleResultSyncPoint(); + + final PepEventListener userTuneListener = (jid, userTune, id, message) -> { + if (userTune.equals(data)) { + userTuneReceived.signal(); + } + }; + + try { + // Register ConTwo's interest in receiving user tune notifications, and wait for that interest to have been propagated. + registerListenerAndWait(utm2, ServiceDiscoveryManager.getInstanceFor(conTwo), userTuneListener); + + // Publish the data. + utm1.publishUserTune(data); // for the purpose of this test, this needs not be blocking/use publishAndWait(); + + // Wait for the data to be received. + try { + Object result = userTuneReceived.waitForResult(timeout); + + // Explicitly assert the success case. + Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); + } catch (TimeoutException e) { + Assertions.fail("Expected to receive a PEP notification, but did not."); + } + } finally { + unregisterListener(utm2, userTuneListener); + } + } + + /** + * Verifies that a notification for a previously sent publication is received as soon as notification filtering + * has been adjusted to allow for the notification to be delivered. + * + * @throws Exception if the test fails + */ + @SmackIntegrationTest + public void testNotificationAfterFilterChange() throws Exception { + URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#8"); + UserTuneElement.Builder builder = UserTuneElement.getBuilder(); + UserTuneElement data = builder.setArtist("No") + .setLength(306) + .setRating(3) + .setSource("NoSongs") + .setTitle("Sunrise of the Heart") + .setTrack("2") + .setUri(uri) + .build(); + + IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); + + final SimpleResultSyncPoint userTuneReceived = new SimpleResultSyncPoint(); + + final PepEventListener userTuneListener = (jid, userTune, id, message) -> { + if (userTune.equals(data)) { + userTuneReceived.signal(); + } + }; + + // TODO Ensure that pre-existing filtering notification excludes userTune. + try { + // Publish the data + publishAndWait(utm1, ServiceDiscoveryManager.getInstanceFor(conOne), data); + + // Adds listener, which implicitly publishes a disco/info filter for userTune notification. + registerListenerAndWait(utm2, ServiceDiscoveryManager.getInstanceFor(conTwo), userTuneListener); + + // Wait for the data to be received. + try { + Object result = userTuneReceived.waitForResult(timeout); + + // Explicitly assert the success case. + Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); + } catch (TimeoutException e) { + Assertions.fail("Expected to receive a PEP notification, but did not."); + } + } finally { + unregisterListener(utm2, userTuneListener); + } + } + + /** + * Registers a listener for User Tune data. This implicitly publishes a CAPS update to include a notification + * filter for the usertune node. This method blocks until the server has indicated that this update has been + * received. + * + * @param userTuneManager The UserTuneManager instance for the connection that is expected to receive data. + * @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data. + * @param listener A listener instance for UserTune data that is to be registered. + * + * @throws Exception if the test fails + */ + public void registerListenerAndWait(UserTuneManager userTuneManager, ServiceDiscoveryManager discoManager, PepEventListener listener) throws Exception { + final SimpleResultSyncPoint notificationFilterReceived = new SimpleResultSyncPoint(); + final EntityCapabilitiesChangedListener notificationFilterReceivedListener = info -> { + if (info.containsFeature(UserTuneManager.USERTUNE_NODE + "+notify")) { + notificationFilterReceived.signal(); + } + }; + + discoManager.addEntityCapabilitiesChangedListener(notificationFilterReceivedListener); + try { + userTuneManager.addUserTuneListener(listener); + notificationFilterReceived.waitForResult(timeout); + } finally { + discoManager.removeEntityCapabilitiesChangedListener(notificationFilterReceivedListener); + } + } + + /** + * The functionally reverse of {@link #registerListenerAndWait(UserTuneManager, ServiceDiscoveryManager, PepEventListener)} + * with the difference of not being a blocking operation. + * + * @param userTuneManager The UserTuneManager instance for the connection that was expected to receive data. + * @param listener A listener instance for UserTune data that is to be removed. + */ + public void unregisterListener(UserTuneManager userTuneManager, PepEventListener listener) { + // Does it make sense to have a method implementation that's one line? This is provided to allow for symmetry in the API. + userTuneManager.removeUserTuneListener(listener); + } + + /** + * Publish data using PEP, and block until the server has echoed the publication back to the publishing user. + * + * @param userTuneManager The UserTuneManager instance for the connection that is expected to publish data. + * @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data. + * @param data The data to be published. + * + * @throws Exception if the test fails + */ + public void publishAndWait(UserTuneManager userTuneManager, ServiceDiscoveryManager discoManager, UserTuneElement data) throws Exception { + final SimpleResultSyncPoint publicationEchoReceived = new SimpleResultSyncPoint(); + final PepEventListener publicationEchoListener = (jid, userTune, id, message) -> { + if (userTune.equals(data)) { + publicationEchoReceived.signal(); + } + }; + try { + registerListenerAndWait(userTuneManager, discoManager, publicationEchoListener); + userTuneManager.addUserTuneListener(publicationEchoListener); + userTuneManager.publishUserTune(data); + } finally { + userTuneManager.removeUserTuneListener(publicationEchoListener); + } + } } From 33e805325837d244a3c5e9ce96b0116a716ab6c1 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 20:50:56 +0100 Subject: [PATCH 53/91] [sinttest] Remove unnecessary catch in UserTuneIntegrationTest --- .../smackx/usertune/UserTuneIntegrationTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java index ac3fa0ac0..653bded72 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java @@ -91,14 +91,10 @@ public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { utm1.publishUserTune(data); // for the purpose of this test, this needs not be blocking/use publishAndWait(); // Wait for the data to be received. - try { - Object result = userTuneReceived.waitForResult(timeout); + Object result = userTuneReceived.waitForResult(timeout); - // Explicitly assert the success case. - Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); - } catch (TimeoutException e) { - Assertions.fail("Expected to receive a PEP notification, but did not."); - } + // Explicitly assert the success case. + Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); } finally { unregisterListener(utm2, userTuneListener); } From 400a2b2564916647947ab61310a6708dfd059640 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 20:51:33 +0100 Subject: [PATCH 54/91] [sinttest] Use popular desktop OS in SoftwareInfoIntegrationTest values --- .../smackx/softwareInfo/SoftwareInfoIntegrationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java index f289c96be..3bf111f53 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java @@ -73,10 +73,10 @@ public class SoftwareInfoIntegrationTest extends AbstractSmackIntegrationTest { .build(); SoftwareInfoForm softwareInfoForm = builder.setIcon(mediaElement) - .setOS("Windows") - .setOSVersion("XP") - .setSoftware("Exodus") - .setSoftwareVersion("0.9.1") + .setOS("Linux") + .setOSVersion("Debian") + .setSoftware("Gajim") + .setSoftwareVersion("1.4.0") .build(); return softwareInfoForm; } From 4e2d0035ac69aa4e154f27d80f4122e79b48446f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 20:52:22 +0100 Subject: [PATCH 55/91] [sinttest] Increase 'try' block in MultiUserChatRolesAffiliations*Test Fixes: aff6283798afebf0f6529f9c027fa0f562d09f0f --- ...AffiliationsPrivilegesIntegrationTest.java | 187 +++++++++--------- 1 file changed, 92 insertions(+), 95 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java index 2010ab146..d26e22593 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java @@ -82,13 +82,14 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - - // This implicitly tests "The service MUST add the user to the moderator list and then inform the admin of - // success" in §9.6, since it'll throw on either an error IQ or on no response. - mucAsSeenByOne.grantModerator(nicknameTwo); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + + // This implicitly tests "The service MUST add the user to the moderator list and then inform the admin of + // success" in §9.6, since it'll throw on either an error IQ or on no response. + mucAsSeenByOne.grantModerator(nicknameTwo); + resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -130,13 +131,13 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - mucAsSeenByOne.grantModerator(nicknameTwo); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.grantModerator(nicknameTwo); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -178,12 +179,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - - mucAsSeenByOne.grantModerator(nicknameTwo); - mucAsSeenByOne.revokeModerator(nicknameTwo); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + + mucAsSeenByOne.grantModerator(nicknameTwo); + mucAsSeenByOne.revokeModerator(nicknameTwo); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -225,14 +226,14 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - mucAsSeenByOne.grantModerator(nicknameTwo); - mucAsSeenByOne.revokeModerator(nicknameTwo); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.grantModerator(nicknameTwo); + mucAsSeenByOne.revokeModerator(nicknameTwo); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -273,10 +274,10 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByOne.revokeVoice(nicknameTwo); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByOne.revokeVoice(nicknameTwo); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -318,13 +319,13 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - mucAsSeenByOne.revokeVoice(nicknameTwo); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.revokeVoice(nicknameTwo); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -366,12 +367,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - - // This implicitly tests "The service MUST add the user to the admin list and then inform the owner of success" in §10.6, since it'll throw on either an error IQ or on no response. - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + + // This implicitly tests "The service MUST add the user to the admin list and then inform the owner of success" in §10.6, since it'll throw on either an error IQ or on no response. + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -414,13 +415,13 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -461,13 +462,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid()); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid()); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -509,14 +509,14 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs }); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - - mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid()); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + + mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid()); resultSyncPoint.waitForResult(timeout); } finally { tryDestroy(mucAsSeenByOne); @@ -546,14 +546,14 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); - mucAsSeenByTwo.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence)); - - mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test."); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + + final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByTwo.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence)); + + mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test."); Presence kickPresence = resultSyncPoint.waitForResult(timeout); MUCUser mucUser = MUCUser.from(kickPresence); assertNotNull(mucUser); @@ -593,16 +593,16 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); createMuc(mucAsSeenByOne, "one-" + randomString); - final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); - final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); - mucAsSeenByThree.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence)); - - mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test."); try { + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByThree.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence)); + + mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test."); Presence kickPresence = resultSyncPoint.waitForResult(timeout); MUCUser mucUser = MUCUser.from(kickPresence); assertNotNull(mucUser); @@ -646,7 +646,6 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); createMuc(mucAsSeenByOne, nicknameOne); - try { mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByThree.join(nicknameThree); @@ -694,7 +693,6 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); createModeratedMuc(mucAsSeenByOne, nicknameOne); - try { mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByOne.grantModerator(nicknameTwo); @@ -732,14 +730,13 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); createModeratedMuc(mucAsSeenByOne, nicknameOne); - - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - mucAsSeenByOne.grantModerator(nicknameThree); - try { + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + mucAsSeenByOne.grantModerator(nicknameThree); + // Admin cannot revoke from Owner XMPPException.XMPPErrorException xe1 = assertThrows(XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.revokeModerator(nicknameOne)); @@ -780,20 +777,20 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); createMuc(mucAsSeenByOne, nicknameOne); - mucAsSeenByTwo.join(nicknameTwo); - mucAsSeenByThree.join(nicknameThree); - - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); - mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { - @Override - public void adminGranted(EntityFullJid participant) { - resultSyncPoint.signal("done"); - } - }); - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - resultSyncPoint.waitForResult(timeout); - try { + mucAsSeenByTwo.join(nicknameTwo); + mucAsSeenByThree.join(nicknameThree); + + final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void adminGranted(EntityFullJid participant) { + resultSyncPoint.signal("done"); + } + }); + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + resultSyncPoint.waitForResult(timeout); + assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole()); From def7c91e6abe904b6013b65d89865858ea3824da Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:03:40 +0100 Subject: [PATCH 56/91] [sinttest] Introduce dirty hack in performActionAndWaitForPresence() --- .../smack/inttest/AbstractSmackIntegrationTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java index 593d5b847..f3b083611 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,6 +85,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest * @param action the action to perform. * @throws Exception in case of an exception. */ + @SuppressWarnings("ThreadPriorityCheck") protected void performActionAndWaitForPresence(XMPPConnection conA, XMPPConnection conB, ThrowingRunnable action) throws Exception { final SimpleResultSyncPoint presenceReceivedSyncPoint = new SimpleResultSyncPoint(); @@ -109,5 +110,8 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest } finally { conA.removeAsyncStanzaListener(presenceListener); } + + // TODO: Ugly hack to make tests using this method more reliable. Ideally no test would use this method. + Thread.yield(); } } From 69a55aaa84ea56c21959b7f8e0035d84692d0311 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:04:32 +0100 Subject: [PATCH 57/91] [sinttest] Evaluate compatibilityMode property --- .../java/org/igniterealtime/smack/inttest/Configuration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index b624cf074..e865fb5db 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -524,6 +524,8 @@ public final class Configuration { builder.setDnsResolver(properties.getProperty("dnsResolver")); + builder.setCompatibilityMode(properties.getProperty("compatibilityMode")); + return builder.build(); } From 10a2687ff19722f4076c28227957b71418868aad Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:08:45 +0100 Subject: [PATCH 58/91] [sinttest] Allow the selection of individual test *methods* --- documentation/developer/integrationtest.md | 21 +++ .../smack/util/CollectionUtil.java | 7 + .../smack/inttest/Configuration.java | 124 +++++++++++++++++- .../SmackIntegrationTestFramework.java | 18 +-- 4 files changed, 153 insertions(+), 17 deletions(-) diff --git a/documentation/developer/integrationtest.md b/documentation/developer/integrationtest.md index 60f3142b8..d351e71e1 100644 --- a/documentation/developer/integrationtest.md +++ b/documentation/developer/integrationtest.md @@ -86,6 +86,27 @@ debugger=console The framework will first load the properties file from `~/.config/smack-integration-test/properties` +### Running selected tests only + +Using `enabledTests` is is possible to run only selected tests. The +tests can be selected on a per class base or by specifying concrete +test methods. In the latter case, the methods must be qualified by a +(simple) class name. + +For example: + +```bash +$ gradle integrationTest -Dsinttest.enabledTests=SoftwareInfoIntegrationTest.test +``` + +will only run the `test()` method of `SoftwareInfoIntegrationTest`, whereas + +```bash +$ gradle integrationTest -Dsinttest.enabledTests=SoftwareInfoIntegrationTest +``` + +would run all tests defined in the `SoftwareInfoIntegrationTest` class. + Overview of the components -------------------------- diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java index 24b73b8b0..408e00bfd 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java @@ -89,4 +89,11 @@ public class CollectionUtil { } return Collections.singletonList(element); } + + public static Set nullSafeUnmodifiableSet(Set set) { + if (set == null) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(set); + } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index e865fb5db..700c12eff 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,11 @@ package org.igniterealtime.smack.inttest; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.lang.reflect.Method; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -33,6 +36,7 @@ import javax.net.ssl.SSLContext; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.debugger.ConsoleDebugger; +import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.Function; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.ParserUtils; @@ -101,8 +105,12 @@ public final class Configuration { public final Set enabledTests; + private final Map> enabledTestsMap; + public final Set disabledTests; + private final Map> disabledTestsMap; + public final String defaultConnectionNickname; public final Set enabledConnections; @@ -169,8 +177,10 @@ public final class Configuration { this.accountTwoPassword = builder.accountTwoPassword; this.accountThreeUsername = builder.accountThreeUsername; this.accountThreePassword = builder.accountThreePassword; - this.enabledTests = builder.enabledTests; - this.disabledTests = builder.disabledTests; + this.enabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.enabledTests); + this.enabledTestsMap = convertTestsToMap(enabledTests); + this.disabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.disabledTests); + this.disabledTestsMap = convertTestsToMap(disabledTests); this.defaultConnectionNickname = builder.defaultConnectionNickname; this.enabledConnections = builder.enabledConnections; this.disabledConnections = builder.disabledConnections; @@ -577,4 +587,112 @@ public final class Configuration { }); } + private static Map> convertTestsToMap(Set tests) { + Map> res = new HashMap<>(); + for (String test : tests) { + String[] testParts = test.split("\\."); + if (testParts.length == 1) { + // The whole test specification does not contain a dot, assume it is a test class specification. + res.put(test, Collections.emptySet()); + continue; + } + + String lastTestPart = testParts[testParts.length - 1]; + if (lastTestPart.isEmpty()) { + throw new IllegalArgumentException("Invalid test specifier: " + test); + } + + char firstCharOfLastTestPart = lastTestPart.charAt(0); + if (!Character.isLowerCase(firstCharOfLastTestPart)) { + // The first character of the last test part is not lowercase, assume this is a fully qualified test + // class specification, e.g. org.foo.bar.TestClass. + res.put(test, Collections.emptySet()); + } + + // The first character of the last test part is lowercase, assume this is a test class *and* method name + // specification. + String testMethodName = lastTestPart; + int classPartsCount = testParts.length - 1; + String[] classParts = new String[classPartsCount]; + System.arraycopy(testParts, 0, classParts, 0, classPartsCount); + String testClass = String.join(".", classParts); + + res.compute(testClass, (k, v) -> { + if (v == null) { + v = new HashSet<>(); + } + v.add(testMethodName); + return v; + }); + } + return res; + } + + private static Set getKey(Class testClass, Map> testsMap) { + String className = testClass.getName(); + if (testsMap.containsKey(className)) { + return testsMap.get(className); + } + + String unqualifiedClassName = testClass.getSimpleName(); + if (testsMap.containsKey(unqualifiedClassName)) { + return testsMap.get(unqualifiedClassName); + } + + return null; + } + + private static boolean contains(Class testClass, Map> testsMap) { + Set enabledMethods = getKey(testClass, testsMap); + return enabledMethods != null; + } + + public boolean isClassEnabled(Class testClass) { + if (enabledTestsMap.isEmpty()) { + return true; + } + + return contains(testClass, enabledTestsMap); + } + + public boolean isClassDisabled(Class testClass) { + if (disabledTestsMap.isEmpty()) { + return false; + } + + return contains(testClass, disabledTestsMap); + } + + private static boolean contains(Method method, Map> testsMap) { + Class testClass = method.getDeclaringClass(); + Set methods = getKey(testClass, testsMap); + + if (methods == null) { + return false; + } + + if (methods.isEmpty()) { + return true; + } + + String methodName = method.getName(); + return methods.contains(methodName); + } + + public boolean isMethodEnabled(Method method) { + if (enabledTestsMap.isEmpty()) { + return true; + } + + return contains(method, enabledTestsMap); + } + + public boolean isMethodDisabled(Method method) { + if (disabledTestsMap.isEmpty()) { + return false; + } + + return contains(method, disabledTestsMap); + } + } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index 769146c6f..433386600 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -282,13 +282,13 @@ public class SmackIntegrationTestFramework { continue; } - if (config.enabledTests != null && !isInSet(testClass, config.enabledTests)) { + if (!config.isClassEnabled(testClass)) { DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is not enabled"); testRunResult.disabledTestClasses.add(disabledTestClass); continue; } - if (isInSet(testClass, config.disabledTests)) { + if (config.isClassDisabled(testClass)) { DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed"); testRunResult.disabledTestClasses.add(disabledTestClass); continue; @@ -377,14 +377,13 @@ public class SmackIntegrationTestFramework { while (it.hasNext()) { final Method method = it.next(); final String methodName = method.getName(); - if (config.enabledTests != null && !(config.enabledTests.contains(methodName) - || isInSet(testClass, config.enabledTests))) { + if (!config.isMethodEnabled(method)) { DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is not enabled"); testRunResult.disabledTests.add(disabledTest); it.remove(); continue; } - if (config.disabledTests != null && config.disabledTests.contains(methodName)) { + if (config.isMethodDisabled(method)) { DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is disabled"); testRunResult.disabledTests.add(disabledTest); it.remove(); @@ -607,15 +606,6 @@ public class SmackIntegrationTestFramework { return (Exception) e; } - private static boolean isInSet(Class clz, Set classes) { - if (classes == null) { - return false; - } - final String className = clz.getName(); - final String unqualifiedClassName = clz.getSimpleName(); - return classes.contains(className) || classes.contains(unqualifiedClassName); - } - public static final class TestRunResult { /** From d1273532ce01625bf7f73885144b55ad830546fb Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:15:30 +0100 Subject: [PATCH 59/91] [muc] Correctly processes self-presences The change in 52a49769f9a8 ("[muc] Check for self-presence first in presence listener") caused all self-presences (MUC user status 110) to be ignored in the further processing chain eventually invoking checkRoleModifications() and checkAffiliationModifications(). However, some servers (e.g., ejabberd) include 110 not only in the initial presence but in all following, and we ant to process these. Fixes SMACK-918 Fixes: 52a49769f9a825a8c0252efcad0d96635fb257a6 --- .../java/org/jivesoftware/smackx/muc/MultiUserChat.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 78d725508..92e8ec3f3 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 @@ -211,13 +211,15 @@ public class MultiUserChat { switch (presence.getType()) { case available: - Presence oldPresence = occupantsMap.put(from, presence); if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) { processedReflectedSelfPresence = true; synchronized (this) { notify(); } - } else if (oldPresence != null) { + } + + Presence oldPresence = occupantsMap.put(from, presence); + if (oldPresence != null) { // Get the previous occupant's affiliation & role MUCUser mucExtension = MUCUser.from(oldPresence); MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation(); From e39adff40fd3feae41e793994f870112f57cf131 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:20:40 +0100 Subject: [PATCH 60/91] [muc] Only notify() about processed self-presence once Since notify() is a rather expensive operation, we should only invoke it once. Especially since some servers include 110 in all self presences, not just the initially reflected one on MUC join. --- .../main/java/org/jivesoftware/smackx/muc/MultiUserChat.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 92e8ec3f3..540e419fe 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 @@ -211,7 +211,8 @@ public class MultiUserChat { switch (presence.getType()) { case available: - if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) { + if (!processedReflectedSelfPresence + && mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) { processedReflectedSelfPresence = true; synchronized (this) { notify(); From fc7fc10c6967926c29df8aceea3ea0079c3356b3 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 13 Dec 2021 21:49:15 +0100 Subject: [PATCH 61/91] [pubsub] Allow for character data before 's payload Fixes SMACK-918. --- .../smackx/pubsub/provider/ItemProvider.java | 35 +++++++------- .../pubsub/provider/ItemProviderTest.java | 48 +++++++++++++++++++ 2 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/provider/ItemProviderTest.java diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java index 4c7d0e7ce..5842fda91 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java @@ -50,24 +50,25 @@ public class ItemProvider extends ExtensionElementProvider { String xmlns = parser.getNamespace(); ItemNamespace itemNamespace = ItemNamespace.fromXmlns(xmlns); - XmlPullParser.Event tag = parser.next(); + XmlPullParser.TagEvent event = parser.nextTag(); + switch (event) { + case START_ELEMENT: + String payloadElemName = parser.getName(); + String payloadNS = parser.getNamespace(); - if (tag == XmlPullParser.Event.END_ELEMENT) { - return new Item(itemNamespace, id, node); - } - else { - String payloadElemName = parser.getName(); - String payloadNS = parser.getNamespace(); - - final ExtensionElementProvider extensionProvider = ProviderManager.getExtensionProvider(payloadElemName, payloadNS); - if (extensionProvider == null) { - // TODO: Should we use StandardExtensionElement in this case? And probably remove SimplePayload all together. - CharSequence payloadText = PacketParserUtils.parseElement(parser, true); - return new PayloadItem<>(itemNamespace, id, node, new SimplePayload(payloadText.toString())); - } - else { - return new PayloadItem<>(itemNamespace, id, node, extensionProvider.parse(parser)); - } + final ExtensionElementProvider extensionProvider = ProviderManager.getExtensionProvider(payloadElemName, payloadNS); + if (extensionProvider == null) { + // TODO: Should we use StandardExtensionElement in this case? And probably remove SimplePayload all together. + CharSequence payloadText = PacketParserUtils.parseElement(parser, true); + return new PayloadItem<>(itemNamespace, id, node, new SimplePayload(payloadText.toString())); + } + else { + return new PayloadItem<>(itemNamespace, id, node, extensionProvider.parse(parser)); + } + case END_ELEMENT: + return new Item(itemNamespace, id, node); + default: + throw new AssertionError("unknown: " + event); } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/provider/ItemProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/provider/ItemProviderTest.java new file mode 100644 index 000000000..e7348e920 --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/provider/ItemProviderTest.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2021 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.pubsub.provider; + +import java.io.IOException; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.SmackTestUtil; +import org.jivesoftware.smack.xml.XmlPullParserException; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class ItemProviderTest { + + /** + * Check that {@link ItemProvider} is able to parse items which have whitespace before their Payload. + * + * @param parserKind the used parser Kind + * @throws XmlPullParserException if an XML pull parser exception occurs. + * @throws IOException if an IO exception occurs. + * @throws SmackParsingException if an Smack parsing exception occurs. + * @see SMACK-918 + */ + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void whitespaceBeforeItemPayload(SmackTestUtil.XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { + String item = "" + + "\n " + + ""; + SmackTestUtil.parse(item, ItemProvider.class, parserKind); + } + +} From e1845a52cad445a77d1e83f8dc6aada0bc1561bf Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 15 Dec 2021 20:14:18 +0100 Subject: [PATCH 62/91] [formtypes] Add FormFieldRegistry.register(String, FormField.Type, String...) --- .../jivesoftware/smackx/formtypes/FormFieldRegistry.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java index 9eab79b73..87ecda00b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java @@ -67,6 +67,12 @@ public class FormFieldRegistry { } } + public static void register(String formType, FormField.Type fieldType, String... fieldNames) { + for (String fieldName : fieldNames) { + register(formType, fieldName, fieldType); + } + } + public static void register(String formType, String fieldName, FormField.Type fieldType) { StringUtils.requireNotNullNorEmpty(fieldName, "fieldName must be provided"); Objects.requireNonNull(fieldType); From 182d01a4b06550bf77dfaec1d2f1fe419fd0ed48 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 15 Dec 2021 20:14:49 +0100 Subject: [PATCH 63/91] [softwareinfo] Register urn:xmpp:dataforms:softwareinfo's field types --- .../smackx/softwareinfo/form/SoftwareInfoForm.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/form/SoftwareInfoForm.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/form/SoftwareInfoForm.java index 6774c3b73..b52949b49 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/form/SoftwareInfoForm.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/form/SoftwareInfoForm.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Aditya Borikar + * Copyright 2020 Aditya Borikar, 2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.util.List; import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.HashCode; +import org.jivesoftware.smackx.formtypes.FormFieldRegistry; import org.jivesoftware.smackx.mediaelement.element.MediaElement; import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.FormFieldChildElement; @@ -47,6 +48,11 @@ public final class SoftwareInfoForm extends FilledForm { public static final String SOFTWARE_VERSION = "software_version"; public static final String ICON = "icon"; + static { + FormFieldRegistry.register(FORM_TYPE, FormField.Type.text_single, + OS, OS_VERSION, SOFTWARE, SOFTWARE_VERSION); + } + private SoftwareInfoForm(DataForm dataForm) { super(dataForm); } From 993a3072225726d74277e24fa87d8ff32e9b0bca Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 17 Dec 2021 09:51:19 +0100 Subject: [PATCH 64/91] Add o.j.smackx.softwareinfo.form.SoftwareInfoForm to startup classes This ensures that the form fields are registered. --- .../resources/org.jivesoftware.smack.extensions/extensions.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml index d234bb5b3..5921821a1 100644 --- a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml +++ b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml @@ -20,5 +20,6 @@ org.jivesoftware.smackx.receipts.DeliveryReceiptManager org.jivesoftware.smackx.iqversion.VersionManager org.jivesoftware.smackx.caps.EntityCapsManager + org.jivesoftware.smackx.softwareinfo.form.SoftwareInfoForm From e51cf47b299f2e323925a7884bef4295abeb80a5 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Sun, 22 Aug 2021 16:31:58 +0100 Subject: [PATCH 65/91] =?UTF-8?q?[sinttest]=20Additional=20tests=20for=20?= =?UTF-8?q?=C2=A7=206=20of=20XEP-0045?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified-by: Florian Schmaus --- .../smackx/muc/MucConfigFormManager.java | 82 +++++++ .../AbstractMultiUserChatIntegrationTest.java | 30 +-- .../MultiUserChatEntityIntegrationTest.java | 200 ++++++++++++++++++ 3 files changed, 299 insertions(+), 13 deletions(-) create mode 100644 smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java index 8d941ee06..1735d0b4f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java @@ -78,6 +78,17 @@ public class MucConfigFormManager { */ public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret"; + /** + * The constant String {@value}. + */ + public static final String MUC_ROOMCONFIG_MODERATEDROOM = "muc#roomconfig_moderatedroom"; + + /** + * The constant String {@value}. + */ + public static final String MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM = "muc#roomconfig_publicroom"; + + private final MultiUserChat multiUserChat; private final FillableForm answerForm; private final List owners; @@ -151,6 +162,15 @@ public class MucConfigFormManager { return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY); } + /** + * Check if the room supports being moderated in the configuration. + * + * @return true if supported, false if not. + */ + public boolean supportsModeration() { + return answerForm.hasField(MUC_ROOMCONFIG_MODERATEDROOM); + } + /** * Make the room for members only. * @@ -176,6 +196,68 @@ public class MucConfigFormManager { return this; } + + /** + * Make the room moderated. + * + * @return a reference to this object. + * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. + */ + public MucConfigFormManager makeModerated() throws MucConfigurationNotSupportedException { + return setModerated(true); + } + + /** + * Set if the room is members only. Rooms are not members only per default. + * + * @param isModerated if the room should be moderated. + * @return a reference to this object. + * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. + */ + public MucConfigFormManager setModerated(boolean isModerated) throws MucConfigurationNotSupportedException { + if (!supportsModeration()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MODERATEDROOM); + } + answerForm.setAnswer(MUC_ROOMCONFIG_MODERATEDROOM, isModerated); + return this; + } + + + /** + * Make the room publicly searchable. + * + * @return a reference to this object. + * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. + */ + public MucConfigFormManager makePublic() throws MucConfigurationNotSupportedException { + return setPublic(true); + } + + /** + * Make the room hidden (not publicly searchable). + * + * @return a reference to this object. + * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. + */ + public MucConfigFormManager makeHidden() throws MucConfigurationNotSupportedException { + return setPublic(false); + } + + /** + * Set if the room is publicly searchable (i.e. visible via discovery requests to the MUC service). + * + * @param isPublic if the room should be publicly searchable. + * @return a reference to this object. + * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. + */ + public MucConfigFormManager setPublic(boolean isPublic) throws MucConfigurationNotSupportedException { + if (!supportsModeration()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM); + } + answerForm.setAnswer(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM, isPublic); + return this; + } + /** * Check if the room supports password protection. * diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java index d20a51f52..9fa3aa8d3 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java @@ -22,8 +22,6 @@ import java.util.List; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.xdata.form.FillableForm; -import org.jivesoftware.smackx.xdata.form.Form; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; @@ -124,16 +122,22 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati } } - static void createModeratedMuc(MultiUserChat muc, Resourcepart resourceName) throws - SmackException.NoResponseException, XMPPException.XMPPErrorException, - InterruptedException, MultiUserChatException.MucAlreadyJoinedException, - SmackException.NotConnectedException, - MultiUserChatException.MissingMucCreationAcknowledgeException, - MultiUserChatException.NotAMucServiceException { - muc.create(resourceName); - Form configForm = muc.getConfigurationForm(); - FillableForm answerForm = configForm.getFillableForm(); - answerForm.setAnswer("muc#roomconfig_moderatedroom", true); //TODO Add this to the MucConfigFormManager? - muc.sendConfigurationForm(answerForm); + static void createModeratedMuc(MultiUserChat muc, Resourcepart resourceName) + throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, + MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, + MultiUserChatException.MissingMucCreationAcknowledgeException, + MultiUserChatException.NotAMucServiceException, + MultiUserChatException.MucConfigurationNotSupportedException { + MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); + handle.getConfigFormManager().makeModerated().submitConfigurationForm(); + } + + static void createHiddenMuc(MultiUserChat muc, Resourcepart resourceName) + throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, + MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, + MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException, + MultiUserChatException.MucConfigurationNotSupportedException { + MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); + handle.getConfigFormManager().makeHidden().submitConfigurationForm(); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java new file mode 100644 index 000000000..3e3b2fb7e --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java @@ -0,0 +1,200 @@ +/** + * + * Copyright 2021 Dan Caseley + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.muc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.StanzaError; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.packet.DiscoverItems; + +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.jid.parts.Resourcepart; + +public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatIntegrationTest { + + public MultiUserChatEntityIntegrationTest(SmackIntegrationTestEnvironment environment) + throws SmackException.NoResponseException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, InterruptedException, TestNotPossibleException { + super(environment); + } + + /** + * Asserts that a MUC service can have its features discovered + * + *

From XEP-0045 § 6.2:

+ *
+ * An entity may wish to discover if a service implements the Multi-User Chat protocol; in order to do so, it + * sends a service discovery information ("disco#info") query to the MUC service's JID. The service MUST return + * its identity and the features it supports. + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestForDiscoveringFeatures() throws Exception { + DiscoverInfo info = mucManagerOne.getMucServiceDiscoInfo(mucManagerOne.getMucServiceDomains().get(0)); + assertTrue(info.getIdentities().size() > 0); + assertTrue(info.getFeatures().size() > 0); + } + + /** + * Asserts that a MUC Service lists its public rooms. + * + *

From XEP-0045 § 6.3:

+ *
+ * The service discovery items ("disco#items") protocol enables an entity to query a service for a list of + * associated items, which in the case of a chat service would consist of the specific chat rooms hosted by the + * service. The service SHOULD return a full list of the public rooms it hosts (i.e., not return any rooms that + * are hidden). + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestForDiscoveringRooms() throws Exception { + EntityBareJid mucAddressPublic = getRandomRoom("smack-inttest-publicroom"); + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddressPublic); + + EntityBareJid mucAddressHidden = getRandomRoom("smack-inttest-hiddenroom"); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddressHidden); + + createMuc(mucAsSeenByOne, Resourcepart.from("one-" + randomString)); + + Map rooms; + try { + createHiddenMuc(mucAsSeenByTwo, Resourcepart.from("two-" + randomString)); + rooms = mucManagerThree.getRoomsHostedBy(mucService); + } finally { + tryDestroy(mucAsSeenByOne); + tryDestroy(mucAsSeenByTwo); + } + + assertTrue(rooms.containsKey(mucAddressPublic)); + assertFalse(rooms.containsKey(mucAddressHidden)); + } + + /** + * Asserts that a MUC Service returns disco info for a room. + * + *

From XEP-0045 § 6.4:

+ *
+ * Using the disco#info protocol, an entity may also query a specific chat room for more detailed information + * about the room....The room MUST return its identity and SHOULD return the features it supports + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestForDiscoveringRoomInfo() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-discoinfo"); + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + createMuc(mucAsSeenByOne, Resourcepart.from("one-" + randomString)); + + DiscoverInfo discoInfo; + try { + // Use SDM because mucManagerOne.getRoomInfo(mucAddress) might not use Disco + discoInfo = ServiceDiscoveryManager.getInstanceFor(conOne).discoverInfo(mucAddress, null); + } finally { + tryDestroy(mucAsSeenByOne); + } + + assertTrue(discoInfo.getIdentities().size() > 0); + assertTrue(discoInfo.getFeatures().size() > 0); + } + + /** + * Asserts that a MUC Service returns disco info for a room's items. + * + *

From XEP-0045 § 6.5:

+ *
+ * An entity MAY also query a specific chat room for its associated items. An implementation MAY return a list + * of existing occupants if that information is publicly available, or return no list at all if this information is + * kept private. + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestForDiscoveringRoomItems() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-discoitems"); + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + createMuc(mucAsSeenByOne, Resourcepart.from("one-" + randomString)); + + DiscoverItems roomItems; + try { + roomItems = ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems(mucAddress, null); + } finally { + tryDestroy(mucAsSeenByOne); + } + + assertEquals(1, roomItems.getItems().size()); + } + + /** + * Asserts that a non-occupant receives a Bad Request error when attempting to query an occupant by their + * occupant JID. + * + *

From XEP-0045 § 6.6:

+ *
+ * If a non-occupant attempts to send a disco request to an address of the form <room@service/nick>, a MUC service + * MUST return a <bad-request/> error + *
+ * + * @throws Exception when errors occur + */ + @SmackIntegrationTest + public void mucTestForRejectingDiscoOnRoomOccupantByNonOccupant() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-discoitems"); + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + createMuc(mucAsSeenByOne, nicknameOne); + + XMPPException.XMPPErrorException xe; + try { + xe = assertThrows(XMPPException.XMPPErrorException.class, + () -> ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems( + JidCreate.entityFullFrom(mucAddress, nicknameOne), null)); + } finally { + tryDestroy(mucAsSeenByOne); + } + + final StanzaError.Condition expectedCondition; + switch (sinttestConfiguration.compatibilityMode) { + default: + expectedCondition = StanzaError.Condition.bad_request; + break; + case ejabberd: + expectedCondition = StanzaError.Condition.not_acceptable; + break; + } + assertEquals(xe.getStanzaError().getCondition(), expectedCondition); + } +} From 56c66f17d825673733c6cacac112c461954f6e08 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 20 Dec 2021 22:39:45 +0100 Subject: [PATCH 66/91] [sinttest] MultiUserChatEntityIntegrationTest cleanups --- .../smackx/muc/MultiUserChatEntityIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java index 3e3b2fb7e..5f38182d4 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java @@ -121,7 +121,7 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt DiscoverInfo discoInfo; try { // Use SDM because mucManagerOne.getRoomInfo(mucAddress) might not use Disco - discoInfo = ServiceDiscoveryManager.getInstanceFor(conOne).discoverInfo(mucAddress, null); + discoInfo = ServiceDiscoveryManager.getInstanceFor(conOne).discoverInfo(mucAddress); } finally { tryDestroy(mucAsSeenByOne); } @@ -150,7 +150,7 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt DiscoverItems roomItems; try { - roomItems = ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems(mucAddress, null); + roomItems = ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems(mucAddress); } finally { tryDestroy(mucAsSeenByOne); } From 56ca31b156a2f88cc55a5ce899e8185abf7df9ab Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 20 Dec 2021 22:40:13 +0100 Subject: [PATCH 67/91] [sinttset] AbstractMultiUserChatIntegrationTest cleanups --- .../AbstractMultiUserChatIntegrationTest.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java index 9fa3aa8d3..5b2879c63 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java @@ -55,9 +55,8 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati if (services.isEmpty()) { throw new TestNotPossibleException("No MUC (XEP-45) service found"); } - else { - mucService = services.get(0); - } + + mucService = services.get(0); } /** @@ -94,10 +93,7 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException { - MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); - if (handle != null) { - handle.makeInstant(); - } + muc.create(resourceName).makeInstant(); } static void createMuc(MultiUserChat muc, String nickname) throws @@ -116,10 +112,10 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.MucConfigurationNotSupportedException, MultiUserChatException.NotAMucServiceException { - MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); - if (handle != null) { - handle.getConfigFormManager().makeMembersOnly().submitConfigurationForm(); - } + muc.create(resourceName) + .getConfigFormManager() + .makeMembersOnly() + .submitConfigurationForm(); } static void createModeratedMuc(MultiUserChat muc, Resourcepart resourceName) @@ -128,8 +124,10 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, MultiUserChatException.MucConfigurationNotSupportedException { - MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); - handle.getConfigFormManager().makeModerated().submitConfigurationForm(); + muc.create(resourceName) + .getConfigFormManager() + .makeModerated() + .submitConfigurationForm(); } static void createHiddenMuc(MultiUserChat muc, Resourcepart resourceName) @@ -137,7 +135,9 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException, MultiUserChatException.MucConfigurationNotSupportedException { - MultiUserChat.MucCreateConfigFormHandle handle = muc.create(resourceName); - handle.getConfigFormManager().makeHidden().submitConfigurationForm(); + muc.create(resourceName) + .getConfigFormManager() + .makeHidden() + .submitConfigurationForm(); } } From e530db2e6d463625470c8ce78b3b795293922515 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 20 Dec 2021 22:40:37 +0100 Subject: [PATCH 68/91] [muc] Add MultiUserChat.getMyRoomJid() --- .../smackx/muc/MultiUserChat.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) 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 22727c931..1064ee59d 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 @@ -205,7 +205,7 @@ public class MultiUserChat { if (from == null) { return; } - final EntityFullJid myRoomJID = myRoomJid; + final EntityFullJid myRoomJID = getMyRoomJid(); final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID); final MUCUser mucUser = MUCUser.from(packet); @@ -732,7 +732,7 @@ public class MultiUserChat { * @return true if currently in the multi user chat room. */ public boolean isJoined() { - return myRoomJid != null; + return getMyRoomJid() != null; } /** @@ -768,7 +768,7 @@ public class MultiUserChat { // "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. - final EntityFullJid myRoomJid = this.myRoomJid; + final EntityFullJid myRoomJid = getMyRoomJid(); if (myRoomJid == null) { throw new MucNotJoinedException(this); } @@ -1200,13 +1200,23 @@ public class MultiUserChat { * @return the nickname currently being used. */ public Resourcepart getNickname() { - final EntityFullJid myRoomJid = this.myRoomJid; + final EntityFullJid myRoomJid = getMyRoomJid(); if (myRoomJid == null) { return null; } return myRoomJid.getResourcepart(); } + /** + * Return the full JID of the user in the room, or null if the room is not joined. + * + * @return the full JID of the user in the room, or null. + * @since 4.5.0 + */ + public EntityFullJid getMyRoomJid() { + return myRoomJid; + } + /** * Changes the occupant's nickname to a new nickname within the room. Each room occupant * will receive two presence packets. One of type "unavailable" for the old nickname and one @@ -1263,7 +1273,7 @@ public class MultiUserChat { * @throws MucNotJoinedException if not joined to the Multi-User Chat. */ public void changeAvailabilityStatus(String status, Presence.Mode mode) throws NotConnectedException, InterruptedException, MucNotJoinedException { - final EntityFullJid myRoomJid = this.myRoomJid; + final EntityFullJid myRoomJid = getMyRoomJid(); if (myRoomJid == null) { throw new MucNotJoinedException(this); } From b3637101ced7921ba62820b0d1c7b47cc08c3f70 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 20 Dec 2021 22:40:55 +0100 Subject: [PATCH 69/91] [sinttest] Use MultiUserChat.getMyRoomJid() in integration test --- .../muc/MultiUserChatEntityIntegrationTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java index 5f38182d4..279160c0f 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java @@ -35,7 +35,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; import org.jxmpp.jid.EntityBareJid; -import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.parts.Resourcepart; public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatIntegrationTest { @@ -176,12 +176,17 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); createMuc(mucAsSeenByOne, nicknameOne); + final EntityFullJid mucAsSeenByOneUserJid = mucAsSeenByOne.getMyRoomJid(); + // Ensure that we do not invoke discoverItems() with null below. This should not happen, as the room JID should + // be non-null after we created and joined the room. But it can not hurt to explicitly test for it either. + if (mucAsSeenByOneUserJid == null) { + throw new AssertionError(); + } XMPPException.XMPPErrorException xe; try { xe = assertThrows(XMPPException.XMPPErrorException.class, - () -> ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems( - JidCreate.entityFullFrom(mucAddress, nicknameOne), null)); + () -> ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems(mucAsSeenByOneUserJid)); } finally { tryDestroy(mucAsSeenByOne); } From 817dc0ed3ae8745d23c6749d4f3ba64164b3e3cc Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 21 Dec 2021 12:46:08 +0100 Subject: [PATCH 70/91] Prevent password enforcement for SASL GSSAPI Similar fix as 9b339efbc1cf ("Prevent password enforcement for SASL anonymous") just for SASL GSSAPI. Fixes SMACK-920. Fixes: 92f4aadfdc45 ("[sasl] Avoid mechanisms that need a password when none is available") --- .../jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java index 210e94ab9..bdef7d673 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java @@ -66,4 +66,8 @@ public class SASLGSSAPIMechanism extends SASLJavaXMechanism { return new SASLGSSAPIMechanism(); } + @Override + public boolean requiresPassword() { + return false; + } } From 8be1568a5d885b713e0172ffe9b50a200dd4181d Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Tue, 21 Dec 2021 15:37:21 +0100 Subject: [PATCH 71/91] Increase performance of EnhancedDebugger When using the Enhanced Debugger, a UI application is very noticably slow, especially around establishing a new session. Profiling shows that much of the overhead is caused by XMPP-data being added to the text areas that are on the "raw sent packets" and "raw received packets" tabs of the debugger. This commit improves performance (considerably) by: - properly limiting the amount of lines in those text areas (this was intended but broken in the old implementation) - buffering data to be added in batches, to reduce the amount of invocations to JTextChat.append() As an aside: some newline-based formatting was removed. As the provided data is now already formatted, retaining that did not make much sense anymore. --- .../smackx/debugger/EnhancedDebugger.java | 138 +++++++++++------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java index c4a8c90dd..3dd474f47 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java @@ -34,7 +34,13 @@ import java.io.Reader; import java.io.Writer; import java.net.URL; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; import java.util.Date; +import java.util.List; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -419,37 +425,52 @@ public class EnhancedDebugger extends SmackDebugger { // Create a special Reader that wraps the main Reader and logs data to the GUI. ObservableReader debugReader = new ObservableReader(reader); readerListener = new ReaderListener() { - @Override - public void read(final String str) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && - !EnhancedDebuggerWindow.getInstance().isVisible()) { - // Do not add content if the parent is not visible - return; - } + private final PriorityBlockingQueue buffer = new PriorityBlockingQueue<>(); - int index = str.lastIndexOf(">"); - if (index != -1) { - if (receivedText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) { - try { - receivedText.replaceRange("", 0, receivedText.getLineEndOffset(0)); - } - catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); - } - } - receivedText.append(str.substring(0, index + 1)); - receivedText.append(NEWLINE); - if (str.length() > index) { - receivedText.append(str.substring(index + 1)); - } + @Override + public void read(final String line) { + + buffer.add(line); + + SwingUtilities.invokeLater(() -> { + List linesToAdd = new ArrayList<>(); + String data; + Instant start = Instant.now(); + try { + // To reduce overhead/increase performance, try to process up to a certain amount of lines at the + // same time, when they arrive in rapid succession. + while (linesToAdd.size() < 50 + && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 + && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { + linesToAdd.add(data); } - else { - receivedText.append(str); + } catch (InterruptedException e) { + // Interrupted wait-for-poll. Process all data now. + } + + if (linesToAdd.isEmpty()) { + return; + } + + if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && + !EnhancedDebuggerWindow.getInstance().isVisible()) { + // Do not add content if the parent is not visible + return; + } + + // Delete lines from the top, if lines to be added will exceed the maximum. + int linesToDelete = receivedText.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; + if (linesToDelete > 0) { + try { + receivedText.replaceRange("", 0, receivedText.getLineEndOffset(linesToDelete - 1)); + } + catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); } } + + // Add the new content. + receivedText.append(String.join(NEWLINE, linesToAdd)); }); } }; @@ -458,34 +479,53 @@ public class EnhancedDebugger extends SmackDebugger { // Create a special Writer that wraps the main Writer and logs data to the GUI. ObservableWriter debugWriter = new ObservableWriter(writer); writerListener = new WriterListener() { + private final PriorityBlockingQueue buffer = new PriorityBlockingQueue<>(); + @Override - public void write(final String str) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && - !EnhancedDebuggerWindow.getInstance().isVisible()) { - // Do not add content if the parent is not visible - return; - } + public void write(final String line) { - if (sentText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) { - try { - sentText.replaceRange("", 0, sentText.getLineEndOffset(0)); - } - catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); - } - } + buffer.add(line); - sentText.append(str); - if (str.endsWith(">")) { - sentText.append(NEWLINE); + SwingUtilities.invokeLater(() -> { + List linesToAdd = new ArrayList<>(); + String data; + Instant start = Instant.now(); + try { + // To reduce overhead/increase performance, try to process up to a certain amount of lines at the + // same time, when they arrive in rapid succession. + while (linesToAdd.size() < 50 + && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 + && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { + linesToAdd.add(data); + } + } catch (InterruptedException e) { + // Interrupted wait-for-poll. Process all data now. + } + + if (linesToAdd.isEmpty()) { + return; + } + + if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && + !EnhancedDebuggerWindow.getInstance().isVisible()) { + // Do not add content if the parent is not visible + return; + } + + // Delete lines from the top, if lines to be added will exceed the maximum. + int linesToDelete = sentText.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; + if (linesToDelete > 0) { + try { + sentText.replaceRange("", 0, sentText.getLineEndOffset(linesToDelete - 1)); + } + catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); } } + + // Add the new content. + sentText.append(String.join(NEWLINE, linesToAdd)); }); - - } }; debugWriter.addWriterListener(writerListener); From 45a498781812791e3c2ed5e843ef1e97996e3381 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 22 Dec 2021 09:39:16 +0100 Subject: [PATCH 72/91] [smack-debug] Factor duplicate code into its own method --- .../smackx/debugger/EnhancedDebugger.java | 136 +++++++----------- 1 file changed, 48 insertions(+), 88 deletions(-) diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java index 3dd474f47..40d2e5263 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java @@ -428,50 +428,8 @@ public class EnhancedDebugger extends SmackDebugger { private final PriorityBlockingQueue buffer = new PriorityBlockingQueue<>(); @Override - public void read(final String line) { - - buffer.add(line); - - SwingUtilities.invokeLater(() -> { - List linesToAdd = new ArrayList<>(); - String data; - Instant start = Instant.now(); - try { - // To reduce overhead/increase performance, try to process up to a certain amount of lines at the - // same time, when they arrive in rapid succession. - while (linesToAdd.size() < 50 - && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 - && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { - linesToAdd.add(data); - } - } catch (InterruptedException e) { - // Interrupted wait-for-poll. Process all data now. - } - - if (linesToAdd.isEmpty()) { - return; - } - - if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && - !EnhancedDebuggerWindow.getInstance().isVisible()) { - // Do not add content if the parent is not visible - return; - } - - // Delete lines from the top, if lines to be added will exceed the maximum. - int linesToDelete = receivedText.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; - if (linesToDelete > 0) { - try { - receivedText.replaceRange("", 0, receivedText.getLineEndOffset(linesToDelete - 1)); - } - catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); - } - } - - // Add the new content. - receivedText.append(String.join(NEWLINE, linesToAdd)); - }); + public void read(final String string) { + addBatched(string, buffer, receivedText); } }; debugReader.addReaderListener(readerListener); @@ -482,50 +440,8 @@ public class EnhancedDebugger extends SmackDebugger { private final PriorityBlockingQueue buffer = new PriorityBlockingQueue<>(); @Override - public void write(final String line) { - - buffer.add(line); - - SwingUtilities.invokeLater(() -> { - List linesToAdd = new ArrayList<>(); - String data; - Instant start = Instant.now(); - try { - // To reduce overhead/increase performance, try to process up to a certain amount of lines at the - // same time, when they arrive in rapid succession. - while (linesToAdd.size() < 50 - && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 - && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { - linesToAdd.add(data); - } - } catch (InterruptedException e) { - // Interrupted wait-for-poll. Process all data now. - } - - if (linesToAdd.isEmpty()) { - return; - } - - if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && - !EnhancedDebuggerWindow.getInstance().isVisible()) { - // Do not add content if the parent is not visible - return; - } - - // Delete lines from the top, if lines to be added will exceed the maximum. - int linesToDelete = sentText.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; - if (linesToDelete > 0) { - try { - sentText.replaceRange("", 0, sentText.getLineEndOffset(linesToDelete - 1)); - } - catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); - } - } - - // Add the new content. - sentText.append(String.join(NEWLINE, linesToAdd)); - }); + public void write(final String string) { + addBatched(string, buffer, sentText); } }; debugWriter.addWriterListener(writerListener); @@ -537,6 +453,50 @@ public class EnhancedDebugger extends SmackDebugger { } + private static void addBatched(String string, PriorityBlockingQueue buffer, JTextArea jTextArea) { + buffer.add(string); + + SwingUtilities.invokeLater(() -> { + List linesToAdd = new ArrayList<>(); + String data; + Instant start = Instant.now(); + try { + // To reduce overhead/increase performance, try to process up to a certain amount of lines at the + // same time, when they arrive in rapid succession. + while (linesToAdd.size() < 50 + && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 + && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { + linesToAdd.add(data); + } + } catch (InterruptedException e) { + LOGGER.log(Level.FINER, "Interrupted wait-for-poll in addBatched(). Process all data now.", e); + } + + if (linesToAdd.isEmpty()) { + return; + } + + if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && !EnhancedDebuggerWindow.getInstance().isVisible()) { + // Do not add content if the parent is not visible + return; + } + + // Delete lines from the top, if lines to be added will exceed the maximum. + int linesToDelete = jTextArea.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; + if (linesToDelete > 0) { + try { + jTextArea.replaceRange("", 0, jTextArea.getLineEndOffset(linesToDelete - 1)); + } catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); + } + } + + // Add the new content. + jTextArea.append(String.join(NEWLINE, linesToAdd)); + }); + } + private void addAdhocPacketPanel() { // Create UI elements for sending ad-hoc messages. final JTextArea adhocMessages = new JTextArea(); From 940d7bf02abc0997eabc4cda12157ad2ceedfcf7 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 Dec 2021 21:17:57 +0100 Subject: [PATCH 73/91] [xdata] Adjust behavior of BooleanFormField.getValueAsBoolean() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to XEP-0004 § 3.3, the default value of a boolean form field is 'false'. And since users are typically interested in getting the value, and not potentially 'null' as result, we adjust the behavior of the getValueAsBoolean() method consider the default value. --- .../jivesoftware/smackx/xdata/BooleanFormField.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java index 85e351649..6208e4979 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java @@ -35,7 +35,17 @@ public class BooleanFormField extends SingleValueFormField { return value.toString(); } - public Boolean getValueAsBoolean() { + /** + * Get the value of the booelan field. Note that, if no explicit boolean value is provided, in the form of "true", + * "false", "0", or "1", then the default value of a boolean field is false, according to + * XEP-0004 § 3.3. + * + * @return the boolean value of this form field. + */ + public boolean getValueAsBoolean() { + if (value == null) { + return false; + } return value; } From 5c46451cd204f932fc48fced90a6b9bdb8847339 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 Dec 2021 21:19:05 +0100 Subject: [PATCH 74/91] [xdata] Add BooleanFormField.getValueAsBooleanOrNull() This method is meant to provide 'raw' access to what has been sent over the wire. It is assumed to be not of much use in the typically case, but provided for completeness. --- .../smackx/xdata/BooleanFormField.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java index 6208e4979..6681e6408 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java @@ -49,6 +49,20 @@ public class BooleanFormField extends SingleValueFormField { return value; } + /** + * Get the value of the boolean field or maybe null. Note that you usually want to use + * {@link #getValueAsBoolean()} instead of this method, as {@link #getValueAsBoolean()} considers the default value + * of boolean fields. That is, boolean form fields have the value false if not explicitly set to + * something else. + * + * @return the boolean value of this form field or null if no value was explicitly provided. + * @see #getValueAsBoolean() + * @since 4.4.5 + */ + public Boolean getValueAsBooleanOrNull() { + return value; + } + public Builder asBuilder() { return new Builder(this); } From 1a727c152e9af15d519a7af6663798a210c13447 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 22 Jan 2022 10:24:01 +0100 Subject: [PATCH 75/91] [core] Improve warning message of ExceptionThrowingCallbackWithHint --- .../smack/parsing/ExceptionThrowingCallbackWithHint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/parsing/ExceptionThrowingCallbackWithHint.java b/smack-core/src/main/java/org/jivesoftware/smack/parsing/ExceptionThrowingCallbackWithHint.java index f7e91355d..b2b2c47ab 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/parsing/ExceptionThrowingCallbackWithHint.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/parsing/ExceptionThrowingCallbackWithHint.java @@ -34,7 +34,7 @@ public class ExceptionThrowingCallbackWithHint extends ExceptionThrowingCallback @Override public void handleUnparsableStanza(UnparseableStanza packetData) throws IOException { - LOGGER.warning("Parsing exception encountered." + LOGGER.warning("Parsing exception \"" + packetData.getParsingException().getMessage() + "\" encountered." + " This exception will be re-thrown, leading to a disconnect." + " You can change this behavior by setting a different ParsingExceptionCallback using setParsingExceptionCallback()." + " More information an be found in AbstractXMPPConnection's javadoc."); From 1dae0c0c32e73035b47fd9efcf0e8695addf910b Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 22 Jan 2022 11:08:35 +0100 Subject: [PATCH 76/91] [core] Use Enum.toString() in XmlStringBuilder.attribute(String, Enum) All other enum-using methods of XmlStringBuilder already use Enum.toString(), as opposed to Enum.name(), this was the only left. I do not remember why I did not to change this method too, probably because of its plenty call sites. But since this method already broke Jingle XML serializaton, JingleAction was e.g., 'session_accept' when it should be 'session-accept', we change it now. Fixes SMACK-921. --- .../java/org/jivesoftware/smack/util/XmlStringBuilder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java index 5a1fae11f..20891c838 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java @@ -289,8 +289,7 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element { public XmlStringBuilder attribute(String name, Enum value) { assert value != null; - // TODO: Should use toString() instead of name(). - attribute(name, value.name()); + attribute(name, value.toString()); return this; } From 0e637068e6de97f7646c8fa6d579c74a218a8072 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 29 Jan 2022 21:46:44 +0100 Subject: [PATCH 77/91] [core] Factor PacketParserUtils.parseIqData() in extra method --- .../smack/util/PacketParserUtils.java | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) 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 d5168ba50..82cace29d 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 @@ -500,6 +500,23 @@ public class PacketParserUtils { return parseIQ(parser, null); } + public static IqData parseIqData(XmlPullParser parser) throws XmppStringprepException { + final String id = parser.getAttributeValue("", "id"); + IqData iqData = StanzaBuilder.buildIqData(id); + + final Jid to = ParserUtils.getJidAttribute(parser, "to"); + iqData.to(to); + + final Jid from = ParserUtils.getJidAttribute(parser, "from"); + iqData.from(from); + + String typeString = parser.getAttributeValue("", "type"); + final IQ.Type type = IQ.Type.fromString(typeString); + iqData.ofType(type); + + return iqData; + } + /** * Parses an IQ packet. * @@ -517,18 +534,7 @@ public class PacketParserUtils { XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); IQ iqPacket = null; StanzaError error = null; - - final String id = parser.getAttributeValue("", "id"); - IqData iqData = StanzaBuilder.buildIqData(id); - - final Jid to = ParserUtils.getJidAttribute(parser, "to"); - iqData.to(to); - - final Jid from = ParserUtils.getJidAttribute(parser, "from"); - iqData.from(from); - - final IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type")); - iqData.ofType(type); + IqData iqData = parseIqData(parser); outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); @@ -570,7 +576,7 @@ public class PacketParserUtils { } // Decide what to do when an IQ packet was not understood if (iqPacket == null) { - switch (type) { + switch (iqData.getType()) { case error: // If an IQ packet wasn't created above, create an empty error IQ packet. iqPacket = new ErrorIQ(error); @@ -584,10 +590,10 @@ public class PacketParserUtils { } // Set basic values on the iq packet. - iqPacket.setStanzaId(id); - iqPacket.setTo(to); - iqPacket.setFrom(from); - iqPacket.setType(type); + iqPacket.setStanzaId(iqData.getStanzaId()); + iqPacket.setTo(iqData.getTo()); + iqPacket.setFrom(iqData.getFrom()); + iqPacket.setType(iqData.getType()); iqPacket.setError(error); return iqPacket; From c06cf72337c0ef439f2ab4a21ab461e28b083b46 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 29 Jan 2022 21:47:04 +0100 Subject: [PATCH 78/91] [core] Support IqProvider in SmackTestUtil --- .../smack/test/util/SmackTestUtil.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestUtil.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestUtil.java index a81c11c97..d7555d048 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestUtil.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2022 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,13 @@ import java.util.function.Predicate; import javax.xml.namespace.QName; import org.jivesoftware.smack.packet.Element; +import org.jivesoftware.smack.packet.IqData; import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.provider.AbstractProvider; +import org.jivesoftware.smack.provider.IqProvider; import org.jivesoftware.smack.provider.Provider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserFactory; @@ -57,41 +62,56 @@ public class SmackTestUtil { } } - public static > E parse(CharSequence xml, Class

providerClass, XmlPullParserKind parserKind) + public static > E parse(CharSequence xml, Class

providerClass, XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { P provider = providerClassToProvider(providerClass); return parse(xml, provider, parserKind); } - public static > E parse(InputStream inputStream, Class

providerClass, XmlPullParserKind parserKind) + public static > E parse(InputStream inputStream, Class

providerClass, XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { P provider = providerClassToProvider(providerClass); return parse(inputStream, provider, parserKind); } - public static > E parse(Reader reader, Class

providerClass, XmlPullParserKind parserKind) + public static > E parse(Reader reader, Class

providerClass, XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { P provider = providerClassToProvider(providerClass); return parse(reader, provider, parserKind); } - public static E parse(CharSequence xml, Provider provider, XmlPullParserKind parserKind) + public static E parse(CharSequence xml, AbstractProvider provider, XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { String xmlString = xml.toString(); Reader reader = new StringReader(xmlString); return parse(reader, provider, parserKind); } - public static E parse(InputStream inputStream, Provider provider, XmlPullParserKind parserKind) + public static E parse(InputStream inputStream, AbstractProvider provider, XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); return parse(inputStreamReader, provider, parserKind); } - public static E parse(Reader reader, Provider provider, XmlPullParserKind parserKind) + @SuppressWarnings("unchecked") + public static E parse(Reader reader, AbstractProvider abstractProvider, XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { XmlPullParser parser = getParserFor(reader, parserKind); - E element = provider.parse(parser); + + final E element; + if (abstractProvider instanceof Provider) { + Provider provider = (Provider) abstractProvider; + element = provider.parse(parser); + } else if (abstractProvider instanceof IqProvider) { + IqData iqData = PacketParserUtils.parseIqData(parser); + parser.next(); + ParserUtils.forwardToStartElement(parser); + IqProvider iqProvider = (IqProvider) abstractProvider; + element = (E) iqProvider.parse(parser, iqData); + } else { + throw new AssertionError(); + } + return element; } @@ -126,7 +146,7 @@ public class SmackTestUtil { } @SuppressWarnings("unchecked") - private static > P providerClassToProvider(Class

providerClass) { + private static > P providerClassToProvider(Class

providerClass) { P provider; try { From 7fad14ac0cf6b1a9b156d099a6521ab5c4723bcf Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 29 Jan 2022 23:09:06 +0100 Subject: [PATCH 79/91] [jingle] Improve Jingle support Fixes SMACK-922. --- .../smackx/jingle/element/JingleReason.java | 37 ++++++++- .../jingle/provider/JingleProvider.java | 68 +++++++++++++--- .../jingle/provider/JingleProviderTest.java | 77 ++++++++++++++----- 3 files changed, 149 insertions(+), 33 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java index 91c909aba..116c8375d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus + * Copyright 2017-2022 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.jingle.element; import java.util.HashMap; import java.util.Map; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.FullyQualifiedElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.StringUtils; @@ -105,9 +106,17 @@ public class JingleReason implements FullyQualifiedElement { } protected final Reason reason; + private final String text; + private final ExtensionElement element; public JingleReason(Reason reason) { + this(reason, null, null); + } + + public JingleReason(Reason reason, String text, ExtensionElement element) { this.reason = reason; + this.text = text; + this.element = element; } @Override @@ -120,6 +129,26 @@ public class JingleReason implements FullyQualifiedElement { return NAMESPACE; } + /** + * An optional text that provides human-readable information about the reason for the action. + * + * @return a human-readable text with information regarding this reason or null. + * @since 4.4.5 + */ + public String getText() { + return text; + } + + /** + * An optional element that provides more detailed machine-readable information about the reason for the action. + * + * @return an elemnet with machine-readable information about this reason or null. + * @since 4.4.5 + */ + public ExtensionElement getElement() { + return element; + } + @Override public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) { XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment); @@ -142,7 +171,11 @@ public class JingleReason implements FullyQualifiedElement { private final String sessionId; public AlternativeSession(String sessionId) { - super(Reason.alternative_session); + this(sessionId, null, null); + } + + public AlternativeSession(String sessionId, String text, ExtensionElement element) { + super(Reason.alternative_session, text, element); if (StringUtils.isNullOrEmpty(sessionId)) { throw new NullPointerException("SessionID must not be null or empty."); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java index f6c352a75..861375995 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2021 Florian Schmaus + * Copyright 2017-2022 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,14 @@ package org.jivesoftware.smackx.jingle.provider; import java.io.IOException; import java.util.logging.Logger; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.IqData; import org.jivesoftware.smack.packet.StandardExtensionElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.StandardExtensionElementProvider; import org.jivesoftware.smack.provider.IqProvider; +import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -76,16 +78,7 @@ public class JingleProvider extends IqProvider { builder.addJingleContent(content); break; case JingleReason.ELEMENT: - parser.next(); - String reasonString = parser.getName(); - JingleReason reason; - if (reasonString.equals("alternative-session")) { - parser.next(); - String sid = parser.nextText(); - reason = new JingleReason.AlternativeSession(sid); - } else { - reason = new JingleReason(Reason.fromString(reasonString)); - } + JingleReason reason = parseJingleReason(parser); builder.setReason(reason); break; default: @@ -178,4 +171,57 @@ public class JingleProvider extends IqProvider { return builder.build(); } + + public static JingleReason parseJingleReason(XmlPullParser parser) + throws XmlPullParserException, IOException, SmackParsingException { + ParserUtils.assertAtStartTag(parser); + final int initialDepth = parser.getDepth(); + final String jingleNamespace = parser.getNamespace(); + + JingleReason.Reason reason = null; + ExtensionElement element = null; + String text = null; + + // 'sid' is only set if the reason is 'alternative-session'. + String sid = null; + + outerloop: while (true) { + XmlPullParser.TagEvent event = parser.nextTag(); + switch (event) { + case START_ELEMENT: + String elementName = parser.getName(); + String namespace = parser.getNamespace(); + if (namespace.equals(jingleNamespace)) { + switch (elementName) { + case "text": + text = parser.nextText(); + break; + case "alternative-session": + parser.next(); + sid = parser.nextText(); + break; + default: + reason = Reason.fromString(elementName); + break; + } + } else { + element = PacketParserUtils.parseExtensionElement(elementName, namespace, parser, null); + } + break; + case END_ELEMENT: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + JingleReason res; + if (sid != null) { + res = new JingleReason.AlternativeSession(sid, text, element); + } else { + res = new JingleReason(reason, text, element); + } + return res; + } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java index c17543148..1ca0fa71a 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus + * Copyright 2017-2022 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,30 @@ package org.jivesoftware.smackx.jingle.provider; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; -import org.jivesoftware.smack.util.PacketParserUtils; -import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.StandardExtensionElement; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.SmackTestUtil; import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.jingle.element.Jingle; import org.jivesoftware.smackx.jingle.element.JingleContentDescription; import org.jivesoftware.smackx.jingle.element.JingleContentTransport; +import org.jivesoftware.smackx.jingle.element.JingleReason; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; public class JingleProviderTest { - @Test - public void testParseUnknownJingleContentDescrption() throws Exception { + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testParseUnknownJingleContentDescrption(SmackTestUtil.XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { final String unknownJingleContentDescriptionNamespace = "urn:xmpp:jingle:unknown-description:5"; final String unknownJingleContentDescription = // @formatter:off @@ -50,8 +57,8 @@ public class JingleProviderTest { "" + ""; // @formatter:on - XmlPullParser parser = createTestJingle(unknownJingleContentDescription); - Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser); + CharSequence xml = createTestJingle(unknownJingleContentDescription); + Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind); JingleContentDescription jingleContentDescription = jingle.getSoleContentOrThrow().getDescription(); @@ -59,8 +66,10 @@ public class JingleProviderTest { assertEquals(unknownJingleContentDescriptionNamespace, parsedUnknownJingleContentDescriptionNamespace); } - @Test - public void testParseUnknownJingleContentTransport() throws Exception { + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testParseUnknownJingleContentTransport(SmackTestUtil.XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { final String unknownJingleContentTransportNamespace = "urn:xmpp:jingle:unknown-transport:foo:1"; final String unknownJingleContentTransport = // @formatter:off @@ -81,8 +90,8 @@ public class JingleProviderTest { " type='direct'/>" + ""; // @formatter:on - XmlPullParser parser = createTestJingle(unknownJingleContentTransport); - Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser); + CharSequence xml = createTestJingle(unknownJingleContentTransport); + Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind); JingleContentTransport jingleContentTransport = jingle.getSoleContentOrThrow().getTransport(); @@ -90,7 +99,38 @@ public class JingleProviderTest { assertEquals(unknownJingleContentTransportNamespace, parsedUnknownJingleContentTransportNamespace); } - private static XmlPullParser createTestJingle(String... childs) throws XmlPullParserException, IOException { + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testReasonElementWithExtraElement(SmackTestUtil.XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { + String xml = "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind); + JingleReason jingleReason = jingle.getReason(); + + assertEquals(JingleReason.Reason.success, jingleReason.asEnum()); + + ExtensionElement element = jingleReason.getElement(); + // TODO: Use JUnit 5.8's assertInstanceOf when possible + // assertInstanceOf(StandardExtesionElement.class, extraElement); + assertTrue(element instanceof StandardExtensionElement); + StandardExtensionElement extraElement = (StandardExtensionElement) element; + assertEquals("https://example.org", extraElement.getNamespace()); + assertEquals("bar", extraElement.getAttributes().get("foo")); + } + + private static CharSequence createTestJingle(String... childs) throws XmlPullParserException, IOException { StringBuilder sb = new StringBuilder(); sb.append(// @formatter:off "" + "" + + "action='session-initiate' " + + "initiator='romeo@montague.example/dr4hcr0st3lup4c' " + + "sid='851ba2'>" + "" // @formatter:on ); @@ -114,9 +154,6 @@ public class JingleProviderTest { // @formatter:on ); - String jingleStanza = sb.toString(); - - XmlPullParser parser = PacketParserUtils.getParserFor(jingleStanza); - return parser; + return sb; } } From 4cdb4acf260e9f9a2a16309d0942785d37a3121b Mon Sep 17 00:00:00 2001 From: cmeng-git Date: Mon, 24 Jan 2022 08:12:12 +0800 Subject: [PATCH 80/91] [core] Fix IQProvider javadoc It is IqProvider that should be used instead. --- .../main/java/org/jivesoftware/smack/provider/IQProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java b/smack-core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java index d0e1f8fc3..acb256785 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java @@ -29,7 +29,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException; /** *

- * Deprecation Notice: This class is deprecated, use {@link IQProvider} instead. + * Deprecation Notice: This class is deprecated, use {@link IqProvider} instead. *

* An abstract class for parsing custom IQ packets. Each IQProvider must be registered with * the ProviderManager class for it to be used. Every implementation of this From 3ff553549a4c10ce7154db6b516e362631045586 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 2 Feb 2022 12:44:04 +0100 Subject: [PATCH 81/91] [SmackFuture] Invoke the callbacks at most once Previously, if a SmackFuture both was successful and unsuccessful, it was possible that the onSuccess() callback was invoked twice. Reported-by: Boris Grozev --- .../src/main/java/org/jivesoftware/smack/SmackFuture.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java index 08646728a..ec99e6c4a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java @@ -162,16 +162,20 @@ public abstract class SmackFuture implements Future, return result; } + private boolean callbacksInvoked; + protected final synchronized void maybeInvokeCallbacks() { - if (cancelled) { + if (cancelled || callbacksInvoked) { return; } if ((result != null || exception != null) && completionCallback != null) { + callbacksInvoked = true; completionCallback.accept(this); } if (result != null && successCallback != null) { + callbacksInvoked = true; AbstractXMPPConnection.asyncGo(new Runnable() { @Override public void run() { @@ -180,6 +184,7 @@ public abstract class SmackFuture implements Future, }); } else if (exception != null && exceptionCallback != null) { + callbacksInvoked = true; AbstractXMPPConnection.asyncGo(new Runnable() { @Override public void run() { From 67a5c3a41a285f1dbcaf97a6cd4f2580036d525b Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Fri, 4 Feb 2022 12:29:38 -0600 Subject: [PATCH 82/91] [core] Correctly handle due time of '0' in SmackReactor Scheduled actions that are due in 0 milliseconds are due immediately. Otherwise we would invoke select() with 0. Fixes SMACK-923. --- .../main/java/org/jivesoftware/smack/SmackReactor.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java index 75e1af8e9..67e68f1fd 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java @@ -221,11 +221,10 @@ public class SmackReactor { selectWait = 0; } else { selectWait = nextScheduledAction.getTimeToDueMillis(); - } - - if (selectWait < 0) { - // A scheduled action was just released and became ready to execute. - return; + if (selectWait <= 0) { + // A scheduled action was just released and became ready to execute. + return; + } } // Before we call select, we handle the pending the interest Ops. This will not block since no other From 1b0e19f699a7e7d4f543b482bccd6be353cd91a6 Mon Sep 17 00:00:00 2001 From: Micha Date: Wed, 9 Feb 2022 18:32:46 +0100 Subject: [PATCH 83/91] Add custom HttpUploadExceptions for more information in case of IOExceptions --- .../AbstractHttpUploadException.java | 101 ++++++++++++++++++ .../httpfileupload/HttpFileUploadManager.java | 9 +- 2 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/AbstractHttpUploadException.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/AbstractHttpUploadException.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/AbstractHttpUploadException.java new file mode 100644 index 000000000..1eb51f2ca --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/AbstractHttpUploadException.java @@ -0,0 +1,101 @@ +/** + * + * Copyright 2022 Micha Kurvers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.httpfileupload; + +import java.io.IOException; +import java.net.URL; + +import org.jivesoftware.smackx.httpfileupload.element.Slot; +/** + * An exception class to provide additional information in case of exceptions during file uploading. + * + */ +public abstract class AbstractHttpUploadException extends IOException { + + private static final long serialVersionUID = 1L; + private final long fileSize; + private final Slot slot; + + protected AbstractHttpUploadException(long fileSize, Slot slot, String message) { + this(fileSize, slot, message, null); + } + + protected AbstractHttpUploadException(long fileSize, Slot slot, String message, Throwable wrappedThrowable) { + super(message, wrappedThrowable); + this.fileSize = fileSize; + this.slot = slot; + } + + public long getFileSize() { + return fileSize; + } + + public URL getPutUrl() { + return slot.getPutUrl(); + } + + public Slot getSlot() { + return slot; + } + + /** + * Exception thrown when http response returned after upload is not 200. + */ + public static class HttpUploadErrorException extends AbstractHttpUploadException { + + private static final long serialVersionUID = 8494356028399474995L; + private final int httpStatus; + private final String responseMsg; + + public HttpUploadErrorException(int httpStatus, String responseMsg, long fileSize, Slot slot) { + super(fileSize, slot, "Error response " + httpStatus + " from server during file upload: " + + responseMsg + ", file size: " + fileSize + ", put URL: " + + slot.getPutUrl()); + this.httpStatus = httpStatus; + this.responseMsg = responseMsg; + } + + public int getHttpStatus() { + return httpStatus; + } + + public String getResponseMsg() { + return responseMsg; + } + + } + + /** + * Exception thrown when an unexpected exception occurred during the upload. + */ + public static class HttpUploadIOException extends AbstractHttpUploadException { + + private static final long serialVersionUID = 5940866318073349451L; + private final IOException wrappedIOException; + + public HttpUploadIOException(long fileSize, Slot slot, IOException cause) { + super(fileSize, slot, "Unexpected error occurred during file upload, file size: " + fileSize + + ", put Url: " + slot.getPutUrl(), cause); + this.wrappedIOException = cause; + } + + public IOException getCausingIOException() { + return this.wrappedIOException; + } + + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java index 3f98a5056..338d519aa 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java @@ -49,6 +49,8 @@ import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.httpfileupload.AbstractHttpUploadException.HttpUploadErrorException; +import org.jivesoftware.smackx.httpfileupload.AbstractHttpUploadException.HttpUploadIOException; import org.jivesoftware.smackx.httpfileupload.UploadService.Version; import org.jivesoftware.smackx.httpfileupload.element.Slot; import org.jivesoftware.smackx.httpfileupload.element.SlotRequest; @@ -494,11 +496,12 @@ public final class HttpFileUploadManager extends Manager { case HttpURLConnection.HTTP_NO_CONTENT: break; default: - throw new IOException("Error response " + status + " from server during file upload: " - + urlConnection.getResponseMessage() + ", file size: " + fileSize + ", put URL: " - + putUrl); + throw new HttpUploadErrorException(status, urlConnection.getResponseMessage(), fileSize, slot); } } + catch (IOException e) { + throw new HttpUploadIOException(fileSize, slot, e); + } finally { urlConnection.disconnect(); } From aafc24a96669d51cb5e3ac9968b5690e8ed73d78 Mon Sep 17 00:00:00 2001 From: Danny Baumann Date: Mon, 7 Feb 2022 14:36:45 +0100 Subject: [PATCH 84/91] Conditionally reduce severity of roster reload error logging If the roster feature is not supported by the server, there's no need to log this at SEVERE log level. --- .../main/java/org/jivesoftware/smack/roster/Roster.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 3d3198315..500894f07 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 @@ -469,11 +469,14 @@ public final class Roster extends Manager { @Override public void processException(Exception exception) { rosterState = RosterState.uninitialized; - Level logLevel; + Level logLevel = Level.SEVERE; if (exception instanceof NotConnectedException) { logLevel = Level.FINE; - } else { - logLevel = Level.SEVERE; + } else if (exception instanceof XMPPErrorException) { + Condition condition = ((XMPPErrorException) exception).getStanzaError().getCondition(); + if (condition == Condition.feature_not_implemented || condition == Condition.service_unavailable) { + logLevel = Level.FINE; + } } LOGGER.log(logLevel, "Exception reloading roster", exception); for (RosterLoadedListener listener : rosterLoadedListeners) { From 4622d00d9e6e1c1c93ec42a0741e291ddf7542b7 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 17 Feb 2022 11:58:02 +0100 Subject: [PATCH 85/91] [tcp] Unravel SSLSocketFactory.createSocket() invocation --- .../java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 c1dc8b7ee..e8de5e131 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 @@ -48,6 +48,7 @@ import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.ConnectionConfiguration; @@ -713,9 +714,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { SmackTlsContext smackTlsContext = getSmackTlsContext(); Socket plain = socket; + int port = plain.getPort(); + String xmppServiceDomainString = config.getXMPPServiceDomain().toString(); + SSLSocketFactory sslSocketFactory = smackTlsContext.sslContext.getSocketFactory(); // Secure the plain connection - socket = smackTlsContext.sslContext.getSocketFactory().createSocket(plain, - config.getXMPPServiceDomain().toString(), plain.getPort(), true); + socket = sslSocketFactory.createSocket(plain, xmppServiceDomainString, port, true); final SSLSocket sslSocket = (SSLSocket) socket; // Immediately set the enabled SSL protocols and ciphers. See SMACK-712 why this is From ba2e36dbc3ac75cf8e5e6c2962481e105dd3b81a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 17 Feb 2022 11:59:03 +0100 Subject: [PATCH 86/91] [core] Deprecate some SSLContext config options and add KeyManager option Smack historically provided fine-grained options for the SSLContext. This is however not flexible enough, as an option to specifiy the KeyManager(s) was missing. This deprecated the options for keystore path, keystore type, and PKCS#11 library, in favor of an option to set the KeyManager, which could be aware of the keystore path and type, and the PKCS#11 library. At some point, Smack may provide some high-level methods to create a KeyManager from provided keystore path, keytsore type and PKCS#11 library. --- .../smack/ConnectionConfiguration.java | 213 ++++++++++++------ 1 file changed, 145 insertions(+), 68 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 2a0165009..e8689d24f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2017-2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2017-2022 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; +import java.security.SecureRandom; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; @@ -189,7 +190,7 @@ public abstract class ConnectionConfiguration { protected ConnectionConfiguration(Builder builder) { try { smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory, - builder.customX509TrustManager, builder.keystoreType, builder.keystorePath, + builder.customX509TrustManager, builder.keyManagers, builder.sslContextSecureRandom, builder.keystoreType, builder.keystorePath, builder.callbackHandler, builder.pkcs11Library); } catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException | KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException @@ -252,7 +253,7 @@ public abstract class ConnectionConfiguration { } private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory, - X509TrustManager trustManager, String keystoreType, String keystorePath, + X509TrustManager trustManager, KeyManager[] keyManagers, SecureRandom secureRandom, String keystoreType, String keystorePath, CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, NoSuchProviderException, UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException, @@ -266,69 +267,10 @@ public abstract class ConnectionConfiguration { context = SSLContext.getInstance("TLS"); } - KeyStore ks = null; - PasswordCallback pcb = null; - KeyManager[] kms = null; - - if ("PKCS11".equals(keystoreType)) { - Constructor c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class); - String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library; - ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8)); - Provider p = (Provider) c.newInstance(config); - Security.addProvider(p); - ks = KeyStore.getInstance("PKCS11", p); - pcb = new PasswordCallback("PKCS11 Password: ", false); - callbackHandler.handle(new Callback[] { pcb }); - ks.load(null, pcb.getPassword()); - } else if ("Apple".equals(keystoreType)) { - ks = KeyStore.getInstance("KeychainStore", "Apple"); - ks.load(null, null); - // pcb = new PasswordCallback("Apple Keychain",false); - // pcb.setPassword(null); - } else if (keystoreType != null) { - ks = KeyStore.getInstance(keystoreType); - if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) { - pcb = new PasswordCallback("Keystore Password: ", false); - callbackHandler.handle(new Callback[] { pcb }); - ks.load(new FileInputStream(keystorePath), pcb.getPassword()); - } else { - InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); - try { - // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous - // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 - char[] password = "changeit".toCharArray(); - try { - ks.load(stream, password); - } finally { - CloseableUtil.maybeClose(stream); - } - } catch (IOException e) { - LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); - - ks = KeyStore.getInstance("jks"); - // Open the stream again, so that we read it from the beginning. - stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); - try { - ks.load(stream, null); - } finally { - CloseableUtil.maybeClose(stream); - } - } - } - } - - if (ks != null) { - String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); - if (kmf != null) { - if (pcb == null) { - kmf.init(ks, null); - } else { - kmf.init(ks, pcb.getPassword()); - pcb.clearPassword(); - } - kms = kmf.getKeyManagers(); - } + // TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHanlder() and + // setPKCS11Library() in the builder, and all related fields and the parameters of this function. + if (keyManagers == null) { + keyManagers = Builder.getKeyManagersFrom(keystoreType, keystorePath, callbackHandler, pkcs11Library); } SmackDaneVerifier daneVerifier = null; @@ -343,7 +285,7 @@ public abstract class ConnectionConfiguration { } // User requested DANE verification. - daneVerifier.init(context, kms, trustManager, null); + daneVerifier.init(context, keyManagers, trustManager, secureRandom); } else { final TrustManager[] trustManagers; if (trustManager != null) { @@ -354,7 +296,7 @@ public abstract class ConnectionConfiguration { trustManagers = null; } - context.init(kms, trustManagers, null); + context.init(keyManagers, trustManagers, secureRandom); } return new SmackTlsContext(context, daneVerifier); @@ -688,6 +630,8 @@ public abstract class ConnectionConfiguration { public abstract static class Builder, C extends ConnectionConfiguration> { private SecurityMode securityMode = SecurityMode.required; private DnssecMode dnssecMode = DnssecMode.disabled; + private KeyManager[] keyManagers; + private SecureRandom sslContextSecureRandom; private String keystorePath; private String keystoreType; private String pkcs11Library = "pkcs11.config"; @@ -942,7 +886,12 @@ public abstract class ConnectionConfiguration { * @param callbackHandler to obtain information, such as the password or * principal information during the SASL authentication. * @return a reference to this builder. + * @deprecated set a callback-handler aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or + * {@link #setKeyManagers(KeyManager[])}, created by + * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. */ + // TODO: Remove in Smack 4.6. + @Deprecated public B setCallbackHandler(CallbackHandler callbackHandler) { this.callbackHandler = callbackHandler; return getThis(); @@ -970,6 +919,47 @@ public abstract class ConnectionConfiguration { return getThis(); } + /** + * Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection. + * + * @param keyManagers an array of {@link KeyManager}s to initialize the {@link SSLContext} with. + * @return a reference to this builder. + * @since 4.4.5 + */ + public B setKeyManagers(KeyManager[] keyManagers) { + this.keyManagers = keyManagers; + return getThis(); + } + + /** + * Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection. + * + * @param keyManager the {@link KeyManager}s to initialize the {@link SSLContext} with. + * @return a reference to this builder. + * @see #setKeyManagers(KeyManager[]) + * @since 4.4.5 + */ + public B setKeyManager(KeyManager keyManager) { + KeyManager[] keyManagers = new KeyManager[] { keyManager }; + return setKeyManagers(keyManagers); + } + + /** + * Set the {@link SecureRandom} used to initialize the {@link SSLContext} used by Smack to establish the XMPP + * connection. Note that you usually do not need (nor want) to set this. Because if the {@link SecureRandom} is + * not explicitly set, Smack will initialize the {@link SSLContext} with null as + * {@link SecureRandom} argument. And all sane {@link SSLContext} implementations will then select a safe secure + * random source by default. + * + * @param secureRandom the {@link SecureRandom} to initialize the {@link SSLContext} with. + * @return a reference to this builder. + * @since 4.4.5 + */ + public B setSslContextSecureRandom(SecureRandom secureRandom) { + this.sslContextSecureRandom = secureRandom; + return getThis(); + } + /** * Sets the path to the keystore file. The key store file contains the * certificates that may be used to authenticate the client to the server, @@ -977,7 +967,12 @@ public abstract class ConnectionConfiguration { * * @param keystorePath the path to the keystore file. * @return a reference to this builder. + * @deprecated set a keystore-path aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or + * {@link #setKeyManagers(KeyManager[])}, created by + * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. */ + // TODO: Remove in Smack 4.6. + @Deprecated public B setKeystorePath(String keystorePath) { this.keystorePath = keystorePath; return getThis(); @@ -988,7 +983,12 @@ public abstract class ConnectionConfiguration { * * @param keystoreType the keystore type. * @return a reference to this builder. + * @deprecated set a key-type aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or + * {@link #setKeyManagers(KeyManager[])}, created by + * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. */ + // TODO: Remove in Smack 4.6. + @Deprecated public B setKeystoreType(String keystoreType) { this.keystoreType = keystoreType; return getThis(); @@ -1000,7 +1000,12 @@ public abstract class ConnectionConfiguration { * * @param pkcs11Library the path to the PKCS11 library file. * @return a reference to this builder. + * @deprecated set a PKCS11-library aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or + * {@link #setKeyManagers(KeyManager[])}, created by + * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. */ + // TODO: Remove in Smack 4.6. + @Deprecated public B setPKCS11Library(String pkcs11Library) { this.pkcs11Library = pkcs11Library; return getThis(); @@ -1276,5 +1281,77 @@ public abstract class ConnectionConfiguration { public abstract C build(); protected abstract B getThis(); + + public static KeyManager[] getKeyManagersFrom(String keystoreType, String keystorePath, + CallbackHandler callbackHandler, String pkcs11Library) + throws NoSuchMethodException, SecurityException, ClassNotFoundException, KeyStoreException, + NoSuchProviderException, NoSuchAlgorithmException, CertificateException, IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, UnsupportedCallbackException, UnrecoverableKeyException { + KeyManager[] keyManagers = null; + KeyStore ks = null; + PasswordCallback pcb = null; + + if ("PKCS11".equals(keystoreType)) { + Constructor c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class); + String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library; + ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8)); + Provider p = (Provider) c.newInstance(config); + Security.addProvider(p); + ks = KeyStore.getInstance("PKCS11", p); + pcb = new PasswordCallback("PKCS11 Password: ", false); + callbackHandler.handle(new Callback[] { pcb }); + ks.load(null, pcb.getPassword()); + } else if ("Apple".equals(keystoreType)) { + ks = KeyStore.getInstance("KeychainStore", "Apple"); + ks.load(null, null); + // pcb = new PasswordCallback("Apple Keychain",false); + // pcb.setPassword(null); + } else if (keystoreType != null) { + ks = KeyStore.getInstance(keystoreType); + if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) { + pcb = new PasswordCallback("Keystore Password: ", false); + callbackHandler.handle(new Callback[] { pcb }); + ks.load(new FileInputStream(keystorePath), pcb.getPassword()); + } else { + InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); + try { + // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous + // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 + char[] password = "changeit".toCharArray(); + try { + ks.load(stream, password); + } finally { + CloseableUtil.maybeClose(stream); + } + } catch (IOException e) { + LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); + + ks = KeyStore.getInstance("jks"); + // Open the stream again, so that we read it from the beginning. + stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); + try { + ks.load(stream, null); + } finally { + CloseableUtil.maybeClose(stream); + } + } + } + } + + if (ks != null) { + String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); + if (kmf != null) { + if (pcb == null) { + kmf.init(ks, null); + } else { + kmf.init(ks, pcb.getPassword()); + pcb.clearPassword(); + } + keyManagers = kmf.getKeyManagers(); + } + } + + return keyManagers; + } } } From 4d026d8ae84819184a8cad93f65630fe6e2dca8c Mon Sep 17 00:00:00 2001 From: cmeng-git Date: Sun, 13 Feb 2022 15:58:37 +0800 Subject: [PATCH 87/91] [jingle] Add element and text to JingleReason's XML --- .../org/jivesoftware/smackx/jingle/element/JingleReason.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java index 116c8375d..32a9e7d0f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java @@ -35,6 +35,7 @@ public class JingleReason implements FullyQualifiedElement { public static final String ELEMENT = "reason"; public static final String NAMESPACE = Jingle.NAMESPACE; + public static final String TEXT_ELEMENT = "text"; public static AlternativeSession AlternativeSession(String sessionId) { return new AlternativeSession(sessionId); @@ -142,7 +143,7 @@ public class JingleReason implements FullyQualifiedElement { /** * An optional element that provides more detailed machine-readable information about the reason for the action. * - * @return an elemnet with machine-readable information about this reason or null. + * @return an element with machine-readable information about this reason or null. * @since 4.4.5 */ public ExtensionElement getElement() { @@ -155,6 +156,8 @@ public class JingleReason implements FullyQualifiedElement { xml.rightAngleBracket(); xml.emptyElement(reason); + xml.optElement(TEXT_ELEMENT, text); + xml.optAppend(element); xml.closeElement(this); return xml; From 229a6edb1b54a1e2ac4bfcd5c788a8e277adbeb6 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 2 Mar 2022 18:36:40 +0100 Subject: [PATCH 88/91] Switch to CHANGELOG.md Bye Bye changelog.html. You have served us good old friend. But JIRA does no longer create compatible HTML changelogs. And markdown seems to be the better alternative anyway. --- CHANGELOG.md | 1841 ++++++++++++++++++++++++++ resources/releasedocs/changelog.html | 1675 ----------------------- 2 files changed, 1841 insertions(+), 1675 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 resources/releasedocs/changelog.html diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..c42f11204 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1841 @@ +# Smack Changelog + +## 4.4.4 -- 2021-11-01 + +### Bug + +- [SMACK-916](https://igniterealtime.atlassian.net/browse/SMACK-916) - + XMPPErrorException.stanza is missing a getter method +- [SMACK-915](https://igniterealtgime.atlassian.net/browse/SMACK-915) - + Smack does not process MUC destroy message if they contain + \'status\' +- [SMACK-914](https://igniterealtime.atlassian.net/browse/SMACK-914) - + MultiUserChat may be become unjoinable due to a race condition +- [SMACK-913](https://igniterealtime.atlassian.net/browse/SMACK-913) - + MultiUserChat.serviceSupportsStableIds\\(\\) may throws a + NullPointerException +- [SMACK-912](https://igniterealtime.atlassian.net/browse/SMACK-912) - + Smack does not start the local SOCKS5 proxy automatically +- [SMACK-910](https://igniterealtime.atlassian.net/browse/SMACK-910) - + FormNode and FormNodeProvide should handle non-existent DataForm +- [SMACK-909](https://igniterealtime.atlassian.net/browse/SMACK-909) - + Must use the raw character data of a form field in entity caps hash + calculation + +## 4.4.3 -- 2021-07-06 + +### Bug + +- [SMACK-905](https://igniterealtime.atlassian.net/browse/SMACK-905) - + The class org.jivesoftware.smackx.offline.packet.OfflineMessageInfo + has no ELEMENT, NAMESPACE or QNAME member +- [SMACK-907](https://igniterealtime.atlassian.net/browse/SMACK-907) - + Possible NPE in MultipleRecipientManager + +## 4.4.2 -- 2021-03-25 + +### Bug + +- [SMACK-903](https://igniterealtime.atlassian.net/browse/SMACK-903) - + StaxXmlPullParser.getNamespace() may throws IllegalArgumentException +- [SMACK-904](https://igniterealtime.atlassian.net/browse/SMACK-904) - + XEP-0096 file transfer fails because of a hidden ClastCastException + +## 4.4.1 -- 2021-03-03 + +### Bug + +- [SMACK-895](https://igniterealtime.atlassian.net/browse/SMACK-895) - + BoBIQ#getIQChildElementBuilder throws NPE when the BoB data does not + contain 'max-age'. +- [SMACK-896](https://igniterealtime.atlassian.net/browse/SMACK-896) - + BoBDataExtension is missing getter for BoBData and ContentId +- [SMACK-897](https://igniterealtime.atlassian.net/browse/SMACK-897) - + DirectoryRosterStore.readEntry() should also catch + IllegalArgumentException +- [SMACK-898](https://igniterealtime.atlassian.net/browse/SMACK-898) - + AbstractProvider should also consider TypeVariable +- [SMACK-899](https://igniterealtime.atlassian.net/browse/SMACK-899) - + NullPointerException in EntityCapsManager.addCapsExtension +- [SMACK-900](https://igniterealtime.atlassian.net/browse/SMACK-900) - + NPE in DataForm.Builder.addItem() +- [SMACK-902](https://igniterealtime.atlassian.net/browse/SMACK-902) - + DataFormProvider should retrieve the type of fields from + \ elements if possible + +### Improvement + +- [SMACK-901](https://igniterealtime.atlassian.net/browse/SMACK-901) - + BoBDataExtension.from() should also allow IQs + +## 4.4.0 -- 2020-12-06 + +### Bug + +- [SMACK-561](https://igniterealtime.atlassian.net/browse/SMACK-561) - + Smack should not reply with multiple stream types after stream + initiation is offered +- [SMACK-624](https://igniterealtime.atlassian.net/browse/SMACK-624) - + AdHocCommandManager\'s session sweeping thread does never stop +- [SMACK-729](https://igniterealtime.atlassian.net/browse/SMACK-729) - + Not all providers from smack-legacy.jar are loaded +- [SMACK-770](https://igniterealtime.atlassian.net/browse/SMACK-770) - + There is no Bits of Binary Extension Element provider registered +- [SMACK-848](https://igniterealtime.atlassian.net/browse/SMACK-848) - + Make MultiUserChat.leave() wait for response +- [SMACK-874](https://igniterealtime.atlassian.net/browse/SMACK-874) - + PacketParserUtilsTest#invalidXMLInMessageBody() fails on non-english + machines +- [SMACK-881](https://igniterealtime.atlassian.net/browse/SMACK-881) - + Deadlock between reader and writer if Stream Mangement unacked + stanza queue is full +- [SMACK-888](https://igniterealtime.atlassian.net/browse/SMACK-888) - + MUC roomDestroyed() callback is not invoked + +### New Feature + +- [SMACK-257](https://igniterealtime.atlassian.net/browse/SMACK-257) - + Add support for XEP-0118: User Tune +- [SMACK-636](https://igniterealtime.atlassian.net/browse/SMACK-636) - + Add support for XEP-0319: Last User Interaction in Presence +- [SMACK-743](https://igniterealtime.atlassian.net/browse/SMACK-743) - + Add support for XEP-0384: OMEMO Encryption +- [SMACK-801](https://igniterealtime.atlassian.net/browse/SMACK-801) - + Update Smack to Java 8 +- [SMACK-824](https://igniterealtime.atlassian.net/browse/SMACK-824) - + Add support for XEP-0221: Data Forms Media Element +- [SMACK-862](https://igniterealtime.atlassian.net/browse/SMACK-862) - + Add support for XEP-0418: DNS Queries over XMPP (DoX) +- [SMACK-871](https://igniterealtime.atlassian.net/browse/SMACK-871) - + Add support for XEP-0350: Data Forms Geolocation Element +- [SMACK-872](https://igniterealtime.atlassian.net/browse/SMACK-872) - + Add support for XEP-0315: Data Forms XML Element +- [SMACK-878](https://igniterealtime.atlassian.net/browse/SMACK-878) - + Add support for XEP-0328: JID Prep +- [SMACK-884](https://igniterealtime.atlassian.net/browse/SMACK-884) - + Add support for XEP-0422: Message Fastening +- [SMACK-885](https://igniterealtime.atlassian.net/browse/SMACK-885) - + Add support for XEP-0420 Stanza Content Encryption +- [SMACK-889](https://igniterealtime.atlassian.net/browse/SMACK-889) - + Add support for XEP-0428: Fallback Indication + +### Improvement + +- [SMACK-591](https://igniterealtime.atlassian.net/browse/SMACK-591) - + Replace XPP3 by SmackXmlPullParser (wrapping Stax\'s XmlStreamReader + and XPP3 on Android) +- [SMACK-650](https://igniterealtime.atlassian.net/browse/SMACK-650) - + Enable Java8\'s javadoc doclint +- [SMACK-651](https://igniterealtime.atlassian.net/browse/SMACK-651) - + Perform sound cross-compilation: Use newer javac\'s \--release + feature +- [SMACK-718](https://igniterealtime.atlassian.net/browse/SMACK-718) - + Prevent extremely long reply timeouts from being set +- [SMACK-821](https://igniterealtime.atlassian.net/browse/SMACK-821) - + Make Forwarded a generic type +- [SMACK-822](https://igniterealtime.atlassian.net/browse/SMACK-822) - + Add API for XEP-0313 § 6.2 Advanced configuration via Ad-Hoc + commands +- [SMACK-825](https://igniterealtime.atlassian.net/browse/SMACK-825) - + Discourage Stanza.getExtension(String, String) in favor of + Stanza.getExtension(Class\) +- [SMACK-826](https://igniterealtime.atlassian.net/browse/SMACK-826) - + Add support for XEP-0373:\" OpenPGP for XMPP\" and XEP-0374: + \"OpenPGP for XMPP Instant Messaging\" +- [SMACK-828](https://igniterealtime.atlassian.net/browse/SMACK-828) - + Add support for XEP-0107: User Mood +- [SMACK-836](https://igniterealtime.atlassian.net/browse/SMACK-836) - + Save a ServiceDiscoveryManager instance in a private field of + MultiUserChatManger +- [SMACK-839](https://igniterealtime.atlassian.net/browse/SMACK-839) - + Provider.parse() should not throw a generic Exception, but instead + IOException and XmlPullParserException +- [SMACK-852](https://igniterealtime.atlassian.net/browse/SMACK-852) - + Message thread and subject should be designed and implemented as + ExtensionElements +- [SMACK-854](https://igniterealtime.atlassian.net/browse/SMACK-854) - + Rename smack-java7 to smack-java8 +- [SMACK-866](https://igniterealtime.atlassian.net/browse/SMACK-866) - + Remove all tabs from the source code and add checkstyle rule that + enforces no-tabs +- [SMACK-867](https://igniterealtime.atlassian.net/browse/SMACK-867) - + Extend HttpFileUploadManager by methods with InputStream parameter +- [SMACK-882](https://igniterealtime.atlassian.net/browse/SMACK-882) - + Add support for MUC status code 333 +- [SMACK-883](https://igniterealtime.atlassian.net/browse/SMACK-883) - + Add generic MUC callback for \"participant left\" caused by + unavailable presences +- [SMACK-890](https://igniterealtime.atlassian.net/browse/SMACK-890) - + Update Message Archive Management (XEP-0313) support to + urn:xmpp:mam:2 +- [SMACK-892](https://igniterealtime.atlassian.net/browse/SMACK-892) - + Smack performs unnecessary escaping in XML text + +### Task + +- [SMACK-750](https://igniterealtime.atlassian.net/browse/SMACK-750) - + Raise Smack\'s minimum required Android SDK level to 19 (Android + 4.4, Kit Kat, 2013-10) +- [SMACK-840](https://igniterealtime.atlassian.net/browse/SMACK-840) - + Remove smack-compression-jzlib, as it is obsolete (Smack uses Java 7 + de- and inflate API now) + +## 4.3.4 -- 2019-05-27 + +### Bug + +- [SMACK-861](https://igniterealtime.atlassian.net/browse/SMACK-861) - + Potential NPE in Roster.getPresencesInternal(BareJid) +- [SMACK-863](https://igniterealtime.atlassian.net/browse/SMACK-863) - + ServiceDiscoveryManger does not use the main identity, causing + setIdentity() to have no effect +- [SMACK-864](https://igniterealtime.atlassian.net/browse/SMACK-864) - + Potential Denial of Service (DOS) by remote entities caused by + unlimited threads for asynchronous operations +- [SMACK-865](https://igniterealtime.atlassian.net/browse/SMACK-865) - + Some Manager.getInsanceFor() methods are missing the + \'synchronized\' keyword +- [SMACK-868](https://igniterealtime.atlassian.net/browse/SMACK-868) - + XHTMLText.appendOpenBodyTag() produces invalid XML +- [SMACK-870](https://igniterealtime.atlassian.net/browse/SMACK-870) - + TLS X.509 certificate verification should be performed with the ACE + representation of the XMPP service domain when possible + +### Improvement + +- [SMACK-869](https://igniterealtime.atlassian.net/browse/SMACK-869) - + Exceptions in async tasks should not go uncaught to the call stack + to avoid program termination + +## 4.3.3 -- 2019-03-14 + +### Bug + +- [SMACK-856](https://igniterealtime.atlassian.net/browse/SMACK-856) - + Smack fails under JDK 11 because com.sun.jndi.dns.DnsContextFactory + is not inaccessible + +### Improvement + +- [SMACK-858](https://igniterealtime.atlassian.net/browse/SMACK-858) - + Dependency version specifier of jxmpp and MiniDNS include + alpha/beta/\... versions of the follow up version when Maven is used +- [SMACK-859](https://igniterealtime.atlassian.net/browse/SMACK-859) - + MultiUserChat enter() should reset the timeout of the collector + waiting for the final self presence to prevent timeouts for large + MUCs + +## 4.3.2 -- 2019-02-22 + +### Bug + +- [SMACK-842](https://igniterealtime.atlassian.net/browse/SMACK-842) - + The RFC 3920 xml-not-well-formed error condition should be handled + in stream error not a stanza error +- [SMACK-843](https://igniterealtime.atlassian.net/browse/SMACK-843) - + ManManager.pagePrevious() pages into the wrong direction +- [SMACK-844](https://igniterealtime.atlassian.net/browse/SMACK-844) - + Check if bounded unacknowledged stanzas queue is full before adding + to it to avoid IllegalStateException +- [SMACK-845](https://igniterealtime.atlassian.net/browse/SMACK-845) - + Ensure that IQ response \'to\' address and ID are set correctly +- [SMACK-846](https://igniterealtime.atlassian.net/browse/SMACK-846) - + XMPPTCPConnection does not wait for stream features after + authentication if compression is disabled +- [SMACK-848](https://igniterealtime.atlassian.net/browse/SMACK-848) - + Make MultiUserChat.leave() wait for response +- [SMACK-850](https://igniterealtime.atlassian.net/browse/SMACK-850) - + DeliveryReceiptManager should not send receipts with messages of + type \'groupchat\' +- [SMACK-855](https://igniterealtime.atlassian.net/browse/SMACK-855) - + XMPPTCPConnection sometimes has two writer threads running + +### Improvement + +- [SMACK-847](https://igniterealtime.atlassian.net/browse/SMACK-847) - + Make TCP socket connection attempt interruptable +- [SMACK-849](https://igniterealtime.atlassian.net/browse/SMACK-849) - + Smack Local SOCKS5 Proxy thread should be marked as daemon thread + +## 4.3.1 -- 2018-10-14 + +### Bug + +- [SMACK-833](https://igniterealtime.atlassian.net/browse/SMACK-833) - + XMLUtil.prettyFormatXml() throws on some Android devices + +### Improvement + +- [SMACK-829](https://igniterealtime.atlassian.net/browse/SMACK-829) - + Disconnect BOSH client on shutdown +- [SMACK-838](https://igniterealtime.atlassian.net/browse/SMACK-838) - + FormField.getFirstValue() throws IndexOutOfBoundsException if there + are no values + +## 4.3.0 -- 2018-08-02 + +### Bug + +- [SMACK-759](https://igniterealtime.atlassian.net/browse/SMACK-759) - + PubSubManager.getLeafNode() throws + PubSubAssertionError.DiscoInfoNodeAssertionError if node exists but + its not a PubSub Node +- [SMACK-814](https://igniterealtime.atlassian.net/browse/SMACK-814) - + NPE when using Node.getAffiliationsAsOwner() +- [SMACK-815](https://igniterealtime.atlassian.net/browse/SMACK-815) - + XEP-0184: DeliveryReceipt requires ID, although the XEP defines it + as optional attribute +- [SMACK-818](https://igniterealtime.atlassian.net/browse/SMACK-818) - + EntityCapsManager sends presences with multiple CapsExtension + causing disco#info lookup to fail +- [SMACK-819](https://igniterealtime.atlassian.net/browse/SMACK-819) - + ConcurrentModification Exception in MultiUserChatManager.java +- [SMACK-820](https://igniterealtime.atlassian.net/browse/SMACK-820) - + DNSUtil.setDaneProvider() does not set the DANE provider + +### Task + +- [SMACK-769](https://igniterealtime.atlassian.net/browse/SMACK-769) - + Rename XMPPError to StanzaError +- [SMACK-776](https://igniterealtime.atlassian.net/browse/SMACK-776) - + Remove deprecated reconnection callbacks in ConnectionListener + +### Improvement + +- [SMACK-761](https://igniterealtime.atlassian.net/browse/SMACK-761) - + Adopt ChatStateManager to new Chat API (chat2) +- [SMACK-812](https://igniterealtime.atlassian.net/browse/SMACK-812) - + Enable ModifierOrder checkstyle check +- [SMACK-816](https://igniterealtime.atlassian.net/browse/SMACK-816) - + SimplePayload should infer the XML element name and namespace + +## 4.2.4 -- 2018-04-15 + +### Bug + +- [SMACK-804](https://igniterealtime.atlassian.net/browse/SMACK-804) - + ServiceAdministrationManager does not use correct form actions +- [SMACK-805](https://igniterealtime.atlassian.net/browse/SMACK-805) - + ServiceDiscoveryManager.findService() only considers the first + service by feature +- [SMACK-813](https://igniterealtime.atlassian.net/browse/SMACK-813) - + Smack uses hostname instead of XMPP service name for SNI + +### New Feature + +- [SMACK-794](https://igniterealtime.atlassian.net/browse/SMACK-794) - + Add support for XEP-0394: Message Markup +- [SMACK-795](https://igniterealtime.atlassian.net/browse/SMACK-795) - + Add support for XEP-0382: Spoiler messages +- [SMACK-799](https://igniterealtime.atlassian.net/browse/SMACK-799) - + Add support for XEP-0372: References +- [SMACK-800](https://igniterealtime.atlassian.net/browse/SMACK-800) - + Add support for XEP-0392: Consistent Color Generation + +### Improvement + +- [SMACK-802](https://igniterealtime.atlassian.net/browse/SMACK-802) - + Rename and deprecate: addPacketSendingListener(), + removePacketSendingListener(), addPacketInterceptor() and + removePacketInterceptor() +- [SMACK-809](https://igniterealtime.atlassian.net/browse/SMACK-809) - + Make Roster\'s non-roster presence map second-level map bounded + +## 4.2.3 -- 2018-02-07 + +### Bug + +- [SMACK-788](https://igniterealtime.atlassian.net/browse/SMACK-788) - + NullPointerException if hostAddresses is null +- [SMACK-789](https://igniterealtime.atlassian.net/browse/SMACK-789) - + AffiliationsExtension toXml() produces invalid XML +- [SMACK-790](https://igniterealtime.atlassian.net/browse/SMACK-790) - + Some HTTP File Upload elements are not correctly parsed and + serialized +- [SMACK-791](https://igniterealtime.atlassian.net/browse/SMACK-791) - + NumberFormatException in IpAddressUtil.isIPv4LiteralAddress +- [SMACK-796](https://igniterealtime.atlassian.net/browse/SMACK-796) - + SOCKS5 authentication erroneously uses \'user\' when it should use + \'passwd\', causes authentication to fail + +## 4.2.2 -- 2017-11-25 + +### Bug + +- [SMACK-775](https://igniterealtime.atlassian.net/browse/SMACK-775) - + Create callback interface for ReconnectionManager +- [SMACK-778](https://igniterealtime.atlassian.net/browse/SMACK-778) - + ReconnectionManager.reconnect() can throw NotConnectedException +- [SMACK-779](https://igniterealtime.atlassian.net/browse/SMACK-779) - + smack-android erroneously depends on smack-omemo and + smack-omemo-signal +- [SMACK-780](https://igniterealtime.atlassian.net/browse/SMACK-780) - + PushNotificationManager\'s isSupported logic does query the server, + whereas it should query the bare JID +- [SMACK-781](https://igniterealtime.atlassian.net/browse/SMACK-781) - + MiniDnsResolver does not correctly handle the case when NOERROR is + returned together with an empty answer section. +- [SMACK-782](https://igniterealtime.atlassian.net/browse/SMACK-782) - + MultiUserChat does not remove the subject listener causing a memory + leak +- [SMACK-783](https://igniterealtime.atlassian.net/browse/SMACK-783) - + InvitationRejectionListener fires multiple times +- [SMACK-784](https://igniterealtime.atlassian.net/browse/SMACK-784) - + StringUtils.numbersAndLetters has the numbers twice, resulting in a + lower entropy +- [SMACK-785](https://igniterealtime.atlassian.net/browse/SMACK-785) - + OfflineMessageManager.getMessages() does count the pending messages + incorrectly, causing an unnecessary delay +- [SMACK-786](https://igniterealtime.atlassian.net/browse/SMACK-786) - + Race condition when resuming a stream +- [SMACK-787](https://igniterealtime.atlassian.net/browse/SMACK-787) - + Presence.getPriority() may return Integer.MIN_VALUE. + +## 4.2.1 -- 2017-08-14 + +### Bug + +- [SMACK-749](https://igniterealtime.atlassian.net/browse/SMACK-749) - + SCRAM-SHA-1 and SCRAM-SHA-1-PLUS SASL mechanisms have the same + priority, causing SASL authentication failures +- [SMACK-755](https://igniterealtime.atlassian.net/browse/SMACK-755) - + DIGEST-MD5 sometimes causes malformed request server response +- [SMACK-756](https://igniterealtime.atlassian.net/browse/SMACK-756) - + IoTIsFriendResponse has invalid name and produces invalid XML +- [SMACK-759](https://igniterealtime.atlassian.net/browse/SMACK-759) - + PubSubManager.getLeafNode() throws + PubSubAssertionError.DiscoInfoNodeAssertionError if node exists but + its not a PubSub Node +- [SMACK-764](https://igniterealtime.atlassian.net/browse/SMACK-764) - + NPE in hashCode() in Occupant when jid is null +- [SMACK-766](https://igniterealtime.atlassian.net/browse/SMACK-766) - + Smack possibly includes \'ask\' attribute in roster items when + sending requests +- [SMACK-768](https://igniterealtime.atlassian.net/browse/SMACK-768) - + Smack throws NoResponse timeout when waiting for IQ although there + was a response +- [SMACK-771](https://igniterealtime.atlassian.net/browse/SMACK-771) - + XMPPTCPConnection should use KeyManagerFactory.getDefaultAlgorithm() + instead of KeyManagerFactory.getInstance(\"sunX509\"); +- [SMACK-772](https://igniterealtime.atlassian.net/browse/SMACK-772) - + HostAddress must deal with \'fqdn\' being null. +- [SMACK-773](https://igniterealtime.atlassian.net/browse/SMACK-773) - + Allow roster pushes from our full JID for backwards compatibility +- [SMACK-774](https://igniterealtime.atlassian.net/browse/SMACK-774) - + HTTP File Upload\'s SlotRequest metadata should be attributes not + child elements + +### New Feature + +- [SMACK-746](https://igniterealtime.atlassian.net/browse/SMACK-746) - + Add support for XEP-0380: Explicit Message Encryption +- [SMACK-758](https://igniterealtime.atlassian.net/browse/SMACK-758) - + Add support for XEP-0334: Message Processing Hints +- [SMACK-760](https://igniterealtime.atlassian.net/browse/SMACK-760) - + Smack does not allow custom extension elements in SM\'s \ + +### Improvement + +- [SMACK-752](https://igniterealtime.atlassian.net/browse/SMACK-752) - + XEP-0357 Push Notification enable IQ uses wrong form type: Should be + \'submit\' instead of \'form\' +- [SMACK-754](https://igniterealtime.atlassian.net/browse/SMACK-754) - + Allow MUC room subject changes from the MUCs bare JID +- [SMACK-777](https://igniterealtime.atlassian.net/browse/SMACK-777) - + MamManager should use the user\'s bare JID to check if MAM is + supported + +## 4.2.0 -- 2017-03-10 + +## Sub-task + +- [SMACK-639](https://igniterealtime.atlassian.net/browse/SMACK-639) - + Add support for pre-approved subscription requests (RFC 6121 § 3.4) + +### Bug + +- [SMACK-306](https://igniterealtime.atlassian.net/browse/SMACK-306) - + loadRosterOnLogin has non-trivial side effect on getRoster +- [SMACK-416](https://igniterealtime.atlassian.net/browse/SMACK-416) - + Refactor PEP to make it use the existing pubsub API. +- [SMACK-674](https://igniterealtime.atlassian.net/browse/SMACK-674) - + PubSub Affiliation extension element is missing \'jid\' attribute, + and is using wrong element name \'subscription\' +- [SMACK-682](https://igniterealtime.atlassian.net/browse/SMACK-682) - + Add support for \"XEP-0360: Nonzas (are not Stanzas)\" +- [SMACK-683](https://igniterealtime.atlassian.net/browse/SMACK-683) - + Using a Proxy with XMPPTCPConnection failes with \"SocketException: + Unconnected sockets not implemented\" +- [SMACK-691](https://igniterealtime.atlassian.net/browse/SMACK-691) - + Add support for MUCItem\'s Actor \'nick\' +- [SMACK-705](https://igniterealtime.atlassian.net/browse/SMACK-705) - + PubSub\'s Affiliation.getElementName() returns wrong name +- [SMACK-722](https://igniterealtime.atlassian.net/browse/SMACK-722) - + SASL X-OAUTH2 implementation incorrectly performs Base64 encoding + twice +- [SMACK-723](https://igniterealtime.atlassian.net/browse/SMACK-723) - + Support \"Caps Optimizations\" (XEP-0115 § 8.4) +- [SMACK-724](https://igniterealtime.atlassian.net/browse/SMACK-724) - + Do not re-use the Socket after connect() failed. +- [SMACK-725](https://igniterealtime.atlassian.net/browse/SMACK-725) - + ReconnectionManager should handle AlreadyConnectedException and + AlreadyLoggedInException not as failure +- [SMACK-741](https://igniterealtime.atlassian.net/browse/SMACK-741) - + Ad-hoc command \'note\' element \'type\' attribute should be treated + as optional +- [SMACK-745](https://igniterealtime.atlassian.net/browse/SMACK-745) - + Memory leak in MultiUserChat + +### New Feature + +- [SMACK-366](https://igniterealtime.atlassian.net/browse/SMACK-366) - + Add support for DNSSEC. +- [SMACK-610](https://igniterealtime.atlassian.net/browse/SMACK-610) - + Add support for XEP-0080: User Location +- [SMACK-619](https://igniterealtime.atlassian.net/browse/SMACK-619) - + Add roomDestroyed to MUC UserStatusListener +- [SMACK-625](https://igniterealtime.atlassian.net/browse/SMACK-625) - + Add support for XEP-313: Message Archive Management +- [SMACK-675](https://igniterealtime.atlassian.net/browse/SMACK-675) - + Add support for PubSub affiliation actions as owner +- [SMACK-677](https://igniterealtime.atlassian.net/browse/SMACK-677) - + Add support for SASL \'authzid\' (Authorization Identity) +- [SMACK-690](https://igniterealtime.atlassian.net/browse/SMACK-690) - + Add support for DNS-Based Authentication of Named Entities (DANE, + RFC 6698) +- [SMACK-731](https://igniterealtime.atlassian.net/browse/SMACK-731) - + Add support for XEP-0191: Blocking Command +- [SMACK-732](https://igniterealtime.atlassian.net/browse/SMACK-732) - + Smack should be able to handle \"single equals sign\" SASL responses +- [SMACK-740](https://igniterealtime.atlassian.net/browse/SMACK-740) - + Add support for Multi-User Chat Light +- [SMACK-742](https://igniterealtime.atlassian.net/browse/SMACK-742) - + Add support for XEP-0133: Service Administration +- [SMACK-747](https://igniterealtime.atlassian.net/browse/SMACK-747) - + Add support for XEP-0363: HTTP File Upload + +### Task + +- [SMACK-638](https://igniterealtime.atlassian.net/browse/SMACK-638) - + Call connection creation listeners from within + AbstractXMPPConnection\'s constructor +- [SMACK-644](https://igniterealtime.atlassian.net/browse/SMACK-644) - + Throw exception if account creation or password change is performed + over insecure connections +- [SMACK-655](https://igniterealtime.atlassian.net/browse/SMACK-655) - + Enable StreamManagement by default + +### Improvement + +- [SMACK-372](https://igniterealtime.atlassian.net/browse/SMACK-372) - + Make package protected methods in PEPItem public +- [SMACK-572](https://igniterealtime.atlassian.net/browse/SMACK-572) - + Rejoin MUC rooms after reconnect +- [SMACK-628](https://igniterealtime.atlassian.net/browse/SMACK-628) - + Rework Roster handling with anonymous connections +- [SMACK-629](https://igniterealtime.atlassian.net/browse/SMACK-629) - + Rework how Smack handles anonymous connections +- [SMACK-631](https://igniterealtime.atlassian.net/browse/SMACK-631) - + Improve ParsingExceptionCallback, allow it to be a functional + interface +- [SMACK-632](https://igniterealtime.atlassian.net/browse/SMACK-632) - + Make Smack interruptible +- [SMACK-633](https://igniterealtime.atlassian.net/browse/SMACK-633) - + Allow clean and graceful disconnects (stream closing) +- [SMACK-634](https://igniterealtime.atlassian.net/browse/SMACK-634) - + Use jxmpp-jid, add Jid class to replace String\'s being used as JIDs +- [SMACK-646](https://igniterealtime.atlassian.net/browse/SMACK-646) - + Add support for MUC roomnick rewrite +- [SMACK-647](https://igniterealtime.atlassian.net/browse/SMACK-647) - + Don\'t automatically call login() on connect() if the connection was + authenticated before +- [SMACK-648](https://igniterealtime.atlassian.net/browse/SMACK-648) - + Improve MultiUserChat API +- [SMACK-657](https://igniterealtime.atlassian.net/browse/SMACK-657) - + Rename RosterEntry.getStatus and RosterPacket.ItemStatus to + ItemAskStatus +- [SMACK-663](https://igniterealtime.atlassian.net/browse/SMACK-663) - + Roster should be fully loaded when + Roster.getInstanceFor(XMPPConnection) is called with a authenticated + connection +- [SMACK-665](https://igniterealtime.atlassian.net/browse/SMACK-665) - + Rename \'serviceName\' to \'xmppServiceDomain\' +- [SMACK-666](https://igniterealtime.atlassian.net/browse/SMACK-666) - + Typo in \'RosterEntries.rosterEntires()\', change to + \'RosterEntries.rosterEntries()\' +- [SMACK-703](https://igniterealtime.atlassian.net/browse/SMACK-703) - + Limit the stored presences of entities not in Roster +- [SMACK-704](https://igniterealtime.atlassian.net/browse/SMACK-704) - + Pass down Message stanza in ChatStateListener +- [SMACK-711](https://igniterealtime.atlassian.net/browse/SMACK-711) - + Improve the logging of TCP connection attempts. +- [SMACK-720](https://igniterealtime.atlassian.net/browse/SMACK-720) - + Improve support for Tor and Hidden Services. +- [SMACK-721](https://igniterealtime.atlassian.net/browse/SMACK-721) - + Report illegal Stream Management states to avoid OOM Exception +- [SMACK-727](https://igniterealtime.atlassian.net/browse/SMACK-727) - + Add partial support for the IoT XEPs (XEP-0323, -0324, -0325, -0347) +- [SMACK-733](https://igniterealtime.atlassian.net/browse/SMACK-733) - + Handle outgoing \'unavailable\' Presences in Roster +- [SMACK-736](https://igniterealtime.atlassian.net/browse/SMACK-736) - + Add support for Chat Markers (XEP-0333) +- [SMACK-737](https://igniterealtime.atlassian.net/browse/SMACK-737) - + Add support for Bits of Binary (XEP-0231) +- [SMACK-738](https://igniterealtime.atlassian.net/browse/SMACK-738) - + Add support for Push Notifications (XEP-0357) + +## 4.1.9 -- 2016-11-19 + +### Bug + +- [SMACK-739](https://igniterealtime.atlassian.net/browse/SMACK-739) - + Smack starts SASL step without TLS in case STARTTLS is stripped even + if SecurityMode.Required is used +- [SMACK-735](https://igniterealtime.atlassian.net/browse/SMACK-735) - + Smack sometimes sends invalid SCRAM-SHA1 nonce + +## 4.1.8 -- 2016-07-30 + +### Bug + +- [SMACK-722](https://igniterealtime.atlassian.net/browse/SMACK-722) - + SASL X-OAUTH2 implementation incorrectly performs Base64 encoding + twice +- [SMACK-724](https://igniterealtime.atlassian.net/browse/SMACK-724) - + Do not re-use the Socket after connect() failed. +- [SMACK-725](https://igniterealtime.atlassian.net/browse/SMACK-725) - + ReconnectionManager should handle AlreadyConnectedException and + AlreadyLoggedInException not as failure +- [SMACK-726](https://igniterealtime.atlassian.net/browse/SMACK-726) - + \'purge\' and \'remove\' IQ of XEP-0013 must be of type \'set\' + +## 4.1.7 -- 2016-04-14 + +### Bug + +- [SMACK-712](https://igniterealtime.atlassian.net/browse/SMACK-712) - + XMPPTCPConnection\'s setEnabledSSL(Protocols\|Ciphers) has no effect +- [SMACK-716](https://igniterealtime.atlassian.net/browse/SMACK-716) - + EntityTimeManager.getTime() does not set the recipients JID +- [SMACK-719](https://igniterealtime.atlassian.net/browse/SMACK-719) - + XMPPError should use Locale.US in toUpperCase() + +### Improvement + +- [SMACK-715](https://igniterealtime.atlassian.net/browse/SMACK-715) - + Add Roster.setRosterLoadedAtLoginDefault(boolean) + +## 4.1.6 -- 2016-01-23 + +### Bug + +- [SMACK-705](https://igniterealtime.atlassian.net/browse/SMACK-705) - + PubSub\'s Affiliation.getElementName() returns wrong name +- [SMACK-706](https://igniterealtime.atlassian.net/browse/SMACK-706) - + Smack may sends \ and \ twice if Stream Management + is used and a previous SM state exists +- [SMACK-707](https://igniterealtime.atlassian.net/browse/SMACK-707) - + Infinite loop of NullPointerExceptions in Socks5Proxy +- [SMACK-708](https://igniterealtime.atlassian.net/browse/SMACK-708) - + DeliveryReceipt(Manager) should ensure that receipts (and requests) + have an ID set +- [SMACK-709](https://igniterealtime.atlassian.net/browse/SMACK-709) - + Don\'t request delivery receipts for messages without a body +- [SMACK-710](https://igniterealtime.atlassian.net/browse/SMACK-710) - + SASL DIGEST-MD5 backslash must be quoted + +## 4.1.5 -- 2015-11-22 + +### Bug + +- [SMACK-698](https://igniterealtime.atlassian.net/browse/SMACK-698) - + Time creates invalid XML +- [SMACK-700](https://igniterealtime.atlassian.net/browse/SMACK-700) - + Duplicate stanzas in unacknowledgedStanzas queue when stream is + resumed +- [SMACK-702](https://igniterealtime.atlassian.net/browse/SMACK-702) - + RejectedExecutionException in AbstractXMPPConnection.processPacket() + causes connection Termination + +## 4.1.4 -- 2015-09-14 + +### Bug + +- [SMACK-688](https://igniterealtime.atlassian.net/browse/SMACK-688) - + Reset carbons state if session got not resumed or cleanly + disconnected +- [SMACK-689](https://igniterealtime.atlassian.net/browse/SMACK-689) - + PEPPubSub creates malformed XML +- [SMACK-693](https://igniterealtime.atlassian.net/browse/SMACK-693) - + MultiUserChat\'s UserStatusListener is not getting triggered +- [SMACK-695](https://igniterealtime.atlassian.net/browse/SMACK-695) - + JSON and GCM parser does an erroneous extra next() +- [SMACK-697](https://igniterealtime.atlassian.net/browse/SMACK-697) - + PrivacyListManager should handle the case where not default and + active list are currently set + +### Improvement + +- [SMACK-686](https://igniterealtime.atlassian.net/browse/SMACK-686) - + Provide a hint that connect() needs to be called prior login() in + NotConnectedException +- [SMACK-687](https://igniterealtime.atlassian.net/browse/SMACK-687) - + Update to jxmpp 0.4.2 +- [SMACK-696](https://igniterealtime.atlassian.net/browse/SMACK-696) - + Drop stream state after stream error + +## 4.1.3 -- 2015-07-15 + +### Bug + +- [SMACK-679](https://igniterealtime.atlassian.net/browse/SMACK-679) - + Memory leak in Socks5BytestreamManager. Should use weak map for + \'managers\' +- [SMACK-680](https://igniterealtime.atlassian.net/browse/SMACK-680) - + XHTML bodies are un-escaped after parsing +- [SMACK-681](https://igniterealtime.atlassian.net/browse/SMACK-681) - + Roster presence callbacks may not be invoked right after login + +## 4.1.2 -- 2015-06-27 + +### Bug + +- [SMACK-664](https://igniterealtime.atlassian.net/browse/SMACK-664) - + Invalid IQ error response to OfferRequestPacket and + OfferRevokePacket +- [SMACK-668](https://igniterealtime.atlassian.net/browse/SMACK-668) - + ReconnectionManager\'s value of \'attempts\' is not reset after + successful reconnection +- [SMACK-669](https://igniterealtime.atlassian.net/browse/SMACK-669) - + Only add Entity Capabilities extension to available presences +- [SMACK-670](https://igniterealtime.atlassian.net/browse/SMACK-670) - + SASLMechanism.authenticate should treat an empty byte array like + \'null\' byte array +- [SMACK-672](https://igniterealtime.atlassian.net/browse/SMACK-672) - + Memory leak caused by RosterGroup declaring a strong reference to + XMPPConnection +- [SMACK-673](https://igniterealtime.atlassian.net/browse/SMACK-673) - + VCard API does not support all elements +- [SMACK-676](https://igniterealtime.atlassian.net/browse/SMACK-676) - + ConcurrentModificationException in ServerPingWithAlarmManager +- [SMACK-678](https://igniterealtime.atlassian.net/browse/SMACK-678) - + Login hangs if starttls advertised, but security is set to + \'disabled\' and compression is also advertised + +### Improvement + +- [SMACK-667](https://igniterealtime.atlassian.net/browse/SMACK-667) - + Request Stream Mangement Acknowledgement after re-sending unack\'ed + stanzas after stream resumption +- [SMACK-671](https://igniterealtime.atlassian.net/browse/SMACK-671) - + Don\'t disable Scoks5BytestreamManager on connection termination + +## 4.1.1 -- 2015-05-09 + +### Bug + +- [SMACK-649](https://igniterealtime.atlassian.net/browse/SMACK-649) - + DIGEST-MD5 challenge/response parsing must handle linear white + spaces after the comma +- [SMACK-652](https://igniterealtime.atlassian.net/browse/SMACK-652) - + SynchronizationPoint should use signalAll +- [SMACK-653](https://igniterealtime.atlassian.net/browse/SMACK-653) - + Integer overflow if both client and server don\'t specify a max + resumption time +- [SMACK-654](https://igniterealtime.atlassian.net/browse/SMACK-654) - + isSmResumptionPossible() returns wrong values +- [SMACK-656](https://igniterealtime.atlassian.net/browse/SMACK-656) - + DeliveryReceipts auto add should use packet interceptors and should + not be requested for messages with ACKs. +- [SMACK-659](https://igniterealtime.atlassian.net/browse/SMACK-659) - + Memory leak caused by RosterEntry declaring a strong reference to + XMPPConnection +- [SMACK-660](https://igniterealtime.atlassian.net/browse/SMACK-660) - + ReconnectionManager\'s RANDOM_INCREASING_DELAY is erroneously using + a fixed value. +- [SMACK-661](https://igniterealtime.atlassian.net/browse/SMACK-661) - + Add method to set ProxyInfo in ConnectionConfiguration.Builder +- [SMACK-662](https://igniterealtime.atlassian.net/browse/SMACK-662) - + RosterEntry.setName() does not change the name + +## 4.1.0 -- 2015-03-29 + +## Sub-task + +- [SMACK-398](https://igniterealtime.atlassian.net/browse/SMACK-398) - + Implement SCRAM support + +### Bug + +- [SMACK-65](https://igniterealtime.atlassian.net/browse/SMACK-65) - + Packet parsing should look for depth +- [SMACK-237](https://igniterealtime.atlassian.net/browse/SMACK-237) - + Handle more vCard values (XEP-0054) +- [SMACK-383](https://igniterealtime.atlassian.net/browse/SMACK-383) - + Allow the garbage collection of all object instances of a closed and + unreferenced connection +- [SMACK-424](https://igniterealtime.atlassian.net/browse/SMACK-424) - + Add a MultiUserChat.presenceChanged callback method to be informed + if a presence within a MUC has changed (joined, leaved, status + change) +- [SMACK-542](https://igniterealtime.atlassian.net/browse/SMACK-542) - + MUC: RoomInfo should hold more data if the result contains a + FORM_TYPE field +- [SMACK-549](https://igniterealtime.atlassian.net/browse/SMACK-549) - + MUCUser#getStatus should be a List +- [SMACK-564](https://igniterealtime.atlassian.net/browse/SMACK-564) - + Some tests fail with Java 8 +- [SMACK-570](https://igniterealtime.atlassian.net/browse/SMACK-570) - + Smack does not support resourceparts which contain the \'@\' + character. +- [SMACK-571](https://igniterealtime.atlassian.net/browse/SMACK-571) - + Don\'t remove the MUC listeners after a disconnect() , keep state of + Connection between disconnect() and connect()/login() +- [SMACK-573](https://igniterealtime.atlassian.net/browse/SMACK-573) - + MessageEventManager treats error replies as message events +- [SMACK-583](https://igniterealtime.atlassian.net/browse/SMACK-583) - + PacketListeners may not be invoked in delivery order +- [SMACK-585](https://igniterealtime.atlassian.net/browse/SMACK-585) - + XMPPTCPConnection does not set \'host\' and \'port\' +- [SMACK-590](https://igniterealtime.atlassian.net/browse/SMACK-590) - + Don\'t use IQReplyFilter for the bind set/result exchange +- [SMACK-597](https://igniterealtime.atlassian.net/browse/SMACK-597) - + PingManager.getLastReceivedPong() always returns -1 +- [SMACK-604](https://igniterealtime.atlassian.net/browse/SMACK-604) - + MUCUser must support multiple status codes +- [SMACK-620](https://igniterealtime.atlassian.net/browse/SMACK-620) - + Smack should use a safe SAX parser, e.g. with entity reference + expansion disabled +- [SMACK-635](https://igniterealtime.atlassian.net/browse/SMACK-635) - + Typo DNSUtil.init() prevents DNS SRV lookups to fail in some cases +- [SMACK-643](https://igniterealtime.atlassian.net/browse/SMACK-643) - + Smack should not set the service name to the vale of the \'from\' + attribute of the opening stream element received from the service + +### Improvement + +- [SMACK-340](https://igniterealtime.atlassian.net/browse/SMACK-340) - + Should make the wait/timeouts on SASL authentication configurable. +- [SMACK-402](https://igniterealtime.atlassian.net/browse/SMACK-402) - + Update obsolete \"Message Delivery Receipts\" support from + (JEP\|XEP)-0022 to XEP-0184 +- [SMACK-453](https://igniterealtime.atlassian.net/browse/SMACK-453) - + Add support for all primitive types in + IntrospectionProvider.decode() +- [SMACK-521](https://igniterealtime.atlassian.net/browse/SMACK-521) - + Clear PacketWriters queue when the connection is shut down +- [SMACK-532](https://igniterealtime.atlassian.net/browse/SMACK-532) - + Evaluate if its possible to guarantee the order of listeners by + using a LinkedHashMap +- [SMACK-566](https://igniterealtime.atlassian.net/browse/SMACK-566) - + Create public method that parses Strings/CharSequences to messages, + IQs and presence instances +- [SMACK-587](https://igniterealtime.atlassian.net/browse/SMACK-587) - + Subprojects should uses versions when importing the OSGi smack-core + components +- [SMACK-595](https://igniterealtime.atlassian.net/browse/SMACK-595) - + Add an API to send a stanza and wait asynchronously for a response +- [SMACK-599](https://igniterealtime.atlassian.net/browse/SMACK-599) - + Provide string messages to all exceptions thrown by Smack +- [SMACK-600](https://igniterealtime.atlassian.net/browse/SMACK-600) - + RoomInfo Class should add the information from the Identity element. +- [SMACK-608](https://igniterealtime.atlassian.net/browse/SMACK-608) - + Add support for XMPP error conditions text +- [SMACK-622](https://igniterealtime.atlassian.net/browse/SMACK-622) - + Add support for \'optional\' in session stream features +- [SMACK-626](https://igniterealtime.atlassian.net/browse/SMACK-626) - + Add support for \'ofrom\' Extended Stanza Addressing type +- [SMACK-627](https://igniterealtime.atlassian.net/browse/SMACK-627) - + Smack should allow null usernames under certain circumstances +- [SMACK-645](https://igniterealtime.atlassian.net/browse/SMACK-645) - + Roster should not leak internal state e.g. presences + +### New Feature + +- [SMACK-234](https://igniterealtime.atlassian.net/browse/SMACK-234) - + Add support for SASL EXTERNAL: PKI (Client SSL Cert) Support +- [SMACK-333](https://igniterealtime.atlassian.net/browse/SMACK-333) - + Implement XEP-0198: Stream Management +- [SMACK-378](https://igniterealtime.atlassian.net/browse/SMACK-378) - + Give access to the socket outside the XMPPconnection +- [SMACK-581](https://igniterealtime.atlassian.net/browse/SMACK-581) - + Add support for \"Result Set Management\" (XEP-59) +- [SMACK-607](https://igniterealtime.atlassian.net/browse/SMACK-607) - + Add support for XEP-0352: Client State Indication +- [SMACK-612](https://igniterealtime.atlassian.net/browse/SMACK-612) - + Add support for XEP-0141: Data Forms Layout +- [SMACK-621](https://igniterealtime.atlassian.net/browse/SMACK-621) - + Add support for XEP-0122: Data Forms Validation +- [SMACK-623](https://igniterealtime.atlassian.net/browse/SMACK-623) - + Add API to retrieve the subscriptions of a PubSub node as owner + +### Task + +- [SMACK-365](https://igniterealtime.atlassian.net/browse/SMACK-365) - + SmackConfiguration should only report errors if the file fails to + load, not when it fails to load for a specific classloader. +- [SMACK-371](https://igniterealtime.atlassian.net/browse/SMACK-371) - + Some MUC tasks are using stanza\'s as defined in an older version of + the spec. Fails to work on some servers. +- [SMACK-569](https://igniterealtime.atlassian.net/browse/SMACK-569) - + Move Message Event code to legacy subproject +- [SMACK-578](https://igniterealtime.atlassian.net/browse/SMACK-578) - + Remove decorators for \"Legacy Delayed Delivery\" (XEP-91) in favor + of Delayed Delivery (XEP-203) +- [SMACK-579](https://igniterealtime.atlassian.net/browse/SMACK-579) - + FileTransferManager and FileTransferNegoiator should use + WeakHashMaps and extend Manager +- [SMACK-582](https://igniterealtime.atlassian.net/browse/SMACK-582) - + Change ReceiptReceivedListener.onReceiptReceived parameters +- [SMACK-637](https://igniterealtime.atlassian.net/browse/SMACK-637) - + Move Roster and Chat code to new smack-im subproject + +## 4.0.7 -- 2015-02-20 + +### Bug + +- [SMACK-635](https://igniterealtime.atlassian.net/browse/SMACK-635) - + Typo DNSUtil.init() prevents DNS SRV lookups to fail in some cases +- [SMACK-643](https://igniterealtime.atlassian.net/browse/SMACK-643) - + Smack should not set the service name to the vale of the \'from\' + attribute of the opening stream element received from the service + +## 4.0.6 -- 2014-11-23 + +### Bug + +- [SMACK-616](https://igniterealtime.atlassian.net/browse/SMACK-616) - + Smack should fallback to using host with default port if DNS SRV + lookup fails +- [SMACK-617](https://igniterealtime.atlassian.net/browse/SMACK-617) - + Message Digest in EntityCapsManager should be synchronized + +## 4.0.5 -- 2014-10-22 + +### Bug + +- [SMACK-609](https://igniterealtime.atlassian.net/browse/SMACK-609) - + PingManager.ping(String, long) does not respect timeout +- [SMACK-613](https://igniterealtime.atlassian.net/browse/SMACK-613) - + Parsing exception causes infinite loop if the exception is not + thrown + +## 4.0.4 -- 2014-09-05 + +### Bug + +- [SMACK-596](https://igniterealtime.atlassian.net/browse/SMACK-596) - + Smack should load roster before sending the initial presence +- [SMACK-598](https://igniterealtime.atlassian.net/browse/SMACK-598) - + Smack should allow the empty string as content of message body + element +- [SMACK-601](https://igniterealtime.atlassian.net/browse/SMACK-601) - + PubSub ItemProvider does only process the outermost namespace + definition when creating PayloadItems +- [SMACK-602](https://igniterealtime.atlassian.net/browse/SMACK-602) - + PacketCollector must handle InterruptException +- [SMACK-603](https://igniterealtime.atlassian.net/browse/SMACK-603) - + XMPPError.Condition.equals() should be null-safe + +## 4.0.3 -- 2014-08-16 + +### Bug + +- [SMACK-589](https://igniterealtime.atlassian.net/browse/SMACK-589) - + FormField.Option toXML() produces malformed XML +- [SMACK-592](https://igniterealtime.atlassian.net/browse/SMACK-592) - + OfflineMessagesManager.getMessages() does send request before + collector is set up and could leak collector +- [SMACK-594](https://igniterealtime.atlassian.net/browse/SMACK-594) - + PrivateData Bookmarks.toXML() returns invalid XML + +### Improvement + +- [SMACK-539](https://igniterealtime.atlassian.net/browse/SMACK-539) - + Verify ConnectionConfiguration parameters +- [SMACK-588](https://igniterealtime.atlassian.net/browse/SMACK-588) - + Typo in org.jivesoftware.smackx.pubsub.ConfigureForm: + s/isSubscibe/isSubscribe/ +- [SMACK-593](https://igniterealtime.atlassian.net/browse/SMACK-593) - + Smack should prefer full flush over sync flush when using + compression + +## 4.0.2 -- 2014-07-27 + +### Improvement + +- [SMACK-576](https://igniterealtime.atlassian.net/browse/SMACK-576) - + smack-resolver-javax should become a OSGi ServiceComponent +- [SMACK-586](https://igniterealtime.atlassian.net/browse/SMACK-586) - + Extend API to configure a HostnameVerifier + +## 4.0.1 -- 2014-07-20 + +## Sub-task + +- [SMACK-346](https://igniterealtime.atlassian.net/browse/SMACK-346) - + Bug in return code for rejection handling in FileTransferManager + +### Bug + +- [SMACK-574](https://igniterealtime.atlassian.net/browse/SMACK-574) - + Documentation still refers at some places to Connection +- [SMACK-575](https://igniterealtime.atlassian.net/browse/SMACK-575) - + PingManager schedules pings after pingInterval when it should be use + nextPingIn instead +- [SMACK-577](https://igniterealtime.atlassian.net/browse/SMACK-577) - + Bookmarks and FormField toXml() methods do not properly escape XML +- [SMACK-583](https://igniterealtime.atlassian.net/browse/SMACK-583) - + PacketListeners may not be invoked in delivery order + +### Improvement + +- [SMACK-576](https://igniterealtime.atlassian.net/browse/SMACK-576) - + smack-resolver-javax should become a OSGi ServiceComponent + +### New Feature + +- [SMACK-580](https://igniterealtime.atlassian.net/browse/SMACK-580) - + Add support for retrieving a PubSub node\'s affiliations + +## 4.0.0 -- 2014-06-08 + +## Sub-task + +- [SMACK-399](https://igniterealtime.atlassian.net/browse/SMACK-399) - + Add support for Roster Versioning (was XEP-0237, now in RFC 6121) +- [SMACK-400](https://igniterealtime.atlassian.net/browse/SMACK-400) - + Change xml-not-well-formed to not-well-formed +- [SMACK-401](https://igniterealtime.atlassian.net/browse/SMACK-401) - + Remove \ +- [SMACK-445](https://igniterealtime.atlassian.net/browse/SMACK-445) - + XMPPError class is based on deprecated XEP-0086 + +### Bug + +- [SMACK-357](https://igniterealtime.atlassian.net/browse/SMACK-357) - + Error in SASL authentication when SASL authzid parameter is null +- [SMACK-410](https://igniterealtime.atlassian.net/browse/SMACK-410) - + Any valid SSL server certificate can be used to perform a + man-in-the-middle attack +- [SMACK-411](https://igniterealtime.atlassian.net/browse/SMACK-411) - + ServiceDiscoveryManager identities should be non-static and kept in + a Set to allow multiple identities as per XEP-30 +- [SMACK-414](https://igniterealtime.atlassian.net/browse/SMACK-414) - + Smack does not announce the support for XEP-54 aka vcard-temp +- [SMACK-427](https://igniterealtime.atlassian.net/browse/SMACK-427) - + Typo in code - StreamInitiation.setSesssionID() +- [SMACK-467](https://igniterealtime.atlassian.net/browse/SMACK-467) - + Don\'t use the default locale for machine-readable output, use + Locale.US instead +- [SMACK-531](https://igniterealtime.atlassian.net/browse/SMACK-531) - + Add missing namespace attribute to XHTML-IM body tags +- [SMACK-533](https://igniterealtime.atlassian.net/browse/SMACK-533) - + Smack should prevent IQ response spoofing +- [SMACK-535](https://igniterealtime.atlassian.net/browse/SMACK-535) - + jul.properties should only configure the \'org.igniterealtime\' + namespace +- [SMACK-538](https://igniterealtime.atlassian.net/browse/SMACK-538) - + ParseRoster does not check the sender of the roster and for pending + roster queries +- [SMACK-541](https://igniterealtime.atlassian.net/browse/SMACK-541) - + XHTMLExtensionProvider relies on incorrect behavior of MXParser, + violating the contract of the XMLPullParser interface +- [SMACK-543](https://igniterealtime.atlassian.net/browse/SMACK-543) - + packet.Time is not thread-safe +- [SMACK-546](https://igniterealtime.atlassian.net/browse/SMACK-546) - + PubSub\'s Item needs to escape its XML payload +- [SMACK-548](https://igniterealtime.atlassian.net/browse/SMACK-548) - + PingManager notifies pingFailedListeners multiple times +- [SMACK-551](https://igniterealtime.atlassian.net/browse/SMACK-551) - + ChatManager throws NPE, when Message has no \'from\' attribute +- [SMACK-554](https://igniterealtime.atlassian.net/browse/SMACK-554) - + Memory leak in BookmarkManager +- [SMACK-555](https://igniterealtime.atlassian.net/browse/SMACK-555) - + VCardProvider should consider some elements as optional +- [SMACK-558](https://igniterealtime.atlassian.net/browse/SMACK-558) - + connect() must wait until the stream features have been parsed +- [SMACK-559](https://igniterealtime.atlassian.net/browse/SMACK-559) - + Roster entries without a group are not updated +- [SMACK-560](https://igniterealtime.atlassian.net/browse/SMACK-560) - + Race condition in PacketWriter +- [SMACK-567](https://igniterealtime.atlassian.net/browse/SMACK-567) - + XMPPConnection leaks listenerExecutor ExecutorService + +### Improvement + +- [SMACK-343](https://igniterealtime.atlassian.net/browse/SMACK-343) - + Make Smack jar an OSGi bundle. +- [SMACK-356](https://igniterealtime.atlassian.net/browse/SMACK-356) - + There is no way to reliably end a Chat and have a new one created. +- [SMACK-454](https://igniterealtime.atlassian.net/browse/SMACK-454) - + Follow XEP-0170 recommendation: Compression before Resource Binding +- [SMACK-459](https://igniterealtime.atlassian.net/browse/SMACK-459) - + Add option to configure the default identity in + ServiceDiscoveryManager +- [SMACK-465](https://igniterealtime.atlassian.net/browse/SMACK-465) - + Replace custom wrapped Throwable in XMPPException with + Exception.cause +- [SMACK-468](https://igniterealtime.atlassian.net/browse/SMACK-468) - + Don\'t throw an IOException in IBBStreams when the stream got closed + by the remote +- [SMACK-536](https://igniterealtime.atlassian.net/browse/SMACK-536) - + JUL Loggers should become final +- [SMACK-537](https://igniterealtime.atlassian.net/browse/SMACK-537) - + Move XMPP Ping code to smackx, add keep-alive functionality to + PingManager +- [SMACK-545](https://igniterealtime.atlassian.net/browse/SMACK-545) - + Change API to the style mentioned in Smack\'s Code Guidelines +- [SMACK-547](https://igniterealtime.atlassian.net/browse/SMACK-547) - + Consistent behavior for \"from\" attribute on outgoing stanzas +- [SMACK-556](https://igniterealtime.atlassian.net/browse/SMACK-556) - + Make ConnectionConfigration getters public +- [SMACK-557](https://igniterealtime.atlassian.net/browse/SMACK-557) - + Provide a MultiUserChat method to create \*or\* join a room +- [SMACK-568](https://igniterealtime.atlassian.net/browse/SMACK-568) - + Don\'t exclude groupchat messages without body element in + MultiUserChat MessageListeners + +### New Feature + +- [SMACK-53](https://igniterealtime.atlassian.net/browse/SMACK-53) - + Add support for XEP-0092: Software Version +- [SMACK-71](https://igniterealtime.atlassian.net/browse/SMACK-71) - + Create new FromFilter that checks for exact matching +- [SMACK-187](https://igniterealtime.atlassian.net/browse/SMACK-187) - + Add HTTP Binding support (BOSH / XEP-0124) +- [SMACK-265](https://igniterealtime.atlassian.net/browse/SMACK-265) - + Move to a newer build process with artifacts published to maven + central repo +- [SMACK-426](https://igniterealtime.atlassian.net/browse/SMACK-426) - + Improve XMPPException +- [SMACK-544](https://igniterealtime.atlassian.net/browse/SMACK-544) - + Add support for XEP-0079: Advanced Message Processing +- [SMACK-552](https://igniterealtime.atlassian.net/browse/SMACK-552) - + Add support for \"HTTP over XMPP transport\" aka. XEP-0332 + +### Task + +- [SMACK-371](https://igniterealtime.atlassian.net/browse/SMACK-371) - + Some MUC tasks are using stanza\'s as defined in an older version of + the spec. Fails to work on some servers. +- [SMACK-432](https://igniterealtime.atlassian.net/browse/SMACK-432) - + Code cleanup of deprecated methods +- [SMACK-446](https://igniterealtime.atlassian.net/browse/SMACK-446) - + Remove non-SASL authentication code + +## 3.4.1 -- 2014-02-09 + +### Bug + +- [SMACK-540](https://igniterealtime.atlassian.net/browse/SMACK-540) - + Memory leak in MultiUserChat + +## 3.4.0 -- 2014-02-02 + +### Bug Fixes + +- [SMACK-442](https://igniterealtime.atlassian.net/browse/SMACK-442) - + Manager\'s should also handle connectionClosedOnError() +- [SMACK-443](https://igniterealtime.atlassian.net/browse/SMACK-443) - + ReconnectionSuccessful listeners are invoked twice on reconnection + if connect() failed before +- [SMACK-452](https://igniterealtime.atlassian.net/browse/SMACK-452) - + PacketParserUtils.parseStreamError() is not aware of optional text + element and therefore failes to parse stream error\'s correctly. + Prevents ReconnectionManager from reconnecting. +- [SMACK-458](https://igniterealtime.atlassian.net/browse/SMACK-458) - + Smack\'s Managers should not remove itself when the connection is + closed or should re-add themselfs if the connection get reconnected +- [SMACK-462](https://igniterealtime.atlassian.net/browse/SMACK-462) - + Prevent duplicate manager instances by using the manager\'s + constructor in the ConnectionCreationListener\'s connectionCreated +- [SMACK-463](https://igniterealtime.atlassian.net/browse/SMACK-463) - + packet listeners silently fail when preceding listener caused + exception +- [SMACK-524](https://igniterealtime.atlassian.net/browse/SMACK-524) - + Use correct block-size definition for IBB transfers +- [SMACK-525](https://igniterealtime.atlassian.net/browse/SMACK-525) - + NPE in XMPPConnection.notifyConnectionError +- [SMACK-529](https://igniterealtime.atlassian.net/browse/SMACK-529) - + Add support for XEP-0280 \"Message Carbons\" +- [SMACK-530](https://igniterealtime.atlassian.net/browse/SMACK-530) - + DNSUtilTest requires an internet connection to work, it should be + moved to integration tests. + +### New Feature + +- [SMACK-286](https://igniterealtime.atlassian.net/browse/SMACK-286) - + Need to change ProviderManager to support loading smack.providers + from alternative locations +- [SMACK-387](https://igniterealtime.atlassian.net/browse/SMACK-387) - + Allow configuration of ChatManager to be able to allow message + handling to be customized. +- [SMACK-403](https://igniterealtime.atlassian.net/browse/SMACK-403) - + Add support for XEP-0297 \"Stanza Forwarding\" +- [SMACK-434](https://igniterealtime.atlassian.net/browse/SMACK-434) - + Create a project to contain non production ready implementations of + specifications + +### Improvement + +- [SMACK-343](https://igniterealtime.atlassian.net/browse/SMACK-343) - + Make Smack jar an OSGi bundle. +- [SMACK-381](https://igniterealtime.atlassian.net/browse/SMACK-381) - + Separate the configuration for smack extension related classes from + the smack jar. +- [SMACK-444](https://igniterealtime.atlassian.net/browse/SMACK-444) - + Allow \'null\' for TruststorePath and TruststorePassword in + ServerTrustManager +- [SMACK-456](https://igniterealtime.atlassian.net/browse/SMACK-456) - + Add the causing exception to the XMPPExceptions thrown in + XMPPConnection +- [SMACK-457](https://igniterealtime.atlassian.net/browse/SMACK-457) - + Remove unnecessary printStackTrace() in XMPPConnection +- [SMACK-460](https://igniterealtime.atlassian.net/browse/SMACK-460) - + ServiceDiscoveryManager should not use the constructor in + connectionCreated() +- [SMACK-461](https://igniterealtime.atlassian.net/browse/SMACK-461) - + Remove incorrect deprecated marker for + DiscoverInfo.Identity.setType() +- [SMACK-464](https://igniterealtime.atlassian.net/browse/SMACK-464) - + Make it clear that PacketListener\'s added with + XMPPConnection.addPacketListener() are only for received packets +- [SMACK-534](https://igniterealtime.atlassian.net/browse/SMACK-534) - + Convert all System.out and printStackTrace calls to use Java util + logging. +- [SMACK-339](https://igniterealtime.atlassian.net/browse/SMACK-339) - + Allow ConnectionListeners to be added before XMPPConnection is + connected. Currently throws exception +- [SMACK-373](https://igniterealtime.atlassian.net/browse/SMACK-373) - + Don\'t remove listeners after a disconnect() , keep state of + XMPPConnection between disconnect() and connect()/login() +- [SMACK-434](https://igniterealtime.atlassian.net/browse/SMACK-434) - + Create a project to contain non production ready implementations of + specifications +- [SMACK-526](https://igniterealtime.atlassian.net/browse/SMACK-526) - + Deprecate all PEP related classes. + +## 3.3.1 -- 2013-10-06 + +### Bug Fixes + +- [SMACK-428](https://igniterealtime.atlassian.net/browse/SMACK-428) - + RosterEntry overrides equals, but not hashcode. +- [SMACK-438](https://igniterealtime.atlassian.net/browse/SMACK-438) - + Possible NPE in + MultiUserChat.InvitationsMonitor.getInvitationsMonitor() +- [SMACK-441](https://igniterealtime.atlassian.net/browse/SMACK-441) - + Memory leak in KeepAliveManager +- [SMACK-447](https://igniterealtime.atlassian.net/browse/SMACK-447) - + Compression is not enabled for Java7ZlibInputOutputStream +- [SMACK-448](https://igniterealtime.atlassian.net/browse/SMACK-448) - + Java7ZlibInputOutputStream does not work. Deflater.DEFAULT_STRATEGY + is used as compression level when it should use + Deflater.DEFAULT_COMPRESSION +- [SMACK-450](https://igniterealtime.atlassian.net/browse/SMACK-450) - + VCard.load() throws null pointer exception if there is no VCard for + the user +- [SMACK-455](https://igniterealtime.atlassian.net/browse/SMACK-455) - + Multiple items doesn\`t not parse correctly in a pubsub message + +### New Feature + +- [SMACK-425](https://igniterealtime.atlassian.net/browse/SMACK-425) - + Collect (parser) Exceptions and unparseable stanzas. Provide a + callback method so that the user is notified about them if he wants + to + +### Improvement + +- [SMACK-369](https://igniterealtime.atlassian.net/browse/SMACK-369) - + Exceptions during login should get thrown back up to the caller. +- [SMACK-439](https://igniterealtime.atlassian.net/browse/SMACK-439) - + Improve documentation for MultiUserChat.InvitationsListener +- [SMACK-451](https://igniterealtime.atlassian.net/browse/SMACK-451) - + PingManager entry in META-INF/smack.providers is within Ad-Hoc + Command section +- [SMACK-431](https://igniterealtime.atlassian.net/browse/SMACK-431) - + Enable Entity Caps as default for new connections and write + extensions documentation html page +- [SMACK-405](https://igniterealtime.atlassian.net/browse/SMACK-405) - + Cleanup of redundant code in XMPPConnection.shutdown() + +## 3.3.0 -- 2013-05-04 + +### Bug Fixes + +- [SMACK-225](https://igniterealtime.atlassian.net/browse/SMACK-225) - + Improper handeling of DNS SRV records +- [SMACK-238](https://igniterealtime.atlassian.net/browse/SMACK-238) - + The vCard avatar type always return jpg +- [SMACK-270](https://igniterealtime.atlassian.net/browse/SMACK-270) - + Fix for a memory leak in MUC with MUC.finalize() +- [SMACK-278](https://igniterealtime.atlassian.net/browse/SMACK-278) - + Deadlock during Smack disconnect +- [SMACK-342](https://igniterealtime.atlassian.net/browse/SMACK-342) - + VCards causes ConcurrentModificationException +- [SMACK-344](https://igniterealtime.atlassian.net/browse/SMACK-344) - + Bug in SASL authentication mechanism when SRV records are being + used. +- [SMACK-351](https://igniterealtime.atlassian.net/browse/SMACK-351) - + Rework File Transfer +- [SMACK-352](https://igniterealtime.atlassian.net/browse/SMACK-352) - + Update the licensing headers in various files. +- [SMACK-355](https://igniterealtime.atlassian.net/browse/SMACK-355) - + IO Error if smack cant use port for local proxy +- [SMACK-371](https://igniterealtime.atlassian.net/browse/SMACK-371) - + Some MUC tasks are using stanza\'s as defined in an older version of + the spec. Fails to work on some servers. +- [SMACK-375](https://igniterealtime.atlassian.net/browse/SMACK-375) - + Node strings in the discovery info packets are not escaped as in the + other packets +- [SMACK-382](https://igniterealtime.atlassian.net/browse/SMACK-382) - + Prevent memory leak in AdHocCommandManager +- [SMACK-384](https://igniterealtime.atlassian.net/browse/SMACK-384) - + Endless waiting for connection to be established +- [SMACK-390](https://igniterealtime.atlassian.net/browse/SMACK-390) - + Smack login will fail if a bad delay packet is received +- [SMACK-392](https://igniterealtime.atlassian.net/browse/SMACK-392) - + In ant build, compile-test target doesn\'t work. +- [SMACK-394](https://igniterealtime.atlassian.net/browse/SMACK-394) - + Erroneous cast in IBBInputStream\'s read() method +- [SMACK-395](https://igniterealtime.atlassian.net/browse/SMACK-395) - + Socks5BytestreamManager\'s establishConnection() should still try to + use the local streamhost proxy if the server doesn\'t provide one +- [SMACK-404](https://igniterealtime.atlassian.net/browse/SMACK-404) - + Smack uses the wrong method to decode Base64 Strings +- [SMACK-413](https://igniterealtime.atlassian.net/browse/SMACK-413) - + VCardProvider incorrectly parses binary value of avatars +- [SMACK-415](https://igniterealtime.atlassian.net/browse/SMACK-415) - + ItemProvider relies on incorrect behavior of MXParser, violating the + contract of the XMLPullParser interface +- [SMACK-417](https://igniterealtime.atlassian.net/browse/SMACK-417) - + If both PacketReader and PacketWriter fail at the same time, + connectionClosedonError() is called two times + +### New Features + +- [SMACK-331](https://igniterealtime.atlassian.net/browse/SMACK-331) - + Add support for XEP-0184: Message Delivery Receipts +- [SMACK-345](https://igniterealtime.atlassian.net/browse/SMACK-345) - + Inproved detection of last activity +- [SMACK-361](https://igniterealtime.atlassian.net/browse/SMACK-361) - + Add support for XEP-0115 Entity Capabilities +- [SMACK-376](https://igniterealtime.atlassian.net/browse/SMACK-376) - + Setting a custom trust manager to control certificates from outside +- [SMACK-388](https://igniterealtime.atlassian.net/browse/SMACK-388) - + XEP-199 XMPP Ping support + +### Improvements + +- [SMACK-341](https://igniterealtime.atlassian.net/browse/SMACK-341) - + Update the PacketCollector and ConnectionDetachedPacketCollector to + use the java concurrent classes. +- [SMACK-358](https://igniterealtime.atlassian.net/browse/SMACK-358) - + Support additional properties for account creation in test cases. +- [SMACK-363](https://igniterealtime.atlassian.net/browse/SMACK-363) - + Code Cleanup +- [SMACK-377](https://igniterealtime.atlassian.net/browse/SMACK-377) - + avoid unnecessary DNS requests in XMPPconnection +- [SMACK-379](https://igniterealtime.atlassian.net/browse/SMACK-379) - + Sessions were removed from the specification but Smack still uses + them. Should be updated to reflect the spec changes. +- [SMACK-385](https://igniterealtime.atlassian.net/browse/SMACK-385) - + Reusing KeyStore in order to reduce memory usage +- [SMACK-389](https://igniterealtime.atlassian.net/browse/SMACK-389) - + Add java.util.zip.Deflater(In\|Out)putStream as Java7 API native + alternative to JZlib +- [SMACK-391](https://igniterealtime.atlassian.net/browse/SMACK-391) - + Improve date parsing in StringUtils and make + DelayInformationProvider use StringUtils for date parsing. +- [SMACK-412](https://igniterealtime.atlassian.net/browse/SMACK-412) - + Replace the whitespace ping with a XEP-0199 ping +- [SMACK-419](https://igniterealtime.atlassian.net/browse/SMACK-419) - + PacketWriter: Only flush the BufferedWriter if the packet queue is + empty +- [SMACK-423](https://igniterealtime.atlassian.net/browse/SMACK-423) - + Investigate whether unhandled packets should still parse the child + xml into a string as content +- [SMACK-430](https://igniterealtime.atlassian.net/browse/SMACK-430) - + Throw an exception if + FileTransferManager.createOutgoingFileTransfer() was used with a + bare JID + +## 3.2.2 -- 2011-12-23 + +### Bug Fixes + +- [SMACK-263](https://igniterealtime.atlassian.net/browse/SMACK-263) - + Set file info in all send\* methods +- [SMACK-322](https://igniterealtime.atlassian.net/browse/SMACK-322) - + NPE in XMPPConnection +- [SMACK-324](https://igniterealtime.atlassian.net/browse/SMACK-324) - + Investigate SASL issue with jabberd2 servers +- [SMACK-338](https://igniterealtime.atlassian.net/browse/SMACK-338) - + IBB filetransfer doesn\'t work as expected +- [SMACK-346](https://igniterealtime.atlassian.net/browse/SMACK-346) - + Bug in return code for rejection handling in FileTransferManager +- [SMACK-348](https://igniterealtime.atlassian.net/browse/SMACK-348) - + Documentation error - broken link +- [SMACK-349](https://igniterealtime.atlassian.net/browse/SMACK-349) - + Smack\'s IBB sends too much data in a packet +- [SMACK-350](https://igniterealtime.atlassian.net/browse/SMACK-350) - + Bytestream is not working in Spark 2.6.3 from XP to W7 +- [SMACK-353](https://igniterealtime.atlassian.net/browse/SMACK-353) - + Thread leak in the FaultTolerantNegotiator +- [SMACK-362](https://igniterealtime.atlassian.net/browse/SMACK-362) - + smack throw NoSuchElementException if the muc#roominfo_subject has + no values + +### Improvements + +- [SMACK-343](https://igniterealtime.atlassian.net/browse/SMACK-343) - + Make Smack jar an OSGi bundle. +- [SMACK-354](https://igniterealtime.atlassian.net/browse/SMACK-354) - + Provide milliseconds in timestamp colum debugwindow + +## 3.2.1 -- 2011-07-04 + +### Bug Fixes + +- [SMACK-129](https://igniterealtime.atlassian.net/browse/SMACK-129) - + MultiUserChat will Store Messages in its PacketCollector + irregardless of whether or not they are being read +- [SMACK-230](https://igniterealtime.atlassian.net/browse/SMACK-230) - + Disconnect Can Cause Null Pointer Exception +- [SMACK-273](https://igniterealtime.atlassian.net/browse/SMACK-273) - + Bug in RoomListenerMultiplexor.java +- [SMACK-329](https://igniterealtime.atlassian.net/browse/SMACK-329) - + XHTMLText uses improper format for br tag +- [SMACK-338](https://igniterealtime.atlassian.net/browse/SMACK-338) - + IBB filetransfer doesn\'t work as expected +- [SMACK-324](https://igniterealtime.atlassian.net/browse/SMACK-324) - + Investigate SASL issue with jabberd2 servers + +## 3.2.0 -- 2011-05-03 + +### New Features + +- [SMACK-272](https://igniterealtime.atlassian.net/browse/SMACK-272) - + Add support for pubsub (XEP-0060) +- [SMACK-296](https://igniterealtime.atlassian.net/browse/SMACK-296) - + Add support for XEP-0224: Attention +- [SMACK-319](https://igniterealtime.atlassian.net/browse/SMACK-319) - + Add common interfaces for SOCKS5 Bytestreams and In-Band Bytestreams + +### Improvements + +- [SMACK-137](https://igniterealtime.atlassian.net/browse/SMACK-137) - + File Transfer Settings +- [SMACK-156](https://igniterealtime.atlassian.net/browse/SMACK-156) - + Add the ability to register for roster events before logging in +- [SMACK-261](https://igniterealtime.atlassian.net/browse/SMACK-261) - + Minor Jingle cleanup to better support Jingle in Spark +- [SMACK-277](https://igniterealtime.atlassian.net/browse/SMACK-277) - + Update XMLUnit to the latest version +- [SMACK-282](https://igniterealtime.atlassian.net/browse/SMACK-282) - + Support SASL-related error conditions. +- [SMACK-283](https://igniterealtime.atlassian.net/browse/SMACK-283) - + Investigate why Jingle is connecting to stun.xten.net +- [SMACK-285](https://igniterealtime.atlassian.net/browse/SMACK-285) - + Add support for Nicks +- [SMACK-289](https://igniterealtime.atlassian.net/browse/SMACK-289) - + There is no way of retrieving items from a pubsub node when the user + has multiple subscriptions. +- [SMACK-294](https://igniterealtime.atlassian.net/browse/SMACK-294) - + Handle empty roster groups and no goups in the same way +- [SMACK-295](https://igniterealtime.atlassian.net/browse/SMACK-295) - + Fire reconnectionSuccessful event when session is established +- [SMACK-297](https://igniterealtime.atlassian.net/browse/SMACK-297) - + add configuration for local Socks5 proxy +- [SMACK-298](https://igniterealtime.atlassian.net/browse/SMACK-298) - + Respond to all incoming Socks5 bytestream requests +- [SMACK-299](https://igniterealtime.atlassian.net/browse/SMACK-299) - + Improve accepting of Socks5 bytestream requests +- [SMACK-300](https://igniterealtime.atlassian.net/browse/SMACK-300) - + improve local Socks5 proxy implemetation +- [SMACK-301](https://igniterealtime.atlassian.net/browse/SMACK-301) - + support for bytestream packets to query Socks5 proxy for network + address +- [SMACK-302](https://igniterealtime.atlassian.net/browse/SMACK-302) - + Improve establishing of Socks5 bytestreams +- [SMACK-303](https://igniterealtime.atlassian.net/browse/SMACK-303) - + integrate of the extracted Socks5 bytestream API in file transfer + API +- [SMACK-304](https://igniterealtime.atlassian.net/browse/SMACK-304) - + Extend the IQ API to create empty IQ results and IQ error response + packets +- [SMACK-307](https://igniterealtime.atlassian.net/browse/SMACK-307) - + Improve Message Parser Robustness and Message Body I18N +- [SMACK-309](https://igniterealtime.atlassian.net/browse/SMACK-309) - + Fully implement XEP-0047 In-Band Bytestreams +- [SMACK-310](https://igniterealtime.atlassian.net/browse/SMACK-310) - + Add Support for Localized Message Subjects + +### Bug Fixes + +- [SMACK-163](https://igniterealtime.atlassian.net/browse/SMACK-163) - + Fix NPE in RoomInfo when subject has not value +- [SMACK-207](https://igniterealtime.atlassian.net/browse/SMACK-207) - + Parsing of messages may disconnect Smack/Spark +- [SMACK-225](https://igniterealtime.atlassian.net/browse/SMACK-225) - + Improper handeling of DNS SRV records +- [SMACK-232](https://igniterealtime.atlassian.net/browse/SMACK-232) - + Better handling of Roster error +- [SMACK-243](https://igniterealtime.atlassian.net/browse/SMACK-243) - + Packet with wrong date format makes Smack to disconnect +- [SMACK-264](https://igniterealtime.atlassian.net/browse/SMACK-264) - + fix for NPE in SASLMechanism.java +- [SMACK-269](https://igniterealtime.atlassian.net/browse/SMACK-269) - + Smack 3.1.0 creates a new chat for every incoming message +- [SMACK-271](https://igniterealtime.atlassian.net/browse/SMACK-271) - + Deadlock in XMPPConnection while login and parsing stream features +- [SMACK-275](https://igniterealtime.atlassian.net/browse/SMACK-275) - + Patch: Fix for broken SASL DIGEST-MD5 implementation +- [SMACK-280](https://igniterealtime.atlassian.net/browse/SMACK-280) - + The authentification should use the XMPPConnection#sendPacket method + and work transparent with packets and packet listeners. +- [SMACK-288](https://igniterealtime.atlassian.net/browse/SMACK-288) - + The parsing of the result for a LeafNode.getItems() call is + incorrect. It creates a DefaultPacketExtension instead of an Item + for every other item in the result. +- [SMACK-290](https://igniterealtime.atlassian.net/browse/SMACK-290) - + Deadlock while getting Roster before it\'s initialized +- [SMACK-291](https://igniterealtime.atlassian.net/browse/SMACK-291) - + RosterGroup modifications should depend on roster push +- [SMACK-293](https://igniterealtime.atlassian.net/browse/SMACK-293) - + Support optional roster subscription attribute +- [SMACK-305](https://igniterealtime.atlassian.net/browse/SMACK-305) - + RosterEntry#getGroups causing a roster reload +- [SMACK-308](https://igniterealtime.atlassian.net/browse/SMACK-308) - + Multiple errors in pubsub GetItemsRequest +- [SMACK-312](https://igniterealtime.atlassian.net/browse/SMACK-312) - + Only fire RosterListener#entriesUpdated for RosterEntries that + changed +- [SMACK-327](https://igniterealtime.atlassian.net/browse/SMACK-327) - + getFeatures() method on DiscoverInfo is improperly set to be package + protected instead of public +- [SMACK-328](https://igniterealtime.atlassian.net/browse/SMACK-328) - + Number format exception while parsing dates. +- [SMACK-332](https://igniterealtime.atlassian.net/browse/SMACK-332) - + Smack 3.2.0b2 shows wrong version in Smack Dubugger Window +- [SMACK-334](https://igniterealtime.atlassian.net/browse/SMACK-334) - + Error in form for FileTransferNegotiator + +## 3.1.0 -- 2008-11-20 + +### New Features + +- [SMACK-142](https://igniterealtime.atlassian.net/browse/SMACK-142) - + Added support for Kerberos/NTLM. **(6 votes)** +- [SMACK-210](https://igniterealtime.atlassian.net/browse/SMACK-210) - + Added support for MD5 SASL. **(1 vote)** +- [SMACK-256](https://igniterealtime.atlassian.net/browse/SMACK-256) - + Added support for new sophisticated TLS mechanisms including + SmartCard and Apple\'s KeychainStore. +- [SMACK-242](https://igniterealtime.atlassian.net/browse/SMACK-242) - + Added support for JEP-50: Ad-hoc commands. +- [SMACK-251](https://igniterealtime.atlassian.net/browse/SMACK-251) - + Added support for XEP-0163: Personal Eventing Protocol. **(1 vote)** +- [SMACK-226](https://igniterealtime.atlassian.net/browse/SMACK-226) - + XMLConnection can now be used with an http/socks proxy. **(2 + votes)** +- [SMACK-254](https://igniterealtime.atlassian.net/browse/SMACK-254) - + Loading the Roster during login is now optional. +- [SMACK-255](https://igniterealtime.atlassian.net/browse/SMACK-255) - + Added ability to set mime type for avatar. +- [SMACK-235](https://igniterealtime.atlassian.net/browse/SMACK-235) - + Improved performance of Roster class. +- [SMACK-241](https://igniterealtime.atlassian.net/browse/SMACK-241) - + Updated Base64 implementation to match Openfire\'s. +- [SMACK-240](https://igniterealtime.atlassian.net/browse/SMACK-240) - + Updated Jingle implementation to newest version. +- [SMACK-246](https://igniterealtime.atlassian.net/browse/SMACK-246) - + Improve Jingle logging using commons-logging +- [SMACK-244](https://igniterealtime.atlassian.net/browse/SMACK-244) - + Updated JSTUN to 0.7.2. +- [SMACK-259](https://igniterealtime.atlassian.net/browse/SMACK-259) - + Updated XPP library to latest version. + +### Bug Fixes + +- [SMACK-231](https://igniterealtime.atlassian.net/browse/SMACK-231) - + IBB Outputstream was not being flushed before it was closed. +- [SMACK-236](https://igniterealtime.atlassian.net/browse/SMACK-236) - + Renamed stanza error \"unexpected-condition\" to + \"unexpected-request\". +- [SMACK-258](https://igniterealtime.atlassian.net/browse/SMACK-258) - + Fixed disconnection issue when parsing SASL success that contained a + payload. +- [SMACK-175](https://igniterealtime.atlassian.net/browse/SMACK-175) - + Fixed typo in RosterPacket.ItemStatus constant. +- [SMACK-260](https://igniterealtime.atlassian.net/browse/SMACK-260) - + Added handling of error presence packets + +## 3.0.3 -- 2007-05-31 + +### New Features + +- [SMACK-99](https://igniterealtime.atlassian.net/browse/SMACK-99) - + Added support for multiple message bodies and message body + languages. +- [SMACK-218](https://igniterealtime.atlassian.net/browse/SMACK-218) - + Implemented GSSAPI for single-sign on. + +### Bug Fixes + +- [SMACK-219](https://igniterealtime.atlassian.net/browse/SMACK-219) - + The getPresence method was not working correctly with offline + presence. +- [SMACK-224](https://igniterealtime.atlassian.net/browse/SMACK-224) - + SASL authenticion was using the XMPP domain instead of the FQDN. + +## 3.0.2 -- 2007-05-03 + +### Bug Fixes + +- [SMACK-212](https://igniterealtime.atlassian.net/browse/SMACK-212) - + Jingle can\'t establish session if only one side has a relay service +- [SMACK-213](https://igniterealtime.atlassian.net/browse/SMACK-213) - + RTP Bridge Resolver get wrong localhost address in certain + situations +- [SMACK-214](https://igniterealtime.atlassian.net/browse/SMACK-214) - + Presences with a negative priority of -1 are not sending the + priority to the server + +## 3.0.1 -- 2007-04-12 + +### Bug Fixes + +- [SMACK-211](https://igniterealtime.atlassian.net/browse/SMACK-211) - + Jingle ICE with relay sometimes closed sessions. +- Upgraded bundled version of JSTUN. + +## 3.0.0 -- 2007-03-31 + +### Important Changes + +- Java 5 is now required. +- Several API changes are not backwards compatible. In particular, + connection handling has been significantly updated, the GroupChat + class has been dropped in favor of the standardized MultiUserChat, + and the Chat class has an updated API. + +### New Features + +- [SMACK-74](https://igniterealtime.atlassian.net/browse/SMACK-74) - + Added support for unavailable presences with status text. **(4 + votes)** +- [SMACK-191](https://igniterealtime.atlassian.net/browse/SMACK-191) - + RosterListener API improvements. +- [SMACK-194](https://igniterealtime.atlassian.net/browse/SMACK-194) - + Roster.getPresence(String) now considers mode after priority to + determine the presence value to return. +- [SMACK-195](https://igniterealtime.atlassian.net/browse/SMACK-195) - + Added the ability to disconnect with a custom presence value (for + offline status). +- [SMACK-200](https://igniterealtime.atlassian.net/browse/SMACK-200) - + Added convenience methods to Presence class. +- [SMACK-31](https://igniterealtime.atlassian.net/browse/SMACK-31) - + Added support for privacy lists. **(4 votes)** +- [SMACK-94](https://igniterealtime.atlassian.net/browse/SMACK-94) - + Added support for last activity of online users. **(1 vote)** +- [SMACK-121](https://igniterealtime.atlassian.net/browse/SMACK-121) - + Added support for stream errors. +- [SMACK-136](https://igniterealtime.atlassian.net/browse/SMACK-136) - + Added support for XEP-0048: bookmark storage. +- [SMACK-144](https://igniterealtime.atlassian.net/browse/SMACK-144) - + Added bookmark manager for central bookmark management. +- [SMACK-150](https://igniterealtime.atlassian.net/browse/SMACK-150) - + Added support for handling node features in disco. +- [SMACK-167](https://igniterealtime.atlassian.net/browse/SMACK-167) - + Added support for XEP-0106: JID Escaping +- [SMACK-171](https://igniterealtime.atlassian.net/browse/SMACK-171) - + The presence of available contacts is now changed to offline when + the connection is closed. +- [SMACK-172](https://igniterealtime.atlassian.net/browse/SMACK-172) - + Added support for re-connection when the connection is abruptly + closed. +- [SMACK-182](https://igniterealtime.atlassian.net/browse/SMACK-182) - + ProviderManager is now pluggable (for Eclipse ECF). +- [SMACK-185](https://igniterealtime.atlassian.net/browse/SMACK-185) - + Added the workgroup API to Smack. +- [SMACK-206](https://igniterealtime.atlassian.net/browse/SMACK-206) - + Added the option to specify the username to use for the automated + test cases. +- [SMACK-208](https://igniterealtime.atlassian.net/browse/SMACK-208) - + Added a max queue size for outgoing packets to prevent memory issues + during extreme load. +- [SMACK-209](https://igniterealtime.atlassian.net/browse/SMACK-209) - + Initial Jingle support implemented. + +### Bug Fixes + +- [SMACK-6](https://igniterealtime.atlassian.net/browse/SMACK-6) - + Don\'t force use of collectors in Chat class. +- [SMACK-10](https://igniterealtime.atlassian.net/browse/SMACK-10) - + Flush pending packets before closing the connection. **(4 votes)** +- [SMACK-51](https://igniterealtime.atlassian.net/browse/SMACK-51) - + Use unique Thread names among connections. +- [SMACK-54](https://igniterealtime.atlassian.net/browse/SMACK-54) - + Add #equals and #hashCode to Occupant. +- [SMACK-86](https://igniterealtime.atlassian.net/browse/SMACK-86) - + Made presence checks case in-sensitive. +- [SMACK-93](https://igniterealtime.atlassian.net/browse/SMACK-93) - + XHTML provider wasn\'t handling some tags correctly. +- [SMACK-138](https://igniterealtime.atlassian.net/browse/SMACK-138) - + Added caching to file transfer negotiation operations. +- [SMACK-143](https://igniterealtime.atlassian.net/browse/SMACK-143) - + Updated XMPPError to be compliant with RFC3920. +- [SMACK-145](https://igniterealtime.atlassian.net/browse/SMACK-145) - + XHTML parsing could fail in some cases. +- [SMACK-146](https://igniterealtime.atlassian.net/browse/SMACK-146) - + DNS lookups were failing with some DNS servers. +- [SMACK-147](https://igniterealtime.atlassian.net/browse/SMACK-147) - + Removed invisibility presence mode. +- [SMACK-148](https://igniterealtime.atlassian.net/browse/SMACK-148) - + Socks 5 listening thread was not cleaning up correctly. **(2 + votes)** +- [SMACK-149](https://igniterealtime.atlassian.net/browse/SMACK-149) - + Fixed possible memory leaking in PacketReader. +- [SMACK-151](https://igniterealtime.atlassian.net/browse/SMACK-151) - + Now use getBytes(\"UTF-8\") instead of getBytes(). +- [SMACK-152](https://igniterealtime.atlassian.net/browse/SMACK-152) - + The FN field is duplicated when loading vCards from the server. +- [SMACK-153](https://igniterealtime.atlassian.net/browse/SMACK-153) - + Optimized performance by replacing StringBuffer with StringBuilder. +- [SMACK-154](https://igniterealtime.atlassian.net/browse/SMACK-154) - + Fixed roster test cases that were sometimes failing. +- [SMACK-155](https://igniterealtime.atlassian.net/browse/SMACK-155) - + Optimized MUC performance by reducing number of packet collectors + and listeners. +- [SMACK-158](https://igniterealtime.atlassian.net/browse/SMACK-158) - + FileTransfer isDone() method was returning true even when the + transfer was refused. +- [SMACK-159](https://igniterealtime.atlassian.net/browse/SMACK-159) - + Filenames were not escaped for file transfers. +- [SMACK-160](https://igniterealtime.atlassian.net/browse/SMACK-160) - + Now use stream:feature to discover registration support. +- [SMACK-161](https://igniterealtime.atlassian.net/browse/SMACK-161) - + Improved connection speed. +- [SMACK-162](https://igniterealtime.atlassian.net/browse/SMACK-162) - + Fixed NPE in SmackConfiguration. +- [SMACK-163](https://igniterealtime.atlassian.net/browse/SMACK-163) - + Fixed NPE in RoomInfo when subject was null. +- [SMACK-164](https://igniterealtime.atlassian.net/browse/SMACK-164) - + Contact name was not being escaped. +- [SMACK-165](https://igniterealtime.atlassian.net/browse/SMACK-165) - + Listeners were not being removed from PacketReader. +- [SMACK-166](https://igniterealtime.atlassian.net/browse/SMACK-166) - + Packet reader thread was freezing when parsing an error text with no + description. +- [SMACK-168](https://igniterealtime.atlassian.net/browse/SMACK-168) - + Fixed possible delay in PacketReader when negotiating TLS. +- [SMACK-173](https://igniterealtime.atlassian.net/browse/SMACK-173) - + Renamed ConnectionEstablishedListener to ConnectionCreationListener. +- [SMACK-176](https://igniterealtime.atlassian.net/browse/SMACK-176) - + Fixed incorrect property initialization. +- [SMACK-177](https://igniterealtime.atlassian.net/browse/SMACK-177) - + Removed synchronization from Roster. +- [SMACK-178](https://igniterealtime.atlassian.net/browse/SMACK-178) - + Added NodeInformation#getNodeIdentities() to return identities of + hosted nodes +- [SMACK-181](https://igniterealtime.atlassian.net/browse/SMACK-181) - + Improved parsing of certificates to get signed domains. +- [SMACK-183](https://igniterealtime.atlassian.net/browse/SMACK-183) - + Documentation fixes. +- [SMACK-184](https://igniterealtime.atlassian.net/browse/SMACK-184) - + Simplified XMPPConnection constructors. +- [SMACK-203](https://igniterealtime.atlassian.net/browse/SMACK-203) - + NULL thread IDs would cause an error inside of the Chat Manager. +- [SMACK-205](https://igniterealtime.atlassian.net/browse/SMACK-205) - + Fixed PacketReader concurrency problems. +- [SMACK-188](https://igniterealtime.atlassian.net/browse/SMACK-188) - + Resources are now closed after reading the keystore. +- [SMACK-189](https://igniterealtime.atlassian.net/browse/SMACK-189) - + The listener was remaining blocked forever in some cases. +- [SMACK-190](https://igniterealtime.atlassian.net/browse/SMACK-190) - + Exceptions while notifying packet reader listeners was stopping the + notification thread. +- [SMACK-192](https://igniterealtime.atlassian.net/browse/SMACK-192) - + Roster.getPresence(String) now forces use of the bare JID. +- [SMACK-193](https://igniterealtime.atlassian.net/browse/SMACK-193) - + New presence packets now default to a null presence mode. +- [SMACK-196](https://igniterealtime.atlassian.net/browse/SMACK-196) - + Now set closed to true at the start of the connection shutdown + method and not the end. +- [SMACK-197](https://igniterealtime.atlassian.net/browse/SMACK-197) - + The source build was failing. +- [SMACK-198](https://igniterealtime.atlassian.net/browse/SMACK-198) - + File transfer streams were not being closed properly in some cases. +- [SMACK-199](https://igniterealtime.atlassian.net/browse/SMACK-199) - + MultiUserChat invitation listeners are no longer removed on + disconnects. +- [SMACK-201](https://igniterealtime.atlassian.net/browse/SMACK-201) - + Roster no longer exposes that it implements ConnectionListener. + +## 2.2.1 -- 2006-06-12 + +- [SMACK-141](https://igniterealtime.atlassian.net/browse/SMACK-141) - + Fixed SSL exception while creating new XMPPConnections. **(1 vote)** +- [SMACK-127](https://igniterealtime.atlassian.net/browse/SMACK-127) - + Fixed incorrect file transfer progress. +- [SMACK-130](https://igniterealtime.atlassian.net/browse/SMACK-130) - + Fixed VCard escaping problem that was crashing connections. +- [SMACK-134](https://igniterealtime.atlassian.net/browse/SMACK-134) - + VCards were not being saved when avatar was the only element. +- [SMACK-131](https://igniterealtime.atlassian.net/browse/SMACK-131) - + Illegal XML characters are now properly escaped in the presence + status. +- [SMACK-133](https://igniterealtime.atlassian.net/browse/SMACK-133) - + Illegal XML characters are now properly escaped in groups names. +- [SMACK-132](https://igniterealtime.atlassian.net/browse/SMACK-132) - + Fixed IBB problem triggered when buffersize was increased. +- [SMACK-135](https://igniterealtime.atlassian.net/browse/SMACK-135) - + Moved to new Base64 implementation to fix line break issue in old + implementation. + +## 2.2.0 -- 2006-03-09 + +- [SMACK-122](https://igniterealtime.atlassian.net/browse/SMACK-122) - + Added support for JEP-96: File Transfer. **(1 vote)** +- [SMACK-72](https://igniterealtime.atlassian.net/browse/SMACK-72) - + Added support for JEP-47: In-Band Bytestreams. **(2 votes)** +- [SMACK-122](https://igniterealtime.atlassian.net/browse/SMACK-122) - + Added support for JEP-65: SOCKS5 Bytestreams. **(1 vote)** +- [SMACK-112](https://igniterealtime.atlassian.net/browse/SMACK-112) - + Added support for JEP-38 Stream Compression. +- [SMACK-117](https://igniterealtime.atlassian.net/browse/SMACK-117) - + Added support for JEP-33: Extended Stanza Addressing. +- [SMACK-27](https://igniterealtime.atlassian.net/browse/SMACK-27) - + Certification validation is now pluggable. +- [SMACK-118](https://igniterealtime.atlassian.net/browse/SMACK-118) - + Added methods to dynamically remove providers. +- [SMACK-125](https://igniterealtime.atlassian.net/browse/SMACK-125) - + Added support for deaf occupant in MUC rooms. +- [SMACK-109](https://igniterealtime.atlassian.net/browse/SMACK-109) - + Optimized client performance. **(1 vote)** +- [SMACK-113](https://igniterealtime.atlassian.net/browse/SMACK-113) - + Added support for choosing if TLS should be used or not. +- [SMACK-114](https://igniterealtime.atlassian.net/browse/SMACK-114) - + Added support for choosing if SASL should be used or not. +- [SMACK-123](https://igniterealtime.atlassian.net/browse/SMACK-123) - + A thread is no longer used for packet writer listeners. +- [SMACK-110](https://igniterealtime.atlassian.net/browse/SMACK-110) - + Resource binding and session establishment are now requested only if + the server offered them. +- [SMACK-111](https://igniterealtime.atlassian.net/browse/SMACK-111) - + Fixed concurrency issue with date formatter. +- [SMACK-116](https://igniterealtime.atlassian.net/browse/SMACK-116) - + Fixed vCard issues. +- [SMACK-119](https://igniterealtime.atlassian.net/browse/SMACK-119) - + Fixed AccessControlException when using vCard from an applet. +- [SMACK-120](https://igniterealtime.atlassian.net/browse/SMACK-120) - + Listener thread was not being shutdown properly. +- [SMACK-124](https://igniterealtime.atlassian.net/browse/SMACK-124) - + Parsing resource binding packets was requiring smackx.jar file to be + in the classpath. +- [SMACK-97](https://igniterealtime.atlassian.net/browse/SMACK-97) - + Fixed functional test failures in PresencePriorityTest and + RosterTest. diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html deleted file mode 100644 index 92b5b2ae9..000000000 --- a/resources/releasedocs/changelog.html +++ /dev/null @@ -1,1675 +0,0 @@ - - - - - Smack Changelog - - - - -
- - - - -
- -

4.4.4 -- 2021-11-01

- -

Bug -

-
    -
  • [SMACK-916] - XMPPErrorException.stanza is missing a getter method -
  • -
  • [SMACK-915] - Smack does not process MUC destroy message if they contain 'status' -
  • -
  • [SMACK-914] - MultiUserChat may be become unjoinable due to a race condition -
  • -
  • [SMACK-913] - MultiUserChat.serviceSupportsStableIds\(\) may throws a NullPointerException -
  • -
  • [SMACK-912] - Smack does not start the local SOCKS5 proxy automatically -
  • -
  • [SMACK-910] - FormNode and FormNodeProvide should handle non-existent DataForm -
  • -
  • [SMACK-909] - Must use the raw character data of a form field in entity caps hash calculation -
  • -
- -

4.4.3 -- 2021-07-06

- -

Bug -

-
    -
  • [SMACK-905] - The class org.jivesoftware.smackx.offline.packet.OfflineMessageInfo has no ELEMENT, NAMESPACE or QNAME member -
  • -
  • [SMACK-907] - Possible NPE in MultipleRecipientManager -
  • -
- -

4.4.2 -- 2021-03-25

- -

Bug -

-
    -
  • [SMACK-903] - StaxXmlPullParser.getNamespace() may throws IllegalArgumentException -
  • -
  • [SMACK-904] - XEP-0096 file transfer fails because of a hidden ClastCastException -
  • -
- -

4.4.1 -- 2021-03-03

- -

Bug -

-
    -
  • [SMACK-895] - BoBIQ#getIQChildElementBuilder throws NPE when the BoB data does not contain ‘max-age’. -
  • -
  • [SMACK-896] - BoBDataExtension is missing getter for BoBData and ContentId -
  • -
  • [SMACK-897] - DirectoryRosterStore.readEntry() should also catch IllegalArgumentException -
  • -
  • [SMACK-898] - AbstractProvider should also consider TypeVariable -
  • -
  • [SMACK-899] - NullPointerException in EntityCapsManager.addCapsExtension -
  • -
  • [SMACK-900] - NPE in DataForm.Builder.addItem() -
  • -
  • [SMACK-902] - DataFormProvider should retrieve the type of fields from <reported/> elements if possible -
  • -
- -

Improvement -

-
    -
  • [SMACK-901] - BoBDataExtension.from() should also allow IQs -
  • -
- -

4.4.0 -- 2020-12-06

- -

Bug -

-
    -
  • [SMACK-561] - Smack should not reply with multiple stream types after stream initiation is offered -
  • -
  • [SMACK-624] - AdHocCommandManager's session sweeping thread does never stop -
  • -
  • [SMACK-729] - Not all providers from smack-legacy.jar are loaded -
  • -
  • [SMACK-770] - There is no Bits of Binary Extension Element provider registered -
  • -
  • [SMACK-848] - Make MultiUserChat.leave() wait for response -
  • -
  • [SMACK-874] - PacketParserUtilsTest#invalidXMLInMessageBody() fails on non-english machines -
  • -
  • [SMACK-881] - Deadlock between reader and writer if Stream Mangement unacked stanza queue is full -
  • -
  • [SMACK-888] - MUC roomDestroyed() callback is not invoked -
  • -
- -

New Feature -

-
    -
  • [SMACK-257] - Add support for XEP-0118: User Tune -
  • -
  • [SMACK-636] - Add support for XEP-0319: Last User Interaction in Presence -
  • -
  • [SMACK-743] - Add support for XEP-0384: OMEMO Encryption -
  • -
  • [SMACK-801] - Update Smack to Java 8 -
  • -
  • [SMACK-824] - Add support for XEP-0221: Data Forms Media Element -
  • -
  • [SMACK-862] - Add support for XEP-0418: DNS Queries over XMPP (DoX) -
  • -
  • [SMACK-871] - Add support for XEP-0350: Data Forms Geolocation Element -
  • -
  • [SMACK-872] - Add support for XEP-0315: Data Forms XML Element -
  • -
  • [SMACK-878] - Add support for XEP-0328: JID Prep -
  • -
  • [SMACK-884] - Add support for XEP-0422: Message Fastening -
  • -
  • [SMACK-885] - Add support for XEP-0420 Stanza Content Encryption -
  • -
  • [SMACK-889] - Add support for XEP-0428: Fallback Indication -
  • -
- -

Improvement -

-
    -
  • [SMACK-591] - Replace XPP3 by SmackXmlPullParser (wrapping Stax's XmlStreamReader and XPP3 on Android) -
  • -
  • [SMACK-650] - Enable Java8's javadoc doclint -
  • -
  • [SMACK-651] - Perform sound cross-compilation: Use newer javac's --release feature -
  • -
  • [SMACK-718] - Prevent extremely long reply timeouts from being set -
  • -
  • [SMACK-821] - Make Forwarded a generic type -
  • -
  • [SMACK-822] - Add API for XEP-0313 § 6.2 Advanced configuration via Ad-Hoc commands -
  • -
  • [SMACK-825] - Discourage Stanza.getExtension(String, String) in favor of Stanza.getExtension(Class<E extends ExtensionElement>) -
  • -
  • [SMACK-826] - Add support for XEP-0373:" OpenPGP for XMPP" and XEP-0374: "OpenPGP for XMPP Instant Messaging" -
  • -
  • [SMACK-828] - Add support for XEP-0107: User Mood -
  • -
  • [SMACK-836] - Save a ServiceDiscoveryManager instance in a private field of MultiUserChatManger -
  • -
  • [SMACK-839] - Provider.parse() should not throw a generic Exception, but instead IOException and XmlPullParserException -
  • -
  • [SMACK-852] - Message thread and subject should be designed and implemented as ExtensionElements -
  • -
  • [SMACK-854] - Rename smack-java7 to smack-java8 -
  • -
  • [SMACK-866] - Remove all tabs from the source code and add checkstyle rule that enforces no-tabs -
  • -
  • [SMACK-867] - Extend HttpFileUploadManager by methods with InputStream parameter -
  • -
  • [SMACK-882] - Add support for MUC status code 333 -
  • -
  • [SMACK-883] - Add generic MUC callback for "participant left" caused by unavailable presences -
  • -
  • [SMACK-890] - Update Message Archive Management (XEP-0313) support to urn:xmpp:mam:2 -
  • -
  • [SMACK-892] - Smack performs unnecessary escaping in XML text -
  • -
- -

Task -

-
    -
  • [SMACK-750] - Raise Smack's minimum required Android SDK level to 19 (Android 4.4, Kit Kat, 2013-10) -
  • -
  • [SMACK-840] - Remove smack-compression-jzlib, as it is obsolete (Smack uses Java 7 de- and inflate API now) -
  • -
- -

4.3.4 -- 2019-05-27

- -

Bug -

-
    -
  • [SMACK-861] - Potential NPE in Roster.getPresencesInternal(BareJid) -
  • -
  • [SMACK-863] - ServiceDiscoveryManger does not use the main identity, causing setIdentity() to have no effect -
  • -
  • [SMACK-864] - Potential Denial of Service (DOS) by remote entities caused by unlimited threads for asynchronous operations -
  • -
  • [SMACK-865] - Some Manager.getInsanceFor() methods are missing the 'synchronized' keyword -
  • -
  • [SMACK-868] - XHTMLText.appendOpenBodyTag() produces invalid XML -
  • -
  • [SMACK-870] - TLS X.509 certificate verification should be performed with the ACE representation of the XMPP service domain when possible -
  • -
- -

Improvement -

-
    -
  • [SMACK-869] - Exceptions in async tasks should not go uncaught to the call stack to avoid program termination -
  • -
- -

4.3.3 -- 2019-03-14

- -

Bug -

-
    -
  • [SMACK-856] - Smack fails under JDK 11 because com.sun.jndi.dns.DnsContextFactory is not inaccessible -
  • -
- -

Improvement -

-
    -
  • [SMACK-858] - Dependency version specifier of jxmpp and MiniDNS include alpha/beta/... versions of the follow up version when Maven is used -
  • -
  • [SMACK-859] - MultiUserChat enter() should reset the timeout of the collector waiting for the final self presence to prevent timeouts for large MUCs -
  • -
- -

4.3.2 -- 2019-02-22

- -

Bug -

-
    -
  • [SMACK-842] - The RFC 3920 xml-not-well-formed error condition should be handled in stream error not a stanza error -
  • -
  • [SMACK-843] - ManManager.pagePrevious() pages into the wrong direction -
  • -
  • [SMACK-844] - Check if bounded unacknowledged stanzas queue is full before adding to it to avoid IllegalStateException -
  • -
  • [SMACK-845] - Ensure that IQ response 'to' address and ID are set correctly -
  • -
  • [SMACK-846] - XMPPTCPConnection does not wait for stream features after authentication if compression is disabled -
  • -
  • [SMACK-848] - Make MultiUserChat.leave() wait for response -
  • -
  • [SMACK-850] - DeliveryReceiptManager should not send receipts with messages of type 'groupchat' -
  • -
  • [SMACK-855] - XMPPTCPConnection sometimes has two writer threads running -
  • -
- -

Improvement -

-
    -
  • [SMACK-847] - Make TCP socket connection attempt interruptable -
  • -
  • [SMACK-849] - Smack Local SOCKS5 Proxy thread should be marked as daemon thread -
  • -
- -

4.3.1 -- 2018-10-14

- -

Bug -

-
    -
  • [SMACK-833] - XMLUtil.prettyFormatXml() throws on some Android devices -
  • -
- -

Improvement -

-
    -
  • [SMACK-829] - Disconnect BOSH client on shutdown -
  • -
  • [SMACK-838] - FormField.getFirstValue() throws IndexOutOfBoundsException if there are no values -
  • -
- -

4.3.0 -- 2018-08-02

- -

Bug -

-
    -
  • [SMACK-759] - PubSubManager.getLeafNode() throws PubSubAssertionError.DiscoInfoNodeAssertionError if node exists but its not a PubSub Node -
  • -
  • [SMACK-814] - NPE when using Node.getAffiliationsAsOwner() -
  • -
  • [SMACK-815] - XEP-0184: DeliveryReceipt requires ID, although the XEP defines it as optional attribute -
  • -
  • [SMACK-818] - EntityCapsManager sends presences with multiple CapsExtension causing disco#info lookup to fail -
  • -
  • [SMACK-819] - ConcurrentModification Exception in MultiUserChatManager.java -
  • -
  • [SMACK-820] - DNSUtil.setDaneProvider() does not set the DANE provider -
  • -
- -

Task -

-
    -
  • [SMACK-769] - Rename XMPPError to StanzaError -
  • -
  • [SMACK-776] - Remove deprecated reconnection callbacks in ConnectionListener -
  • -
- -

Improvement -

-
    -
  • [SMACK-761] - Adopt ChatStateManager to new Chat API (chat2) -
  • -
  • [SMACK-812] - Enable ModifierOrder checkstyle check -
  • -
  • [SMACK-816] - SimplePayload should infer the XML element name and namespace -
  • -
- -

4.2.4 -- 2018-04-15

- -

Bug -

-
    -
  • [SMACK-804] - ServiceAdministrationManager does not use correct form actions -
  • -
  • [SMACK-805] - ServiceDiscoveryManager.findService() only considers the first service by feature -
  • -
  • [SMACK-813] - Smack uses hostname instead of XMPP service name for SNI -
  • -
- -

New Feature -

-
    -
  • [SMACK-794] - Add support for XEP-0394: Message Markup -
  • -
  • [SMACK-795] - Add support for XEP-0382: Spoiler messages -
  • -
  • [SMACK-799] - Add support for XEP-0372: References -
  • -
  • [SMACK-800] - Add support for XEP-0392: Consistent Color Generation -
  • -
- -

Improvement -

-
    -
  • [SMACK-802] - Rename and deprecate: addPacketSendingListener(), removePacketSendingListener(), addPacketInterceptor() and removePacketInterceptor() -
  • -
  • [SMACK-809] - Make Roster's non-roster presence map second-level map bounded -
  • -
- -

4.2.3 -- 2018-02-07

- -

Bug -

-
    -
  • [SMACK-788] - NullPointerException if hostAddresses is null -
  • -
  • [SMACK-789] - AffiliationsExtension toXml() produces invalid XML -
  • -
  • [SMACK-790] - Some HTTP File Upload elements are not correctly parsed and serialized -
  • -
  • [SMACK-791] - NumberFormatException in IpAddressUtil.isIPv4LiteralAddress -
  • -
  • [SMACK-796] - SOCKS5 authentication erroneously uses 'user' when it should use 'passwd', causes authentication to fail -
  • -
- -

4.2.2 -- 2017-11-25

- -

Bug -

-
    -
  • [SMACK-775] - Create callback interface for ReconnectionManager -
  • -
  • [SMACK-778] - ReconnectionManager.reconnect() can throw NotConnectedException -
  • -
  • [SMACK-779] - smack-android erroneously depends on smack-omemo and smack-omemo-signal -
  • -
  • [SMACK-780] - PushNotificationManager's isSupported logic does query the server, whereas it should query the bare JID -
  • -
  • [SMACK-781] - MiniDnsResolver does not correctly handle the case when NOERROR is returned together with an empty answer section. -
  • -
  • [SMACK-782] - MultiUserChat does not remove the subject listener causing a memory leak -
  • -
  • [SMACK-783] - InvitationRejectionListener fires multiple times -
  • -
  • [SMACK-784] - StringUtils.numbersAndLetters has the numbers twice, resulting in a lower entropy -
  • -
  • [SMACK-785] - OfflineMessageManager.getMessages() does count the pending messages incorrectly, causing an unnecessary delay -
  • -
  • [SMACK-786] - Race condition when resuming a stream -
  • -
  • [SMACK-787] - Presence.getPriority() may return Integer.MIN_VALUE. -
  • -
- -

4.2.1 -- 2017-08-14

- -

Bug -

-
    -
  • [SMACK-749] - SCRAM-SHA-1 and SCRAM-SHA-1-PLUS SASL mechanisms have the same priority, causing SASL authentication failures -
  • -
  • [SMACK-755] - DIGEST-MD5 sometimes causes malformed request server response -
  • -
  • [SMACK-756] - IoTIsFriendResponse has invalid name and produces invalid XML -
  • -
  • [SMACK-759] - PubSubManager.getLeafNode() throws PubSubAssertionError.DiscoInfoNodeAssertionError if node exists but its not a PubSub Node -
  • -
  • [SMACK-764] - NPE in hashCode() in Occupant when jid is null -
  • -
  • [SMACK-766] - Smack possibly includes 'ask' attribute in roster items when sending requests -
  • -
  • [SMACK-768] - Smack throws NoResponse timeout when waiting for IQ although there was a response -
  • -
  • [SMACK-771] - XMPPTCPConnection should use KeyManagerFactory.getDefaultAlgorithm() instead of KeyManagerFactory.getInstance("sunX509"); -
  • -
  • [SMACK-772] - HostAddress must deal with 'fqdn' being null. -
  • -
  • [SMACK-773] - Allow roster pushes from our full JID for backwards compatibility -
  • -
  • [SMACK-774] - HTTP File Upload's SlotRequest metadata should be attributes not child elements -
  • -
- -

New Feature -

-
    -
  • [SMACK-746] - Add support for XEP-0380: Explicit Message Encryption -
  • -
  • [SMACK-758] - Add support for XEP-0334: Message Processing Hints -
  • -
  • [SMACK-760] - Smack does not allow custom extension elements in SM's <failed/> -
  • -
- -

Improvement -

-
    -
  • [SMACK-752] - XEP-0357 Push Notification enable IQ uses wrong form type: Should be 'submit' instead of 'form' -
  • -
  • [SMACK-754] - Allow MUC room subject changes from the MUCs bare JID -
  • -
  • [SMACK-777] - MamManager should use the user's bare JID to check if MAM is supported -
  • -
- -

4.2.0 -- 2017-03-10

- -

Sub-task -

-
    -
  • [SMACK-639] - Add support for pre-approved subscription requests (RFC 6121 § 3.4) -
  • -
- -

Bug -

-
    -
  • [SMACK-306] - loadRosterOnLogin has non-trivial side effect on getRoster -
  • -
  • [SMACK-416] - Refactor PEP to make it use the existing pubsub API. -
  • -
  • [SMACK-674] - PubSub Affiliation extension element is missing 'jid' attribute, and is using wrong element name 'subscription' -
  • -
  • [SMACK-682] - Add support for "XEP-0360: Nonzas (are not Stanzas)" -
  • -
  • [SMACK-683] - Using a Proxy with XMPPTCPConnection failes with "SocketException: Unconnected sockets not implemented" -
  • -
  • [SMACK-691] - Add support for MUCItem's Actor 'nick' -
  • -
  • [SMACK-705] - PubSub's Affiliation.getElementName() returns wrong name -
  • -
  • [SMACK-722] - SASL X-OAUTH2 implementation incorrectly performs Base64 encoding twice -
  • -
  • [SMACK-723] - Support "Caps Optimizations" (XEP-0115 § 8.4) -
  • -
  • [SMACK-724] - Do not re-use the Socket after connect() failed. -
  • -
  • [SMACK-725] - ReconnectionManager should handle AlreadyConnectedException and AlreadyLoggedInException not as failure -
  • -
  • [SMACK-741] - Ad-hoc command 'note' element 'type' attribute should be treated as optional -
  • -
  • [SMACK-745] - Memory leak in MultiUserChat -
  • -
- -

New Feature -

-
    -
  • [SMACK-366] - Add support for DNSSEC. -
  • -
  • [SMACK-610] - Add support for XEP-0080: User Location -
  • -
  • [SMACK-619] - Add roomDestroyed to MUC UserStatusListener -
  • -
  • [SMACK-625] - Add support for XEP-313: Message Archive Management -
  • -
  • [SMACK-675] - Add support for PubSub affiliation actions as owner -
  • -
  • [SMACK-677] - Add support for SASL 'authzid' (Authorization Identity) -
  • -
  • [SMACK-690] - Add support for DNS-Based Authentication of Named Entities (DANE, RFC 6698) -
  • -
  • [SMACK-731] - Add support for XEP-0191: Blocking Command -
  • -
  • [SMACK-732] - Smack should be able to handle "single equals sign" SASL responses -
  • -
  • [SMACK-740] - Add support for Multi-User Chat Light -
  • -
  • [SMACK-742] - Add support for XEP-0133: Service Administration -
  • -
  • [SMACK-747] - Add support for XEP-0363: HTTP File Upload -
  • -
- -

Task -

-
    -
  • [SMACK-638] - Call connection creation listeners from within AbstractXMPPConnection's constructor -
  • -
  • [SMACK-644] - Throw exception if account creation or password change is performed over insecure connections -
  • -
  • [SMACK-655] - Enable StreamManagement by default -
  • -
- -

Improvement -

-
    -
  • [SMACK-372] - Make package protected methods in PEPItem public -
  • -
  • [SMACK-572] - Rejoin MUC rooms after reconnect -
  • -
  • [SMACK-628] - Rework Roster handling with anonymous connections -
  • -
  • [SMACK-629] - Rework how Smack handles anonymous connections -
  • -
  • [SMACK-631] - Improve ParsingExceptionCallback, allow it to be a functional interface -
  • -
  • [SMACK-632] - Make Smack interruptible -
  • -
  • [SMACK-633] - Allow clean and graceful disconnects (stream closing) -
  • -
  • [SMACK-634] - Use jxmpp-jid, add Jid class to replace String's being used as JIDs -
  • -
  • [SMACK-646] - Add support for MUC roomnick rewrite -
  • -
  • [SMACK-647] - Don't automatically call login() on connect() if the connection was authenticated before -
  • -
  • [SMACK-648] - Improve MultiUserChat API -
  • -
  • [SMACK-657] - Rename RosterEntry.getStatus and RosterPacket.ItemStatus to ItemAskStatus -
  • -
  • [SMACK-663] - Roster should be fully loaded when Roster.getInstanceFor(XMPPConnection) is called with a authenticated connection -
  • -
  • [SMACK-665] - Rename 'serviceName' to 'xmppServiceDomain' -
  • -
  • [SMACK-666] - Typo in 'RosterEntries.rosterEntires()', change to 'RosterEntries.rosterEntries()' -
  • -
  • [SMACK-703] - Limit the stored presences of entities not in Roster -
  • -
  • [SMACK-704] - Pass down Message stanza in ChatStateListener -
  • -
  • [SMACK-711] - Improve the logging of TCP connection attempts. -
  • -
  • [SMACK-720] - Improve support for Tor and Hidden Services. -
  • -
  • [SMACK-721] - Report illegal Stream Management states to avoid OOM Exception -
  • -
  • [SMACK-727] - Add partial support for the IoT XEPs (XEP-0323, -0324, -0325, -0347) -
  • -
  • [SMACK-733] - Handle outgoing 'unavailable' Presences in Roster -
  • -
  • [SMACK-736] - Add support for Chat Markers (XEP-0333) -
  • -
  • [SMACK-737] - Add support for Bits of Binary (XEP-0231) -
  • -
  • [SMACK-738] - Add support for Push Notifications (XEP-0357) -
  • -
- -

4.1.9 -- 2016-11-19

- -

Bug -

-
    -
  • [SMACK-739] - Smack starts SASL step without TLS in case STARTTLS is stripped even if SecurityMode.Required is used -
  • -
  • [SMACK-735] - Smack sometimes sends invalid SCRAM-SHA1 nonce -
  • -
- -

4.1.8 -- 2016-07-30

- -

Bug -

-
    -
  • [SMACK-722] - SASL X-OAUTH2 implementation incorrectly performs Base64 encoding twice -
  • -
  • [SMACK-724] - Do not re-use the Socket after connect() failed. -
  • -
  • [SMACK-725] - ReconnectionManager should handle AlreadyConnectedException and AlreadyLoggedInException not as failure -
  • -
  • [SMACK-726] - 'purge' and 'remove' IQ of XEP-0013 must be of type 'set' -
  • -
- -

4.1.7 -- 2016-04-14

- -

Bug -

-
    -
  • [SMACK-712] - XMPPTCPConnection's setEnabledSSL(Protocols|Ciphers) has no effect -
  • -
  • [SMACK-716] - EntityTimeManager.getTime() does not set the recipients JID -
  • -
  • [SMACK-719] - XMPPError should use Locale.US in toUpperCase() -
  • -
- -

Improvement -

-
    -
  • [SMACK-715] - Add Roster.setRosterLoadedAtLoginDefault(boolean) -
  • -
- -

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 -

-
    -
  • [SMACK-698] - Time creates invalid XML -
  • -
  • [SMACK-700] - Duplicate stanzas in unacknowledgedStanzas queue when stream is resumed -
  • -
  • [SMACK-702] - RejectedExecutionException in AbstractXMPPConnection.processPacket() causes connection Termination -
  • -
- -

4.1.4 -- 2015-09-14

- -

Bug -

-
    -
  • [SMACK-688] - Reset carbons state if session got not resumed or cleanly disconnected -
  • -
  • [SMACK-689] - PEPPubSub creates malformed XML -
  • -
  • [SMACK-693] - MultiUserChat's UserStatusListener is not getting triggered -
  • -
  • [SMACK-695] - JSON and GCM parser does an erroneous extra next() -
  • -
  • [SMACK-697] - PrivacyListManager should handle the case where not default and active list are currently set -
  • -
- -

Improvement -

-
    -
  • [SMACK-686] - Provide a hint that connect() needs to be called prior login() in NotConnectedException -
  • -
  • [SMACK-687] - Update to jxmpp 0.4.2 -
  • -
  • [SMACK-696] - Drop stream state after stream error -
  • -
- -

4.1.3 -- 2015-07-15

- -

Bug -

-
    -
  • [SMACK-679] - Memory leak in Socks5BytestreamManager. Should use weak map for 'managers' -
  • -
  • [SMACK-680] - XHTML bodies are un-escaped after parsing -
  • -
  • [SMACK-681] - Roster presence callbacks may not be invoked right after login -
  • -
- -

4.1.2 -- 2015-06-27

- -

Bug -

-
    -
  • [SMACK-664] - Invalid IQ error response to OfferRequestPacket and OfferRevokePacket -
  • -
  • [SMACK-668] - ReconnectionManager's value of 'attempts' is not reset after successful reconnection -
  • -
  • [SMACK-669] - Only add Entity Capabilities extension to available presences -
  • -
  • [SMACK-670] - SASLMechanism.authenticate should treat an empty byte array like 'null' byte array -
  • -
  • [SMACK-672] - Memory leak caused by RosterGroup declaring a strong reference to XMPPConnection -
  • -
  • [SMACK-673] - VCard API does not support all elements -
  • -
  • [SMACK-676] - ConcurrentModificationException in ServerPingWithAlarmManager -
  • -
  • [SMACK-678] - Login hangs if starttls advertised, but security is set to 'disabled' and compression is also advertised -
  • -
- -

Improvement -

-
    -
  • [SMACK-667] - Request Stream Mangement Acknowledgement after re-sending unack'ed stanzas after stream resumption -
  • -
  • [SMACK-671] - Don't disable Scoks5BytestreamManager on connection termination -
  • -
- -

4.1.1 -- 2015-05-09

- -

Bug -

-
    -
  • [SMACK-649] - DIGEST-MD5 challenge/response parsing must handle linear white spaces after the comma -
  • -
  • [SMACK-652] - SynchronizationPoint should use signalAll -
  • -
  • [SMACK-653] - Integer overflow if both client and server don't specify a max resumption time -
  • -
  • [SMACK-654] - isSmResumptionPossible() returns wrong values -
  • -
  • [SMACK-656] - DeliveryReceipts auto add should use packet interceptors and should not be requested for messages with ACKs. -
  • -
  • [SMACK-659] - Memory leak caused by RosterEntry declaring a strong reference to XMPPConnection -
  • -
  • [SMACK-660] - ReconnectionManager's RANDOM_INCREASING_DELAY is erroneously using a fixed value. -
  • -
  • [SMACK-661] - Add method to set ProxyInfo in ConnectionConfiguration.Builder -
  • -
  • [SMACK-662] - RosterEntry.setName() does not change the name -
  • -
- -

4.1.0 -- 2015-03-29

- -

Sub-task -

- - -

Bug -

-
    -
  • [SMACK-65] - Packet parsing should look for depth -
  • -
  • [SMACK-237] - Handle more vCard values (XEP-0054) -
  • -
  • [SMACK-383] - Allow the garbage collection of all object instances of a closed and unreferenced connection -
  • -
  • [SMACK-424] - Add a MultiUserChat.presenceChanged callback method to be informed if a presence within a MUC has changed (joined, leaved, status change) -
  • -
  • [SMACK-542] - MUC: RoomInfo should hold more data if the result contains a FORM_TYPE field -
  • -
  • [SMACK-549] - MUCUser#getStatus should be a List -
  • -
  • [SMACK-564] - Some tests fail with Java 8 -
  • -
  • [SMACK-570] - Smack does not support resourceparts which contain the '@' character. -
  • -
  • [SMACK-571] - Don't remove the MUC listeners after a disconnect() , keep state of Connection between disconnect() and connect()/login() -
  • -
  • [SMACK-573] - MessageEventManager treats error replies as message events -
  • -
  • [SMACK-583] - PacketListeners may not be invoked in delivery order -
  • -
  • [SMACK-585] - XMPPTCPConnection does not set 'host' and 'port' -
  • -
  • [SMACK-590] - Don't use IQReplyFilter for the bind set/result exchange -
  • -
  • [SMACK-597] - PingManager.getLastReceivedPong() always returns -1 -
  • -
  • [SMACK-604] - MUCUser must support multiple status codes -
  • -
  • [SMACK-620] - Smack should use a safe SAX parser, e.g. with entity reference expansion disabled -
  • -
  • [SMACK-635] - Typo DNSUtil.init() prevents DNS SRV lookups to fail in some cases -
  • -
  • [SMACK-643] - Smack should not set the service name to the vale of the 'from' attribute of the opening stream element received from the service -
  • -
- -

Improvement -

-
    -
  • [SMACK-340] - Should make the wait/timeouts on SASL authentication configurable. -
  • -
  • [SMACK-402] - Update obsolete "Message Delivery Receipts" support from (JEP|XEP)-0022 to XEP-0184 -
  • -
  • [SMACK-453] - Add support for all primitive types in IntrospectionProvider.decode() -
  • -
  • [SMACK-521] - Clear PacketWriters queue when the connection is shut down -
  • -
  • [SMACK-532] - Evaluate if its possible to guarantee the order of listeners by using a LinkedHashMap -
  • -
  • [SMACK-566] - Create public method that parses Strings/CharSequences to messages, IQs and presence instances -
  • -
  • [SMACK-587] - Subprojects should uses versions when importing the OSGi smack-core components -
  • -
  • [SMACK-595] - Add an API to send a stanza and wait asynchronously for a response -
  • -
  • [SMACK-599] - Provide string messages to all exceptions thrown by Smack -
  • -
  • [SMACK-600] - RoomInfo Class should add the information from the Identity element. -
  • -
  • [SMACK-608] - Add support for XMPP error conditions text -
  • -
  • [SMACK-622] - Add support for 'optional' in session stream features -
  • -
  • [SMACK-626] - Add support for 'ofrom' Extended Stanza Addressing type -
  • -
  • [SMACK-627] - Smack should allow null usernames under certain circumstances -
  • -
  • [SMACK-645] - Roster should not leak internal state e.g. presences -
  • -
- -

New Feature -

-
    -
  • [SMACK-234] - Add support for SASL EXTERNAL: PKI (Client SSL Cert) Support -
  • -
  • [SMACK-333] - Implement XEP-0198: Stream Management -
  • -
  • [SMACK-378] - Give access to the socket outside the XMPPconnection -
  • -
  • [SMACK-581] - Add support for "Result Set Management" (XEP-59) -
  • -
  • [SMACK-607] - Add support for XEP-0352: Client State Indication -
  • -
  • [SMACK-612] - Add support for XEP-0141: Data Forms Layout -
  • -
  • [SMACK-621] - Add support for XEP-0122: Data Forms Validation -
  • -
  • [SMACK-623] - Add API to retrieve the subscriptions of a PubSub node as owner -
  • -
- -

Task -

-
    -
  • [SMACK-365] - SmackConfiguration should only report errors if the file fails to load, not when it fails to load for a specific classloader. -
  • -
  • [SMACK-371] - Some MUC tasks are using stanza's as defined in an older version of the spec. Fails to work on some servers. -
  • -
  • [SMACK-569] - Move Message Event code to legacy subproject -
  • -
  • [SMACK-578] - Remove decorators for "Legacy Delayed Delivery" (XEP-91) in favor of Delayed Delivery (XEP-203) -
  • -
  • [SMACK-579] - FileTransferManager and FileTransferNegoiator should use WeakHashMaps and extend Manager -
  • -
  • [SMACK-582] - Change ReceiptReceivedListener.onReceiptReceived parameters -
  • -
  • [SMACK-637] - Move Roster and Chat code to new smack-im subproject -
  • -
- -

4.0.7 -- 2015-02-20

- -

Bug -

-
    -
  • [SMACK-635] - Typo DNSUtil.init() prevents DNS SRV lookups to fail in some cases -
  • -
  • [SMACK-643] - Smack should not set the service name to the vale of the 'from' attribute of the opening stream element received from the service -
  • -
- -

4.0.6 -- 2014-11-23

- -

Bug -

-
    -
  • [SMACK-616] - Smack should fallback to using host with default port if DNS SRV lookup fails -
  • -
  • [SMACK-617] - Message Digest in EntityCapsManager should be synchronized -
  • -
- -

4.0.5 -- 2014-10-22

- -

Bug -

-
    -
  • [SMACK-609] - PingManager.ping(String, long) does not respect timeout -
  • -
  • [SMACK-613] - Parsing exception causes infinite loop if the exception is not thrown -
  • -
- -

4.0.4 -- 2014-09-05

- -

Bug -

-
    -
  • [SMACK-596] - Smack should load roster before sending the initial presence -
  • -
  • [SMACK-598] - Smack should allow the empty string as content of message body element -
  • -
  • [SMACK-601] - PubSub ItemProvider does only process the outermost namespace definition when creating PayloadItems -
  • -
  • [SMACK-602] - PacketCollector must handle InterruptException -
  • -
  • [SMACK-603] - XMPPError.Condition.equals() should be null-safe -
  • -
- -

4.0.3 -- 2014-08-16

- -

Bug -

-
    -
  • [SMACK-589] - FormField.Option toXML() produces malformed XML -
  • -
  • [SMACK-592] - OfflineMessagesManager.getMessages() does send request before collector is set up and could leak collector -
  • -
  • [SMACK-594] - PrivateData Bookmarks.toXML() returns invalid XML -
  • -
- -

Improvement -

-
    -
  • [SMACK-539] - Verify ConnectionConfiguration parameters -
  • -
  • [SMACK-588] - Typo in org.jivesoftware.smackx.pubsub.ConfigureForm: s/isSubscibe/isSubscribe/ -
  • -
  • [SMACK-593] - Smack should prefer full flush over sync flush when using compression -
  • -
- -

4.0.2 -- 2014-07-27

- -

Improvement -

-
    -
  • [SMACK-576] - smack-resolver-javax should become a OSGi ServiceComponent -
  • -
  • [SMACK-586] - Extend API to configure a HostnameVerifier -
  • -
- -

4.0.1 -- 2014-07-20

- -

Sub-task -

-
    -
  • [SMACK-346] - Bug in return code for rejection handling in FileTransferManager -
  • -
- -

Bug -

-
    -
  • [SMACK-574] - Documentation still refers at some places to Connection -
  • -
  • [SMACK-575] - PingManager schedules pings after pingInterval when it should be use nextPingIn instead -
  • -
  • [SMACK-577] - Bookmarks and FormField toXml() methods do not properly escape XML -
  • -
  • [SMACK-583] - PacketListeners may not be invoked in delivery order -
  • -
- -

Improvement -

-
    -
  • [SMACK-576] - smack-resolver-javax should become a OSGi ServiceComponent -
  • -
- -

New Feature -

-
    -
  • [SMACK-580] - Add support for retrieving a PubSub node's affiliations -
  • -
- -

4.0.0 -- 2014-06-08

- -

Sub-task -

-
    -
  • [SMACK-399] - Add support for Roster Versioning (was XEP-0237, now in RFC 6121) -
  • -
  • [SMACK-400] - Change xml-not-well-formed to not-well-formed -
  • -
  • [SMACK-401] - Remove <invalid-id/> -
  • -
  • [SMACK-445] - XMPPError class is based on deprecated XEP-0086 -
  • -
- -

Bug -

-
    -
  • [SMACK-357] - Error in SASL authentication when SASL authzid parameter is null -
  • -
  • [SMACK-410] - Any valid SSL server certificate can be used to perform a man-in-the-middle attack -
  • -
  • [SMACK-411] - ServiceDiscoveryManager identities should be non-static and kept in a Set to allow multiple identities as per XEP-30 -
  • -
  • [SMACK-414] - Smack does not announce the support for XEP-54 aka vcard-temp -
  • -
  • [SMACK-427] - Typo in code - StreamInitiation.setSesssionID() -
  • -
  • [SMACK-467] - Don't use the default locale for machine-readable output, use Locale.US instead -
  • -
  • [SMACK-531] - Add missing namespace attribute to XHTML-IM body tags -
  • -
  • [SMACK-533] - Smack should prevent IQ response spoofing -
  • -
  • [SMACK-535] - jul.properties should only configure the 'org.igniterealtime' namespace -
  • -
  • [SMACK-538] - ParseRoster does not check the sender of the roster and for pending roster queries -
  • -
  • [SMACK-541] - XHTMLExtensionProvider relies on incorrect behavior of MXParser, violating the contract of the XMLPullParser interface -
  • -
  • [SMACK-543] - packet.Time is not thread-safe -
  • -
  • [SMACK-546] - PubSub's Item needs to escape its XML payload -
  • -
  • [SMACK-548] - PingManager notifies pingFailedListeners multiple times -
  • -
  • [SMACK-551] - ChatManager throws NPE, when Message has no 'from' attribute -
  • -
  • [SMACK-554] - Memory leak in BookmarkManager -
  • -
  • [SMACK-555] - VCardProvider should consider some elements as optional -
  • -
  • [SMACK-558] - connect() must wait until the stream features have been parsed -
  • -
  • [SMACK-559] - Roster entries without a group are not updated -
  • -
  • [SMACK-560] - Race condition in PacketWriter -
  • -
  • [SMACK-567] - XMPPConnection leaks listenerExecutor ExecutorService -
  • -
- -

Improvement -

-
    -
  • [SMACK-343] - Make Smack jar an OSGi bundle. -
  • -
  • [SMACK-356] - There is no way to reliably end a Chat and have a new one created. -
  • -
  • [SMACK-454] - Follow XEP-0170 recommendation: Compression before Resource Binding -
  • -
  • [SMACK-459] - Add option to configure the default identity in ServiceDiscoveryManager -
  • -
  • [SMACK-465] - Replace custom wrapped Throwable in XMPPException with Exception.cause -
  • -
  • [SMACK-468] - Don't throw an IOException in IBBStreams when the stream got closed by the remote -
  • -
  • [SMACK-536] - JUL Loggers should become final -
  • -
  • [SMACK-537] - Move XMPP Ping code to smackx, add keep-alive functionality to PingManager -
  • -
  • [SMACK-545] - Change API to the style mentioned in Smack's Code Guidelines -
  • -
  • [SMACK-547] - Consistent behavior for "from" attribute on outgoing stanzas -
  • -
  • [SMACK-556] - Make ConnectionConfigration getters public -
  • -
  • [SMACK-557] - Provide a MultiUserChat method to create *or* join a room -
  • -
  • [SMACK-568] - Don't exclude groupchat messages without body element in MultiUserChat MessageListeners -
  • -
- -

New Feature -

-
    -
  • [SMACK-53] - Add support for XEP-0092: Software Version -
  • -
  • [SMACK-71] - Create new FromFilter that checks for exact matching -
  • -
  • [SMACK-187] - Add HTTP Binding support (BOSH / XEP-0124) -
  • -
  • [SMACK-265] - Move to a newer build process with artifacts published to maven central repo -
  • -
  • [SMACK-426] - Improve XMPPException -
  • -
  • [SMACK-544] - Add support for XEP-0079: Advanced Message Processing -
  • -
  • [SMACK-552] - Add support for "HTTP over XMPP transport" aka. XEP-0332 -
  • -
- -

Task -

-
    -
  • [SMACK-371] - Some MUC tasks are using stanza's as defined in an older version of the spec. Fails to work on some servers. -
  • -
  • [SMACK-432] - Code cleanup of deprecated methods -
  • -
  • [SMACK-446] - Remove non-SASL authentication code -
  • -
- -

3.4.1 -- Feb 9, 2014

- -

Bug

-
    -
  • [SMACK-540] - Memory leak in MultiUserChat -
  • -
- -

3.4.0 -- Feb 2, 2014

- -

Bug Fixes

-
    -
  • [SMACK-442] - Manager's should also handle connectionClosedOnError()
  • -
  • [SMACK-443] - ReconnectionSuccessful listeners are invoked twice on reconnection if connect() failed before
  • -
  • [SMACK-452] - PacketParserUtils.parseStreamError() is not aware of optional text element and therefore failes to parse stream error's correctly. Prevents ReconnectionManager from reconnecting.
  • -
  • [SMACK-458] - Smack's Managers should not remove itself when the connection is closed or should re-add themselfs if the connection get reconnected
  • -
  • [SMACK-462] - Prevent duplicate manager instances by using the manager's constructor in the ConnectionCreationListener's connectionCreated
  • -
  • [SMACK-463] - packet listeners silently fail when preceding listener caused exception
  • -
  • [SMACK-524] - Use correct block-size definition for IBB transfers
  • -
  • [SMACK-525] - NPE in XMPPConnection.notifyConnectionError
  • -
  • [SMACK-529] - Add support for XEP-0280 "Message Carbons"
  • -
  • [SMACK-530] - DNSUtilTest requires an internet connection to work, it should be moved to integration tests.
  • -
- -

New Feature

-
    -
  • [SMACK-286] - Need to change ProviderManager to support loading smack.providers from alternative locations
  • -
  • [SMACK-387] - Allow configuration of ChatManager to be able to allow message handling to be customized.
  • -
  • [SMACK-403] - Add support for XEP-0297 "Stanza Forwarding"
  • -
  • [SMACK-434] - Create a project to contain non production ready implementations of specifications
  • -
- -

Improvement

-
    -
  • [SMACK-343] - Make Smack jar an OSGi bundle.
  • -
  • [SMACK-381] - Separate the configuration for smack extension related classes from the smack jar.
  • -
  • [SMACK-444] - Allow 'null' for TruststorePath and TruststorePassword in ServerTrustManager
  • -
  • [SMACK-456] - Add the causing exception to the XMPPExceptions thrown in XMPPConnection
  • -
  • [SMACK-457] - Remove unnecessary printStackTrace() in XMPPConnection
  • -
  • [SMACK-460] - ServiceDiscoveryManager should not use the constructor in connectionCreated()
  • -
  • [SMACK-461] - Remove incorrect deprecated marker for DiscoverInfo.Identity.setType()
  • -
  • [SMACK-464] - Make it clear that PacketListener's added with XMPPConnection.addPacketListener() are only for received packets
  • -
  • [SMACK-534] - Convert all System.out and printStackTrace calls to use Java util logging.
  • -
  • [SMACK-339] - Allow ConnectionListeners to be added before XMPPConnection is connected. Currently throws exception
  • -
  • [SMACK-373] - Don't remove listeners after a disconnect() , keep state of XMPPConnection between disconnect() and connect()/login()
  • -
  • [SMACK-434] - Create a project to contain non production ready implementations of specifications
  • -
  • [SMACK-526] - Deprecate all PEP related classes.
  • -
- -

3.3.1 -- Oct 6, 2013

- -

Bug Fixes

-
    -
  • [SMACK-428] - RosterEntry overrides equals, but not hashcode.
  • -
  • [SMACK-438] - Possible NPE in MultiUserChat.InvitationsMonitor.getInvitationsMonitor()
  • -
  • [SMACK-441] - Memory leak in KeepAliveManager
  • -
  • [SMACK-447] - Compression is not enabled for Java7ZlibInputOutputStream
  • -
  • [SMACK-448] - Java7ZlibInputOutputStream does not work. Deflater.DEFAULT_STRATEGY is used as compression level when it should use Deflater.DEFAULT_COMPRESSION
  • -
  • [SMACK-450] - VCard.load() throws null pointer exception if there is no VCard for the user
  • -
  • [SMACK-455] - Multiple items doesn`t not parse correctly in a pubsub message
  • -
- -

New Feature

-
    -
  • [SMACK-425] - Collect (parser) Exceptions and unparseable stanzas. Provide a callback method so that the user is notified about them if he wants to
  • -
- -

Improvement

-
    -
  • [SMACK-369] - Exceptions during login should get thrown back up to the caller.
  • -
  • [SMACK-439] - Improve documentation for MultiUserChat.InvitationsListener
  • -
  • [SMACK-451] - PingManager entry in META-INF/smack.providers is within Ad-Hoc Command section
  • -
  • [SMACK-431] - Enable Entity Caps as default for new connections and write extensions documentation html page
  • -
  • [SMACK-405] - Cleanup of redundant code in XMPPConnection.shutdown()
  • -
- -

3.3.0 -- May 4, 2013

- -

Bug Fixes

-
    -
  • [SMACK-225] - Improper handeling of DNS SRV records
  • -
  • [SMACK-238] - The vCard avatar type always return jpg
  • -
  • [SMACK-270] - Fix for a memory leak in MUC with MUC.finalize()
  • -
  • [SMACK-278] - Deadlock during Smack disconnect
  • -
  • [SMACK-342] - VCards causes ConcurrentModificationException
  • -
  • [SMACK-344] - Bug in SASL authentication mechanism when SRV records are being used.
  • -
  • [SMACK-351] - Rework File Transfer
  • -
  • [SMACK-352] - Update the licensing headers in various files.
  • -
  • [SMACK-355] - IO Error if smack cant use port for local proxy
  • -
  • [SMACK-371] - Some MUC tasks are using stanza's as defined in an older version of the spec. Fails to work on some servers.
  • -
  • [SMACK-375] - Node strings in the discovery info packets are not escaped as in the other packets
  • -
  • [SMACK-382] - Prevent memory leak in AdHocCommandManager
  • -
  • [SMACK-384] - Endless waiting for connection to be established
  • -
  • [SMACK-390] - Smack login will fail if a bad delay packet is received
  • -
  • [SMACK-392] - In ant build, compile-test target doesn't work.
  • -
  • [SMACK-394] - Erroneous cast in IBBInputStream's read() method
  • -
  • [SMACK-395] - Socks5BytestreamManager's establishConnection() should still try to use the local streamhost proxy if the server doesn't provide one
  • -
  • [SMACK-404] - Smack uses the wrong method to decode Base64 Strings
  • -
  • [SMACK-413] - VCardProvider incorrectly parses binary value of avatars
  • -
  • [SMACK-415] - ItemProvider relies on incorrect behavior of MXParser, violating the contract of the XMLPullParser interface
  • -
  • [SMACK-417] - If both PacketReader and PacketWriter fail at the same time, connectionClosedonError() is called two times
  • -
- -

New Features

-
    -
  • [SMACK-331] - Add support for XEP-0184: Message Delivery Receipts
  • -
  • [SMACK-345] - Inproved detection of last activity
  • -
  • [SMACK-361] - Add support for XEP-0115 Entity Capabilities
  • -
  • [SMACK-376] - Setting a custom trust manager to control certificates from outside
  • -
  • [SMACK-388] - XEP-199 XMPP Ping support
  • -
- -

Improvements

-
    -
  • [SMACK-341] - Update the PacketCollector and ConnectionDetachedPacketCollector to use the java concurrent classes.
  • -
  • [SMACK-358] - Support additional properties for account creation in test cases.
  • -
  • [SMACK-363] - Code Cleanup
  • -
  • [SMACK-377] - avoid unnecessary DNS requests in XMPPconnection
  • -
  • [SMACK-379] - Sessions were removed from the specification but Smack still uses them. Should be updated to reflect the spec changes.
  • -
  • [SMACK-385] - Reusing KeyStore in order to reduce memory usage
  • -
  • [SMACK-389] - Add java.util.zip.Deflater(In|Out)putStream as Java7 API native alternative to JZlib
  • -
  • [SMACK-391] - Improve date parsing in StringUtils and make DelayInformationProvider use StringUtils for date parsing.
  • -
  • [SMACK-412] - Replace the whitespace ping with a XEP-0199 ping
  • -
  • [SMACK-419] - PacketWriter: Only flush the BufferedWriter if the packet queue is empty
  • -
  • [SMACK-423] - Investigate whether unhandled packets should still parse the child xml into a string as content
  • -
  • [SMACK-430] - Throw an exception if FileTransferManager.createOutgoingFileTransfer() was used with a bare JID
  • -
- -

3.2.2 -- Dec. 23, 2011

- -

Bug Fixes

-
    -
  • [SMACK-263] - Set file info in all send* methods
  • -
  • [SMACK-322] - NPE in XMPPConnection
  • -
  • [SMACK-324] - Investigate SASL issue with jabberd2 servers
  • -
  • [SMACK-338] - IBB filetransfer doesn't work as expected
  • -
  • [SMACK-346] - Bug in return code for rejection handling in FileTransferManager
  • -
  • [SMACK-348] - Documentation error - broken link
  • -
  • [SMACK-349] - Smack's IBB sends too much data in a packet
  • -
  • [SMACK-350] - Bytestream is not working in Spark 2.6.3 from XP to W7
  • -
  • [SMACK-353] - Thread leak in the FaultTolerantNegotiator
  • -
  • [SMACK-362] - smack throw NoSuchElementException if the muc#roominfo_subject has no values
  • -
- -

Improvements

-
    -
  • [SMACK-343] - Make Smack jar an OSGi bundle.
  • -
  • [SMACK-354] - Provide milliseconds in timestamp colum debugwindow
  • -
- -

3.2.1 -- July 4, 2011

-

Bug Fixes

-
    -
  • [SMACK-129] - MultiUserChat will Store Messages in its PacketCollector irregardless of whether or not they are being read
  • -
  • [SMACK-230] - Disconnect Can Cause Null Pointer Exception
  • -
  • [SMACK-273] - Bug in RoomListenerMultiplexor.java
  • -
  • [SMACK-329] - XHTMLText uses improper format for br tag
  • -
  • [SMACK-338] - IBB filetransfer doesn't work as expected
  • -
  • [SMACK-324] - Investigate SASL issue with jabberd2 servers
  • -
- -

3.2.0 -- May 3, 2011

-

New Features

-
    -
  • [SMACK-272] - Add support for pubsub (XEP-0060)
  • -
  • [SMACK-296] - Add support for XEP-0224: Attention
  • -
  • [SMACK-319] - Add common interfaces for SOCKS5 Bytestreams and In-Band Bytestreams
  • -
- -

Improvements

-
    -
  • [SMACK-137] - File Transfer Settings
  • -
  • [SMACK-156] - Add the ability to register for roster events before logging in
  • -
  • [SMACK-261] - Minor Jingle cleanup to better support Jingle in Spark
  • -
  • [SMACK-277] - Update XMLUnit to the latest version
  • -
  • [SMACK-282] - Support SASL-related error conditions.
  • -
  • [SMACK-283] - Investigate why Jingle is connecting to stun.xten.net
  • -
  • [SMACK-285] - Add support for Nicks
  • -
  • [SMACK-289] - There is no way of retrieving items from a pubsub node when the user has multiple subscriptions.
  • -
  • [SMACK-294] - Handle empty roster groups and no goups in the same way
  • -
  • [SMACK-295] - Fire reconnectionSuccessful event when session is established
  • -
  • [SMACK-297] - add configuration for local Socks5 proxy
  • -
  • [SMACK-298] - Respond to all incoming Socks5 bytestream requests
  • -
  • [SMACK-299] - Improve accepting of Socks5 bytestream requests
  • -
  • [SMACK-300] - improve local Socks5 proxy implemetation
  • -
  • [SMACK-301] - support for bytestream packets to query Socks5 proxy for network address
  • -
  • [SMACK-302] - Improve establishing of Socks5 bytestreams
  • -
  • [SMACK-303] - integrate of the extracted Socks5 bytestream API in file transfer API
  • -
  • [SMACK-304] - Extend the IQ API to create empty IQ results and IQ error response packets
  • -
  • [SMACK-307] - Improve Message Parser Robustness and Message Body I18N
  • -
  • [SMACK-309] - Fully implement XEP-0047 In-Band Bytestreams
  • -
  • [SMACK-310] - Add Support for Localized Message Subjects
  • -
- -

Bug Fixes

-
    -
  • [SMACK-163] - Fix NPE in RoomInfo when subject has not value
  • -
  • [SMACK-207] - Parsing of messages may disconnect Smack/Spark
  • -
  • [SMACK-225] - Improper handeling of DNS SRV records
  • -
  • [SMACK-232] - Better handling of Roster error
  • -
  • [SMACK-243] - Packet with wrong date format makes Smack to disconnect
  • -
  • [SMACK-264] - fix for NPE in SASLMechanism.java
  • -
  • [SMACK-269] - Smack 3.1.0 creates a new chat for every incoming message
  • -
  • [SMACK-271] - Deadlock in XMPPConnection while login and parsing stream features
  • -
  • [SMACK-275] - Patch: Fix for broken SASL DIGEST-MD5 implementation
  • -
  • [SMACK-280] - The authentification should use the XMPPConnection#sendPacket method and work transparent with packets and packet listeners.
  • -
  • [SMACK-288] - The parsing of the result for a LeafNode.getItems() call is incorrect. It creates a DefaultPacketExtension instead of an Item for every other item in the result.
  • -
  • [SMACK-290] - Deadlock while getting Roster before it's initialized
  • -
  • [SMACK-291] - RosterGroup modifications should depend on roster push
  • -
  • [SMACK-293] - Support optional roster subscription attribute
  • -
  • [SMACK-305] - RosterEntry#getGroups causing a roster reload
  • -
  • [SMACK-308] - Multiple errors in pubsub GetItemsRequest
  • -
  • [SMACK-312] - Only fire RosterListener#entriesUpdated for RosterEntries that changed
  • -
  • [SMACK-327] - getFeatures() method on DiscoverInfo is improperly set to be package protected instead of public
  • -
  • [SMACK-328] - Number format exception while parsing dates.
  • -
  • [SMACK-332] - Smack 3.2.0b2 shows wrong version in Smack Dubugger Window
  • -
  • [SMACK-334] - Error in form for FileTransferNegotiator
  • -
- -

3.1.0 -- November 20, 2008

- -

New Features

-
    -
  • [SMACK-142] - Added support for Kerberos/NTLM. (6 votes)
  • -
  • [SMACK-210] - Added support for MD5 SASL. (1 vote)
  • -
  • [SMACK-256] - Added support for new sophisticated TLS mechanisms including SmartCard and Apple's KeychainStore.
  • -
  • [SMACK-242] - Added support for JEP-50: Ad-hoc commands.
  • -
  • [SMACK-251] - Added support for XEP-0163: Personal Eventing Protocol. (1 vote)
  • -
  • [SMACK-226] - XMLConnection can now be used with an http/socks proxy. (2 votes)
  • -
  • [SMACK-254] - Loading the Roster during login is now optional.
  • -
  • [SMACK-255] - Added ability to set mime type for avatar.
  • -
  • [SMACK-235] - Improved performance of Roster class.
  • -
  • [SMACK-241] - Updated Base64 implementation to match Openfire's.
  • -
  • [SMACK-240] - Updated Jingle implementation to newest version.
  • -
  • [SMACK-246] - Improve Jingle logging using commons-logging
  • -
  • [SMACK-244] - Updated JSTUN to 0.7.2.
  • -
  • [SMACK-259] - Updated XPP library to latest version.
  • -
- -

Bug Fixes

-
    -
  • [SMACK-231] - IBB Outputstream was not being flushed before it was closed.
  • -
  • [SMACK-236] - Renamed stanza error "unexpected-condition" to "unexpected-request".
  • -
  • [SMACK-258] - Fixed disconnection issue when parsing SASL success that contained a payload.
  • -
  • [SMACK-175] - Fixed typo in RosterPacket.ItemStatus constant.
  • -
  • [SMACK-260] - Added handling of error presence packets
  • -
- -

3.0.3 -- May 31, 2007

- -

New Features

-
    -
  • [SMACK-99] - Added support for multiple message bodies and message body languages.
  • -
  • [SMACK-218] - Implemented GSSAPI for single-sign on.
  • -
- -

Bug Fixes

-
    -
  • [SMACK-219] - The getPresence method was not working correctly with offline presence.
  • -
  • [SMACK-224] - SASL authenticion was using the XMPP domain instead of the FQDN.
  • -
- -

3.0.2 -- May 3, 2007

- -

Bug Fixes

-
    -
  • [SMACK-212] - Jingle can't establish session if only one side has a relay service
  • -
  • [SMACK-213] - RTP Bridge Resolver get wrong localhost address in certain situations
  • -
  • [SMACK-214] - Presences with a negative priority of -1 are not sending the priority to the server
  • -
- -

3.0.1 -- April 12, 2007

- -

Bug Fixes

-
    -
  • [SMACK-211] - Jingle ICE with relay sometimes closed sessions.
  • -
  • Upgraded bundled version of JSTUN.
  • -
- - -

3.0.0 -- March 31, 2007

- -

Important Changes

-
    -
  • Java 5 is now required.
  • -
  • Several API changes are not backwards compatible. In particular, connection handling has - been significantly updated, the GroupChat class has been dropped in favor of the standardized - MultiUserChat, and the Chat class has an updated API.
  • -
- -

New Features

-
    -
  • [SMACK-74] - Added support for unavailable presences with status text. (4 votes)
  • -
  • [SMACK-191] - RosterListener API improvements.
  • -
  • [SMACK-194] - Roster.getPresence(String) now considers mode after priority to determine the presence value to return.
  • -
  • [SMACK-195] - Added the ability to disconnect with a custom presence value (for offline status).
  • -
  • [SMACK-200] - Added convenience methods to Presence class.
  • -
  • [SMACK-31] - Added support for privacy lists. (4 votes)
  • -
  • [SMACK-94] - Added support for last activity of online users. (1 vote)
  • -
  • [SMACK-121] - Added support for stream errors.
  • -
  • [SMACK-136] - Added support for XEP-0048: bookmark storage.
  • -
  • [SMACK-144] - Added bookmark manager for central bookmark management.
  • -
  • [SMACK-150] - Added support for handling node features in disco.
  • -
  • [SMACK-167] - Added support for XEP-0106: JID Escaping
  • -
  • [SMACK-171] - The presence of available contacts is now changed to offline when the connection is closed.
  • -
  • [SMACK-172] - Added support for re-connection when the connection is abruptly closed.
  • -
  • [SMACK-182] - ProviderManager is now pluggable (for Eclipse ECF).
  • -
  • [SMACK-185] - Added the workgroup API to Smack.
  • -
  • [SMACK-206] - Added the option to specify the username to use for the automated test cases.
  • -
  • [SMACK-208] - Added a max queue size for outgoing packets to prevent memory issues during extreme load.
  • -
  • [SMACK-209] - Initial Jingle support implemented.
  • -
- -

Bug Fixes

-
    -
  • [SMACK-6] - Don't force use of collectors in Chat class.
  • -
  • [SMACK-10] - Flush pending packets before closing the connection. (4 votes)
  • -
  • [SMACK-51] - Use unique Thread names among connections.
  • -
  • [SMACK-54] - Add #equals and #hashCode to Occupant.
  • -
  • [SMACK-86] - Made presence checks case in-sensitive.
  • -
  • [SMACK-93] - XHTML provider wasn't handling some tags correctly.
  • -
  • [SMACK-138] - Added caching to file transfer negotiation operations.
  • -
  • [SMACK-143] - Updated XMPPError to be compliant with RFC3920.
  • -
  • [SMACK-145] - XHTML parsing could fail in some cases.
  • -
  • [SMACK-146] - DNS lookups were failing with some DNS servers.
  • -
  • [SMACK-147] - Removed invisibility presence mode.
  • -
  • [SMACK-148] - Socks 5 listening thread was not cleaning up correctly. (2 votes)
  • -
  • [SMACK-149] - Fixed possible memory leaking in PacketReader.
  • -
  • [SMACK-151] - Now use getBytes("UTF-8") instead of getBytes().
  • -
  • [SMACK-152] - The FN field is duplicated when loading vCards from the server.
  • -
  • [SMACK-153] - Optimized performance by replacing StringBuffer with StringBuilder.
  • -
  • [SMACK-154] - Fixed roster test cases that were sometimes failing.
  • -
  • [SMACK-155] - Optimized MUC performance by reducing number of packet collectors and listeners.
  • -
  • [SMACK-158] - FileTransfer isDone() method was returning true even when the transfer was refused.
  • -
  • [SMACK-159] - Filenames were not escaped for file transfers.
  • -
  • [SMACK-160] - Now use stream:feature to discover registration support.
  • -
  • [SMACK-161] - Improved connection speed.
  • -
  • [SMACK-162] - Fixed NPE in SmackConfiguration.
  • -
  • [SMACK-163] - Fixed NPE in RoomInfo when subject was null.
  • -
  • [SMACK-164] - Contact name was not being escaped.
  • -
  • [SMACK-165] - Listeners were not being removed from PacketReader.
  • -
  • [SMACK-166] - Packet reader thread was freezing when parsing an error text with no description.
  • -
  • [SMACK-168] - Fixed possible delay in PacketReader when negotiating TLS.
  • -
  • [SMACK-173] - Renamed ConnectionEstablishedListener to ConnectionCreationListener.
  • -
  • [SMACK-176] - Fixed incorrect property initialization.
  • -
  • [SMACK-177] - Removed synchronization from Roster.
  • -
  • [SMACK-178] - Added NodeInformation#getNodeIdentities() to return identities of hosted nodes
  • -
  • [SMACK-181] - Improved parsing of certificates to get signed domains.
  • -
  • [SMACK-183] - Documentation fixes.
  • -
  • [SMACK-184] - Simplified XMPPConnection constructors.
  • -
  • [SMACK-203] - NULL thread IDs would cause an error inside of the Chat Manager.
  • -
  • [SMACK-205] - Fixed PacketReader concurrency problems.
  • -
  • [SMACK-188] - Resources are now closed after reading the keystore.
  • -
  • [SMACK-189] - The listener was remaining blocked forever in some cases.
  • -
  • [SMACK-190] - Exceptions while notifying packet reader listeners was stopping the notification thread.
  • -
  • [SMACK-192] - Roster.getPresence(String) now forces use of the bare JID.
  • -
  • [SMACK-193] - New presence packets now default to a null presence mode.
  • -
  • [SMACK-196] - Now set closed to true at the start of the connection shutdown method and not the end.
  • -
  • [SMACK-197] - The source build was failing.
  • -
  • [SMACK-198] - File transfer streams were not being closed properly in some cases.
  • -
  • [SMACK-199] - MultiUserChat invitation listeners are no longer removed on disconnects.
  • -
  • [SMACK-201] - Roster no longer exposes that it implements ConnectionListener.
  • - -
- -

2.2.1 -- June 12, 2006

- -
    -
  • [SMACK-141] - Fixed SSL exception while creating new XMPPConnections. (1 vote)
  • -
  • [SMACK-127] - Fixed incorrect file transfer progress.
  • -
  • [SMACK-130] - Fixed VCard escaping problem that was crashing connections.
  • -
  • [SMACK-134] - VCards were not being saved when avatar was the only element.
  • -
  • [SMACK-131] - Illegal XML characters are now properly escaped in the presence status.
  • -
  • [SMACK-133] - Illegal XML characters are now properly escaped in groups names.
  • -
  • [SMACK-132] - Fixed IBB problem triggered when buffersize was increased.
  • -
  • [SMACK-135] - Moved to new Base64 implementation to fix line break issue in old implementation.
  • -
- -

2.2.0 -- March 9, 2006

-
    -
  • [SMACK-122] - Added support for JEP-96: File Transfer. (1 vote)
  • -
  • [SMACK-72] - Added support for JEP-47: In-Band Bytestreams. (2 votes)
  • -
  • [SMACK-122] - Added support for JEP-65: SOCKS5 Bytestreams. (1 vote)
  • -
  • [SMACK-112] - Added support for JEP-38 Stream Compression.
  • -
  • [SMACK-117] - Added support for JEP-33: Extended Stanza Addressing.
  • -
  • [SMACK-27] - Certification validation is now pluggable.
  • -
  • [SMACK-118] - Added methods to dynamically remove providers.
  • -
  • [SMACK-125] - Added support for deaf occupant in MUC rooms.
  • - -
  • [SMACK-109] - Optimized client performance. (1 vote)
  • -
  • [SMACK-113] - Added support for choosing if TLS should be used or not.
  • -
  • [SMACK-114] - Added support for choosing if SASL should be used or not.
  • -
  • [SMACK-123] - A thread is no longer used for packet writer listeners.
  • - -
  • [SMACK-110] - Resource binding and session establishment are now requested only if the server offered them.
  • -
  • [SMACK-111] - Fixed concurrency issue with date formatter.
  • -
  • [SMACK-116] - Fixed vCard issues.
  • -
  • [SMACK-119] - Fixed AccessControlException when using vCard from an applet.
  • -
  • [SMACK-120] - Listener thread was not being shutdown properly.
  • -
  • [SMACK-124] - Parsing resource binding packets was requiring smackx.jar file to be in the classpath.
  • -
  • [SMACK-97] - Fixed functional test failures in PresencePriorityTest and RosterTest.
  • -
- - - -
-
- - - From cccf3ce5f3dc5a74f5406a2b197ec0b11ad9a3e3 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 2 Mar 2022 19:30:01 +0100 Subject: [PATCH 89/91] Smack 4.4.5 --- CHANGELOG.md | 14 ++++++++++++++ version | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c42f11204..cf36d4182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Smack Changelog +# 4.4.5 -- 2022-03-02 + +### Bug + +- [SMACK-923](https://igniterealtime.atlassian.net/browse/SMACK-923) Smack reactor should immediately handle scheduled actions that are due in zero milliseconds +- [SMACK-921](https://igniterealtime.atlassian.net/browse/SMACK-921) XmlStringBuilder.attribute\(String name, Enum value\) should use value.toString\(\) \(and not value.name\(\)\) +- [SMACK-920](https://igniterealtime.atlassian.net/browse/SMACK-920) SASL GSSAPI mechanism should be marked to not require a password +- [SMACK-918](https://igniterealtime.atlassian.net/browse/SMACK-918) Self presences in MUC are no longer handled correctly + +### Improvement + +- [SMACK-922](https://igniterealtime.atlassian.net/browse/SMACK-922) Support 'optional text' and arbitrary element in Jingle 'reason' element +- [SMACK-919](https://igniterealtime.atlassian.net/browse/SMACK-919) PubSub ItemProvider should ignore character data in s + ## 4.4.4 -- 2021-11-01 ### Bug diff --git a/version b/version index dd9c2fc4f..fa1ba0458 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.5-SNAPSHOT +4.4.5 From ea601d0f9f03cd07ce8ca1e7d9e3fad7a346456a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 2 Mar 2022 22:41:29 +0100 Subject: [PATCH 90/91] Smack 4.5.0-alpha1 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 07b8ab2a4..80042f254 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.5.0-alpha1-SNAPSHOT +4.5.0-alpha1 From 01cd0507b6ca1473a3ec3d30bd56dfbd6bb19e55 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 2 Mar 2022 22:54:05 +0100 Subject: [PATCH 91/91] Smack 4.5.0-alpha2-SNAPSHOT --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 80042f254..891d39303 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.5.0-alpha1 +4.5.0-alpha2-SNAPSHOT