1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 10:19:41 +02:00

Smack 4.4.0

-----BEGIN PGP SIGNATURE-----
 
 iQGTBAABCgB9FiEEl3UFnzoh3OFr5PuuIjmn6PWFIFIFAl/Msl5fFIAAAAAALgAo
 aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDk3
 NzUwNTlGM0EyMURDRTE2QkU0RkJBRTIyMzlBN0U4RjU4NTIwNTIACgkQIjmn6PWF
 IFIq8gf9GZp0Cl9Gr+seA/C8nczE/cNRKfHnDR224klbjlGrnojb+bL2QgeKG6wN
 u/eL3+UMus8uw3+R8NKARawqt/r6mbk4bgdGEbzoByLO6iiPYTr7TuZmTOQyZa5L
 tMNkFIvfvMzDeDDqI4z4uTgT3s7YBg0d4BT7cL8T6RaZGHF57OxqhWnKxMuaiTXH
 HJzVTPrtAcYg17QZl5+mrhiWyynE5+4QGeluYHlh7vvsniwNOqrwO1vWHg2BX003
 DjaHK+6oLjKxVljCklXxE7Wsx0VcMrBfv2qZwW3Q4ZzZVcrAn8TC4HrUtR8DL176
 DpGlEhmmdr3xKKX0JQ9RmIod+xSabw==
 =4wGX
 -----END PGP SIGNATURE-----

Merge tag '4.4.0'

Smack 4.4.0
This commit is contained in:
Florian Schmaus 2020-12-06 12:16:44 +01:00
commit d8642847ea
18 changed files with 403 additions and 81 deletions

View file

@ -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();
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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();
}
/**

View file

@ -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<Data, IQ>() {
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"));
}
/**

View file

@ -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());
}