From a4bb5bfda86ff8fdd1ea8df551a8c4c523cec081 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 3 Dec 2020 08:53:19 +0100 Subject: [PATCH 1/2] [ibb] Use UInt16 for 'seq' and fix its handling Fixes a off-by-one error when incrementing 'seq'. Thanks to Kim Alvefur for spotting this. --- .../jivesoftware/smack/datatypes/Scalar.java | 13 +++- .../jivesoftware/smack/datatypes/UInt16.java | 31 +++++++- .../jivesoftware/smack/datatypes/UInt32.java | 24 ++++++- .../smack/parsing/SmackParsingException.java | 24 +++++++ .../smack/util/DefaultCharSequence.java | 36 ++++++++++ .../jivesoftware/smack/util/ParserUtils.java | 9 +++ .../smack/util/XmlStringBuilder.java | 14 ++++ .../smack/datatypes/UInt16Test.java | 30 ++++++++ .../smack/datatypes/UInt32Test.java | 30 ++++++++ .../ibb/InBandBytestreamSession.java | 70 ++++++++++++------- .../ibb/packet/DataPacketExtension.java | 27 ++++--- .../ibb/provider/DataPacketProvider.java | 7 +- .../mediaelement/element/MediaElement.java | 6 +- .../packet/ValidateElement.java | 4 +- .../InBandBytestreamSessionMessageTest.java | 17 ++--- .../ibb/InBandBytestreamSessionTest.java | 34 +++------ .../ibb/packet/DataPacketExtensionTest.java | 2 +- 17 files changed, 297 insertions(+), 81 deletions(-) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/DefaultCharSequence.java create mode 100644 smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt16Test.java create mode 100644 smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt32Test.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/Scalar.java b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/Scalar.java index 9891c2e42..205b1a134 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/Scalar.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/Scalar.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ */ package org.jivesoftware.smack.datatypes; -public abstract class Scalar extends java.lang.Number { +import org.jivesoftware.smack.util.DefaultCharSequence; + +public abstract class Scalar extends java.lang.Number implements DefaultCharSequence { /** * @@ -83,4 +85,11 @@ public abstract class Scalar extends java.lang.Number { public final String toString() { return number.toString(); } + + public abstract Scalar getMinValue(); + + public abstract Scalar getMaxValue(); + + public abstract Scalar incrementedByOne(); + } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java index 22dd8cd8d..271de8cbc 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2020 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,12 +21,18 @@ import org.jivesoftware.smack.util.NumberUtil; /** * A number representing an unsigned 16-bit integer. Can be used for values with the XML schema type "xs:unsingedShort". */ -public final class UInt16 extends Scalar { +public final class UInt16 extends Scalar implements Comparable { private static final long serialVersionUID = 1L; private final int number; + public static final int MIN_VALUE_INT = 0; + public static final int MAX_VALUE_INT = (1 << 16) - 1; + + public static final UInt16 MIN_VALUE = UInt16.from(MIN_VALUE_INT); + public static final UInt16 MAX_VALUE = UInt16.from(MAX_VALUE_INT); + private UInt16(int number) { super(NumberUtil.requireUShort16(number)); this.number = number; @@ -54,4 +60,25 @@ public final class UInt16 extends Scalar { return super.equals(other); } + + @Override + public int compareTo(UInt16 o) { + return Integer.compare(number, o.number); + } + + @Override + public UInt16 getMinValue() { + return MIN_VALUE; + } + + @Override + public UInt16 getMaxValue() { + return MAX_VALUE; + } + + @Override + public UInt16 incrementedByOne() { + int incrementedValue = number < MAX_VALUE_INT ? number + 1 : 0; + return UInt16.from(incrementedValue); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt32.java b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt32.java index e3e6bc670..68cfb2fe1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt32.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt32.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2020 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,6 +27,12 @@ public final class UInt32 extends Scalar { private final long number; + public static final long MIN_VALUE_LONG = 0; + public static final long MAX_VALUE_LONG = (1L << 32) - 1; + + public static final UInt32 MIN_VALUE = UInt32.from(MAX_VALUE_LONG); + public static final UInt32 MAX_VALUE = UInt32.from(MAX_VALUE_LONG); + private UInt32(long number) { super(NumberUtil.requireUInt32(number)); this.number = number; @@ -55,4 +61,20 @@ public final class UInt32 extends Scalar { return super.equals(other); } + + @Override + public UInt32 getMinValue() { + return MIN_VALUE; + } + + @Override + public UInt32 getMaxValue() { + return MAX_VALUE; + } + + @Override + public UInt32 incrementedByOne() { + long incrementedValue = number < MAX_VALUE_LONG ? number + 1 : 0; + return UInt32.from(incrementedValue); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/parsing/SmackParsingException.java b/smack-core/src/main/java/org/jivesoftware/smack/parsing/SmackParsingException.java index 08b4586fb..518990780 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/parsing/SmackParsingException.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/parsing/SmackParsingException.java @@ -55,4 +55,28 @@ public class SmackParsingException extends Exception { super(uriSyntaxException); } } + + public static class RequiredValueMissingException extends SmackParsingException { + /** + * + */ + private static final long serialVersionUID = 1L; + + public RequiredValueMissingException(String message) { + super(message); + } + + } + + public static class RequiredAttributeMissingException extends RequiredValueMissingException { + /** + * + */ + private static final long serialVersionUID = 1L; + + public RequiredAttributeMissingException(String attributeName) { + super("The required attribute '" + attributeName + "' is missing (or has the empty String as value)"); + } + + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/DefaultCharSequence.java b/smack-core/src/main/java/org/jivesoftware/smack/util/DefaultCharSequence.java new file mode 100644 index 000000000..27f95f538 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/DefaultCharSequence.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2020 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 interface DefaultCharSequence extends CharSequence { + + @Override + default int length() { + return toString().length(); + } + + @Override + default char charAt(int index) { + return toString().charAt(index); + } + + @Override + default CharSequence subSequence(int start, int end) { + return toString().subSequence(start, end); + } + +} 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 4b038cf8f..8f67c5ccf 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 @@ -29,6 +29,7 @@ import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.datatypes.UInt32; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.parsing.SmackParsingException.RequiredAttributeMissingException; import org.jivesoftware.smack.parsing.SmackParsingException.SmackTextParseException; import org.jivesoftware.smack.parsing.SmackParsingException.SmackUriSyntaxParsingException; import org.jivesoftware.smack.xml.XmlPullParser; @@ -231,6 +232,14 @@ public class ParserUtils { return UInt16.from(integer); } + public static UInt16 getRequiredUInt16Attribute(XmlPullParser parser, String name) throws RequiredAttributeMissingException { + UInt16 uint16 = getUInt16Attribute(parser, name); + if (uint16 == null) { + throw new SmackParsingException.RequiredAttributeMissingException(name); + } + return uint16; + } + public static int getIntegerFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException { String intString = parser.nextText(); return Integer.valueOf(intString); 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 f32e8dc7c..629b0b969 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 @@ -362,6 +362,20 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element { return this; } + /** + * Same as {@link #optAttribute(String, CharSequence)}, but with a different method name. This method can be used if + * the provided attribute value argument type causes ambiguity in method overloading. For example if the type is a + * subclass of Number and CharSequence. + * + * @param name the name of the attribute. + * @param value the value of the attribute. + * @return a reference to this object. + * @since 4.5 + */ + public XmlStringBuilder optAttributeCs(String name, CharSequence value) { + return optAttribute(name, value); + } + /** * Add the given attribute if {@code value => 0}. * diff --git a/smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt16Test.java b/smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt16Test.java new file mode 100644 index 000000000..e6c72e6ad --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt16Test.java @@ -0,0 +1,30 @@ +/** + * + * Copyright 2020 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.datatypes; + +import static org.junit.Assert.assertEquals; + +import org.junit.jupiter.api.Test; + +public class UInt16Test { + + @Test + public void testMaxValue() { + assertEquals(65535, UInt16.MAX_VALUE_INT); + } + +} diff --git a/smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt32Test.java b/smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt32Test.java new file mode 100644 index 000000000..c08414961 --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/datatypes/UInt32Test.java @@ -0,0 +1,30 @@ +/** + * + * Copyright 2020 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.datatypes; + +import static org.junit.Assert.assertEquals; + +import org.junit.jupiter.api.Test; + +public class UInt32Test { + + @Test + public void testMaxValue() { + assertEquals(4294967295L, UInt32.MAX_VALUE_LONG); + } + +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java index c629ab4d0..df783618f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java @@ -23,11 +23,14 @@ import java.net.SocketTimeoutException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; @@ -61,6 +64,10 @@ import org.jxmpp.jid.Jid; */ public class InBandBytestreamSession implements BytestreamSession { + private static final Logger LOGGER = Logger.getLogger(InBandBytestreamSession.class.getName()); + + static final String UNEXPECTED_IBB_SEQUENCE = "Unexpected IBB sequence"; + /* XMPP connection */ private final XMPPConnection connection; @@ -261,7 +268,7 @@ public class InBandBytestreamSession implements BytestreamSession { private int bufferPointer = -1; /* data packet sequence (range from 0 to 65535) */ - private long seq = -1; + private UInt16 expectedSeq = UInt16.MIN_VALUE; /* flag to indicate if input stream is closed */ private boolean isClosed = false; @@ -383,21 +390,16 @@ public class InBandBytestreamSession implements BytestreamSession { return false; } - // handle sequence overflow - if (this.seq == 65535) { - this.seq = -1; - } - + final UInt16 dataSeq = data.getSeq(); // check if data packets sequence is successor of last seen sequence - long seq = data.getSeq(); - if (seq - 1 != this.seq) { + if (!expectedSeq.equals(dataSeq)) { // packets out of order; close stream/session InBandBytestreamSession.this.close(); - throw new IOException("Packets out of sequence"); - } - else { - this.seq = seq; + String message = UNEXPECTED_IBB_SEQUENCE + " " + dataSeq + " received, expected " + + expectedSeq; + throw new IOException(message); } + expectedSeq = dataSeq.incrementedByOne(); // set buffer to decoded data buffer = data.getDecodedData(); @@ -465,22 +467,41 @@ public class InBandBytestreamSession implements BytestreamSession { protected StanzaListener getDataPacketListener() { return new StanzaListener() { - private long lastSequence = -1; + private UInt16 expectedSequence = UInt16.MIN_VALUE;; @Override public void processStanza(Stanza packet) throws NotConnectedException, InterruptedException { + final Data dataIq = (Data) packet; // get data packet extension - DataPacketExtension data = ((Data) packet).getDataPacketExtension(); + DataPacketExtension data = dataIq.getDataPacketExtension(); + final UInt16 seq = data.getSeq(); /* * check if sequence was not used already (see XEP-0047 Section 2.2) */ - if (data.getSeq() <= this.lastSequence) { - IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet, - StanzaError.Condition.unexpected_request); + if (!expectedSequence.equals(seq)) { + String descriptiveEnTest = UNEXPECTED_IBB_SEQUENCE + " " + seq + " received, expected " + + expectedSequence; + StanzaError stanzaError = StanzaError.getBuilder() + .setCondition(StanzaError.Condition.unexpected_request) + .setDescriptiveEnText(descriptiveEnTest) + .build(); + IQ unexpectedRequest = IQ.createErrorResponse(dataIq, stanzaError); connection.sendStanza(unexpectedRequest); - return; + try { + // TODO: It would be great if close would take a "close error reason" argument. Also there + // is the question if this is really a reason to close the stream. We could have some more + // tolerance regarding out-of-sequence stanzas arriving: Even though XMPP has the in-order + // guarantee, I could imagine that there are cases where stanzas are, for example, + // duplicated because of stream resumption. + close(); + } catch (IOException e) { + LOGGER.log(Level.FINER, "Could not close session, because of IOException. Close reason: " + + descriptiveEnTest); + } + + return; } // check if encoded data is valid (see XEP-0047 Section 2.2) @@ -492,19 +513,14 @@ public class InBandBytestreamSession implements BytestreamSession { return; } + expectedSequence = seq.incrementedByOne(); + // data is valid; add to data queue dataQueue.offer(data); // confirm IQ IQ confirmData = IQ.createResultIQ((IQ) packet); connection.sendStanza(confirmData); - - // set last seen sequence - this.lastSequence = data.getSeq(); - if (this.lastSequence == 65535) { - this.lastSequence = -1; - } - } }; @@ -618,7 +634,7 @@ public class InBandBytestreamSession implements BytestreamSession { protected int bufferPointer = 0; /* data packet sequence (range from 0 to 65535) */ - protected long seq = 0; + protected UInt16 seq = UInt16.from(0); /* flag to indicate if output stream is closed */ protected boolean isClosed = false; @@ -756,7 +772,7 @@ public class InBandBytestreamSession implements BytestreamSession { bufferPointer = 0; // increment sequence, considering sequence overflow - this.seq = this.seq + 1 == 65535 ? 0 : this.seq + 1; + seq = seq.incrementedByOne(); } 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 e885c9013..532ac9a11 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 @@ -18,8 +18,10 @@ package org.jivesoftware.smackx.bytestreams.ibb.packet; import javax.xml.namespace.QName; +import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.IQ.IQChildElementXmlStringBuilder; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.stringencoder.Base64; @@ -47,7 +49,7 @@ public class DataPacketExtension implements ExtensionElement { private final String sessionID; /* sequence of this packet in regard to the other data packets */ - private final long seq; + private final UInt16 seq; /* the data contained in this packet */ private final String data; @@ -60,19 +62,28 @@ public class DataPacketExtension implements ExtensionElement { * @param sessionID unique session ID identifying this In-Band Bytestream * @param seq sequence of this stanza in regard to the other data packets * @param data the base64 encoded data contained in this packet + * @throws IllegalArgumentException if seq is not within the range [0, 65535]. */ - public DataPacketExtension(String sessionID, long seq, String data) { + public DataPacketExtension(String sessionID, int seq, String data) { + this(sessionID, UInt16.from(seq), data); + } + + /** + * Creates a new In-Band Bytestream data packet. + * + * @param sessionID unique session ID identifying this In-Band Bytestream + * @param seq sequence of this stanza in regard to the other data packets + * @param data the base64 encoded data contained in this packet + */ + public DataPacketExtension(String sessionID, UInt16 seq, String data) { if (sessionID == null || "".equals(sessionID)) { throw new IllegalArgumentException("Session ID must not be null or empty"); } - if (seq < 0 || seq > 65535) { - throw new IllegalArgumentException("Sequence must not be between 0 and 65535"); - } if (data == null) { throw new IllegalArgumentException("Data must not be null"); } this.sessionID = sessionID; - this.seq = seq; + this.seq = Objects.requireNonNull(seq); this.data = data; } @@ -90,7 +101,7 @@ public class DataPacketExtension implements ExtensionElement { * * @return the sequence of this stanza in regard to the other data packets. */ - public long getSeq() { + public UInt16 getSeq() { return seq; } @@ -148,7 +159,7 @@ public class DataPacketExtension implements ExtensionElement { } protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { - xml.attribute("seq", Long.toString(seq)); + xml.attribute("seq", seq); xml.attribute("sid", sessionID); xml.rightAngleBracket(); xml.append(data); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java index 6db9ed434..3213a1470 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java @@ -18,8 +18,11 @@ package org.jivesoftware.smackx.bytestreams.ibb.provider; import java.io.IOException; +import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.parsing.SmackParsingException.RequiredAttributeMissingException; +import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -51,9 +54,9 @@ public class DataPacketProvider { @Override public DataPacketExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, - IOException { + IOException, RequiredAttributeMissingException { String sessionID = parser.getAttributeValue("", "sid"); - long seq = Long.parseLong(parser.getAttributeValue("", "seq")); + UInt16 seq = ParserUtils.getRequiredUInt16Attribute(parser, "seq"); String data = parser.nextText(); return new DataPacketExtension(sessionID, seq, data); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/mediaelement/element/MediaElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/mediaelement/element/MediaElement.java index f82e47c40..406d65b7c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/mediaelement/element/MediaElement.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/mediaelement/element/MediaElement.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,8 +83,8 @@ public class MediaElement implements FormFieldChildElement { @Override public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); - xml.optAttribute("height", height) - .optAttribute("width", width) + xml.optAttributeCs("height", height) + .optAttributeCs("width", width) .rightAngleBracket(); xml.append(uris); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java index aef326c39..23bd29096 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java @@ -401,8 +401,8 @@ public abstract class ValidateElement implements FormFieldChildElement { @Override public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) { XmlStringBuilder buf = new XmlStringBuilder(this, enclosingXmlEnvironment); - buf.optAttribute("min", getMin()); - buf.optAttribute("max", getMax()); + buf.optAttributeCs("min", getMin()); + buf.optAttributeCs("max", getMax()); buf.closeEmptyElement(); return buf; } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java index 678ede38f..c6b46fa32 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java @@ -17,8 +17,8 @@ package org.jivesoftware.smackx.bytestreams.ibb; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; @@ -102,7 +102,7 @@ public class InBandBytestreamSessionMessageTest extends SmackTestSuite { public void verify(Message request, IQ response) { DataPacketExtension dpe = request.getExtension( DataPacketExtension.class); - assertEquals(lastSeq++, dpe.getSeq()); + assertEquals(lastSeq++, dpe.getSeq().longValue()); } }; @@ -275,16 +275,13 @@ public class InBandBytestreamSessionMessageTest extends SmackTestSuite { listener.processStanza(dataMessage); // read until exception is thrown - try { - inputStream.read(); - fail("exception should be thrown"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("Packets out of sequence")); - } + IOException ioException = assertThrows(IOException.class, () -> + inputStream.read() + ); + String ioExceptionMessage = ioException.getMessage(); + assertTrue(ioExceptionMessage.startsWith(InBandBytestreamSession.UNEXPECTED_IBB_SEQUENCE)); protocol.verifyAll(); - } /** diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java index 8a861cc32..56f3302b5 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java @@ -17,6 +17,7 @@ package org.jivesoftware.smackx.bytestreams.ibb; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -29,6 +30,7 @@ import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.test.util.SmackTestSuite; @@ -96,11 +98,11 @@ public class InBandBytestreamSessionTest extends SmackTestSuite { incrementingSequence = new Verification() { - long lastSeq = 0; + int lastSeq = 0; @Override public void verify(Data request, IQ response) { - assertEquals(lastSeq++, request.getDataPacketExtension().getSeq()); + assertEquals(lastSeq++, request.getDataPacketExtension().getSeq().intValue()); } }; @@ -266,9 +268,9 @@ public class InBandBytestreamSessionTest extends SmackTestSuite { @Override public void verify(Data request, IQ response) { byte[] decodedData = request.getDataPacketExtension().getDecodedData(); - int seq = (int) request.getDataPacketExtension().getSeq(); + UInt16 seq = request.getDataPacketExtension().getSeq(); for (int i = 0; i < decodedData.length; i++) { - assertEquals(controlData[(seq * blockSize) + i], decodedData[i]); + assertEquals(controlData[(seq.intValue() * blockSize) + i], decodedData[i]); } } @@ -441,15 +443,6 @@ public class InBandBytestreamSessionTest extends SmackTestSuite { */ @Test public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception { - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - - // confirm data packet with invalid sequence - protocol.addResponse(resultIQ); - - // confirm close request - protocol.addResponse(resultIQ, Verification.requestTypeSET, - Verification.correspondingSenderReceiver); - // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); @@ -465,16 +458,11 @@ public class InBandBytestreamSessionTest extends SmackTestSuite { listener.processStanza(data); // read until exception is thrown - try { - inputStream.read(); - fail("exception should be thrown"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("Packets out of sequence")); - } - - protocol.verifyAll(); - + IOException ioException = assertThrows(IOException.class, () -> + inputStream.read() + ); + String ioExceptionMessage = ioException.getMessage(); + assertTrue(ioExceptionMessage.equals("Stream is closed")); } /** diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java index beab45d02..65fb067b4 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java @@ -75,7 +75,7 @@ public class DataPacketExtensionTest extends SmackTestSuite { public void shouldSetAllFieldsCorrectly() { DataPacketExtension data = new DataPacketExtension("sessionID", 0, "data"); assertEquals("sessionID", data.getSessionID()); - assertEquals(0, data.getSeq()); + assertEquals(0, data.getSeq().intValue()); assertEquals("data", data.getData()); } From d8864b62ca61b78ec5dd594fa853bf9101b2223e Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 6 Dec 2020 11:28:41 +0100 Subject: [PATCH 2/2] Smack 4.4.0 --- resources/releasedocs/changelog.html | 106 +++++++++++++++++++++++++++ version | 2 +- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html index e11abe56a..3d872c85f 100644 --- a/resources/releasedocs/changelog.html +++ b/resources/releasedocs/changelog.html @@ -141,6 +141,112 @@ hr {
+ + +

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 diff --git a/version b/version index 1a50af830..fdc669880 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-rc3-SNAPSHOT +4.4.0