mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-22 10:37:59 +01:00
Merge branch '4.4'
This commit is contained in:
commit
b857f33ac3
10 changed files with 82 additions and 14 deletions
|
@ -590,7 +590,7 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
||||||
public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) {
|
public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment);
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment);
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
xml.escape(message);
|
xml.text(message);
|
||||||
xml.closeElement(getElementName());
|
xml.closeElement(getElementName());
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2019 Florian Schmaus.
|
* Copyright 2015-2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -142,7 +142,9 @@ public final class StandardExtensionElement implements ExtensionElement {
|
||||||
}
|
}
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
xml.optEscape(text);
|
if (text != null) {
|
||||||
|
xml.text(text);
|
||||||
|
}
|
||||||
|
|
||||||
if (elements != null) {
|
if (elements != null) {
|
||||||
for (Map.Entry<QName, StandardExtensionElement> entry : elements.entrySet()) {
|
for (Map.Entry<QName, StandardExtensionElement> entry : elements.entrySet()) {
|
||||||
|
|
|
@ -293,6 +293,30 @@ public class ArrayBlockingQueueWithShutdown<E> extends AbstractQueue<E> implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put if the queue has not been shutdown yet.
|
||||||
|
*
|
||||||
|
* @param e the element to put into the queue.
|
||||||
|
* @return <code>true</code> if the element has been put into the queue, <code>false</code> if the queue was shutdown.
|
||||||
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public boolean putIfNotShutdown(E e) throws InterruptedException {
|
||||||
|
checkNotNull(e);
|
||||||
|
lock.lockInterruptibly();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isShutdown) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
putInternal(e, true);
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void putAll(Collection<? extends E> elements) throws InterruptedException {
|
public void putAll(Collection<? extends E> elements) throws InterruptedException {
|
||||||
checkNotNull(elements);
|
checkNotNull(elements);
|
||||||
lock.lockInterruptibly();
|
lock.lockInterruptibly();
|
||||||
|
|
|
@ -456,6 +456,13 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder text(CharSequence text) {
|
||||||
|
assert text != null;
|
||||||
|
CharSequence escapedText = StringUtils.escapeForXmlText(text);
|
||||||
|
sb.append(escapedText);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder escape(String text) {
|
public XmlStringBuilder escape(String text) {
|
||||||
assert text != null;
|
assert text != null;
|
||||||
sb.append(StringUtils.escapeForXml(text));
|
sb.append(StringUtils.escapeForXml(text));
|
||||||
|
|
|
@ -206,4 +206,17 @@ public class MessageTest {
|
||||||
|
|
||||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that only required characters are XML escaped in body.
|
||||||
|
*
|
||||||
|
* @see <a href="https://issues.igniterealtime.org/browse/SMACK-892">SMACK-892</a>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void escapeInBodyTest() {
|
||||||
|
String theFive = "\"'<>&";
|
||||||
|
Message.Body body = new Message.Body(null, theFive);
|
||||||
|
|
||||||
|
assertEquals("<body xmlns='jabber:client'>\"'<>&</body>", body.toXML().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -35,7 +35,7 @@ public abstract class AbstractJsonPacketExtension implements ExtensionElement {
|
||||||
public final XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public final XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
xml.append(json);
|
xml.text(json);
|
||||||
xml.closeElement(this);
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import org.jxmpp.jid.impl.JidCreate;
|
||||||
public class MarkableExtensionTest {
|
public class MarkableExtensionTest {
|
||||||
|
|
||||||
String markableMessageStanza = "<message xmlns='jabber:client' to='ingrichard@royalty.england.lit/throne' id='message-1'>"
|
String markableMessageStanza = "<message xmlns='jabber:client' to='ingrichard@royalty.england.lit/throne' id='message-1'>"
|
||||||
+ "<body>My lord, dispatch; read o'er these articles.</body>"
|
+ "<body>My lord, dispatch; read o'er these articles.</body>"
|
||||||
+ "<markable xmlns='urn:xmpp:chat-markers:0'/>" + "</message>";
|
+ "<markable xmlns='urn:xmpp:chat-markers:0'/>" + "</message>";
|
||||||
|
|
||||||
String markableExtension = "<markable xmlns='urn:xmpp:chat-markers:0'/>";
|
String markableExtension = "<markable xmlns='urn:xmpp:chat-markers:0'/>";
|
||||||
|
|
|
@ -8,7 +8,7 @@ dependencies {
|
||||||
compile project(':smack-extensions')
|
compile project(':smack-extensions')
|
||||||
compile project(':smack-experimental')
|
compile project(':smack-experimental')
|
||||||
|
|
||||||
api 'org.pgpainless:pgpainless-core:0.0.1-alpha11'
|
api 'org.pgpainless:pgpainless-core:0.1.0'
|
||||||
|
|
||||||
testImplementation "org.bouncycastle:bcprov-jdk15on:${bouncyCastleVersion}"
|
testImplementation "org.bouncycastle:bcprov-jdk15on:${bouncyCastleVersion}"
|
||||||
|
|
||||||
|
|
|
@ -1592,7 +1592,12 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSmAcknowledgementInternal() throws NotConnectedException, InterruptedException {
|
private void sendSmAcknowledgementInternal() throws NotConnectedException, InterruptedException {
|
||||||
packetWriter.sendStreamElement(new AckAnswer(clientHandledStanzasCount));
|
AckAnswer ackAnswer = new AckAnswer(clientHandledStanzasCount);
|
||||||
|
// Do net put an ack to the queue if it has already been shutdown. Some servers, like ejabberd, like to request
|
||||||
|
// an ack even after we have send a stream close (and hance the queue was shutdown). If we would not check here,
|
||||||
|
// then the ack would dangle around in the queue, and be send on the next re-connection attempt even before the
|
||||||
|
// stream open.
|
||||||
|
packetWriter.queue.putIfNotShutdown(ackAnswer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -500,9 +500,9 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
|
||||||
pendingInputFilterData = false;
|
pendingInputFilterData = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have successfully read something. It is now possible that a filter is now also able to write
|
|
||||||
// additional data (for example SSLEngine).
|
|
||||||
if (pendingWriteInterestAfterRead) {
|
if (pendingWriteInterestAfterRead) {
|
||||||
|
// We have successfully read something and someone announced a write interest after a read. It is
|
||||||
|
// now possible that a filter is now also able to write additional data (for example SSLEngine).
|
||||||
pendingWriteInterestAfterRead = false;
|
pendingWriteInterestAfterRead = false;
|
||||||
newInterestedOps |= SelectionKey.OP_WRITE;
|
newInterestedOps |= SelectionKey.OP_WRITE;
|
||||||
}
|
}
|
||||||
|
@ -1048,12 +1048,15 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer input(ByteBuffer inputData) throws SSLException {
|
public ByteBuffer input(ByteBuffer inputData) throws SSLException {
|
||||||
ByteBuffer accumulatedData;
|
ByteBuffer accumulatedData;
|
||||||
if (pendingInputData == null) {
|
if (pendingInputData == null) {
|
||||||
accumulatedData = inputData;
|
accumulatedData = inputData;
|
||||||
} else {
|
} else {
|
||||||
|
assert pendingInputData != inputData;
|
||||||
|
|
||||||
int accumulatedDataBytes = pendingInputData.remaining() + inputData.remaining();
|
int accumulatedDataBytes = pendingInputData.remaining() + inputData.remaining();
|
||||||
accumulatedData = ByteBuffer.allocate(accumulatedDataBytes);
|
accumulatedData = ByteBuffer.allocate(accumulatedDataBytes);
|
||||||
accumulatedData.put(pendingInputData)
|
accumulatedData.put(pendingInputData)
|
||||||
|
@ -1084,18 +1087,25 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
|
||||||
SSLEngineResult.HandshakeStatus handshakeStatus = handleHandshakeStatus(result);
|
SSLEngineResult.HandshakeStatus handshakeStatus = handleHandshakeStatus(result);
|
||||||
switch (handshakeStatus) {
|
switch (handshakeStatus) {
|
||||||
case NEED_TASK:
|
case NEED_TASK:
|
||||||
// A delegated task is asynchronously running. Signal that there is pending input data and
|
// A delegated task is asynchronously running. Take care of the remaining accumulatedData.
|
||||||
// cycle again through the smack reactor.
|
|
||||||
addAsPendingInputData(accumulatedData);
|
addAsPendingInputData(accumulatedData);
|
||||||
break;
|
// Return here, as the async task created by handleHandshakeStatus will continue calling the
|
||||||
|
// cannelSelectedCallback.
|
||||||
|
return null;
|
||||||
case NEED_UNWRAP:
|
case NEED_UNWRAP:
|
||||||
continue;
|
continue;
|
||||||
case NEED_WRAP:
|
case NEED_WRAP:
|
||||||
// NEED_WRAP means that the SSLEngine needs to send data, probably without consuming data.
|
// NEED_WRAP means that the SSLEngine needs to send data, probably without consuming data.
|
||||||
// We exploit here the fact that the channelSelectedCallback is single threaded and that the
|
// We exploit here the fact that the channelSelectedCallback is single threaded and that the
|
||||||
// input processing is after the output processing.
|
// input processing is after the output processing.
|
||||||
|
addAsPendingInputData(accumulatedData);
|
||||||
|
// Note that it is ok that we the provided argument for pending input filter data to channel
|
||||||
|
// selected callback is false, as setPendingInputFilterData() will have set the internal state
|
||||||
|
// boolean accordingly.
|
||||||
connectionInternal.asyncGo(() -> callChannelSelectedCallback(false, true));
|
connectionInternal.asyncGo(() -> callChannelSelectedCallback(false, true));
|
||||||
break;
|
// Do not break here, but instead return and let the asynchronously invoked
|
||||||
|
// callChannelSelectedCallback() do its work.
|
||||||
|
return null;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1127,8 +1137,15 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAsPendingInputData(ByteBuffer byteBuffer) {
|
private void addAsPendingInputData(ByteBuffer byteBuffer) {
|
||||||
|
// Note that we can not simply write
|
||||||
|
// pendingInputData = byteBuffer;
|
||||||
|
// we have to copy the provided byte buffer, because it is possible that this byteBuffer is re-used by some
|
||||||
|
// higher layer. That is, here 'byteBuffer' is typically 'incomingBuffer', which is a direct buffer only
|
||||||
|
// allocated once per connection for performance reasons and hence re-used for read() calls.
|
||||||
pendingInputData = ByteBuffer.allocate(byteBuffer.remaining());
|
pendingInputData = ByteBuffer.allocate(byteBuffer.remaining());
|
||||||
pendingInputData.put(byteBuffer).flip();
|
pendingInputData.put(byteBuffer).flip();
|
||||||
|
|
||||||
|
pendingInputFilterData = pendingInputData.hasRemaining();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SSLEngineResult.HandshakeStatus handleHandshakeStatus(SSLEngineResult sslEngineResult) {
|
private SSLEngineResult.HandshakeStatus handleHandshakeStatus(SSLEngineResult sslEngineResult) {
|
||||||
|
|
Loading…
Reference in a new issue