1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-09-13 19:19:38 +02:00

Compare commits

...

21 commits

Author SHA1 Message Date
Florian Schmaus
5782fff2a4 Merge branch '4.4' 2020-09-23 21:42:51 +02:00
Florian Schmaus
6d39a4e3ac [bob] Add BoBDataExtension, remove BoBExtension
BoBExtension extending XHTMLExtension was poorly designed and only
worked for a single paragraphy.

Fixes SMACK-770.
2020-09-23 19:57:13 +02:00
Florian Schmaus
15e3d267f6 Add Pair utility class 2020-09-23 19:46:01 +02:00
Florian Schmaus
048226960b Rename smack-java7 to smack-java8
Fixes SMACK-854.
2020-09-23 17:50:11 +02:00
Florian Schmaus
fe7d3bec30 Make Forwarded a generic type
Fixes SMACK-821.
2020-09-23 17:48:04 +02:00
Florian Schmaus
c1b32f8e11 [carbons] Throw SmackParsingException instead of IOException 2020-09-23 17:47:04 +02:00
Florian Schmaus
b857f33ac3 Merge branch '4.4' 2020-09-20 14:12:37 +02:00
Florian Schmaus
02341f6330 Smack 4.4.0-beta3-SNAPSHOT 2020-09-20 14:10:53 +02:00
Florian Schmaus
6837c305e8 Smack 4.4.0-beta2 2020-09-20 13:57:09 +02:00
Florian Schmaus
4db7d787f7 [tcp] Add code comment why we have to copy the ByteBuffer 2020-09-20 13:53:13 +02:00
Florian Schmaus
08fc0ba0b4 [tcp] Improve pendingWriteInterestAfterRead code comment 2020-09-20 13:06:02 +02:00
Florian Schmaus
525ee09ea1 [tcp] Do not send SM ack after we send a </stream:stream>
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.

See the following trace of the MUC bookmarks integration test. The
fact that it is a MUC test does not matter, but this test does
disconnect the connection and reconnect it. Not how the server,
ejabberd in this case, requests an SM ack by sending an <r/> even
though we already send the </stream:stream>:

22:22:05 SENT (4):
<iq id='MD4UC-61' type='set'>
  <query xmlns='jabber:iq:private'>
    <storage xmlns='storage:bookmarks'>
      <conference name='Smack Inttest: 7in7j' autojoin='true' jid='y9jcn5@conference.salem.geekplace.eu'>
        <nick>
          Nick-P2VXD7
        </nick>
      </conference>
    </storage>
  </query>
</iq>
22:22:05 RECV (4):
<r xmlns='urn:xmpp:sm:3'/>
22:22:05 SENT (4):
<a xmlns='urn:xmpp:sm:3' h='29'/>
22:22:05 RECV (4):
<message to='sinttest-7in7j-4@salem.geekplace.eu' from='sinttest-7in7j-4@salem.geekplace.eu' type='headline'>
  <event xmlns='http://jabber.org/protocol/pubsub#event'>
    <items node='storage:bookmarks'>
      <item id='current'>
        <storage xmlns='storage:bookmarks'>
          <conference name='Smack Inttest: 7in7j' autojoin='true' jid='y9jcn5@conference.salem.geekplace.eu'>
            <nick>
              Nick-P2VXD7
            </nick>
          </conference>
        </storage>
      </item>
    </items>
  </event>
  <addresses xmlns='http://jabber.org/protocol/address'>
    <address jid='sinttest-7in7j-4@salem.geekplace.eu/1415073683806426185213090' type='replyto'/>
  </addresses>
</message>
22:22:05 RECV (4):
<iq xml:lang='en-US' to='sinttest-7in7j-4@salem.geekplace.eu/1415073683806426185213090' from='sinttest-7in7j-4@salem.geekplace.eu' type='result' id='MD4UC-61'/>
22:22:05 SENT (4):
<presence id='6MS6J-20' type='unavailable'/>
<a xmlns='urn:xmpp:sm:3' h='31'/>
<!-- We have closed the stream -->
</stream:stream>
<!-- But the server still requests an SM ack -->
22:22:05 RECV (4):
<r xmlns='urn:xmpp:sm:3'/>
22:22:05 RECV (4):
</stream:stream>
22:22:05 XMPPConnection closed (XMPPTCPConnection[sinttest-7in7j-4@salem.geekplace.eu/1415073683806426185213090] (4))
22:22:05 SENT (4):
<a xmlns='urn:xmpp:sm:3' h='31'/>
22:22:05 SENT (4):
<stream:stream xmlns='jabber:client' to='salem.geekplace.eu' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='sinttest-7in7j-4@salem.geekplace.eu' xml:lang='en-US'>
22:22:05 RECV (4): ?xml version='1.0'?>
<stream:stream id='3379123514446782311' ver
22:22:05 RECV (4): sion='1.0' xml:lang='en' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'>
<stream:error>
  <invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
</stream:error>
</stream:stream>
22:22:05 XMPPConnection closed due to an exception (XMPPTCPConnection[sinttest-7in7j-4@salem.geekplace.eu/1415073683806426185213090] (4))
org.jivesoftware.smack.XMPPException$StreamErrorException: invalid-xml You can read more about the meaning of this stream error at http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions
<stream:error><invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>
	at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:981)
	at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$700(XMPPTCPConnection.java:913)
	at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:936)
	at java.base/java.lang.Thread.run(Thread.java:834)
2020-09-18 14:36:26 +02:00
Florian Schmaus
488d01796a [tcp] Fix TlsState by aborting the channel selected callback
Instead of breaking in case the SSLEngine signals NEED_WRAP, which
leads to an endless loop while holding the
channelSelectedCallbackLock, we have to return, so that the
asynchronously invoked callback can aquire it, and do its work.
2020-09-17 22:15:30 +02:00
Florian Schmaus
b7824f008d Introduce and use XmlStringBuilder.text()
Smack currently does unnecessary escaping of XML text, where it
escapes e.g. '"' to '&quot;'. This bloats the stanza size, especially
if JSON payloads are involved.

Fixes SMACK-892 (although there are probably still places where
XmlStringBuilder.escape() is used when XmlStringBuild.text() could
have been used).
2020-09-17 14:20:11 +02:00
Florian Schmaus
9e4153435a
Merge pull request #434 from Fishbowler/building_on_a_mac
Mac & Windows build instructions
2020-09-14 21:46:59 +02:00
Dan Caseley
8c33f56047 Mac & Windows build instructions 2020-09-14 17:04:21 +01:00
Florian Schmaus
0a6c21982b
Merge pull request #430 from Flowdalic/websocket
Introduce smack-websocket-okhttp
2020-09-02 11:13:06 +02:00
Florian Schmaus
9002be8e7a s/Websocket/WebSocket/
Java SE as well as OkHttp use 'WebSocket' (not 'Websocket'). Let us do
the same.

SMACK-835.
2020-09-01 21:47:36 +02:00
Florian Schmaus
6533cb7ed1 Introduce smack-websocket-okhttp
This uses Java's Service Provider Interface (SPI) to abstract
different WebSocket implementations.

SMACK-835
2020-09-01 21:36:13 +02:00
Florian Schmaus
9e9d30074c
Merge pull request #428 from vanitasvitae/pgpainlessalpha12
Bump pgpainless version to 0.1.0
2020-08-31 09:47:15 +02:00
4cc0f1d129
Bump pgpainless version to 0.1.0 2020-08-30 23:08:26 +02:00
85 changed files with 962 additions and 582 deletions

View file

@ -62,7 +62,7 @@ allprojects {
':smack-bosh', ':smack-bosh',
':smack-debug', ':smack-debug',
':smack-debug-slf4j', ':smack-debug-slf4j',
':smack-java7', ':smack-java8',
':smack-jingle-old', ':smack-jingle-old',
':smack-resolver-dnsjava', ':smack-resolver-dnsjava',
':smack-resolver-javax', ':smack-resolver-javax',

View file

@ -0,0 +1,50 @@
Building Smack
==============
Linux
-----
Building Smack is as simple as
```
git clone git@github.com:igniterealtime/Smack.git
cd Smack
gradle assemble
```
Mac
---
Smack requires a case-sensitive file system in order to build. Unfortunately, the macOS operating system is case-insensitive by default.
To get around this, you can create a case-sensitive disk image to work from.
1. Launch Disk Utility (Applications > Utilities)
2. Click the +, or go to Edit > Add APFS Volume
3. Give it a name, e.g. "Smack"
4. Change the format to "APFS (Case-sensitive)"
5. Click Add
It'll auto-mount into /Volumes, e.g. /Volumes/Smack
```bash
cd /Volumes/Smack
git clone git@github.com:igniterealtime/Smack.git
cd Smack
gradle assemble
```
Windows
-------
Smack requires a case-sensitive file system in order to build. Unfortunately, Windows NTFS is case-insensitive by default.
To get around this, you can set specific folders as case-sensitive (requires Windows 10 v1803 or higher).
In an Administrator console:
```batch
fsutil.exe file SetCaseSensitiveInfo C:\git\Smack enable
cd \git\Smack
git clone git@github.com:igniterealtime/Smack.git
cd Smack
gradle assemble
```

View file

@ -22,7 +22,7 @@ include 'smack-core',
'smack-bosh', 'smack-bosh',
'smack-android', 'smack-android',
'smack-android-extensions', 'smack-android-extensions',
'smack-java7', 'smack-java8',
'smack-java8-full', 'smack-java8-full',
'smack-integration-test', 'smack-integration-test',
'smack-omemo', 'smack-omemo',
@ -31,6 +31,7 @@ include 'smack-core',
'smack-repl', 'smack-repl',
'smack-openpgp', 'smack-openpgp',
'smack-websocket', 'smack-websocket',
'smack-websocket-okhttp',
'smack-xmlparser', 'smack-xmlparser',
'smack-xmlparser-stax', 'smack-xmlparser-stax',
'smack-xmlparser-xpp3' 'smack-xmlparser-xpp3'

View file

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

View file

@ -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()) {

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"); * 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.
@ -36,7 +36,16 @@ public class AbstractProvider<E extends Element> {
Type[] actualTypeArguments = parameterizedGenericSuperclass.getActualTypeArguments(); Type[] actualTypeArguments = parameterizedGenericSuperclass.getActualTypeArguments();
Type elementType = actualTypeArguments[0]; Type elementType = actualTypeArguments[0];
elementClass = (Class<E>) elementType;
if (elementType instanceof Class) {
elementClass = (Class<E>) elementType;
} else if (elementType instanceof ParameterizedType) {
ParameterizedType parameteriezedElementType = (ParameterizedType) elementType;
elementClass = (Class<E>) parameteriezedElementType.getRawType();
} else {
throw new AssertionError(
"Element type '" + elementType + "' is neither of type Class or ParameterizedType");
}
} }
public final Class<E> getElementClass() { public final Class<E> getElementClass() {

View file

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

View file

@ -0,0 +1,64 @@
/**
*
* 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 final class Pair<F, S> {
private final F first;
private final S second;
private Pair(F first, S second) {
this.first = first;
this.second = second;
}
public static <F extends Object, S extends Object> Pair<F, S> create(F first, S second) {
return new Pair<>(first, second);
}
public static <F extends Object, S extends Object> Pair<F, S> createAndInitHashCode(F first, S second) {
Pair<F, S> pair = new Pair<>(first, second);
pair.hashCode();
return pair;
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
private final HashCode.Cache hashCodeCache = new HashCode.Cache();
@Override
public int hashCode() {
return hashCodeCache.getHashCode(c ->
c.append(first)
.append(second)
);
}
@Override
public boolean equals(Object object) {
return EqualsUtil.equals(this, object, (e, o) ->
e.append(first, o.first)
.append(second, o.second)
);
}
}

View file

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

View file

@ -20,7 +20,7 @@
<className>org.jivesoftware.smack.android.AndroidSmackInitializer</className> <className>org.jivesoftware.smack.android.AndroidSmackInitializer</className>
<className>org.jivesoftware.smack.java7.Java7SmackInitializer</className> <className>org.jivesoftware.smack.java7.Java7SmackInitializer</className>
<className>org.jivesoftware.smack.im.SmackImInitializer</className> <className>org.jivesoftware.smack.im.SmackImInitializer</className>
<className>org.jivesoftware.smack.websocket.WebsocketInitializer</className> <className>org.jivesoftware.smack.websocket.WebSocketInitializer</className>
<className>org.jivesoftware.smackx.omemo.OmemoInitializer</className> <className>org.jivesoftware.smackx.omemo.OmemoInitializer</className>
<className>org.jivesoftware.smackx.ox.util.OpenPgpInitializer</className> <className>org.jivesoftware.smackx.ox.util.OpenPgpInitializer</className>
</optionalStartupClasses> </optionalStartupClasses>

View file

@ -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'>\"'&lt;>&amp;</body>", body.toXML().toString());
}
} }

View file

@ -132,8 +132,8 @@ public final class CarbonManager extends Manager {
final Message wrappingMessage = (Message) stanza; final Message wrappingMessage = (Message) stanza;
final CarbonExtension carbonExtension = CarbonExtension.from(wrappingMessage); final CarbonExtension carbonExtension = CarbonExtension.from(wrappingMessage);
final Direction direction = carbonExtension.getDirection(); final Direction direction = carbonExtension.getDirection();
final Forwarded forwarded = carbonExtension.getForwarded(); final Forwarded<Message> forwarded = carbonExtension.getForwarded();
final Message carbonCopy = (Message) forwarded.getForwardedStanza(); final Message carbonCopy = forwarded.getForwardedStanza();
final BareJid from = carbonCopy.getFrom().asBareJid(); final BareJid from = carbonCopy.getFrom().asBareJid();
carbonsListenerAsyncButOrdered.performAsyncButOrdered(from, new Runnable() { carbonsListenerAsyncButOrdered.performAsyncButOrdered(from, new Runnable() {

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2014 Georg Lukas * Copyright 2013-2014 Georg Lukas, 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.
@ -38,7 +38,7 @@ public class CarbonExtension implements ExtensionElement {
public static final String NAMESPACE = Carbon.NAMESPACE; public static final String NAMESPACE = Carbon.NAMESPACE;
private final Direction dir; private final Direction dir;
private final Forwarded fwd; private final Forwarded<Message> fwd;
/** /**
* Construct a Carbon message extension. * Construct a Carbon message extension.
@ -46,7 +46,7 @@ public class CarbonExtension implements ExtensionElement {
* @param dir Determines if the carbon is being sent/received * @param dir Determines if the carbon is being sent/received
* @param fwd The forwarded message. * @param fwd The forwarded message.
*/ */
public CarbonExtension(Direction dir, Forwarded fwd) { public CarbonExtension(Direction dir, Forwarded<Message> fwd) {
this.dir = dir; this.dir = dir;
this.fwd = fwd; this.fwd = fwd;
} }
@ -65,7 +65,7 @@ public class CarbonExtension implements ExtensionElement {
* *
* @return the {@link Forwarded} message contained in this Carbon. * @return the {@link Forwarded} message contained in this Carbon.
*/ */
public Forwarded getForwarded() { public Forwarded<Message> getForwarded() {
return fwd; return fwd;
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2014 Georg Lukas * Copyright 2013-2014 Georg Lukas, 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.
@ -18,6 +18,7 @@ package org.jivesoftware.smackx.carbons.provider;
import java.io.IOException; import java.io.IOException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
@ -38,25 +39,22 @@ import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
*/ */
public class CarbonManagerProvider extends ExtensionElementProvider<CarbonExtension> { public class CarbonManagerProvider extends ExtensionElementProvider<CarbonExtension> {
private static final ForwardedProvider FORWARDED_PROVIDER = new ForwardedProvider();
@Override @Override
public CarbonExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public CarbonExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
Direction dir = Direction.valueOf(parser.getName()); Direction dir = Direction.valueOf(parser.getName());
Forwarded fwd = null; Forwarded<Message> fwd = null;
boolean done = false; boolean done = false;
while (!done) { while (!done) {
XmlPullParser.Event eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
if (eventType == XmlPullParser.Event.START_ELEMENT && parser.getName().equals("forwarded")) { if (eventType == XmlPullParser.Event.START_ELEMENT && parser.getName().equals("forwarded")) {
fwd = FORWARDED_PROVIDER.parse(parser); fwd = ForwardedProvider.parseForwardedMessage(parser, xmlEnvironment);
} }
else if (eventType == XmlPullParser.Event.END_ELEMENT && dir == Direction.valueOf(parser.getName())) else if (eventType == XmlPullParser.Event.END_ELEMENT && dir == Direction.valueOf(parser.getName()))
done = true; done = true;
} }
if (fwd == null) { if (fwd == null) {
// TODO: Should be SmackParseException. throw new SmackParsingException("sent/received must contain exactly one <forwarded/> element");
throw new IOException("sent/received must contain exactly one <forwarded> tag");
} }
return new CarbonExtension(dir, fwd); return new CarbonExtension(dir, fwd);
} }

View file

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

View file

@ -632,7 +632,7 @@ public final class MamManager extends Manager {
private final MamFinIQ mamFin; private final MamFinIQ mamFin;
private final List<Message> mamResultCarrierMessages; private final List<Message> mamResultCarrierMessages;
private final List<MamResultExtension> mamResultExtensions; private final List<MamResultExtension> mamResultExtensions;
private final List<Forwarded> forwardedMessages; private final List<Forwarded<Message>> forwardedMessages;
private final List<Message> messages; private final List<Message> messages;
private MamQueryPage(StanzaCollector stanzaCollector, MamFinIQ mamFin) { private MamQueryPage(StanzaCollector stanzaCollector, MamFinIQ mamFin) {
@ -642,7 +642,7 @@ public final class MamManager extends Manager {
List<Message> mamResultCarrierMessages = new ArrayList<>(mamResultCarrierStanzas.size()); List<Message> mamResultCarrierMessages = new ArrayList<>(mamResultCarrierStanzas.size());
List<MamResultExtension> mamResultExtensions = new ArrayList<>(mamResultCarrierStanzas.size()); List<MamResultExtension> mamResultExtensions = new ArrayList<>(mamResultCarrierStanzas.size());
List<Forwarded> forwardedMessages = new ArrayList<>(mamResultCarrierStanzas.size()); List<Forwarded<Message>> forwardedMessages = new ArrayList<>(mamResultCarrierStanzas.size());
for (Stanza mamResultStanza : mamResultCarrierStanzas) { for (Stanza mamResultStanza : mamResultCarrierStanzas) {
Message resultMessage = (Message) mamResultStanza; Message resultMessage = (Message) mamResultStanza;
@ -665,7 +665,7 @@ public final class MamManager extends Manager {
return messages; return messages;
} }
public List<Forwarded> getForwarded() { public List<Forwarded<Message>> getForwarded() {
return forwardedMessages; return forwardedMessages;
} }

View file

@ -22,6 +22,7 @@ import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageView; import org.jivesoftware.smack.packet.MessageView;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
@ -69,7 +70,7 @@ public class MamElements {
/** /**
* the forwarded element. * the forwarded element.
*/ */
private final Forwarded forwarded; private final Forwarded<Message> forwarded;
/** /**
* the query id. * the query id.
@ -83,7 +84,7 @@ public class MamElements {
* @param id TODO javadoc me please * @param id TODO javadoc me please
* @param forwarded TODO javadoc me please * @param forwarded TODO javadoc me please
*/ */
public MamResultExtension(String queryId, String id, Forwarded forwarded) { public MamResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
if (StringUtils.isEmpty(id)) { if (StringUtils.isEmpty(id)) {
throw new IllegalArgumentException("id must not be null or empty"); throw new IllegalArgumentException("id must not be null or empty");
} }
@ -109,7 +110,7 @@ public class MamElements {
* *
* @return the forwarded element * @return the forwarded element
*/ */
public Forwarded getForwarded() { public Forwarded<Message> getForwarded() {
return forwarded; return forwarded;
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2016 Fernando Ramirez * Copyright 2016 Fernando Ramirez, 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.
@ -18,6 +18,7 @@ package org.jivesoftware.smackx.mam.provider;
import java.io.IOException; import java.io.IOException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
@ -40,7 +41,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
@Override @Override
public MamResultExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public MamResultExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
Forwarded forwarded = null; Forwarded<Message> forwarded = null;
String queryId = parser.getAttributeValue("", "queryid"); String queryId = parser.getAttributeValue("", "queryid");
String id = parser.getAttributeValue("", "id"); String id = parser.getAttributeValue("", "id");
@ -51,7 +52,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
case START_ELEMENT: case START_ELEMENT:
switch (name) { switch (name) {
case Forwarded.ELEMENT: case Forwarded.ELEMENT:
forwarded = ForwardedProvider.INSTANCE.parse(parser); forwarded = ForwardedProvider.parseForwardedMessage(parser, xmlEnvironment);
break; break;
} }
break; break;

View file

@ -19,9 +19,10 @@ package org.jivesoftware.smackx.carbons;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.util.Properties; import java.util.Properties;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.test.util.SmackTestUtil; import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
@ -48,7 +49,7 @@ public class CarbonTest extends ExperimentalInitializerTest {
XmlPullParser parser; XmlPullParser parser;
String control; String control;
CarbonExtension cc; CarbonExtension cc;
Forwarded fwd; Forwarded<Message> fwd;
control = XMLBuilder.create("sent") control = XMLBuilder.create("sent")
.e("forwarded") .e("forwarded")
@ -107,6 +108,6 @@ public class CarbonTest extends ExperimentalInitializerTest {
.a("xmlns", "urn:xmpp:forwarded:0") .a("xmlns", "urn:xmpp:forwarded:0")
.asString(outputProperties); .asString(outputProperties);
assertThrows(IOException.class, () -> SmackTestUtil.parse(control, CarbonManagerProvider.class, parserKind)); assertThrows(SmackParsingException.class, () -> SmackTestUtil.parse(control, CarbonManagerProvider.class, parserKind));
} }
} }

View file

@ -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&apos;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'/>";

View file

@ -59,10 +59,10 @@ public class MamResultProviderTest {
calendar.setTimeZone(TimeZone.getTimeZone("UTC")); calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = calendar.getTime(); Date date = calendar.getTime();
Forwarded forwarded = mamResultExtension.getForwarded(); Forwarded<Message> forwarded = mamResultExtension.getForwarded();
assertEquals(forwarded.getDelayInformation().getStamp(), date); assertEquals(forwarded.getDelayInformation().getStamp(), date);
Message message = (Message) forwarded.getForwardedStanza(); Message message = forwarded.getForwardedStanza();
assertEquals(message.getFrom().toString(), "romeo@montague.lit/orchard"); assertEquals(message.getFrom().toString(), "romeo@montague.lit/orchard");
assertEquals(message.getTo().toString(), "juliet@capulet.lit/balcony"); assertEquals(message.getTo().toString(), "juliet@capulet.lit/balcony");
assertEquals(message.getBody(), assertEquals(message.getBody(),
@ -81,10 +81,10 @@ public class MamResultProviderTest {
calendar.setTimeZone(TimeZone.getTimeZone("UTC")); calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = calendar.getTime(); Date date = calendar.getTime();
Forwarded forwarded = mamResultExtension.getForwarded(); Forwarded<Message> forwarded = mamResultExtension.getForwarded();
assertEquals(forwarded.getDelayInformation().getStamp(), date); assertEquals(forwarded.getDelayInformation().getStamp(), date);
Message forwardedMessage = (Message) forwarded.getForwardedStanza(); Message forwardedMessage = forwarded.getForwardedStanza();
assertEquals(forwardedMessage.getFrom().toString(), "witch@shakespeare.lit"); assertEquals(forwardedMessage.getFrom().toString(), "witch@shakespeare.lit");
assertEquals(forwardedMessage.getTo().toString(), "macbeth@shakespeare.lit"); assertEquals(forwardedMessage.getTo().toString(), "macbeth@shakespeare.lit");
assertEquals(forwardedMessage.getBody(), "Hail to thee"); assertEquals(forwardedMessage.getBody(), "Hail to thee");

View file

@ -79,7 +79,7 @@ public class QueryArchiveTest extends MamTest {
.setBody("Thrice the brinded cat hath mew.") .setBody("Thrice the brinded cat hath mew.")
.build(); .build();
Forwarded forwarded = new Forwarded(delay, forwardedMessage); Forwarded<Message> forwarded = new Forwarded<>(forwardedMessage, delay);
message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded)); message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded));
@ -90,7 +90,7 @@ public class QueryArchiveTest extends MamTest {
assertEquals(mamResultExtension.getId(), "34482-21985-73620"); assertEquals(mamResultExtension.getId(), "34482-21985-73620");
assertEquals(mamResultExtension.getForwarded().getDelayInformation().getStamp(), date); assertEquals(mamResultExtension.getForwarded().getDelayInformation().getStamp(), date);
Message resultMessage = (Message) mamResultExtension.getForwarded().getForwardedStanza(); Message resultMessage = mamResultExtension.getForwarded().getForwardedStanza();
assertEquals(resultMessage.getFrom(), JidCreate.from("coven@chat.shakespeare.lit/firstwitch")); assertEquals(resultMessage.getFrom(), JidCreate.from("coven@chat.shakespeare.lit/firstwitch"));
assertEquals(resultMessage.getStanzaId(), "162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2"); assertEquals(resultMessage.getStanzaId(), "162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2");
assertEquals(resultMessage.getType(), Type.chat); assertEquals(resultMessage.getType(), Type.chat);

View file

@ -29,14 +29,19 @@ import org.jivesoftware.smack.util.stringencoder.Base64;
*/ */
public class BoBData { public class BoBData {
private final int maxAge; private final Integer maxAge;
private final String type; private final String type;
private byte[] contentBinary; private byte[] contentBinary;
private String contentString; private String contentString;
private BoBData(String type, Integer maxAge) {
this.type = type;
this.maxAge = maxAge;
}
public BoBData(String type, byte[] content) { public BoBData(String type, byte[] content) {
this(type, content, -1); this(type, content, null);
} }
/** /**
@ -46,20 +51,18 @@ public class BoBData {
* @param content TODO javadoc me please * @param content TODO javadoc me please
* @param maxAge TODO javadoc me please * @param maxAge TODO javadoc me please
*/ */
public BoBData(String type, byte[] content, int maxAge) { public BoBData(String type, byte[] content, Integer maxAge) {
this.type = type; this(type, maxAge);
this.contentBinary = content; this.contentBinary = content;
this.maxAge = maxAge;
} }
public BoBData(String type, String content) { public BoBData(String type, String content) {
this(type, content, -1); this(type, content, null);
} }
public BoBData(String type, String content, int maxAge) { public BoBData(String type, String content, Integer maxAge) {
this.type = type; this(type, maxAge);
this.contentString = content; this.contentString = content;
this.maxAge = maxAge;
} }
/** /**
@ -67,7 +70,7 @@ public class BoBData {
* *
* @return the max age * @return the max age
*/ */
public int getMaxAge() { public Integer getMaxAge() {
return maxAge; return maxAge;
} }

View file

@ -20,15 +20,15 @@ import java.util.Set;
public class BoBInfo { public class BoBInfo {
private final Set<BoBHash> hashes; private final Set<ContentId> hashes;
private final BoBData data; private final BoBData data;
BoBInfo(Set<BoBHash> hashes, BoBData data) { BoBInfo(Set<ContentId> hashes, BoBData data) {
this.hashes = hashes; this.hashes = hashes;
this.data = data; this.data = data;
} }
public Set<BoBHash> getHashes() { public Set<ContentId> getHashes() {
return hashes; return hashes;
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2016-2017 Fernando Ramirez, Florian Schmaus * Copyright 2016-2020 Fernando Ramirez, 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.
@ -81,9 +81,9 @@ public final class BoBManager extends Manager {
return bobManager; return bobManager;
} }
private static final LruCache<BoBHash, BoBData> BOB_CACHE = new LruCache<>(128); private static final LruCache<ContentId, BoBData> BOB_CACHE = new LruCache<>(128);
private final Map<BoBHash, BoBInfo> bobs = new ConcurrentHashMap<>(); private final Map<ContentId, BoBInfo> bobs = new ConcurrentHashMap<>();
private BoBManager(XMPPConnection connection) { private BoBManager(XMPPConnection connection) {
super(connection); super(connection);
@ -95,15 +95,16 @@ public final class BoBManager extends Manager {
@Override @Override
public IQ handleIQRequest(IQ iqRequest) { public IQ handleIQRequest(IQ iqRequest) {
BoBIQ bobIQRequest = (BoBIQ) iqRequest; BoBIQ bobIQRequest = (BoBIQ) iqRequest;
ContentId contentId = bobIQRequest.getContentId();
BoBInfo bobInfo = bobs.get(bobIQRequest.getBoBHash()); BoBInfo bobInfo = bobs.get(contentId);
if (bobInfo == null) { if (bobInfo == null) {
// TODO return item-not-found // TODO return item-not-found
return null; return null;
} }
BoBData bobData = bobInfo.getData(); BoBData bobData = bobInfo.getData();
BoBIQ responseBoBIQ = new BoBIQ(bobIQRequest.getBoBHash(), bobData); BoBIQ responseBoBIQ = new BoBIQ(contentId, bobData);
responseBoBIQ.setType(Type.result); responseBoBIQ.setType(Type.result);
responseBoBIQ.setTo(bobIQRequest.getFrom()); responseBoBIQ.setTo(bobIQRequest.getFrom());
return responseBoBIQ; return responseBoBIQ;
@ -137,7 +138,7 @@ public final class BoBManager extends Manager {
* @throws NotConnectedException if the XMPP connection is not connected. * @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted. * @throws InterruptedException if the calling thread was interrupted.
*/ */
public BoBData requestBoB(Jid to, BoBHash bobHash) throws NotLoggedInException, NoResponseException, public BoBData requestBoB(Jid to, ContentId bobHash) throws NotLoggedInException, NoResponseException,
XMPPErrorException, NotConnectedException, InterruptedException { XMPPErrorException, NotConnectedException, InterruptedException {
BoBData bobData = BOB_CACHE.lookup(bobHash); BoBData bobData = BOB_CACHE.lookup(bobHash);
if (bobData != null) { if (bobData != null) {
@ -159,9 +160,9 @@ public final class BoBManager extends Manager {
public BoBInfo addBoB(BoBData bobData) { public BoBInfo addBoB(BoBData bobData) {
// We only support SHA-1 for now. // We only support SHA-1 for now.
BoBHash bobHash = new BoBHash(SHA1.hex(bobData.getContent()), "sha1"); ContentId bobHash = new ContentId(SHA1.hex(bobData.getContent()), "sha1");
Set<BoBHash> bobHashes = Collections.singleton(bobHash); Set<ContentId> bobHashes = Collections.singleton(bobHash);
bobHashes = Collections.unmodifiableSet(bobHashes); bobHashes = Collections.unmodifiableSet(bobHashes);
BoBInfo bobInfo = new BoBInfo(bobHashes, bobData); BoBInfo bobInfo = new BoBInfo(bobHashes, bobData);
@ -171,12 +172,12 @@ public final class BoBManager extends Manager {
return bobInfo; return bobInfo;
} }
public BoBInfo removeBoB(BoBHash bobHash) { public BoBInfo removeBoB(ContentId bobHash) {
BoBInfo bobInfo = bobs.remove(bobHash); BoBInfo bobInfo = bobs.remove(bobHash);
if (bobInfo == null) { if (bobInfo == null) {
return null; return null;
} }
for (BoBHash otherBobHash : bobInfo.getHashes()) { for (ContentId otherBobHash : bobInfo.getHashes()) {
bobs.remove(otherBobHash); bobs.remove(otherBobHash);
} }
return bobInfo; return bobInfo;

View file

@ -19,29 +19,32 @@ package org.jivesoftware.smackx.bob;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
/** /**
* Bits of Binary hash class. * Content-ID class.
* *
* @author Fernando Ramirez * @author Fernando Ramirez
* @author Florian Schmaus * @author Florian Schmaus
* @see <a href="http://xmpp.org/extensions/xep-0231.html">XEP-0231: Bits of * @see <a href="https://tools.ietf.org/html/rfc2392">RFC 2392: Content-ID and Message-ID Uniform Resource Locators</a>
* Binary</a>
*/ */
public class BoBHash { public class ContentId {
private final String hash; private final String hash;
private final String hashType; private final String hashType;
private final String cid; private final String cid;
private ContentId(String hash, String hashType, String cid) {
this.hash = StringUtils.requireNotNullNorEmpty(hash, "hash must not be null nor empty");
this.hashType = StringUtils.requireNotNullNorEmpty(hashType, "hashType must not be null nor empty");
this.cid = cid;
}
/** /**
* BoB hash constructor. * BoB hash constructor.
* *
* @param hash TODO javadoc me please * @param hash TODO javadoc me please
* @param hashType TODO javadoc me please * @param hashType TODO javadoc me please
*/ */
public BoBHash(String hash, String hashType) { public ContentId(String hash, String hashType) {
this.hash = StringUtils.requireNotNullNorEmpty(hash, "hash must not be null nor empty"); this(hash, hashType, hashType + '+' + hash + "@bob.xmpp.org");
this.hashType = StringUtils.requireNotNullNorEmpty(hashType, "hashType must not be null nor empty");
this.cid = this.hashType + '+' + this.hash + "@bob.xmpp.org";
} }
/** /**
@ -82,8 +85,8 @@ public class BoBHash {
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other instanceof BoBHash) { if (other instanceof ContentId) {
BoBHash otherBob = (BoBHash) other; ContentId otherBob = (ContentId) other;
return cid.equals(otherBob.cid); return cid.equals(otherBob.cid);
} }
return false; return false;
@ -100,10 +103,10 @@ public class BoBHash {
* @param src TODO javadoc me please * @param src TODO javadoc me please
* @return the BoB hash * @return the BoB hash
*/ */
public static BoBHash fromSrc(String src) { public static ContentId fromSrc(String src) {
String hashType = src.substring(src.lastIndexOf("cid:") + 4, src.indexOf("+")); String hashType = src.substring(src.lastIndexOf("cid:") + 4, src.indexOf("+"));
String hash = src.substring(src.indexOf("+") + 1, src.indexOf("@bob.xmpp.org")); String hash = src.substring(src.indexOf("+") + 1, src.indexOf("@bob.xmpp.org"));
return new BoBHash(hash, hashType); return new ContentId(hash, hashType);
} }
/** /**
@ -112,10 +115,10 @@ public class BoBHash {
* @param cid TODO javadoc me please * @param cid TODO javadoc me please
* @return the BoB hash * @return the BoB hash
*/ */
public static BoBHash fromCid(String cid) { public static ContentId fromCid(String cid) {
String hashType = cid.substring(0, cid.indexOf("+")); String hashType = cid.substring(0, cid.indexOf("+"));
String hash = cid.substring(cid.indexOf("+") + 1, cid.indexOf("@bob.xmpp.org")); String hash = cid.substring(cid.indexOf("+") + 1, cid.indexOf("@bob.xmpp.org"));
return new BoBHash(hash, hashType); return new ContentId(hash, hashType, cid);
} }
} }

View file

@ -0,0 +1,81 @@
/**
*
* 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.smackx.bob.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.bob.BoBData;
import org.jivesoftware.smackx.bob.BoBManager;
import org.jivesoftware.smackx.bob.ContentId;
/**
* Bits of Binary data extension element.
*
* @author Florian Schmaus
* @see <a href="http://xmpp.org/extensions/xep-0231.html">XEP-0231: Bits of
* Binary</a>
*/
public class BoBDataExtension implements ExtensionElement {
public static final String ELEMENT = "data";
public static final String NAMESPACE = BoBManager.NAMESPACE;
private final ContentId cid;
private final BoBData bobData;
/**
* Bits of Binary data extension constructor.
*
* @param cid TODO javadoc me please
* @param bobData TODO javadoc me please
*/
public BoBDataExtension(ContentId cid, BoBData bobData) {
this.cid = Objects.requireNonNull(cid);
this.bobData = Objects.requireNonNull(bobData);
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("cid", cid.getCid());
xml.attribute("type", bobData.getType());
xml.optAttribute("max-age", bobData.getMaxAge());
xml.rightAngleBracket();
xml.append(bobData.getContentBase64Encoded());
xml.closeElement(this);
return xml;
}
public static BoBDataExtension from(Message message) {
return message.getExtension(BoBDataExtension.class);
}
}

View file

@ -1,97 +0,0 @@
/**
*
* Copyright 2016 Fernando Ramirez
*
* 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.bob.element;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.bob.BoBHash;
import org.jivesoftware.smackx.xhtmlim.XHTMLText;
import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
/**
* Bits of Binary extension element.
*
* @author Fernando Ramirez
* @see <a href="http://xmpp.org/extensions/xep-0231.html">XEP-0231: Bits of
* Binary</a>
*/
public class BoBExtension extends XHTMLExtension {
private final BoBHash bobHash;
private final String alt;
private final String paragraph;
/**
* Bits of Binary extension constructor.
*
* @param bobHash TODO javadoc me please
* @param alt TODO javadoc me please
* @param paragraph TODO javadoc me please
*/
public BoBExtension(BoBHash bobHash, String alt, String paragraph) {
this.bobHash = bobHash;
this.alt = alt;
this.paragraph = paragraph;
}
/**
* Get the BoB hash.
*
* @return the BoB hash
*/
public BoBHash getBoBHash() {
return bobHash;
}
/**
* Get the alt field.
*
* @return the alt field
*/
public String getAlt() {
return alt;
}
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.rightAngleBracket();
xml.halfOpenElement(Message.BODY);
xml.xmlnsAttribute(XHTMLText.NAMESPACE);
xml.rightAngleBracket();
xml.openElement(XHTMLText.P);
xml.optEscape(paragraph);
xml.halfOpenElement(XHTMLText.IMG);
xml.optAttribute("alt", alt);
xml.attribute("src", bobHash.toSrc());
xml.closeEmptyElement();
xml.closeElement(XHTMLText.P);
xml.closeElement(Message.BODY);
xml.closeElement(this);
return xml;
}
public static BoBExtension from(Message message) {
return (BoBExtension) message.getExtensionElement(ELEMENT, NAMESPACE);
}
}

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2016 Fernando Ramirez * Copyright 2016 Fernando Ramirez, 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.
@ -19,8 +19,8 @@ package org.jivesoftware.smackx.bob.element;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.bob.BoBData; import org.jivesoftware.smackx.bob.BoBData;
import org.jivesoftware.smackx.bob.BoBHash;
import org.jivesoftware.smackx.bob.BoBManager; import org.jivesoftware.smackx.bob.BoBManager;
import org.jivesoftware.smackx.bob.ContentId;
/** /**
* Bits of Binary IQ class. * Bits of Binary IQ class.
@ -41,28 +41,40 @@ public class BoBIQ extends IQ {
*/ */
public static final String NAMESPACE = BoBManager.NAMESPACE; public static final String NAMESPACE = BoBManager.NAMESPACE;
private final BoBHash bobHash; private final ContentId cid;
private final BoBData bobData; private final BoBData bobData;
/** /**
* Bits of Binary IQ constructor. * Bits of Binary IQ constructor.
* *
* @param bobHash TODO javadoc me please * @param cid TODO javadoc me please
* @param bobData TODO javadoc me please * @param bobData TODO javadoc me please
*/ */
public BoBIQ(BoBHash bobHash, BoBData bobData) { public BoBIQ(ContentId cid, BoBData bobData) {
super(ELEMENT, NAMESPACE); super(ELEMENT, NAMESPACE);
this.bobHash = bobHash; this.cid = cid;
this.bobData = bobData; this.bobData = bobData;
} }
/** /**
* Bits of Binary IQ constructor. * Bits of Binary IQ constructor.
* *
* @param bobHash TODO javadoc me please * @param cid TODO javadoc me please
*/ */
public BoBIQ(BoBHash bobHash) { public BoBIQ(ContentId cid) {
this(bobHash, null); this(cid, null);
}
/**
* Get the BoB hash.
*
* @return the BoB hash
* @deprecated use {@link #getContentId()} instead.
*/
// TODO: Remove in Smack 4.5.
@Deprecated
public ContentId getBoBHash() {
return cid;
} }
/** /**
@ -70,8 +82,8 @@ public class BoBIQ extends IQ {
* *
* @return the BoB hash * @return the BoB hash
*/ */
public BoBHash getBoBHash() { public ContentId getContentId() {
return bobHash; return cid;
} }
/** /**
@ -85,7 +97,7 @@ public class BoBIQ extends IQ {
@Override @Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("cid", bobHash.getCid()); xml.attribute("cid", cid.getCid());
if (bobData != null) { if (bobData != null) {
xml.optIntAttribute("max_age", bobData.getMaxAge()); xml.optIntAttribute("max_age", bobData.getMaxAge());

View file

@ -0,0 +1,41 @@
/**
*
* 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.smackx.bob.provider;
import java.io.IOException;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.Pair;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.bob.BoBData;
import org.jivesoftware.smackx.bob.ContentId;
import org.jivesoftware.smackx.bob.element.BoBDataExtension;
public class BoBDataExtensionProvider extends ExtensionElementProvider<BoBDataExtension> {
@Override
public BoBDataExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException {
Pair<ContentId, BoBData> parserResult = BoBProviderUtil.parseContentIdAndBobData(parser, initialDepth,
xmlEnvironment);
return new BoBDataExtension(parserResult.getFirst(), parserResult.getSecond());
}
}

View file

@ -20,12 +20,12 @@ import java.io.IOException;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.Pair;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.bob.BoBData; import org.jivesoftware.smackx.bob.BoBData;
import org.jivesoftware.smackx.bob.BoBHash; import org.jivesoftware.smackx.bob.ContentId;
import org.jivesoftware.smackx.bob.element.BoBIQ; import org.jivesoftware.smackx.bob.element.BoBIQ;
/** /**
@ -39,22 +39,10 @@ public class BoBIQProvider extends IQProvider<BoBIQ> {
@Override @Override
public BoBIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { public BoBIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
String cid = parser.getAttributeValue("", "cid"); Pair<ContentId, BoBData> parserResult = BoBProviderUtil.parseContentIdAndBobData(parser, initialDepth,
BoBHash bobHash = BoBHash.fromCid(cid); xmlEnvironment);
String dataType = parser.getAttributeValue("", "type"); return new BoBIQ(parserResult.getFirst(), parserResult.getSecond());
int maxAge = ParserUtils.getIntegerAttribute(parser, "max-age", -1);
String base64EncodedData = parser.nextText();
BoBData bobData;
if (dataType != null) {
bobData = new BoBData(dataType, base64EncodedData, maxAge);
} else {
bobData = null;
}
return new BoBIQ(bobHash, bobData);
} }
} }

View file

@ -0,0 +1,48 @@
/**
*
* 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.smackx.bob.provider;
import java.io.IOException;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.Pair;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.bob.BoBData;
import org.jivesoftware.smackx.bob.ContentId;
public class BoBProviderUtil {
public static Pair<ContentId, BoBData> parseContentIdAndBobData(XmlPullParser parser, int initialDepth,
XmlEnvironment xmlEnvironment) throws IOException, XmlPullParserException {
String cid = parser.getAttributeValue("", "cid");
ContentId contentId = ContentId.fromCid(cid);
String dataType = parser.getAttributeValue("", "type");
Integer maxAge = ParserUtils.getIntegerAttribute(parser, "max-age");
String base64EncodedData = parser.nextText();
BoBData bobData = null;
if (dataType != null) {
bobData = new BoBData(dataType, base64EncodedData, maxAge);
}
return Pair.create(contentId, bobData);
}
}

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2014 Georg Lukas * Copyright 2013-2014 Georg Lukas, 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.
@ -25,6 +25,7 @@ import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.delay.packet.DelayInformation; import org.jivesoftware.smackx.delay.packet.DelayInformation;
@ -35,23 +36,24 @@ import org.jivesoftware.smackx.delay.packet.DelayInformation;
* @author Georg Lukas * @author Georg Lukas
* @see <a href="http://xmpp.org/extensions/xep-0297.html">XEP-0297: Stanza Forwarding</a> * @see <a href="http://xmpp.org/extensions/xep-0297.html">XEP-0297: Stanza Forwarding</a>
*/ */
public class Forwarded implements ExtensionElement { public class Forwarded<S extends Stanza> implements ExtensionElement {
public static final String NAMESPACE = "urn:xmpp:forward:0"; public static final String NAMESPACE = "urn:xmpp:forward:0";
public static final String ELEMENT = "forwarded"; public static final String ELEMENT = "forwarded";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT); public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final DelayInformation delay; private final DelayInformation delay;
private final Stanza forwardedPacket; private final S forwardedStanza;
/** /**
* Creates a new Forwarded stanza extension. * Creates a new Forwarded stanza extension.
* *
* @param delay an optional {@link DelayInformation} timestamp of the packet. * @param delay an optional {@link DelayInformation} timestamp of the packet.
* @param fwdPacket the stanza that is forwarded (required). * @param forwardedStanza the stanza that is forwarded (required).
* @deprecated use {@link #Forwarded(Stanza, DelayInformation)} instead.
*/ */
public Forwarded(DelayInformation delay, Stanza fwdPacket) { @Deprecated
this.delay = delay; public Forwarded(DelayInformation delay, S forwardedStanza) {
this.forwardedPacket = fwdPacket; this(forwardedStanza, delay);
} }
/** /**
@ -59,8 +61,19 @@ public class Forwarded implements ExtensionElement {
* *
* @param fwdPacket the stanza that is forwarded (required). * @param fwdPacket the stanza that is forwarded (required).
*/ */
public Forwarded(Stanza fwdPacket) { public Forwarded(S fwdPacket) {
this(null, fwdPacket); this(fwdPacket, null);
}
/**
* Creates a new Forwarded stanza extension.
*
* @param forwardedStanza the stanza that is forwarded (required).
* @param delay an optional {@link DelayInformation} timestamp of the packet.
*/
public Forwarded(S forwardedStanza, DelayInformation delay) {
this.forwardedStanza = Objects.requireNonNull(forwardedStanza);
this.delay = delay;
} }
@Override @Override
@ -78,7 +91,7 @@ public class Forwarded implements ExtensionElement {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
xml.rightAngleBracket(); xml.rightAngleBracket();
xml.optElement(getDelayInformation()); xml.optElement(getDelayInformation());
xml.append(forwardedPacket); xml.append(forwardedStanza);
xml.closeElement(this); xml.closeElement(this);
return xml; return xml;
} }
@ -88,8 +101,8 @@ public class Forwarded implements ExtensionElement {
* *
* @return the {@link Stanza} (typically a message) that was forwarded. * @return the {@link Stanza} (typically a message) that was forwarded.
*/ */
public Stanza getForwardedStanza() { public S getForwardedStanza() {
return forwardedPacket; return forwardedStanza;
} }
/** /**
@ -101,12 +114,23 @@ public class Forwarded implements ExtensionElement {
return delay; return delay;
} }
/**
* Check if this is forwarding a stanza of the provided class.
*
* @param stanzaClass the class to check for.
* @return <code>true</code> if this is forwarding a stanza of the provided class.
* @since 4.4
*/
public boolean isForwarded(Class<? extends Stanza> stanzaClass) {
return stanzaClass.isAssignableFrom(forwardedStanza.getClass());
}
/** /**
* Get the forwarded extension. * Get the forwarded extension.
* @param packet TODO javadoc me please * @param packet TODO javadoc me please
* @return the Forwarded extension or null * @return the Forwarded extension or null
*/ */
public static Forwarded from(Stanza packet) { public static Forwarded<?> from(Stanza packet) {
return packet.getExtension(Forwarded.class); return packet.getExtension(Forwarded.class);
} }
@ -118,10 +142,10 @@ public class Forwarded implements ExtensionElement {
* @return a list a the extracted messages. * @return a list a the extracted messages.
* @since 4.3.0 * @since 4.3.0
*/ */
public static List<Message> extractMessagesFrom(Collection<Forwarded> forwardedCollection) { public static List<Message> extractMessagesFrom(Collection<Forwarded<Message>> forwardedCollection) {
List<Message> res = new ArrayList<>(forwardedCollection.size()); List<Message> res = new ArrayList<>(forwardedCollection.size());
for (Forwarded forwarded : forwardedCollection) { for (Forwarded<Message> forwarded : forwardedCollection) {
Message message = (Message) forwarded.forwardedPacket; Message message = forwarded.getForwardedStanza();
res.add(message); res.add(message);
} }
return res; return res;

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2014 Georg Lukas * Copyright 2013-2014 Georg Lukas, 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.
@ -38,14 +38,14 @@ import org.jivesoftware.smackx.forward.packet.Forwarded;
* *
* @author Georg Lukas * @author Georg Lukas
*/ */
public class ForwardedProvider extends ExtensionElementProvider<Forwarded> { public class ForwardedProvider extends ExtensionElementProvider<Forwarded<?>> {
public static final ForwardedProvider INSTANCE = new ForwardedProvider(); public static final ForwardedProvider INSTANCE = new ForwardedProvider();
private static final Logger LOGGER = Logger.getLogger(ForwardedProvider.class.getName()); private static final Logger LOGGER = Logger.getLogger(ForwardedProvider.class.getName());
@Override @Override
public Forwarded parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public Forwarded<?> parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
DelayInformation di = null; DelayInformation di = null;
Stanza packet = null; Stanza packet = null;
@ -86,6 +86,21 @@ public class ForwardedProvider extends ExtensionElementProvider<Forwarded> {
// TODO: Should be SmackParseException. // TODO: Should be SmackParseException.
throw new IOException("forwarded extension must contain a packet"); throw new IOException("forwarded extension must contain a packet");
} }
return new Forwarded(di, packet); return new Forwarded<>(packet, di);
}
public static Forwarded<Message> parseForwardedMessage(XmlPullParser parser, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
return parseForwardedMessage(parser, parser.getDepth(), xmlEnvironment);
}
@SuppressWarnings("unchecked")
public static Forwarded<Message> parseForwardedMessage(XmlPullParser parser, int initialDepth,
XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
Forwarded<?> forwarded = INSTANCE.parse(parser, initialDepth, xmlEnvironment);
if (!forwarded.isForwarded(Message.class)) {
throw new SmackParsingException("Expecting a forwarded message, but got " + forwarded);
}
return (Forwarded<Message>) forwarded;
} }
} }

View file

@ -37,7 +37,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* *
* @author Gaston Dombiak * @author Gaston Dombiak
*/ */
public class XHTMLExtension implements ExtensionElement { public final class XHTMLExtension implements ExtensionElement {
public static final String ELEMENT = "html"; public static final String ELEMENT = "html";
public static final String NAMESPACE = "http://jabber.org/protocol/xhtml-im"; public static final String NAMESPACE = "http://jabber.org/protocol/xhtml-im";

View file

@ -500,6 +500,12 @@
</extensionProvider> </extensionProvider>
<!-- XEP-0231: Bits of Binary --> <!-- XEP-0231: Bits of Binary -->
<extensionProvider>
<elementName>data</elementName>
<namespace>urn:xmpp:bob</namespace>
<className>org.jivesoftware.smackx.bob.provider.BoBDataExtensionProvider</className>
</extensionProvider>
<iqProvider> <iqProvider>
<elementName>data</elementName> <elementName>data</elementName>
<namespace>urn:xmpp:bob</namespace> <namespace>urn:xmpp:bob</namespace>

View file

@ -41,7 +41,7 @@ public class BoBIQTest extends SmackTestSuite {
@Test @Test
public void checkBoBIQRequest() throws Exception { public void checkBoBIQRequest() throws Exception {
BoBHash bobHash = new BoBHash("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1"); ContentId bobHash = new ContentId("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1");
BoBIQ createdBoBIQ = new BoBIQ(bobHash); BoBIQ createdBoBIQ = new BoBIQ(bobHash);
createdBoBIQ.setStanzaId("sarasa"); createdBoBIQ.setStanzaId("sarasa");
@ -55,7 +55,7 @@ public class BoBIQTest extends SmackTestSuite {
public void checkBoBIQResponse() throws Exception { public void checkBoBIQResponse() throws Exception {
BoBIQ bobIQ = PacketParserUtils.parseStanza(sampleBoBIQResponse); BoBIQ bobIQ = PacketParserUtils.parseStanza(sampleBoBIQResponse);
BoBHash bobHash = new BoBHash("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1"); ContentId bobHash = new ContentId("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1");
BoBData bobData = new BoBData("image/png", "sarasade2354j2".getBytes(StandardCharsets.UTF_8), 86400); BoBData bobData = new BoBData("image/png", "sarasade2354j2".getBytes(StandardCharsets.UTF_8), 86400);
BoBIQ createdBoBIQ = new BoBIQ(bobHash, bobData); BoBIQ createdBoBIQ = new BoBIQ(bobHash, bobData);
@ -63,8 +63,8 @@ public class BoBIQTest extends SmackTestSuite {
createdBoBIQ.setTo(JidCreate.from("doctor@shakespeare.lit/pda")); createdBoBIQ.setTo(JidCreate.from("doctor@shakespeare.lit/pda"));
createdBoBIQ.setType(Type.result); createdBoBIQ.setType(Type.result);
assertEquals(bobIQ.getBoBHash().getHash(), createdBoBIQ.getBoBHash().getHash()); assertEquals(bobIQ.getContentId().getHash(), createdBoBIQ.getContentId().getHash());
assertEquals(bobIQ.getBoBHash().getHashType(), createdBoBIQ.getBoBHash().getHashType()); assertEquals(bobIQ.getContentId().getHashType(), createdBoBIQ.getContentId().getHashType());
assertEquals(bobIQ.getBoBData().getMaxAge(), createdBoBIQ.getBoBData().getMaxAge()); assertEquals(bobIQ.getBoBData().getMaxAge(), createdBoBIQ.getBoBData().getMaxAge());
assertEquals(bobIQ.getBoBData().getType(), createdBoBIQ.getBoBData().getType()); assertEquals(bobIQ.getBoBData().getType(), createdBoBIQ.getBoBData().getType());
assertEquals(bobIQ.getBoBData().getContentBase64Encoded(), createdBoBIQ.getBoBData().getContentBase64Encoded()); assertEquals(bobIQ.getBoBData().getContentBase64Encoded(), createdBoBIQ.getBoBData().getContentBase64Encoded());

View file

@ -45,7 +45,7 @@ public class ForwardedTest {
public void forwardedTest() throws Exception { public void forwardedTest() throws Exception {
XmlPullParser parser; XmlPullParser parser;
String control; String control;
Forwarded fwd; Forwarded<?> fwd;
control = XMLBuilder.create("forwarded") control = XMLBuilder.create("forwarded")
.a("xmlns", "urn:xmpp:forwarded:0") .a("xmlns", "urn:xmpp:forwarded:0")
@ -71,7 +71,7 @@ public class ForwardedTest {
public void forwardedWithDelayTest() throws Exception { public void forwardedWithDelayTest() throws Exception {
XmlPullParser parser; XmlPullParser parser;
String control; String control;
Forwarded fwd; Forwarded<?> fwd;
// @formatter:off // @formatter:off
control = XMLBuilder.create("forwarded").a("xmlns", "urn:xmpp:forwarded:0") control = XMLBuilder.create("forwarded").a("xmlns", "urn:xmpp:forwarded:0")

View file

@ -44,7 +44,7 @@ import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModuleDescriptor; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModuleDescriptor;
import org.jivesoftware.smackx.admin.ServiceAdministrationManager; import org.jivesoftware.smackx.admin.ServiceAdministrationManager;
import org.jivesoftware.smackx.iqregister.AccountManager; import org.jivesoftware.smackx.iqregister.AccountManager;
@ -92,7 +92,7 @@ public class XmppConnectionManager {
.withNickname("modular-websocket") .withNickname("modular-websocket")
.applyExtraConfguration(cb -> { .applyExtraConfguration(cb -> {
cb.removeAllModules(); cb.removeAllModules();
cb.addModule(XmppWebsocketTransportModuleDescriptor.class); cb.addModule(XmppWebSocketTransportModuleDescriptor.class);
}) })
.build() .build()
); );

View file

@ -6,7 +6,7 @@ dependencies {
api project(':smack-debug') api project(':smack-debug')
api project(':smack-experimental') api project(':smack-experimental')
api project(':smack-extensions') api project(':smack-extensions')
api project(':smack-java7') api project(':smack-java8')
api project(':smack-legacy') api project(':smack-legacy')
api project(':smack-omemo') api project(':smack-omemo')
api project(':smack-openpgp') api project(':smack-openpgp')

View file

@ -32,7 +32,7 @@ digraph {
"ConnectedButUnauthenticated" -> "InstantShutdown" [xlabel="5"]; "ConnectedButUnauthenticated" -> "InstantShutdown" [xlabel="5"];
"ConnectedButUnauthenticated" [ style=filled ] "ConnectedButUnauthenticated" [ style=filled ]
"EstablishingTcpConnection" -> "ConnectedButUnauthenticated" [xlabel="2"]; "EstablishingTcpConnection" -> "ConnectedButUnauthenticated" [xlabel="2"];
"LookupRemoteConnectionEndpoints" -> "EstablishingWebsocketConnection" [xlabel="2"]; "LookupRemoteConnectionEndpoints" -> "EstablishingWebSocketConnection" [xlabel="2"];
"EstablishingWebsocketConnection" -> "ConnectedButUnauthenticated"; "EstablishingWebSocketConnection" -> "ConnectedButUnauthenticated";
"Disconnected" [ style=filled ] "Disconnected" [ style=filled ]
} }

View file

@ -33,7 +33,7 @@ public class Java7SmackInitializer implements SmackInitializer {
if (SystemUtil.onAndroid()) { if (SystemUtil.onAndroid()) {
// @formatter:off // @formatter:off
throw new RuntimeException( throw new RuntimeException(
"You need to remove the smack-java7 dependency/jar from your build, " + "You need to remove the smack-java8 dependency/jar from your build, " +
"as it does not run on Android. " + "as it does not run on Android. " +
"Use smack-android instead."); "Use smack-android instead.");
// @formatter:on // @formatter:on

View file

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

View file

@ -29,9 +29,9 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModuleDescriptor; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModuleDescriptor;
public class WebsocketConnection { public class WebSocketConnection {
public static void main(String[] args) throws SmackException, IOException, XMPPException, InterruptedException, URISyntaxException { public static void main(String[] args) throws SmackException, IOException, XMPPException, InterruptedException, URISyntaxException {
ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration.builder(); ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration.builder();
@ -39,8 +39,8 @@ public class WebsocketConnection {
builder.setXmppAddressAndPassword(args[0], args[1]); builder.setXmppAddressAndPassword(args[0], args[1]);
// Set a fallback uri into websocket transport descriptor and add this descriptor into connection builder. // Set a fallback uri into websocket transport descriptor and add this descriptor into connection builder.
XmppWebsocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebsocketTransportModuleDescriptor.getBuilder(builder); XmppWebSocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebSocketTransportModuleDescriptor.getBuilder(builder);
websocketBuilder.explicitlySetWebsocketEndpointAndDiscovery(new URI(args[2]), false); websocketBuilder.explicitlySetWebSocketEndpointAndDiscovery(new URI(args[2]), false);
builder.addModule(websocketBuilder.build()); builder.addModule(websocketBuilder.build());
ModularXmppClientToServerConnectionConfiguration config = builder.build(); ModularXmppClientToServerConnectionConfiguration config = builder.build();

View file

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

View file

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

View file

@ -0,0 +1,10 @@
description = """\
Smack for XMPP connections over WebSocket (RFC 7395) using OkHttp."""
dependencies {
api project(':smack-websocket')
testFixturesApi(testFixtures(project(':smack-websocket')))
implementation("com.squareup.okhttp3:okhttp:4.6.0")
}

View file

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations.okhttp; package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;

View file

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations.okhttp; package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
@ -27,11 +27,10 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.websocket.WebsocketException; import org.jivesoftware.smack.websocket.WebSocketException;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints; import org.jivesoftware.smack.websocket.elements.WebSocketOpenElement;
import org.jivesoftware.smack.websocket.elements.WebsocketOpenElement; import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -40,9 +39,9 @@ import okhttp3.Response;
import okhttp3.WebSocket; import okhttp3.WebSocket;
import okhttp3.WebSocketListener; import okhttp3.WebSocketListener;
public final class OkHttpWebsocket extends AbstractWebsocket { public final class OkHttpWebSocket extends AbstractWebSocket {
private static final Logger LOGGER = Logger.getLogger(OkHttpWebsocket.class.getName()); private static final Logger LOGGER = Logger.getLogger(OkHttpWebSocket.class.getName());
private static OkHttpClient okHttpClient = null; private static OkHttpClient okHttpClient = null;
@ -50,12 +49,11 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
private final LoggingInterceptor interceptor; private final LoggingInterceptor interceptor;
private String openStreamHeader; private String openStreamHeader;
private WebSocket currentWebsocket; private WebSocket currentWebSocket;
private WebsocketConnectionPhase phase; private WebSocketConnectionPhase phase;
private WebsocketRemoteConnectionEndpoint connectedEndpoint; private WebSocketRemoteConnectionEndpoint connectedEndpoint;
public OkHttpWebsocket(ModularXmppClientToServerConnectionInternal connectionInternal, public OkHttpWebSocket(ModularXmppClientToServerConnectionInternal connectionInternal) {
DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints) {
this.connectionInternal = connectionInternal; this.connectionInternal = connectionInternal;
if (okHttpClient == null) { if (okHttpClient == null) {
@ -72,8 +70,8 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
} }
@Override @Override
public void connect(WebsocketRemoteConnectionEndpoint endpoint) throws InterruptedException, SmackException, XMPPException { public void connect(WebSocketRemoteConnectionEndpoint endpoint) throws InterruptedException, SmackException, XMPPException {
final String currentUri = endpoint.getWebsocketEndpoint().toString(); final String currentUri = endpoint.getWebSocketEndpoint().toString();
Request request = new Request.Builder() Request request = new Request.Builder()
.url(currentUri) .url(currentUri)
.header("Sec-WebSocket-Protocol", "xmpp") .header("Sec-WebSocket-Protocol", "xmpp")
@ -83,12 +81,12 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
@Override @Override
public void onOpen(WebSocket webSocket, Response response) { public void onOpen(WebSocket webSocket, Response response) {
LOGGER.log(Level.FINER, "Websocket is open"); LOGGER.log(Level.FINER, "WebSocket is open");
phase = WebsocketConnectionPhase.openFrameSent; phase = WebSocketConnectionPhase.openFrameSent;
if (interceptor != null) { if (interceptor != null) {
interceptor.interceptOpenResponse(response); interceptor.interceptOpenResponse(response);
} }
send(new WebsocketOpenElement(connectionInternal.connection.getXMPPServiceDomain())); send(new WebSocketOpenElement(connectionInternal.connection.getXMPPServiceDomain()));
} }
@Override @Override
@ -107,7 +105,7 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
if (isOpenElement(text)) { if (isOpenElement(text)) {
// Converts the <open> element received into <stream> element. // Converts the <open> element received into <stream> element.
openStreamHeader = getStreamFromOpenElement(text); openStreamHeader = getStreamFromOpenElement(text);
phase = WebsocketConnectionPhase.exchangingTopLevelStreamElements; phase = WebSocketConnectionPhase.exchangingTopLevelStreamElements;
try { try {
connectionInternal.onStreamOpen(PacketParserUtils.getParserFor(openStreamHeader)); connectionInternal.onStreamOpen(PacketParserUtils.getParserFor(openStreamHeader));
@ -129,7 +127,7 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
@Override @Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) { public void onFailure(WebSocket webSocket, Throwable t, Response response) {
LOGGER.log(Level.INFO, "Exception caught", t); LOGGER.log(Level.INFO, "Exception caught", t);
WebsocketException websocketException = new WebsocketException(t); WebSocketException websocketException = new WebSocketException(t);
if (connectionInternal.connection.isConnected()) { if (connectionInternal.connection.isConnected()) {
connectionInternal.notifyConnectionError(websocketException); connectionInternal.notifyConnectionError(websocketException);
} else { } else {
@ -139,7 +137,7 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
}; };
// Creates an instance of websocket through okHttpClient. // Creates an instance of websocket through okHttpClient.
currentWebsocket = okHttpClient.newWebSocket(request, listener); currentWebSocket = okHttpClient.newWebSocket(request, listener);
// Open a new stream and wait until features are received. // Open a new stream and wait until features are received.
connectionInternal.waitForFeaturesReceived("Waiting to receive features"); connectionInternal.waitForFeaturesReceived("Waiting to receive features");
@ -153,13 +151,13 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
if (interceptor != null) { if (interceptor != null) {
interceptor.interceptSentText(textToBeSent); interceptor.interceptSentText(textToBeSent);
} }
currentWebsocket.send(textToBeSent); currentWebSocket.send(textToBeSent);
} }
@Override @Override
public void disconnect(int code, String message) { public void disconnect(int code, String message) {
currentWebsocket.close(code, message); currentWebSocket.close(code, message);
LOGGER.log(Level.INFO, "Websocket has been closed with message: " + message); LOGGER.log(Level.INFO, "WebSocket has been closed with message: " + message);
} }
@Override @Override

View file

@ -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.websocket.okhttp;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.impl.WebSocketFactory;
public class OkHttpWebSocketFactory implements WebSocketFactory {
@Override
public AbstractWebSocket create(ModularXmppClientToServerConnectionInternal connectionInternal) {
return new OkHttpWebSocket(connectionInternal);
}
}

View file

@ -14,4 +14,4 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations.okhttp; package org.jivesoftware.smack.websocket.okhttp;

View file

@ -0,0 +1 @@
org.jivesoftware.smack.websocket.okhttp.OkHttpWebSocketFactory

View file

@ -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.websocket.okhttp;
import org.jivesoftware.smack.websocket.test.WebSocketFactoryServiceTestUtil;
import org.junit.jupiter.api.Test;
public class OkHttpWebSocketFactoryServiceTest {
@Test
public void createWebSocketTest() {
WebSocketFactoryServiceTestUtil.createWebSocketTest(OkHttpWebSocket.class);
}
}

View file

@ -1,10 +1,8 @@
description = """\ description = """\
Smack for standard XMPP connections over Websockets.""" Smack for XMPP connections over WebSocket (RFC 7395)."""
dependencies { dependencies {
compile project(':smack-core') api project(':smack-core')
testFixturesApi(testFixtures(project(":smack-core"))) testFixturesApi(testFixtures(project(':smack-core')))
implementation("com.squareup.okhttp3:okhttp:4.6.0")
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2020 Aditya Borikar * Copyright 2020 Aditya Borikar, 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.
@ -16,58 +16,48 @@
*/ */
package org.jivesoftware.smack.websocket; package org.jivesoftware.smack.websocket;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.EstablishingWebsocketConnectionState; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModule.EstablishingWebSocketConnectionState;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket; import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.implementations.WebsocketImplProvider; import org.jivesoftware.smack.websocket.impl.WebSocketFactoryService;
import org.jivesoftware.smack.websocket.implementations.okhttp.OkHttpWebsocket; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
public final class WebsocketConnectionAttemptState { public final class WebSocketConnectionAttemptState {
private final ModularXmppClientToServerConnectionInternal connectionInternal; private final ModularXmppClientToServerConnectionInternal connectionInternal;
private final XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints discoveredEndpoints; private final XmppWebSocketTransportModule.XmppWebSocketTransport.DiscoveredWebSocketEndpoints discoveredEndpoints;
private WebsocketRemoteConnectionEndpoint connectedEndpoint; private WebSocketRemoteConnectionEndpoint connectedEndpoint;
WebsocketConnectionAttemptState(ModularXmppClientToServerConnectionInternal connectionInternal, WebSocketConnectionAttemptState(ModularXmppClientToServerConnectionInternal connectionInternal,
XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints, XmppWebSocketTransportModule.XmppWebSocketTransport.DiscoveredWebSocketEndpoints discoveredWebSocketEndpoints,
EstablishingWebsocketConnectionState establishingWebsocketConnectionState) { EstablishingWebSocketConnectionState establishingWebSocketConnectionState) {
assert discoveredWebsocketEndpoints != null; assert discoveredWebSocketEndpoints != null;
this.connectionInternal = connectionInternal; this.connectionInternal = connectionInternal;
this.discoveredEndpoints = discoveredWebsocketEndpoints; this.discoveredEndpoints = discoveredWebSocketEndpoints;
} }
/** /**
* Establish a websocket connection with one of the discoveredRemoteConnectionEndpoints.<br> * Establish a websocket connection with one of the discoveredRemoteConnectionEndpoints.<br>
* *
* @return {@link AbstractWebsocket} with which connection is establised * @return {@link AbstractWebSocket} with which connection is establised
* @throws InterruptedException if the calling thread was interrupted * @throws InterruptedException if the calling thread was interrupted
* @throws WebsocketException if encounters a websocket exception * @throws WebSocketException if encounters a websocket exception
*/ */
AbstractWebsocket establishWebsocketConnection() throws InterruptedException, WebsocketException { AbstractWebSocket establishWebSocketConnection() throws InterruptedException, WebSocketException {
List<WebsocketRemoteConnectionEndpoint> endpoints = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints; List<WebSocketRemoteConnectionEndpoint> endpoints = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints;
if (endpoints.isEmpty()) { if (endpoints.isEmpty()) {
throw new WebsocketException(new Throwable("No Endpoints discovered to establish connection")); throw new WebSocketException(new Throwable("No Endpoints discovered to establish connection"));
} }
List<Throwable> connectionFailureList = new ArrayList<>(); List<Throwable> connectionFailureList = new ArrayList<>();
AbstractWebsocket websocket; AbstractWebSocket websocket = WebSocketFactoryService.createWebSocket(connectionInternal);
try {
// Obtain desired websocket implementation by using WebsocketImplProvider
websocket = WebsocketImplProvider.getWebsocketImpl(OkHttpWebsocket.class, connectionInternal, discoveredEndpoints);
} catch (NoSuchMethodException | SecurityException | InstantiationException |
IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) {
throw new WebsocketException(exception);
}
// Keep iterating over available endpoints until a connection is establised or all endpoints are tried to create a connection with. // Keep iterating over available endpoints until a connection is establised or all endpoints are tried to create a connection with.
for (WebsocketRemoteConnectionEndpoint endpoint : endpoints) { for (WebSocketRemoteConnectionEndpoint endpoint : endpoints) {
try { try {
websocket.connect(endpoint); websocket.connect(endpoint);
connectedEndpoint = endpoint; connectedEndpoint = endpoint;
@ -78,8 +68,8 @@ public final class WebsocketConnectionAttemptState {
// If the number of entries in connectionFailureList is equal to the number of endpoints, // If the number of entries in connectionFailureList is equal to the number of endpoints,
// it means that all endpoints have been tried and have been unsuccessful. // it means that all endpoints have been tried and have been unsuccessful.
if (connectionFailureList.size() == endpoints.size()) { if (connectionFailureList.size() == endpoints.size()) {
WebsocketException websocketException = new WebsocketException(connectionFailureList); WebSocketException websocketException = new WebSocketException(connectionFailureList);
throw new WebsocketException(websocketException); throw new WebSocketException(websocketException);
} }
} }
} }
@ -95,7 +85,7 @@ public final class WebsocketConnectionAttemptState {
* *
* @return connected websocket endpoint * @return connected websocket endpoint
*/ */
public WebsocketRemoteConnectionEndpoint getConnectedEndpoint() { public WebSocketRemoteConnectionEndpoint getConnectedEndpoint() {
return connectedEndpoint; return connectedEndpoint;
} }
} }

View file

@ -19,16 +19,16 @@ package org.jivesoftware.smack.websocket;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
public final class WebsocketException extends Exception { public final class WebSocketException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final List<Throwable> throwableList; private final List<Throwable> throwableList;
public WebsocketException(List<Throwable> throwableList) { public WebSocketException(List<Throwable> throwableList) {
this.throwableList = throwableList; this.throwableList = throwableList;
} }
public WebsocketException(Throwable throwable) { public WebSocketException(Throwable throwable) {
this.throwableList = Collections.singletonList(throwable); this.throwableList = Collections.singletonList(throwable);
} }

View file

@ -19,10 +19,10 @@ package org.jivesoftware.smack.websocket;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.initializer.UrlInitializer; import org.jivesoftware.smack.initializer.UrlInitializer;
public final class WebsocketInitializer extends UrlInitializer { public final class WebSocketInitializer extends UrlInitializer {
static { static {
SmackConfiguration.addModule(XmppWebsocketTransportModuleDescriptor.class); SmackConfiguration.addModule(XmppWebSocketTransportModuleDescriptor.class);
} }
} }

View file

@ -50,13 +50,13 @@ import org.jivesoftware.smack.packet.AbstractStreamOpen;
import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure; import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModule.XmppWebSocketTransport.DiscoveredWebSocketEndpoints;
import org.jivesoftware.smack.websocket.elements.WebsocketCloseElement; import org.jivesoftware.smack.websocket.elements.WebSocketCloseElement;
import org.jivesoftware.smack.websocket.elements.WebsocketOpenElement; import org.jivesoftware.smack.websocket.elements.WebSocketOpenElement;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket; import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpointLookup;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpointLookup.Result;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
@ -65,44 +65,44 @@ import org.jxmpp.stringprep.XmppStringprepException;
/** /**
* The websocket transport module that goes with Smack's modular architecture. * The websocket transport module that goes with Smack's modular architecture.
*/ */
public final class XmppWebsocketTransportModule public final class XmppWebSocketTransportModule
extends ModularXmppClientToServerConnectionModule<XmppWebsocketTransportModuleDescriptor> { extends ModularXmppClientToServerConnectionModule<XmppWebSocketTransportModuleDescriptor> {
private final XmppWebsocketTransport websocketTransport; private final XmppWebSocketTransport websocketTransport;
private AbstractWebsocket websocket; private AbstractWebSocket websocket;
protected XmppWebsocketTransportModule(XmppWebsocketTransportModuleDescriptor moduleDescriptor, protected XmppWebSocketTransportModule(XmppWebSocketTransportModuleDescriptor moduleDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) { ModularXmppClientToServerConnectionInternal connectionInternal) {
super(moduleDescriptor, connectionInternal); super(moduleDescriptor, connectionInternal);
websocketTransport = new XmppWebsocketTransport(connectionInternal); websocketTransport = new XmppWebSocketTransport(connectionInternal);
} }
@Override @Override
protected XmppWebsocketTransport getTransport() { protected XmppWebSocketTransport getTransport() {
return websocketTransport; return websocketTransport;
} }
static final class EstablishingWebsocketConnectionStateDescriptor extends StateDescriptor { static final class EstablishingWebSocketConnectionStateDescriptor extends StateDescriptor {
private EstablishingWebsocketConnectionStateDescriptor() { private EstablishingWebSocketConnectionStateDescriptor() {
super(XmppWebsocketTransportModule.EstablishingWebsocketConnectionState.class); super(XmppWebSocketTransportModule.EstablishingWebSocketConnectionState.class);
addPredeccessor(LookupRemoteConnectionEndpointsStateDescriptor.class); addPredeccessor(LookupRemoteConnectionEndpointsStateDescriptor.class);
addSuccessor(ConnectedButUnauthenticatedStateDescriptor.class); addSuccessor(ConnectedButUnauthenticatedStateDescriptor.class);
// This states preference to TCP transports over this Websocket transport implementation. // This states preference to TCP transports over this WebSocket transport implementation.
declareInferiorityTo("org.jivesoftware.smack.tcp.XmppTcpTransportModule$EstablishingTcpConnectionStateDescriptor"); declareInferiorityTo("org.jivesoftware.smack.tcp.XmppTcpTransportModule$EstablishingTcpConnectionStateDescriptor");
} }
@Override @Override
protected State constructState(ModularXmppClientToServerConnectionInternal connectionInternal) { protected State constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
XmppWebsocketTransportModule websocketTransportModule = connectionInternal.connection.getConnectionModuleFor( XmppWebSocketTransportModule websocketTransportModule = connectionInternal.connection.getConnectionModuleFor(
XmppWebsocketTransportModuleDescriptor.class); XmppWebSocketTransportModuleDescriptor.class);
return websocketTransportModule.constructEstablishingWebsocketConnectionState(this, connectionInternal); return websocketTransportModule.constructEstablishingWebSocketConnectionState(this, connectionInternal);
} }
} }
final class EstablishingWebsocketConnectionState extends State { final class EstablishingWebSocketConnectionState extends State {
protected EstablishingWebsocketConnectionState(StateDescriptor stateDescriptor, protected EstablishingWebSocketConnectionState(StateDescriptor stateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) { ModularXmppClientToServerConnectionInternal connectionInternal) {
super(stateDescriptor, connectionInternal); super(stateDescriptor, connectionInternal);
} }
@ -110,78 +110,78 @@ public final class XmppWebsocketTransportModule
@Override @Override
public AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) public AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext)
throws IOException, SmackException, InterruptedException, XMPPException { throws IOException, SmackException, InterruptedException, XMPPException {
WebsocketConnectionAttemptState connectionAttemptState = new WebsocketConnectionAttemptState( WebSocketConnectionAttemptState connectionAttemptState = new WebSocketConnectionAttemptState(
connectionInternal, discoveredWebsocketEndpoints, this); connectionInternal, discoveredWebSocketEndpoints, this);
try { try {
websocket = connectionAttemptState.establishWebsocketConnection(); websocket = connectionAttemptState.establishWebSocketConnection();
} catch (InterruptedException | WebsocketException e) { } catch (InterruptedException | WebSocketException e) {
StateTransitionResult.Failure failure = new StateTransitionResult.FailureCausedByException<Exception>(e); StateTransitionResult.Failure failure = new StateTransitionResult.FailureCausedByException<Exception>(e);
return failure; return failure;
} }
connectionInternal.setTransport(websocketTransport); connectionInternal.setTransport(websocketTransport);
WebsocketRemoteConnectionEndpoint connectedEndpoint = connectionAttemptState.getConnectedEndpoint(); WebSocketRemoteConnectionEndpoint connectedEndpoint = connectionAttemptState.getConnectedEndpoint();
// Construct a WebsocketConnectedResult using the connected endpoint. // Construct a WebSocketConnectedResult using the connected endpoint.
return new WebsocketConnectedResult(connectedEndpoint); return new WebSocketConnectedResult(connectedEndpoint);
} }
} }
public EstablishingWebsocketConnectionState constructEstablishingWebsocketConnectionState( public EstablishingWebSocketConnectionState constructEstablishingWebSocketConnectionState(
EstablishingWebsocketConnectionStateDescriptor establishingWebsocketConnectionStateDescriptor, EstablishingWebSocketConnectionStateDescriptor establishingWebSocketConnectionStateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) { ModularXmppClientToServerConnectionInternal connectionInternal) {
return new EstablishingWebsocketConnectionState(establishingWebsocketConnectionStateDescriptor, return new EstablishingWebSocketConnectionState(establishingWebSocketConnectionStateDescriptor,
connectionInternal); connectionInternal);
} }
public static final class WebsocketConnectedResult extends StateTransitionResult.Success { public static final class WebSocketConnectedResult extends StateTransitionResult.Success {
final WebsocketRemoteConnectionEndpoint connectedEndpoint; final WebSocketRemoteConnectionEndpoint connectedEndpoint;
public WebsocketConnectedResult(WebsocketRemoteConnectionEndpoint connectedEndpoint) { public WebSocketConnectedResult(WebSocketRemoteConnectionEndpoint connectedEndpoint) {
super("Websocket connection establised with endpoint: " + connectedEndpoint.getWebsocketEndpoint()); super("WebSocket connection establised with endpoint: " + connectedEndpoint.getWebSocketEndpoint());
this.connectedEndpoint = connectedEndpoint; this.connectedEndpoint = connectedEndpoint;
} }
} }
private DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints; private DiscoveredWebSocketEndpoints discoveredWebSocketEndpoints;
/** /**
* Transport class for {@link ModularXmppClientToServerConnectionModule}'s websocket implementation. * Transport class for {@link ModularXmppClientToServerConnectionModule}'s websocket implementation.
*/ */
public final class XmppWebsocketTransport extends XmppClientToServerTransport { public final class XmppWebSocketTransport extends XmppClientToServerTransport {
AsyncButOrdered<Queue<TopLevelStreamElement>> asyncButOrderedOutgoingElementsQueue; AsyncButOrdered<Queue<TopLevelStreamElement>> asyncButOrderedOutgoingElementsQueue;
protected XmppWebsocketTransport(ModularXmppClientToServerConnectionInternal connectionInternal) { protected XmppWebSocketTransport(ModularXmppClientToServerConnectionInternal connectionInternal) {
super(connectionInternal); super(connectionInternal);
asyncButOrderedOutgoingElementsQueue = new AsyncButOrdered<Queue<TopLevelStreamElement>>(); asyncButOrderedOutgoingElementsQueue = new AsyncButOrdered<Queue<TopLevelStreamElement>>();
} }
@Override @Override
protected void resetDiscoveredConnectionEndpoints() { protected void resetDiscoveredConnectionEndpoints() {
discoveredWebsocketEndpoints = null; discoveredWebSocketEndpoints = null;
} }
@Override @Override
protected List<SmackFuture<LookupConnectionEndpointsResult, Exception>> lookupConnectionEndpoints() { protected List<SmackFuture<LookupConnectionEndpointsResult, Exception>> lookupConnectionEndpoints() {
// Assert that there are no stale discovered endpoints prior performing the lookup. // Assert that there are no stale discovered endpoints prior performing the lookup.
assert discoveredWebsocketEndpoints == null; assert discoveredWebSocketEndpoints == null;
InternalSmackFuture<LookupConnectionEndpointsResult, Exception> websocketEndpointsLookupFuture = new InternalSmackFuture<>(); InternalSmackFuture<LookupConnectionEndpointsResult, Exception> websocketEndpointsLookupFuture = new InternalSmackFuture<>();
connectionInternal.asyncGo(() -> { connectionInternal.asyncGo(() -> {
WebsocketRemoteConnectionEndpoint providedEndpoint = null; WebSocketRemoteConnectionEndpoint providedEndpoint = null;
// Check if there is a websocket endpoint already configured. // Check if there is a websocket endpoint already configured.
URI uri = moduleDescriptor.getExplicitlyProvidedUri(); URI uri = moduleDescriptor.getExplicitlyProvidedUri();
if (uri != null) { if (uri != null) {
providedEndpoint = new WebsocketRemoteConnectionEndpoint(uri); providedEndpoint = new WebSocketRemoteConnectionEndpoint(uri);
} }
if (!moduleDescriptor.isWebsocketEndpointDiscoveryEnabled()) { if (!moduleDescriptor.isWebSocketEndpointDiscoveryEnabled()) {
// If discovery is disabled, assert that the provided endpoint isn't null. // If discovery is disabled, assert that the provided endpoint isn't null.
assert providedEndpoint != null; assert providedEndpoint != null;
@ -190,14 +190,14 @@ public final class XmppWebsocketTransportModule
mode.equals(SecurityMode.disabled)) mode.equals(SecurityMode.disabled))
|| (!providedEndpoint.isSecureEndpoint() && || (!providedEndpoint.isSecureEndpoint() &&
mode.equals(SecurityMode.required))) { mode.equals(SecurityMode.required))) {
throw new IllegalStateException("Explicitly configured uri: " + providedEndpoint.getWebsocketEndpoint().toString() throw new IllegalStateException("Explicitly configured uri: " + providedEndpoint.getWebSocketEndpoint().toString()
+ " does not comply with the configured security mode: " + mode); + " does not comply with the configured security mode: " + mode);
} }
// Generate Result for explicitly configured endpoint. // Generate Result for explicitly configured endpoint.
Result manualResult = new Result(Arrays.asList(providedEndpoint), null); Result manualResult = new Result(Arrays.asList(providedEndpoint), null);
LookupConnectionEndpointsResult endpointsResult = new DiscoveredWebsocketEndpoints(manualResult); LookupConnectionEndpointsResult endpointsResult = new DiscoveredWebSocketEndpoints(manualResult);
websocketEndpointsLookupFuture.setResult(endpointsResult); websocketEndpointsLookupFuture.setResult(endpointsResult);
} else { } else {
@ -206,14 +206,14 @@ public final class XmppWebsocketTransportModule
SecurityMode mode = configuration.getSecurityMode(); SecurityMode mode = configuration.getSecurityMode();
// Fetch remote endpoints. // Fetch remote endpoints.
Result xep0156result = WebsocketRemoteConnectionEndpointLookup.lookup(host, mode); Result xep0156result = WebSocketRemoteConnectionEndpointLookup.lookup(host, mode);
List<WebsocketRemoteConnectionEndpoint> discoveredEndpoints = xep0156result.discoveredRemoteConnectionEndpoints; List<WebSocketRemoteConnectionEndpoint> discoveredEndpoints = xep0156result.discoveredRemoteConnectionEndpoints;
// Generate result considering both manual and fetched endpoints. // Generate result considering both manual and fetched endpoints.
Result finalResult = new Result(discoveredEndpoints, xep0156result.getLookupFailures()); Result finalResult = new Result(discoveredEndpoints, xep0156result.getLookupFailures());
LookupConnectionEndpointsResult endpointsResult = new DiscoveredWebsocketEndpoints(finalResult); LookupConnectionEndpointsResult endpointsResult = new DiscoveredWebSocketEndpoints(finalResult);
websocketEndpointsLookupFuture.setResult(endpointsResult); websocketEndpointsLookupFuture.setResult(endpointsResult);
} }
@ -224,7 +224,7 @@ public final class XmppWebsocketTransportModule
@Override @Override
protected void loadConnectionEndpoints(LookupConnectionEndpointsSuccess lookupConnectionEndpointsSuccess) { protected void loadConnectionEndpoints(LookupConnectionEndpointsSuccess lookupConnectionEndpointsSuccess) {
discoveredWebsocketEndpoints = (DiscoveredWebsocketEndpoints) lookupConnectionEndpointsSuccess; discoveredWebSocketEndpoints = (DiscoveredWebSocketEndpoints) lookupConnectionEndpointsSuccess;
} }
@Override @Override
@ -233,7 +233,7 @@ public final class XmppWebsocketTransportModule
@Override @Override
protected void disconnect() { protected void disconnect() {
websocket.disconnect(1000, "Websocket closed normally"); websocket.disconnect(1000, "WebSocket closed normally");
} }
@Override @Override
@ -272,7 +272,7 @@ public final class XmppWebsocketTransportModule
@Override @Override
public AbstractStreamOpen createStreamOpen(CharSequence to, CharSequence from, String id, String lang) { public AbstractStreamOpen createStreamOpen(CharSequence to, CharSequence from, String id, String lang) {
try { try {
return new WebsocketOpenElement(JidCreate.domainBareFrom(to)); return new WebSocketOpenElement(JidCreate.domainBareFrom(to));
} catch (XmppStringprepException e) { } catch (XmppStringprepException e) {
Logger.getAnonymousLogger().log(Level.WARNING, "Couldn't create OpenElement", e); Logger.getAnonymousLogger().log(Level.WARNING, "Couldn't create OpenElement", e);
return null; return null;
@ -280,7 +280,7 @@ public final class XmppWebsocketTransportModule
} }
@Override @Override
public AbstractStreamClose createStreamClose() { public AbstractStreamClose createStreamClose() {
return new WebsocketCloseElement(); return new WebSocketCloseElement();
} }
}; };
} }
@ -288,15 +288,15 @@ public final class XmppWebsocketTransportModule
/** /**
* Contains {@link Result} for successfully discovered endpoints. * Contains {@link Result} for successfully discovered endpoints.
*/ */
public final class DiscoveredWebsocketEndpoints implements LookupConnectionEndpointsSuccess { public final class DiscoveredWebSocketEndpoints implements LookupConnectionEndpointsSuccess {
final WebsocketRemoteConnectionEndpointLookup.Result result; final WebSocketRemoteConnectionEndpointLookup.Result result;
DiscoveredWebsocketEndpoints(Result result) { DiscoveredWebSocketEndpoints(Result result) {
assert result != null; assert result != null;
this.result = result; this.result = result;
} }
public WebsocketRemoteConnectionEndpointLookup.Result getResult() { public WebSocketRemoteConnectionEndpointLookup.Result getResult() {
return result; return result;
} }
} }
@ -305,11 +305,11 @@ public final class XmppWebsocketTransportModule
* Contains list of {@link RemoteConnectionEndpointLookupFailure} when no endpoint * Contains list of {@link RemoteConnectionEndpointLookupFailure} when no endpoint
* could be found during http lookup. * could be found during http lookup.
*/ */
final class WebsocketEndpointsDiscoveryFailed implements LookupConnectionEndpointsFailed { final class WebSocketEndpointsDiscoveryFailed implements LookupConnectionEndpointsFailed {
final List<RemoteConnectionEndpointLookupFailure> lookupFailures; final List<RemoteConnectionEndpointLookupFailure> lookupFailures;
WebsocketEndpointsDiscoveryFailed( WebSocketEndpointsDiscoveryFailed(
WebsocketRemoteConnectionEndpointLookup.Result result) { WebSocketRemoteConnectionEndpointLookup.Result result) {
assert result != null; assert result != null;
lookupFailures = Collections.unmodifiableList(result.lookupFailures); lookupFailures = Collections.unmodifiableList(result.lookupFailures);
} }

View file

@ -28,20 +28,20 @@ import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescr
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.fsm.StateDescriptor; import org.jivesoftware.smack.fsm.StateDescriptor;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.EstablishingWebsocketConnectionStateDescriptor; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModule.EstablishingWebSocketConnectionStateDescriptor;
/** /**
* The descriptor class for {@link XmppWebsocketTransportModule}. * The descriptor class for {@link XmppWebSocketTransportModule}.
* <br> * <br>
* To add {@link XmppWebsocketTransportModule} to {@link ModularXmppClientToServerConnection}, * To add {@link XmppWebSocketTransportModule} to {@link ModularXmppClientToServerConnection},
* use {@link ModularXmppClientToServerConnectionConfiguration.Builder#addModule(ModularXmppClientToServerConnectionModuleDescriptor)}. * use {@link ModularXmppClientToServerConnectionConfiguration.Builder#addModule(ModularXmppClientToServerConnectionModuleDescriptor)}.
*/ */
public final class XmppWebsocketTransportModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor { public final class XmppWebSocketTransportModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor {
private boolean performWebsocketEndpointDiscovery; private boolean performWebSocketEndpointDiscovery;
private URI uri; private URI uri;
public XmppWebsocketTransportModuleDescriptor(Builder builder) { public XmppWebSocketTransportModuleDescriptor(Builder builder) {
this.performWebsocketEndpointDiscovery = builder.performWebsocketEndpointDiscovery; this.performWebSocketEndpointDiscovery = builder.performWebSocketEndpointDiscovery;
this.uri = builder.uri; this.uri = builder.uri;
} }
@ -49,8 +49,8 @@ public final class XmppWebsocketTransportModuleDescriptor extends ModularXmppCli
* Returns true if websocket endpoint discovery is true, returns false otherwise. * Returns true if websocket endpoint discovery is true, returns false otherwise.
* @return boolean * @return boolean
*/ */
public boolean isWebsocketEndpointDiscoveryEnabled() { public boolean isWebSocketEndpointDiscoveryEnabled() {
return performWebsocketEndpointDiscovery; return performWebSocketEndpointDiscovery;
} }
/** /**
@ -64,14 +64,14 @@ public final class XmppWebsocketTransportModuleDescriptor extends ModularXmppCli
@Override @Override
protected Set<Class<? extends StateDescriptor>> getStateDescriptors() { protected Set<Class<? extends StateDescriptor>> getStateDescriptors() {
Set<Class<? extends StateDescriptor>> res = new HashSet<>(); Set<Class<? extends StateDescriptor>> res = new HashSet<>();
res.add(EstablishingWebsocketConnectionStateDescriptor.class); res.add(EstablishingWebSocketConnectionStateDescriptor.class);
return res; return res;
} }
@Override @Override
protected ModularXmppClientToServerConnectionModule<? extends ModularXmppClientToServerConnectionModuleDescriptor> constructXmppConnectionModule( protected ModularXmppClientToServerConnectionModule<? extends ModularXmppClientToServerConnectionModuleDescriptor> constructXmppConnectionModule(
ModularXmppClientToServerConnectionInternal connectionInternal) { ModularXmppClientToServerConnectionInternal connectionInternal) {
return new XmppWebsocketTransportModule(this, connectionInternal); return new XmppWebSocketTransportModule(this, connectionInternal);
} }
/** /**
@ -86,19 +86,19 @@ public final class XmppWebsocketTransportModuleDescriptor extends ModularXmppCli
} }
/** /**
* Builder class for {@link XmppWebsocketTransportModuleDescriptor}. * Builder class for {@link XmppWebSocketTransportModuleDescriptor}.
* <br> * <br>
* To obtain an instance of {@link XmppWebsocketTransportModuleDescriptor.Builder}, use {@link XmppWebsocketTransportModuleDescriptor#getBuilder(ModularXmppClientToServerConnectionConfiguration.Builder)} method. * To obtain an instance of {@link XmppWebSocketTransportModuleDescriptor.Builder}, use {@link XmppWebSocketTransportModuleDescriptor#getBuilder(ModularXmppClientToServerConnectionConfiguration.Builder)} method.
* <br> * <br>
* Use {@link Builder#explicitlySetWebsocketEndpoint(URI)} to configure the URI of an endpoint as a backup in case connection couldn't be established with endpoints through http lookup. * Use {@link Builder#explicitlySetWebSocketEndpoint(URI)} to configure the URI of an endpoint as a backup in case connection couldn't be established with endpoints through http lookup.
* <br> * <br>
* Use {@link Builder#explicitlySetWebsocketEndpointAndDiscovery(URI, boolean)} to configure endpoint and disallow websocket endpoint discovery through http lookup. * Use {@link Builder#explicitlySetWebSocketEndpointAndDiscovery(URI, boolean)} to configure endpoint and disallow websocket endpoint discovery through http lookup.
* By default, {@link Builder#performWebsocketEndpointDiscovery} is set to true. * By default, {@link Builder#performWebSocketEndpointDiscovery} is set to true.
* <br> * <br>
* Use {@link Builder#build()} to obtain {@link XmppWebsocketTransportModuleDescriptor}. * Use {@link Builder#build()} to obtain {@link XmppWebSocketTransportModuleDescriptor}.
*/ */
public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder { public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder {
private boolean performWebsocketEndpointDiscovery = true; private boolean performWebSocketEndpointDiscovery = true;
private URI uri; private URI uri;
private Builder( private Builder(
@ -106,31 +106,31 @@ public final class XmppWebsocketTransportModuleDescriptor extends ModularXmppCli
super(connectionConfigurationBuilder); super(connectionConfigurationBuilder);
} }
public Builder explicitlySetWebsocketEndpoint(URI endpoint) { public Builder explicitlySetWebSocketEndpoint(URI endpoint) {
return explicitlySetWebsocketEndpointAndDiscovery(endpoint, true); return explicitlySetWebSocketEndpointAndDiscovery(endpoint, true);
} }
public Builder explicitlySetWebsocketEndpointAndDiscovery(URI endpoint, boolean performWebsocketEndpointDiscovery) { public Builder explicitlySetWebSocketEndpointAndDiscovery(URI endpoint, boolean performWebSocketEndpointDiscovery) {
Objects.requireNonNull(endpoint, "Provided endpoint URI must not be null"); Objects.requireNonNull(endpoint, "Provided endpoint URI must not be null");
this.uri = endpoint; this.uri = endpoint;
this.performWebsocketEndpointDiscovery = performWebsocketEndpointDiscovery; this.performWebSocketEndpointDiscovery = performWebSocketEndpointDiscovery;
return this; return this;
} }
public Builder explicitlySetWebsocketEndpoint(CharSequence endpoint) throws URISyntaxException { public Builder explicitlySetWebSocketEndpoint(CharSequence endpoint) throws URISyntaxException {
URI endpointUri = new URI(endpoint.toString()); URI endpointUri = new URI(endpoint.toString());
return explicitlySetWebsocketEndpointAndDiscovery(endpointUri, true); return explicitlySetWebSocketEndpointAndDiscovery(endpointUri, true);
} }
public Builder explicitlySetWebsocketEndpoint(CharSequence endpoint, boolean performWebsocketEndpointDiscovery) public Builder explicitlySetWebSocketEndpoint(CharSequence endpoint, boolean performWebSocketEndpointDiscovery)
throws URISyntaxException { throws URISyntaxException {
URI endpointUri = new URI(endpoint.toString()); URI endpointUri = new URI(endpoint.toString());
return explicitlySetWebsocketEndpointAndDiscovery(endpointUri, performWebsocketEndpointDiscovery); return explicitlySetWebSocketEndpointAndDiscovery(endpointUri, performWebSocketEndpointDiscovery);
} }
@Override @Override
public ModularXmppClientToServerConnectionModuleDescriptor build() { public ModularXmppClientToServerConnectionModuleDescriptor build() {
return new XmppWebsocketTransportModuleDescriptor(this); return new XmppWebSocketTransportModuleDescriptor(this);
} }
} }
} }

View file

@ -22,12 +22,12 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
public abstract class AbstractWebsocketNonza implements Nonza { public abstract class AbstractWebSocketNonza implements Nonza {
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-framing"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-framing";
private static final String VERSION = "1.0"; private static final String VERSION = "1.0";
private final DomainBareJid to; private final DomainBareJid to;
public AbstractWebsocketNonza(DomainBareJid jid) { public AbstractWebSocketNonza(DomainBareJid jid) {
this.to = jid; this.to = jid;
} }

View file

@ -22,12 +22,12 @@ import org.jivesoftware.smack.packet.AbstractStreamClose;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
public final class WebsocketCloseElement extends AbstractStreamClose { public final class WebSocketCloseElement extends AbstractStreamClose {
public static final String ELEMENT = "close"; public static final String ELEMENT = "close";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-framing"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-framing";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT); public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public WebsocketCloseElement() { public WebSocketCloseElement() {
} }
@Override @Override

View file

@ -25,12 +25,12 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
public final class WebsocketOpenElement extends AbstractStreamOpen { public final class WebSocketOpenElement extends AbstractStreamOpen {
public static final String ELEMENT = "open"; public static final String ELEMENT = "open";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-framing"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-framing";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT); public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public WebsocketOpenElement(DomainBareJid to) { public WebSocketOpenElement(DomainBareJid to) {
super(to, null, null, null, StreamContentNamespace.client); super(to, null, null, null, StreamContentNamespace.client);
} }

View file

@ -14,16 +14,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations; package org.jivesoftware.smack.websocket.impl;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
public abstract class AbstractWebsocket { public abstract class AbstractWebSocket {
protected enum WebsocketConnectionPhase { protected enum WebSocketConnectionPhase {
openFrameSent, openFrameSent,
exchangingTopLevelStreamElements exchangingTopLevelStreamElements
} }
@ -49,7 +49,7 @@ public abstract class AbstractWebsocket {
return false; return false;
} }
public abstract void connect(WebsocketRemoteConnectionEndpoint endpoint) throws Throwable; public abstract void connect(WebSocketRemoteConnectionEndpoint endpoint) throws Throwable;
public abstract void send(TopLevelStreamElement element); public abstract void send(TopLevelStreamElement element);

View file

@ -0,0 +1,25 @@
/**
*
* 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.websocket.impl;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
public interface WebSocketFactory {
AbstractWebSocket create(ModularXmppClientToServerConnectionInternal connectionInternal);
}

View file

@ -0,0 +1,40 @@
/**
*
* 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.websocket.impl;
import java.util.Iterator;
import java.util.ServiceLoader;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
public final class WebSocketFactoryService {
private static final ServiceLoader<WebSocketFactory> SERVICE_LOADER = ServiceLoader.load(WebSocketFactory.class);
public static AbstractWebSocket createWebSocket(ModularXmppClientToServerConnectionInternal connectionInternal) {
assert connectionInternal != null;
Iterator<WebSocketFactory> websocketFactories = SERVICE_LOADER.iterator();
if (!websocketFactories.hasNext()) {
throw new IllegalStateException("No smack websocket service configured");
}
WebSocketFactory websocketFactory = websocketFactories.next();
return websocketFactory.create(connectionInternal);
}
}

View file

@ -17,4 +17,4 @@
/** /**
* This package contains websocket implementations to be plugged inside websocket transport. * This package contains websocket implementations to be plugged inside websocket transport.
*/ */
package org.jivesoftware.smack.websocket.implementations; package org.jivesoftware.smack.websocket.impl;

View file

@ -1,35 +0,0 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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.websocket.implementations;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
public final class WebsocketImplProvider {
public static AbstractWebsocket getWebsocketImpl(Class<? extends AbstractWebsocket> websocketImpl, ModularXmppClientToServerConnectionInternal connectionInternal, DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Objects.requireNonNull(connectionInternal, "ConnectionInternal cannot be null");
// Creates an instance of the constructor for the desired websocket implementation.
Constructor<? extends AbstractWebsocket> constructor = websocketImpl.getConstructor(ModularXmppClientToServerConnectionInternal.class, DiscoveredWebsocketEndpoints.class);
return (AbstractWebsocket) constructor.newInstance(connectionInternal, discoveredWebsocketEndpoints);
}
}

View file

@ -15,6 +15,6 @@
* limitations under the License. * limitations under the License.
*/ */
/** /**
* Websocket related classes for Smack. * WebSocket related classes for Smack.
*/ */
package org.jivesoftware.smack.websocket; package org.jivesoftware.smack.websocket;

View file

@ -28,17 +28,17 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint; import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
public final class WebsocketRemoteConnectionEndpoint implements RemoteConnectionEndpoint { public final class WebSocketRemoteConnectionEndpoint implements RemoteConnectionEndpoint {
private static final Logger LOGGER = Logger.getAnonymousLogger(); private static final Logger LOGGER = Logger.getAnonymousLogger();
private final URI uri; private final URI uri;
public WebsocketRemoteConnectionEndpoint(String uri) throws URISyntaxException { public WebSocketRemoteConnectionEndpoint(String uri) throws URISyntaxException {
this(new URI(uri)); this(new URI(uri));
} }
public WebsocketRemoteConnectionEndpoint(URI uri) { public WebSocketRemoteConnectionEndpoint(URI uri) {
this.uri = uri; this.uri = uri;
String scheme = uri.getScheme(); String scheme = uri.getScheme();
if (!(scheme.equals("ws") || scheme.equals("wss"))) { if (!(scheme.equals("ws") || scheme.equals("wss"))) {
@ -46,7 +46,7 @@ public final class WebsocketRemoteConnectionEndpoint implements RemoteConnection
} }
} }
public URI getWebsocketEndpoint() { public URI getWebSocketEndpoint() {
return uri; return uri;
} }

View file

@ -31,11 +31,11 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
public final class WebsocketRemoteConnectionEndpointLookup { public final class WebSocketRemoteConnectionEndpointLookup {
public static Result lookup(DomainBareJid domainBareJid, SecurityMode securityMode) { public static Result lookup(DomainBareJid domainBareJid, SecurityMode securityMode) {
List<RemoteConnectionEndpointLookupFailure> lookupFailures = new ArrayList<>(1); List<RemoteConnectionEndpointLookupFailure> lookupFailures = new ArrayList<>(1);
List<WebsocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints = new ArrayList<>(); List<WebSocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints = new ArrayList<>();
List<URI> rcUriList = null; List<URI> rcUriList = null;
try { try {
@ -52,11 +52,11 @@ public final class WebsocketRemoteConnectionEndpointLookup {
throw new IllegalStateException("No endpoints were found inside host-meta"); throw new IllegalStateException("No endpoints were found inside host-meta");
} }
// Convert rcUriList to List<WebsocketRemoteConnectionEndpoint> // Convert rcUriList to List<WebSocketRemoteConnectionEndpoint>
Iterator<URI> iterator = rcUriList.iterator(); Iterator<URI> iterator = rcUriList.iterator();
List<WebsocketRemoteConnectionEndpoint> rceList = new ArrayList<>(); List<WebSocketRemoteConnectionEndpoint> rceList = new ArrayList<>();
while (iterator.hasNext()) { while (iterator.hasNext()) {
rceList.add(new WebsocketRemoteConnectionEndpoint(iterator.next())); rceList.add(new WebSocketRemoteConnectionEndpoint(iterator.next()));
} }
switch (securityMode) { switch (securityMode) {
@ -64,9 +64,9 @@ public final class WebsocketRemoteConnectionEndpointLookup {
// If security mode equals `if-possible`, give priority to secure endpoints over insecure endpoints. // If security mode equals `if-possible`, give priority to secure endpoints over insecure endpoints.
// Seprate secure and unsecure endpoints. // Seprate secure and unsecure endpoints.
List<WebsocketRemoteConnectionEndpoint> secureEndpointsForSecurityModeIfPossible = new ArrayList<>(); List<WebSocketRemoteConnectionEndpoint> secureEndpointsForSecurityModeIfPossible = new ArrayList<>();
List<WebsocketRemoteConnectionEndpoint> insecureEndpointsForSecurityModeIfPossible = new ArrayList<>(); List<WebSocketRemoteConnectionEndpoint> insecureEndpointsForSecurityModeIfPossible = new ArrayList<>();
for (WebsocketRemoteConnectionEndpoint uri : rceList) { for (WebSocketRemoteConnectionEndpoint uri : rceList) {
if (uri.isSecureEndpoint()) { if (uri.isSecureEndpoint()) {
secureEndpointsForSecurityModeIfPossible.add(uri); secureEndpointsForSecurityModeIfPossible.add(uri);
} else { } else {
@ -82,7 +82,7 @@ public final class WebsocketRemoteConnectionEndpointLookup {
* If, SecurityMode equals to required, accept wss endpoints (secure endpoints) only or, * If, SecurityMode equals to required, accept wss endpoints (secure endpoints) only or,
* if SecurityMode equals to disabled, accept ws endpoints (unsecure endpoints) only. * if SecurityMode equals to disabled, accept ws endpoints (unsecure endpoints) only.
*/ */
for (WebsocketRemoteConnectionEndpoint uri : rceList) { for (WebSocketRemoteConnectionEndpoint uri : rceList) {
if ((securityMode.equals(SecurityMode.disabled) && !uri.isSecureEndpoint()) if ((securityMode.equals(SecurityMode.disabled) && !uri.isSecureEndpoint())
|| (securityMode.equals(SecurityMode.required) && uri.isSecureEndpoint())) { || (securityMode.equals(SecurityMode.required) && uri.isSecureEndpoint())) {
discoveredRemoteConnectionEndpoints.add(uri); discoveredRemoteConnectionEndpoints.add(uri);
@ -95,16 +95,16 @@ public final class WebsocketRemoteConnectionEndpointLookup {
} }
public static final class Result { public static final class Result {
public final List<WebsocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints; public final List<WebSocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints;
public final List<RemoteConnectionEndpointLookupFailure> lookupFailures; public final List<RemoteConnectionEndpointLookupFailure> lookupFailures;
public Result(List<WebsocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints, public Result(List<WebSocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints,
List<RemoteConnectionEndpointLookupFailure> lookupFailures) { List<RemoteConnectionEndpointLookupFailure> lookupFailures) {
this.discoveredRemoteConnectionEndpoints = discoveredRemoteConnectionEndpoints; this.discoveredRemoteConnectionEndpoints = discoveredRemoteConnectionEndpoints;
this.lookupFailures = lookupFailures; this.lookupFailures = lookupFailures;
} }
public List<WebsocketRemoteConnectionEndpoint> getDiscoveredRemoteConnectionEndpoints() { public List<WebSocketRemoteConnectionEndpoint> getDiscoveredRemoteConnectionEndpoints() {
return discoveredRemoteConnectionEndpoints; return discoveredRemoteConnectionEndpoints;
} }

View file

@ -20,9 +20,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class WebsocketConnectionAttemptStateTest { public class WebSocketConnectionAttemptStateTest {
@Test @Test
public void constructorTest() { public void constructorTest() {
assertThrows(AssertionError.class, () -> new WebsocketConnectionAttemptState(null, null, null)); assertThrows(AssertionError.class, () -> new WebSocketConnectionAttemptState(null, null, null));
} }
} }

View file

@ -22,10 +22,10 @@ import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class WebsocketInitializerTest { public class WebSocketInitializerTest {
@Test @Test
public void testExtensionInitializer() { public void testExtensionInitializer() {
WebsocketInitializer initializer = new WebsocketInitializer(); WebSocketInitializer initializer = new WebSocketInitializer();
List<Exception> exceptions = initializer.initialize(); List<Exception> exceptions = initializer.initialize();
assertTrue(exceptions.size() == 0); assertTrue(exceptions.size() == 0);
} }

View file

@ -31,27 +31,27 @@ import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfigurati
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure; import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure.HttpLookupFailure; import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure.HttpLookupFailure;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModule.XmppWebSocketTransport.DiscoveredWebSocketEndpoints;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.WebsocketEndpointsDiscoveryFailed; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModule.XmppWebSocketTransport.WebSocketEndpointsDiscoveryFailed;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result; import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpointLookup.Result;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
public class XmppWebsocketTransportModuleTest { public class XmppWebSocketTransportModuleTest {
@Test @Test
public void createWebsocketModuleConnectionInstanceTest() throws URISyntaxException, XmppStringprepException { public void createWebSocketModuleConnectionInstanceTest() throws URISyntaxException, XmppStringprepException {
ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration
.builder(); .builder();
builder.removeAllModules(); builder.removeAllModules();
builder.addModule(XmppWebsocketTransportModuleDescriptor.class); builder.addModule(XmppWebSocketTransportModuleDescriptor.class);
builder.setXmppAddressAndPassword("user5@localhost.org", "user5"); builder.setXmppAddressAndPassword("user5@localhost.org", "user5");
builder.setHost("localhost.org"); builder.setHost("localhost.org");
XmppWebsocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebsocketTransportModuleDescriptor.getBuilder(builder); XmppWebSocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebSocketTransportModuleDescriptor.getBuilder(builder);
websocketBuilder.explicitlySetWebsocketEndpointAndDiscovery(new URI("wss://localhost.org:7443/ws/"), false); websocketBuilder.explicitlySetWebSocketEndpointAndDiscovery(new URI("wss://localhost.org:7443/ws/"), false);
ModularXmppClientToServerConnectionConfiguration config = builder.build(); ModularXmppClientToServerConnectionConfiguration config = builder.build();
ModularXmppClientToServerConnection connection = new ModularXmppClientToServerConnection(config); ModularXmppClientToServerConnection connection = new ModularXmppClientToServerConnection(config);
@ -60,26 +60,26 @@ public class XmppWebsocketTransportModuleTest {
@Test @Test
public void createDescriptorTest() throws URISyntaxException, XmppStringprepException { public void createDescriptorTest() throws URISyntaxException, XmppStringprepException {
XmppWebsocketTransportModuleDescriptor websocketTransportModuleDescriptor = getWebsocketDescriptor(); XmppWebSocketTransportModuleDescriptor websocketTransportModuleDescriptor = getWebSocketDescriptor();
assertNotNull(websocketTransportModuleDescriptor); assertNotNull(websocketTransportModuleDescriptor);
} }
@Test @Test
public void websocketEndpointDiscoveryTest() throws URISyntaxException { public void websocketEndpointDiscoveryTest() throws URISyntaxException {
XmppWebsocketTransportModuleDescriptor websocketTransportModuleDescriptor = getWebsocketDescriptor(); XmppWebSocketTransportModuleDescriptor websocketTransportModuleDescriptor = getWebSocketDescriptor();
ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class); ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class);
XmppWebsocketTransportModule transportModule XmppWebSocketTransportModule transportModule
= new XmppWebsocketTransportModule(websocketTransportModuleDescriptor, connectionInternal); = new XmppWebSocketTransportModule(websocketTransportModuleDescriptor, connectionInternal);
XmppWebsocketTransportModule.XmppWebsocketTransport transport = transportModule.getTransport(); XmppWebSocketTransportModule.XmppWebSocketTransport transport = transportModule.getTransport();
assertThrows(AssertionError.class, () -> transport.new DiscoveredWebsocketEndpoints(null)); assertThrows(AssertionError.class, () -> transport.new DiscoveredWebSocketEndpoints(null));
assertThrows(AssertionError.class, () -> transport.new WebsocketEndpointsDiscoveryFailed(null)); assertThrows(AssertionError.class, () -> transport.new WebSocketEndpointsDiscoveryFailed(null));
WebsocketRemoteConnectionEndpoint endpoint = new WebsocketRemoteConnectionEndpoint("wss://localhost.org:7443/ws/"); WebSocketRemoteConnectionEndpoint endpoint = new WebSocketRemoteConnectionEndpoint("wss://localhost.org:7443/ws/");
List<WebsocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints = new ArrayList<>(); List<WebSocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints = new ArrayList<>();
discoveredRemoteConnectionEndpoints.add(endpoint); discoveredRemoteConnectionEndpoints.add(endpoint);
HttpLookupFailure httpLookupFailure = new RemoteConnectionEndpointLookupFailure.HttpLookupFailure(null, null); HttpLookupFailure httpLookupFailure = new RemoteConnectionEndpointLookupFailure.HttpLookupFailure(null, null);
@ -87,38 +87,38 @@ public class XmppWebsocketTransportModuleTest {
failureList.add(httpLookupFailure); failureList.add(httpLookupFailure);
Result result = new Result(discoveredRemoteConnectionEndpoints, failureList); Result result = new Result(discoveredRemoteConnectionEndpoints, failureList);
DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints = transport.new DiscoveredWebsocketEndpoints(result); DiscoveredWebSocketEndpoints discoveredWebSocketEndpoints = transport.new DiscoveredWebSocketEndpoints(result);
assertNotNull(discoveredWebsocketEndpoints.getResult()); assertNotNull(discoveredWebSocketEndpoints.getResult());
WebsocketEndpointsDiscoveryFailed endpointsDiscoveryFailed = transport.new WebsocketEndpointsDiscoveryFailed(result); WebSocketEndpointsDiscoveryFailed endpointsDiscoveryFailed = transport.new WebSocketEndpointsDiscoveryFailed(result);
assertNotNull(endpointsDiscoveryFailed.toString()); assertNotNull(endpointsDiscoveryFailed.toString());
} }
@Test @Test
public void websocketConnectedResultTest() throws URISyntaxException { public void websocketConnectedResultTest() throws URISyntaxException {
WebsocketRemoteConnectionEndpoint connectedEndpoint = new WebsocketRemoteConnectionEndpoint("wss://localhost.org:7443/ws/"); WebSocketRemoteConnectionEndpoint connectedEndpoint = new WebSocketRemoteConnectionEndpoint("wss://localhost.org:7443/ws/");
assertNotNull(new XmppWebsocketTransportModule.WebsocketConnectedResult(connectedEndpoint)); assertNotNull(new XmppWebSocketTransportModule.WebSocketConnectedResult(connectedEndpoint));
} }
@Test @Test
public void lookupConnectionEndpointsTest() throws URISyntaxException { public void lookupConnectionEndpointsTest() throws URISyntaxException {
XmppWebsocketTransportModuleDescriptor websocketTransportModuleDescriptor = getWebsocketDescriptor(); XmppWebSocketTransportModuleDescriptor websocketTransportModuleDescriptor = getWebSocketDescriptor();
ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class); ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class);
XmppWebsocketTransportModule transportModule XmppWebSocketTransportModule transportModule
= new XmppWebsocketTransportModule(websocketTransportModuleDescriptor, connectionInternal); = new XmppWebSocketTransportModule(websocketTransportModuleDescriptor, connectionInternal);
XmppWebsocketTransportModule.XmppWebsocketTransport transport = transportModule.getTransport(); XmppWebSocketTransportModule.XmppWebSocketTransport transport = transportModule.getTransport();
assertNotNull(transport.lookupConnectionEndpoints()); assertNotNull(transport.lookupConnectionEndpoints());
} }
private static XmppWebsocketTransportModuleDescriptor getWebsocketDescriptor() throws URISyntaxException { private static XmppWebSocketTransportModuleDescriptor getWebSocketDescriptor() throws URISyntaxException {
ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration
.builder(); .builder();
XmppWebsocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebsocketTransportModuleDescriptor.getBuilder(builder); XmppWebSocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebSocketTransportModuleDescriptor.getBuilder(builder);
websocketBuilder.explicitlySetWebsocketEndpointAndDiscovery(new URI("wss://localhost.org:7443/ws/"), false); websocketBuilder.explicitlySetWebSocketEndpointAndDiscovery(new URI("wss://localhost.org:7443/ws/"), false);
return (XmppWebsocketTransportModuleDescriptor) websocketBuilder.build(); return (XmppWebSocketTransportModuleDescriptor) websocketBuilder.build();
} }
} }

View file

@ -23,20 +23,20 @@ import org.junit.jupiter.api.Test;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
public class WebsocketElementTest { public class WebSocketElementTest {
private static final String OPEN_ELEMENT = "<open xmlns='urn:ietf:params:xml:ns:xmpp-framing' to='foodomain.foo' version='1.0'/>"; private static final String OPEN_ELEMENT = "<open xmlns='urn:ietf:params:xml:ns:xmpp-framing' to='foodomain.foo' version='1.0'/>";
private static final String CLOSE_ELEMENT = "<close xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>"; private static final String CLOSE_ELEMENT = "<close xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>";
@Test @Test
public void websocketOpenElementTest() throws XmppStringprepException { public void websocketOpenElementTest() throws XmppStringprepException {
String openElementXml = new WebsocketOpenElement(JidCreate.domainBareFrom("foodomain.foo")).toXML().toString(); String openElementXml = new WebSocketOpenElement(JidCreate.domainBareFrom("foodomain.foo")).toXML().toString();
assertXmlSimilar(OPEN_ELEMENT, openElementXml); assertXmlSimilar(OPEN_ELEMENT, openElementXml);
assertXmlNotSimilar(CLOSE_ELEMENT, new WebsocketOpenElement(JidCreate.domainBareFrom("foodomain.foo")).toXML()); assertXmlNotSimilar(CLOSE_ELEMENT, new WebSocketOpenElement(JidCreate.domainBareFrom("foodomain.foo")).toXML());
} }
@Test @Test
public void websocketCloseElementTest() throws XmppStringprepException { public void websocketCloseElementTest() throws XmppStringprepException {
String closeElementXml = new WebsocketCloseElement().toXML().toString(); String closeElementXml = new WebSocketCloseElement().toXML().toString();
assertXmlSimilar(CLOSE_ELEMENT, closeElementXml); assertXmlSimilar(CLOSE_ELEMENT, closeElementXml);
assertXmlNotSimilar(OPEN_ELEMENT, closeElementXml); assertXmlNotSimilar(OPEN_ELEMENT, closeElementXml);
} }

View file

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations; package org.jivesoftware.smack.websocket.impl;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
@ -22,26 +22,26 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public final class AbstractWebsocketTest { public final class AbstractWebSocketTest {
private static final String OPEN_ELEMENT = "<open from='localhost.org' id='aov9ihhmmn' xmlns='urn:ietf:params:xml:ns:xmpp-framing' xml:lang='en' version='1.0'/>"; private static final String OPEN_ELEMENT = "<open from='localhost.org' id='aov9ihhmmn' xmlns='urn:ietf:params:xml:ns:xmpp-framing' xml:lang='en' version='1.0'/>";
private static final String OPEN_STREAM = "<stream from='localhost.org' id='aov9ihhmmn' xmlns='jabber:client' xml:lang='en' version='1.0'>"; private static final String OPEN_STREAM = "<stream from='localhost.org' id='aov9ihhmmn' xmlns='jabber:client' xml:lang='en' version='1.0'>";
private static final String CLOSE_ELEMENT = "<close xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>"; private static final String CLOSE_ELEMENT = "<close xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>";
@Test @Test
public void getStreamFromOpenElementTest() { public void getStreamFromOpenElementTest() {
String generatedOpenStream = AbstractWebsocket.getStreamFromOpenElement(OPEN_ELEMENT); String generatedOpenStream = AbstractWebSocket.getStreamFromOpenElement(OPEN_ELEMENT);
assertEquals(generatedOpenStream, OPEN_STREAM); assertEquals(generatedOpenStream, OPEN_STREAM);
} }
@Test @Test
public void isOpenElementTest() { public void isOpenElementTest() {
assertTrue(AbstractWebsocket.isOpenElement(OPEN_ELEMENT)); assertTrue(AbstractWebSocket.isOpenElement(OPEN_ELEMENT));
assertFalse(AbstractWebsocket.isOpenElement(OPEN_STREAM)); assertFalse(AbstractWebSocket.isOpenElement(OPEN_STREAM));
} }
@Test @Test
public void isCloseElementTest() { public void isCloseElementTest() {
assertTrue(AbstractWebsocket.isCloseElement(CLOSE_ELEMENT)); assertTrue(AbstractWebSocket.isCloseElement(CLOSE_ELEMENT));
assertFalse(AbstractWebsocket.isCloseElement(OPEN_STREAM)); assertFalse(AbstractWebSocket.isCloseElement(OPEN_STREAM));
} }
} }

View file

@ -1,61 +0,0 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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.websocket.implementations;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
import org.jivesoftware.smack.websocket.implementations.okhttp.OkHttpWebsocket;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result;
import org.junit.jupiter.api.Test;
public class ProviderTest {
@Test
public void providerTest() {
assertThrows(IllegalArgumentException.class, () -> WebsocketImplProvider.getWebsocketImpl(OkHttpWebsocket.class, null, null));
}
@Test
public void getImplTest() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, URISyntaxException {
WebsocketRemoteConnectionEndpoint endpoint = new WebsocketRemoteConnectionEndpoint("wss://localhost.org:7443/ws/");
List<WebsocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints = new ArrayList<>();
discoveredRemoteConnectionEndpoints.add(endpoint);
Result result = new Result(discoveredRemoteConnectionEndpoints, null);
DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints = mock(DiscoveredWebsocketEndpoints.class);
when(discoveredWebsocketEndpoints.getResult()).thenReturn(result);
ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class);
assertNotNull(WebsocketImplProvider.getWebsocketImpl(OkHttpWebsocket.class, connectionInternal, discoveredWebsocketEndpoints));
}
}

View file

@ -25,21 +25,21 @@ import org.jivesoftware.smack.datatypes.UInt16;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class WebsocketRemoteConnectionEndpointTest { public class WebSocketRemoteConnectionEndpointTest {
@Test @Test
public void endpointTest() throws URISyntaxException { public void endpointTest() throws URISyntaxException {
String endpointString = "ws://fooDomain.org:7070/ws/"; String endpointString = "ws://fooDomain.org:7070/ws/";
WebsocketRemoteConnectionEndpoint endpoint = new WebsocketRemoteConnectionEndpoint(endpointString); WebSocketRemoteConnectionEndpoint endpoint = new WebSocketRemoteConnectionEndpoint(endpointString);
assertEquals("fooDomain.org", endpoint.getHost()); assertEquals("fooDomain.org", endpoint.getHost());
assertEquals(UInt16.from(7070), endpoint.getPort()); assertEquals(UInt16.from(7070), endpoint.getPort());
assertEquals(endpointString, endpoint.getWebsocketEndpoint().toString()); assertEquals(endpointString, endpoint.getWebSocketEndpoint().toString());
} }
@Test @Test
public void faultyEndpointTest() { public void faultyEndpointTest() {
String faultyProtocolString = "wst://fooDomain.org:7070/ws/"; String faultyProtocolString = "wst://fooDomain.org:7070/ws/";
assertThrows(IllegalArgumentException.class, () -> { assertThrows(IllegalArgumentException.class, () -> {
new WebsocketRemoteConnectionEndpoint(faultyProtocolString); new WebSocketRemoteConnectionEndpoint(faultyProtocolString);
}); });
} }
} }

View file

@ -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.websocket.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.impl.WebSocketFactoryService;
public class WebSocketFactoryServiceTestUtil {
public static void createWebSocketTest(Class<? extends AbstractWebSocket> expected) {
ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class);
AbstractWebSocket websocket = WebSocketFactoryService.createWebSocket(connectionInternal);
assertEquals(expected, websocket.getClass());
}
}