mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 05:52:06 +01:00
Compare commits
31 commits
8692c0187e
...
b9be45ae2c
Author | SHA1 | Date | |
---|---|---|---|
|
b9be45ae2c | ||
|
59d3d75a71 | ||
|
b47225c2c1 | ||
|
097d245358 | ||
|
4643d07ef4 | ||
|
a057db107e | ||
|
f64288ba6d | ||
|
c9af25c674 | ||
|
0a8da1a07f | ||
|
56507a761f | ||
|
524e4b1d9b | ||
|
d33a5a23c3 | ||
|
b6c8754012 | ||
|
31e4e1faf2 | ||
|
5eef31e49c | ||
|
4120b42761 | ||
|
3fde4830e4 | ||
|
2762325639 | ||
|
6434e77336 | ||
|
2e18442b11 | ||
|
b3e1082c09 | ||
|
e0754df043 | ||
|
7b3bd6ff96 | ||
|
a0b8ad98c9 | ||
|
82d5ee6ac4 | ||
|
cedced5b13 | ||
|
cd6ff363c6 | ||
|
899813a668 | ||
|
26e6efa767 | ||
|
090858f467 | ||
|
68edc8b9f5 |
56 changed files with 1177 additions and 877 deletions
|
@ -48,3 +48,16 @@ git clone git@github.com:igniterealtime/Smack.git
|
||||||
cd Smack
|
cd Smack
|
||||||
gradle assemble
|
gradle assemble
|
||||||
```
|
```
|
||||||
|
|
||||||
|
IDE Config
|
||||||
|
----------
|
||||||
|
|
||||||
|
### Eclipse
|
||||||
|
|
||||||
|
Import IDE settings from `./resources/eclipse/` to configure proper ordering of imports and correct formatting that should pass the CheckStyle rules.
|
||||||
|
|
||||||
|
### IntelliJ IDEA
|
||||||
|
|
||||||
|
Import Java Code Style settings from `./resources/intellij/smack_formatter.xml` to configure import optimisation and code formatting to pass the CheckStyle rules when building or submitting PRs.
|
||||||
|
|
||||||
|
_We've noticed, at time of writing, that IntelliJ often requires a restart when applying new rules - no amount of OK/Apply will do the trick._
|
||||||
|
|
|
@ -4,8 +4,8 @@ Smack's Integration Test Framework
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Smack's Integration Test Framwork is used to run a set of tests against a real XMPP service.
|
Smack's Integration Test Framework is used to run a set of tests against a real XMPP service.
|
||||||
The framework discovers on startup the available tests by reflection.
|
The framework discovers on start-up the available tests by reflection.
|
||||||
|
|
||||||
Quickstart
|
Quickstart
|
||||||
----------
|
----------
|
||||||
|
@ -120,7 +120,7 @@ The methods are supposed to throw an exception if their integration test fails.
|
||||||
|
|
||||||
### `TestNotPossibleException`
|
### `TestNotPossibleException`
|
||||||
|
|
||||||
Can be thrown by test methods or constructors to signal that their test it no possible, e.g. because the service does not support the required feature.
|
Can be thrown by test methods or constructors to signal that their test is not possible, e.g. because the service does not support the required feature.
|
||||||
|
|
||||||
Running the integration tests
|
Running the integration tests
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
@ -130,6 +130,7 @@ Smack's Gradle build system is configured with a special task called `integratio
|
||||||
```bash
|
```bash
|
||||||
$ gradle integrationTest -Dsinttest.service=my.xmppservice.org
|
$ gradle integrationTest -Dsinttest.service=my.xmppservice.org
|
||||||
```
|
```
|
||||||
|
|
||||||
If one of `accountOneUsername`, `accountOnePassword`, `accountTwoUsername` or `accountTwoPassword` is not configured, then the framework will automatically create the accounts on the service. Of course this requires account registration (IBR) to be enabled.
|
If one of `accountOneUsername`, `accountOnePassword`, `accountTwoUsername` or `accountTwoPassword` is not configured, then the framework will automatically create the accounts on the service. Of course this requires account registration (IBR) to be enabled.
|
||||||
If the accounts got created automatically by the framework, then they will also be deleted at the end of the test.
|
If the accounts got created automatically by the framework, then they will also be deleted at the end of the test.
|
||||||
|
|
||||||
|
@ -161,6 +162,19 @@ The test methods can declare as many parameters as they need to, but every param
|
||||||
The framework will automatically create, register and login the connections.
|
The framework will automatically create, register and login the connections.
|
||||||
After the test is finished, the connections will be unregistered with the XMPP service and terminated.
|
After the test is finished, the connections will be unregistered with the XMPP service and terminated.
|
||||||
|
|
||||||
|
Debugging Integration Tests
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
A test, like any other code, may not be perfect on the first attempt, and you may require more information in order to ascertain quite what's wrong.
|
||||||
|
|
||||||
|
### Smack Debugger options
|
||||||
|
|
||||||
|
As listed in the main Smack [Debugging](../debugging.md) doc, there are two built-in debuggers that could surface you more information. Using the 'enhanced' debugger config option listed above, you'll get the Smack Debug Window launching when your tests launch, and you'll get a stanza-by-stanza account of what happened on each connection, hopefully enough to diagnose what went wrong.
|
||||||
|
|
||||||
|
### Debugging in the IDE
|
||||||
|
|
||||||
|
If the output isn't enough, you may need to debug and inspect running code within the IDE. Depending on the IDE, in order to get execution to pause at your breakpoints, you may need to switch your configuration. Instead of running `gradle integrationTest`, instead run the `SmackIntegrationTestFramework` class directly with the same command-line options.
|
||||||
|
|
||||||
Running your own integration tests
|
Running your own integration tests
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
|
|
@ -472,7 +472,7 @@ muc = manager.getMultiUserChat("myroom@conference.jabber.org");
|
||||||
muc.create("testbot");
|
muc.create("testbot");
|
||||||
// User1 (which is the room owner) configures the room as a moderated room
|
// User1 (which is the room owner) configures the room as a moderated room
|
||||||
Form form = muc.getConfigurationForm();
|
Form form = muc.getConfigurationForm();
|
||||||
Form answerForm = form.createAnswerForm();
|
FillableForm answerForm = configForm.getFillableForm();
|
||||||
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
|
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
|
||||||
muc.sendConfigurationForm(answerForm);
|
muc.sendConfigurationForm(answerForm);
|
||||||
|
|
||||||
|
@ -612,7 +612,7 @@ muc = manager.getMultiUserChat("myroom@conference.jabber.org");
|
||||||
muc.create("testbot");
|
muc.create("testbot");
|
||||||
// User1 (which is the room owner) configures the room as a moderated room
|
// User1 (which is the room owner) configures the room as a moderated room
|
||||||
Form form = muc.getConfigurationForm();
|
Form form = muc.getConfigurationForm();
|
||||||
Form answerForm = form.createAnswerForm();
|
FillableForm answerForm = configForm.getFillableForm();
|
||||||
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
|
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
|
||||||
muc.sendConfigurationForm(answerForm);
|
muc.sendConfigurationForm(answerForm);
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<code_scheme name="Smack" version="173">
|
|
||||||
<JavaCodeStyleSettings>
|
|
||||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="10000" />
|
|
||||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000" />
|
|
||||||
<option name="IMPORT_LAYOUT_TABLE">
|
|
||||||
<value>
|
|
||||||
<package name="" withSubpackages="true" static="true" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java" withSubpackages="true" static="false" />
|
|
||||||
<package name="javax" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.jivesoftware" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="" withSubpackages="true" static="false" />
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
</JavaCodeStyleSettings>
|
|
||||||
</code_scheme>
|
|
51
resources/intellij/smack_formatter.xml
Normal file
51
resources/intellij/smack_formatter.xml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<option name="FORMATTER_TAGS_ENABLED" value="true" />
|
||||||
|
<JavaCodeStyleSettings>
|
||||||
|
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||||
|
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||||
|
<option name="IMPORT_LAYOUT_TABLE">
|
||||||
|
<value>
|
||||||
|
<package name="" withSubpackages="true" static="true" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="java" withSubpackages="true" static="false" />
|
||||||
|
<package name="javax" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="org.jivesoftware.smack" withSubpackages="true" static="false" />
|
||||||
|
<package name="org.jivesoftware.smackx" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="" withSubpackages="true" static="false" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="JD_KEEP_EMPTY_LINES" value="false" />
|
||||||
|
</JavaCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="JAVA">
|
||||||
|
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||||
|
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||||
|
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||||
|
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||||
|
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
|
||||||
|
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||||
|
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
|
||||||
|
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
||||||
|
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||||
|
<option name="METHOD_PARAMETERS_WRAP" value="1" />
|
||||||
|
<option name="RESOURCE_LIST_WRAP" value="5" />
|
||||||
|
<option name="EXTENDS_LIST_WRAP" value="1" />
|
||||||
|
<option name="THROWS_LIST_WRAP" value="1" />
|
||||||
|
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
|
||||||
|
<option name="THROWS_KEYWORD_WRAP" value="1" />
|
||||||
|
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||||
|
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="TERNARY_OPERATION_WRAP" value="5" />
|
||||||
|
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||||
|
<option name="METHOD_ANNOTATION_WRAP" value="0" />
|
||||||
|
<option name="CLASS_ANNOTATION_WRAP" value="0" />
|
||||||
|
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="16" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
|
@ -141,6 +141,17 @@ hr {
|
||||||
|
|
||||||
<div id="pageBody">
|
<div id="pageBody">
|
||||||
|
|
||||||
|
<h2>4.4.3 -- <span style="font-weight: normal;">2021-07-06</span></h2>
|
||||||
|
|
||||||
|
<h2> Bug
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>[<a href='https://igniterealtime.atlassian.net/browse/SMACK-905'>SMACK-905</a>] - The class org.jivesoftware.smackx.offline.packet.OfflineMessageInfo has no ELEMENT, NAMESPACE or QNAME member
|
||||||
|
</li>
|
||||||
|
<li>[<a href='https://igniterealtime.atlassian.net/browse/SMACK-907'>SMACK-907</a>] - Possible NPE in MultipleRecipientManager
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>4.4.2 -- <span style="font-weight: normal;">2021-03-25</span></h2>
|
<h2>4.4.2 -- <span style="font-weight: normal;">2021-03-25</span></h2>
|
||||||
|
|
||||||
<h2> Bug
|
<h2> Bug
|
||||||
|
|
|
@ -54,7 +54,7 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
initialStateDescriptorVertex = StateDescriptorGraph.constructStateDescriptorGraph(backwardEdgeStateDescriptors);
|
initialStateDescriptorVertex = StateDescriptorGraph.constructStateDescriptorGraph(backwardEdgeStateDescriptors, builder.failOnUnknownStates);
|
||||||
}
|
}
|
||||||
catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||||
| NoSuchMethodException | SecurityException e) {
|
| NoSuchMethodException | SecurityException e) {
|
||||||
|
@ -91,6 +91,8 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
|
||||||
|
|
||||||
private final Map<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>, ModularXmppClientToServerConnectionModuleDescriptor> modulesDescriptors = new HashMap<>();
|
private final Map<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>, ModularXmppClientToServerConnectionModuleDescriptor> modulesDescriptors = new HashMap<>();
|
||||||
|
|
||||||
|
private boolean failOnUnknownStates;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
SmackConfiguration.addAllKnownModulesTo(this);
|
SmackConfiguration.addAllKnownModulesTo(this);
|
||||||
}
|
}
|
||||||
|
@ -163,6 +165,17 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fail if there are unknown states in Smack's state descriptor graph. This method is used mostly for testing
|
||||||
|
* the internals of Smack. Users can safely ignore it.
|
||||||
|
*
|
||||||
|
* @return a reference to this builder.
|
||||||
|
*/
|
||||||
|
public Builder failOnUnknownStates() {
|
||||||
|
failOnUnknownStates = true;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Builder getThis() {
|
protected Builder getThis() {
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2018 Florian Schmaus
|
* Copyright 2018-2021 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.
|
||||||
|
@ -28,7 +28,6 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.DisconnectedStateDescriptor;
|
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.DisconnectedStateDescriptor;
|
||||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||||
|
@ -47,8 +46,6 @@ import org.jivesoftware.smack.util.MultiMap;
|
||||||
*/
|
*/
|
||||||
public class StateDescriptorGraph {
|
public class StateDescriptorGraph {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(StateDescriptorGraph.class.getName());
|
|
||||||
|
|
||||||
private static GraphVertex<StateDescriptor> addNewStateDescriptorGraphVertex(
|
private static GraphVertex<StateDescriptor> addNewStateDescriptorGraphVertex(
|
||||||
Class<? extends StateDescriptor> stateDescriptorClass,
|
Class<? extends StateDescriptor> stateDescriptorClass,
|
||||||
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes)
|
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes)
|
||||||
|
@ -102,7 +99,8 @@ public class StateDescriptorGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleStateDescriptorGraphVertex(GraphVertex<StateDescriptor> node,
|
private static void handleStateDescriptorGraphVertex(GraphVertex<StateDescriptor> node,
|
||||||
HandleStateDescriptorGraphVertexContext context)
|
HandleStateDescriptorGraphVertexContext context,
|
||||||
|
boolean failOnUnknownStates)
|
||||||
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
|
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||||
Class<? extends StateDescriptor> stateDescriptorClass = node.element.getClass();
|
Class<? extends StateDescriptor> stateDescriptorClass = node.element.getClass();
|
||||||
boolean alreadyHandled = context.recurseInto(stateDescriptorClass);
|
boolean alreadyHandled = context.recurseInto(stateDescriptorClass);
|
||||||
|
@ -126,7 +124,7 @@ public class StateDescriptorGraph {
|
||||||
case 1:
|
case 1:
|
||||||
GraphVertex<StateDescriptor> soleSuccessorNode = successorStateDescriptors.values().iterator().next();
|
GraphVertex<StateDescriptor> soleSuccessorNode = successorStateDescriptors.values().iterator().next();
|
||||||
node.addOutgoingEdge(soleSuccessorNode);
|
node.addOutgoingEdge(soleSuccessorNode);
|
||||||
handleStateDescriptorGraphVertex(soleSuccessorNode, context);
|
handleStateDescriptorGraphVertex(soleSuccessorNode, context, failOnUnknownStates);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,9 +142,8 @@ public class StateDescriptorGraph {
|
||||||
StateDescriptor successorStateDescriptor = successorStateDescriptorGraphNode.element;
|
StateDescriptor successorStateDescriptor = successorStateDescriptorGraphNode.element;
|
||||||
Class<? extends StateDescriptor> successorStateDescriptorClass = successorStateDescriptor.getClass();
|
Class<? extends StateDescriptor> successorStateDescriptorClass = successorStateDescriptor.getClass();
|
||||||
for (Class<? extends StateDescriptor> subordinateClass : successorStateDescriptor.getSubordinates()) {
|
for (Class<? extends StateDescriptor> subordinateClass : successorStateDescriptor.getSubordinates()) {
|
||||||
if (!successorClasses.contains(subordinateClass)) {
|
if (failOnUnknownStates && !successorClasses.contains(subordinateClass)) {
|
||||||
LOGGER.severe(successorStateDescriptor + " points to a subordinate '" + subordinateClass + "' which is not part of the successor set");
|
throw new IllegalStateException(successorStateDescriptor + " points to a subordinate '" + subordinateClass + "' which is not part of the successor set");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphVertex<Class<? extends StateDescriptor>> superiorClassNode = lookupAndCreateIfRequired(
|
GraphVertex<Class<? extends StateDescriptor>> superiorClassNode = lookupAndCreateIfRequired(
|
||||||
|
@ -157,10 +154,9 @@ public class StateDescriptorGraph {
|
||||||
superiorClassNode.addOutgoingEdge(subordinateClassNode);
|
superiorClassNode.addOutgoingEdge(subordinateClassNode);
|
||||||
}
|
}
|
||||||
for (Class<? extends StateDescriptor> superiorClass : successorStateDescriptor.getSuperiors()) {
|
for (Class<? extends StateDescriptor> superiorClass : successorStateDescriptor.getSuperiors()) {
|
||||||
if (!successorClasses.contains(superiorClass)) {
|
if (failOnUnknownStates && !successorClasses.contains(superiorClass)) {
|
||||||
LOGGER.severe(successorStateDescriptor + " points to a superior '" + superiorClass
|
throw new IllegalStateException(successorStateDescriptor + " points to a superior '" + superiorClass
|
||||||
+ "' which is not part of the successor set");
|
+ "' which is not part of the successor set");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphVertex<Class<? extends StateDescriptor>> subordinateClassNode = lookupAndCreateIfRequired(
|
GraphVertex<Class<? extends StateDescriptor>> subordinateClassNode = lookupAndCreateIfRequired(
|
||||||
|
@ -193,11 +189,13 @@ public class StateDescriptorGraph {
|
||||||
node.addOutgoingEdge(successorVertex);
|
node.addOutgoingEdge(successorVertex);
|
||||||
|
|
||||||
// Recurse further.
|
// Recurse further.
|
||||||
handleStateDescriptorGraphVertex(successorVertex, context);
|
handleStateDescriptorGraphVertex(successorVertex, context, failOnUnknownStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GraphVertex<StateDescriptor> constructStateDescriptorGraph(Set<Class<? extends StateDescriptor>> backwardEdgeStateDescriptors)
|
public static GraphVertex<StateDescriptor> constructStateDescriptorGraph(
|
||||||
|
Set<Class<? extends StateDescriptor>> backwardEdgeStateDescriptors,
|
||||||
|
boolean failOnUnknownStates)
|
||||||
throws InstantiationException, IllegalAccessException, IllegalArgumentException,
|
throws InstantiationException, IllegalAccessException, IllegalArgumentException,
|
||||||
InvocationTargetException, NoSuchMethodException, SecurityException {
|
InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||||
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes = new HashMap<>();
|
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes = new HashMap<>();
|
||||||
|
@ -219,7 +217,7 @@ public class StateDescriptorGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleStateDescriptorGraphVertexContext context = new HandleStateDescriptorGraphVertexContext(graphVertexes, inferredForwardEdges);
|
HandleStateDescriptorGraphVertexContext context = new HandleStateDescriptorGraphVertexContext(graphVertexes, inferredForwardEdges);
|
||||||
handleStateDescriptorGraphVertex(initialNode, context);
|
handleStateDescriptorGraphVertex(initialNode, context, failOnUnknownStates);
|
||||||
|
|
||||||
return initialNode;
|
return initialNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2020 Florian Schmaus
|
* Copyright 2015-2021 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.
|
||||||
|
@ -82,4 +82,11 @@ public class CollectionUtil {
|
||||||
}
|
}
|
||||||
return new HashSet<>(collection);
|
return new HashSet<>(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> emptyOrSingletonListFrom(T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Collections.singletonList(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,12 +140,12 @@ public final class EnhancedDebuggerWindow {
|
||||||
if (frame == null) {
|
if (frame == null) {
|
||||||
createDebug();
|
createDebug();
|
||||||
}
|
}
|
||||||
debugger.tabbedPane.setName("XMPPConnection_" + tabbedPane.getComponentCount());
|
debugger.tabbedPane.setName("XMPPConnection_" + tabbedPane.getTabCount());
|
||||||
tabbedPane.add(debugger.tabbedPane, tabbedPane.getComponentCount() - 1);
|
tabbedPane.add(debugger.tabbedPane, -1);
|
||||||
tabbedPane.setIconAt(tabbedPane.indexOfComponent(debugger.tabbedPane), connectionCreatedIcon);
|
tabbedPane.setIconAt(tabbedPane.indexOfComponent(debugger.tabbedPane), connectionCreatedIcon);
|
||||||
tabbedPane.setSelectedIndex(tabbedPane.indexOfComponent(debugger.tabbedPane));
|
tabbedPane.setSelectedIndex(tabbedPane.indexOfComponent(debugger.tabbedPane));
|
||||||
frame.setTitle(
|
frame.setTitle(
|
||||||
"Smack Debug Window -- Total connections: " + (tabbedPane.getComponentCount() - 1));
|
"Smack Debug Window -- Total connections: " + (tabbedPane.getTabCount() - 1));
|
||||||
// Keep the added debugger for later access
|
// Keep the added debugger for later access
|
||||||
debuggers.add(debugger);
|
debuggers.add(debugger);
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ public final class EnhancedDebuggerWindow {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
// Remove the selected tab pane if it's not the Smack info pane
|
// Remove the selected tab pane if it's not the Smack info pane
|
||||||
if (tabbedPane.getSelectedIndex() < tabbedPane.getComponentCount() - 1) {
|
if (tabbedPane.getSelectedIndex() < tabbedPane.getTabCount() - 1) {
|
||||||
int index = tabbedPane.getSelectedIndex();
|
int index = tabbedPane.getSelectedIndex();
|
||||||
// Notify to the debugger to stop debugging
|
// Notify to the debugger to stop debugging
|
||||||
EnhancedDebugger debugger = debuggers.get(index);
|
EnhancedDebugger debugger = debuggers.get(index);
|
||||||
|
@ -297,7 +297,7 @@ public final class EnhancedDebuggerWindow {
|
||||||
// Update the root window title
|
// Update the root window title
|
||||||
frame.setTitle(
|
frame.setTitle(
|
||||||
"Smack Debug Window -- Total connections: "
|
"Smack Debug Window -- Total connections: "
|
||||||
+ (tabbedPane.getComponentCount() - 1));
|
+ (tabbedPane.getTabCount() - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -309,7 +309,7 @@ public final class EnhancedDebuggerWindow {
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
ArrayList<EnhancedDebugger> debuggersToRemove = new ArrayList<>();
|
ArrayList<EnhancedDebugger> debuggersToRemove = new ArrayList<>();
|
||||||
// Remove all the debuggers of which their connections are no longer valid
|
// Remove all the debuggers of which their connections are no longer valid
|
||||||
for (int index = 0; index < tabbedPane.getComponentCount() - 1; index++) {
|
for (int index = 0; index < tabbedPane.getTabCount() - 1; index++) {
|
||||||
EnhancedDebugger debugger = debuggers.get(index);
|
EnhancedDebugger debugger = debuggers.get(index);
|
||||||
if (!debugger.isConnectionActive()) {
|
if (!debugger.isConnectionActive()) {
|
||||||
// Notify to the debugger to stop debugging
|
// Notify to the debugger to stop debugging
|
||||||
|
@ -325,7 +325,7 @@ public final class EnhancedDebuggerWindow {
|
||||||
// Update the root window title
|
// Update the root window title
|
||||||
frame.setTitle(
|
frame.setTitle(
|
||||||
"Smack Debug Window -- Total connections: "
|
"Smack Debug Window -- Total connections: "
|
||||||
+ (tabbedPane.getComponentCount() - 1));
|
+ (tabbedPane.getTabCount() - 1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menu.add(menuItem);
|
menu.add(menuItem);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2016-2019 Florian Schmaus
|
* Copyright © 2016-2021 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.
|
||||||
|
@ -39,9 +39,9 @@ public class IoTSetRequestProvider extends IQProvider<IoTSetRequest> {
|
||||||
List<SetData> data = new ArrayList<>(4);
|
List<SetData> data = new ArrayList<>(4);
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
final XmlPullParser.Event eventType = parser.next();
|
final XmlPullParser.Event eventType = parser.next();
|
||||||
final String name = parser.getName();
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
final String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "bool": {
|
case "bool": {
|
||||||
String valueName = parser.getAttributeValue(null, "name");
|
String valueName = parser.getAttributeValue(null, "name");
|
||||||
|
|
|
@ -110,9 +110,9 @@ public class IoTFieldsExtensionProvider extends ExtensionElementProvider<IoTFiel
|
||||||
List<IoTDataField> fields = new ArrayList<>();
|
List<IoTDataField> fields = new ArrayList<>();
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
final XmlPullParser.Event eventType = parser.next();
|
final XmlPullParser.Event eventType = parser.next();
|
||||||
final String name = parser.getName();
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
final String name = parser.getName();
|
||||||
IoTDataField field = null;
|
IoTDataField field = null;
|
||||||
final String fieldName = parser.getAttributeValue(null, "name");
|
final String fieldName = parser.getAttributeValue(null, "name");
|
||||||
final String fieldValue = parser.getAttributeValue(null, "value");
|
final String fieldValue = parser.getAttributeValue(null, "value");
|
||||||
|
|
|
@ -59,9 +59,9 @@ public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
final XmlPullParser.Event eventType = parser.next();
|
final XmlPullParser.Event eventType = parser.next();
|
||||||
final String name = parser.getName();
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
final String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "always":
|
case "always":
|
||||||
alwaysJids = iterateJids(parser);
|
alwaysJids = iterateJids(parser);
|
||||||
|
@ -92,9 +92,9 @@ public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
final XmlPullParser.Event eventType = parser.next();
|
final XmlPullParser.Event eventType = parser.next();
|
||||||
final String name = parser.getName();
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
final String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "jid":
|
case "jid":
|
||||||
parser.next();
|
parser.next();
|
||||||
|
|
|
@ -47,10 +47,10 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
final XmlPullParser.Event eventType = parser.next();
|
final XmlPullParser.Event eventType = parser.next();
|
||||||
final String name = parser.getName();
|
|
||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
final String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case DataForm.ELEMENT:
|
case DataForm.ELEMENT:
|
||||||
dataForm = DataFormProvider.INSTANCE.parse(parser);
|
dataForm = DataFormProvider.INSTANCE.parse(parser);
|
||||||
|
|
|
@ -49,9 +49,9 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
final XmlPullParser.Event eventType = parser.next();
|
final XmlPullParser.Event eventType = parser.next();
|
||||||
final String name = parser.getName();
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
final String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case Forwarded.ELEMENT:
|
case Forwarded.ELEMENT:
|
||||||
forwarded = ForwardedProvider.parseForwardedMessage(parser, xmlEnvironment);
|
forwarded = ForwardedProvider.parseForwardedMessage(parser, xmlEnvironment);
|
||||||
|
|
|
@ -50,10 +50,11 @@ public class MarkupElementProvider extends ExtensionElementProvider<MarkupElemen
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.Event tag = parser.next();
|
||||||
String name = parser.getName();
|
String name;
|
||||||
int start, end;
|
int start, end;
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case BlockQuoteElement.ELEMENT:
|
case BlockQuoteElement.ELEMENT:
|
||||||
start = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
|
start = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
|
||||||
|
@ -108,6 +109,11 @@ public class MarkupElementProvider extends ExtensionElementProvider<MarkupElemen
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case END_ELEMENT:
|
case END_ELEMENT:
|
||||||
|
if (parser.getDepth() == initialDepth) {
|
||||||
|
return markup.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case SpanElement.ELEMENT:
|
case SpanElement.ELEMENT:
|
||||||
markup.addSpan(spanStart, spanEnd, spanStyles);
|
markup.addSpan(spanStart, spanEnd, spanStyles);
|
||||||
|
@ -128,9 +134,6 @@ public class MarkupElementProvider extends ExtensionElementProvider<MarkupElemen
|
||||||
}
|
}
|
||||||
listBuilder.endList();
|
listBuilder.endList();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MarkupElement.ELEMENT:
|
|
||||||
return markup.build();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.jivesoftware.smackx.address;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
||||||
|
@ -228,18 +229,16 @@ public class MultipleRecipientManager {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (to == null) to = Collections.emptyList();
|
||||||
|
if (cc == null) cc = Collections.emptyList();
|
||||||
|
if (bcc == null) bcc = Collections.emptyList();
|
||||||
|
|
||||||
final int numRecipients = to.size() + cc.size() + bcc.size();
|
final int numRecipients = to.size() + cc.size() + bcc.size();
|
||||||
final List<Jid> recipients = new ArrayList<>(numRecipients);
|
final List<Jid> recipients = new ArrayList<>(numRecipients);
|
||||||
|
|
||||||
if (to != null) {
|
recipients.addAll(to);
|
||||||
recipients.addAll(to);
|
recipients.addAll(cc);
|
||||||
}
|
recipients.addAll(bcc);
|
||||||
if (cc != null) {
|
|
||||||
recipients.addAll(cc);
|
|
||||||
}
|
|
||||||
if (bcc != null) {
|
|
||||||
recipients.addAll(bcc);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<Stanza> stanzasToSend = new ArrayList<>(numRecipients);
|
final List<Stanza> stanzasToSend = new ArrayList<>(numRecipients);
|
||||||
for (Jid recipient : recipients) {
|
for (Jid recipient : recipients) {
|
||||||
|
|
|
@ -55,8 +55,8 @@ public class BytestreamsProvider extends IQProvider<Bytestream> {
|
||||||
String elementName;
|
String elementName;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
eventType = parser.next();
|
eventType = parser.next();
|
||||||
elementName = parser.getName();
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
elementName = parser.getName();
|
||||||
if (elementName.equals(Bytestream.StreamHost.ELEMENT)) {
|
if (elementName.equals(Bytestream.StreamHost.ELEMENT)) {
|
||||||
JID = ParserUtils.getJidAttribute(parser);
|
JID = ParserUtils.getJidAttribute(parser);
|
||||||
host = parser.getAttributeValue("", "host");
|
host = parser.getAttributeValue("", "host");
|
||||||
|
@ -70,6 +70,7 @@ public class BytestreamsProvider extends IQProvider<Bytestream> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
||||||
|
elementName = parser.getName();
|
||||||
if (elementName.equals("streamhost")) {
|
if (elementName.equals("streamhost")) {
|
||||||
if (port == null) {
|
if (port == null) {
|
||||||
toReturn.addStreamHost(JID, host);
|
toReturn.addStreamHost(JID, host);
|
||||||
|
|
|
@ -700,7 +700,7 @@ public final class EntityCapsManager extends Manager {
|
||||||
for (FormField f : fs) {
|
for (FormField f : fs) {
|
||||||
sb.append(f.getFieldName());
|
sb.append(f.getFieldName());
|
||||||
sb.append('<');
|
sb.append('<');
|
||||||
formFieldValuesToCaps(f.getValues(), sb);
|
formFieldValuesToCaps(f.getRawValues(), sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,9 +78,9 @@ public class AdHocCommandDataProvider extends IQProvider<AdHocCommandData> {
|
||||||
}
|
}
|
||||||
while (!done) {
|
while (!done) {
|
||||||
eventType = parser.next();
|
eventType = parser.next();
|
||||||
elementName = parser.getName();
|
|
||||||
namespace = parser.getNamespace();
|
namespace = parser.getNamespace();
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
elementName = parser.getName();
|
||||||
if (parser.getName().equals("actions")) {
|
if (parser.getName().equals("actions")) {
|
||||||
String execute = parser.getAttributeValue("", "execute");
|
String execute = parser.getAttributeValue("", "execute");
|
||||||
if (execute != null) {
|
if (execute != null) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
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.jingle.element.JingleContentTransport;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
||||||
|
@ -62,9 +61,9 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
|
||||||
JingleS5BTransportCandidate.Builder cb;
|
JingleS5BTransportCandidate.Builder cb;
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.TagEvent tag = parser.nextTag();
|
XmlPullParser.TagEvent tag = parser.nextTag();
|
||||||
String name = parser.getName();
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case START_ELEMENT: {
|
case START_ELEMENT: {
|
||||||
|
String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
|
||||||
case JingleContentTransportCandidate.ELEMENT:
|
case JingleContentTransportCandidate.ELEMENT:
|
||||||
|
@ -110,8 +109,7 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case END_ELEMENT: {
|
case END_ELEMENT: {
|
||||||
switch (name) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
case JingleContentTransport.ELEMENT:
|
|
||||||
break outerloop;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,11 +45,11 @@ public class MoodProvider extends ExtensionElementProvider<MoodElement> {
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.Event tag = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
String namespace = parser.getNamespace();
|
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
String name = parser.getName();
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
if (MoodElement.ELEM_TEXT.equals(name)) {
|
if (MoodElement.ELEM_TEXT.equals(name)) {
|
||||||
text = parser.nextText();
|
text = parser.nextText();
|
||||||
continue outerloop;
|
continue outerloop;
|
||||||
|
@ -74,7 +74,7 @@ public class MoodProvider extends ExtensionElementProvider<MoodElement> {
|
||||||
}
|
}
|
||||||
|
|
||||||
case END_ELEMENT:
|
case END_ELEMENT:
|
||||||
if (MoodElement.ELEMENT.equals(name)) {
|
if (MoodElement.ELEMENT.equals(parser.getName())) {
|
||||||
MoodElement.MoodSubjectElement subjectElement = (mood == null && concretisation == null) ?
|
MoodElement.MoodSubjectElement subjectElement = (mood == null && concretisation == null) ?
|
||||||
null : new MoodElement.MoodSubjectElement(mood, concretisation);
|
null : new MoodElement.MoodSubjectElement(mood, concretisation);
|
||||||
return new MoodElement(subjectElement, text);
|
return new MoodElement(subjectElement, text);
|
||||||
|
|
|
@ -46,8 +46,6 @@ public class StreamInitiationProvider extends IQProvider<StreamInitiation> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamInitiation parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
public StreamInitiation parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
boolean done = false;
|
|
||||||
|
|
||||||
// si
|
// si
|
||||||
String id = parser.getAttributeValue("", "id");
|
String id = parser.getAttributeValue("", "id");
|
||||||
String mimeType = parser.getAttributeValue("", "mime-type");
|
String mimeType = parser.getAttributeValue("", "mime-type");
|
||||||
|
@ -66,13 +64,11 @@ public class StreamInitiationProvider extends IQProvider<StreamInitiation> {
|
||||||
DataForm form = null;
|
DataForm form = null;
|
||||||
DataFormProvider dataFormProvider = new DataFormProvider();
|
DataFormProvider dataFormProvider = new DataFormProvider();
|
||||||
|
|
||||||
String elementName;
|
outerloop: while (true) {
|
||||||
String namespace;
|
|
||||||
while (!done) {
|
|
||||||
XmlPullParser.Event eventType = parser.next();
|
XmlPullParser.Event eventType = parser.next();
|
||||||
elementName = parser.getName();
|
|
||||||
namespace = parser.getNamespace();
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String elementName = parser.getName();
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
if (elementName.equals("file")) {
|
if (elementName.equals("file")) {
|
||||||
name = parser.getAttributeValue("", "name");
|
name = parser.getAttributeValue("", "name");
|
||||||
size = parser.getAttributeValue("", "size");
|
size = parser.getAttributeValue("", "size");
|
||||||
|
@ -87,9 +83,10 @@ public class StreamInitiationProvider extends IQProvider<StreamInitiation> {
|
||||||
form = dataFormProvider.parse(parser);
|
form = dataFormProvider.parse(parser);
|
||||||
}
|
}
|
||||||
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
||||||
if (elementName.equals("si")) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
done = true;
|
break outerloop;
|
||||||
} else if (elementName.equals("file")) {
|
}
|
||||||
|
if (parser.getName().equals("file")) {
|
||||||
long fileSize = 0;
|
long fileSize = 0;
|
||||||
if (size != null && size.trim().length() != 0) {
|
if (size != null && size.trim().length() != 0) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus
|
* Copyright 2020-2021 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.
|
||||||
|
@ -29,9 +29,12 @@ public class AbstractMultiFormField extends FormField {
|
||||||
|
|
||||||
private final List<String> values;
|
private final List<String> values;
|
||||||
|
|
||||||
|
private final List<String> rawValues;
|
||||||
|
|
||||||
protected AbstractMultiFormField(Builder<?, ?> builder) {
|
protected AbstractMultiFormField(Builder<?, ?> builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
values = CollectionUtil.cloneAndSeal(builder.values);
|
values = CollectionUtil.cloneAndSeal(builder.values);
|
||||||
|
rawValues = CollectionUtil.cloneAndSeal(builder.rawValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,11 +42,16 @@ public class AbstractMultiFormField extends FormField {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final List<String> getRawValues() {
|
||||||
|
return rawValues;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract static class Builder<F extends FormField, B extends FormField.Builder<F, B>>
|
public abstract static class Builder<F extends AbstractMultiFormField, B extends FormField.Builder<F, B>>
|
||||||
extends FormField.Builder<F, B> {
|
extends FormField.Builder<F, B> {
|
||||||
|
|
||||||
private List<String> values;
|
private List<String> values;
|
||||||
|
private List<String> rawValues;
|
||||||
|
|
||||||
protected Builder(AbstractMultiFormField formField) {
|
protected Builder(AbstractMultiFormField formField) {
|
||||||
super(formField);
|
super(formField);
|
||||||
|
@ -57,6 +65,7 @@ public class AbstractMultiFormField extends FormField {
|
||||||
private void ensureValuesAreInitialized() {
|
private void ensureValuesAreInitialized() {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new ArrayList<>();
|
values = new ArrayList<>();
|
||||||
|
rawValues = new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +79,9 @@ public class AbstractMultiFormField extends FormField {
|
||||||
public B addValueVerbatim(CharSequence value) {
|
public B addValueVerbatim(CharSequence value) {
|
||||||
ensureValuesAreInitialized();
|
ensureValuesAreInitialized();
|
||||||
|
|
||||||
values.add(value.toString());
|
String valueString = value.toString();
|
||||||
|
values.add(valueString);
|
||||||
|
rawValues.add(valueString);
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +94,7 @@ public class AbstractMultiFormField extends FormField {
|
||||||
ensureValuesAreInitialized();
|
ensureValuesAreInitialized();
|
||||||
|
|
||||||
for (CharSequence value : values) {
|
for (CharSequence value : values) {
|
||||||
this.values.add(value.toString());
|
addValueVerbatim(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getThis();
|
return getThis();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus.
|
* Copyright 2020-2021 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.
|
||||||
|
@ -42,7 +42,8 @@ public class AbstractSingleStringValueFormField extends SingleValueFormField {
|
||||||
return Integer.valueOf(value);
|
return Integer.valueOf(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class Builder<F extends FormField, B extends FormField.Builder<F, B>> extends FormField.Builder<F, B> {
|
public abstract static class Builder<F extends SingleValueFormField, B extends SingleValueFormField.Builder<F, B>>
|
||||||
|
extends SingleValueFormField.Builder<F, B> {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
|
@ -74,18 +75,16 @@ public class AbstractSingleStringValueFormField extends SingleValueFormField {
|
||||||
}
|
}
|
||||||
|
|
||||||
public B setValue(CharSequence value) {
|
public B setValue(CharSequence value) {
|
||||||
this.value = value.toString();
|
this.rawValue = this.value = value.toString();
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public B setValue(Enum<?> value) {
|
public B setValue(Enum<?> value) {
|
||||||
this.value = value.toString();
|
return setValue(value.toString());
|
||||||
return getThis();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public B setValue(int value) {
|
public B setValue(int value) {
|
||||||
this.value = Integer.toString(value);
|
return setValue(Integer.toString(value));
|
||||||
return getThis();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public B setValue(URL value) {
|
public B setValue(URL value) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus.
|
* Copyright 2020-2021 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.
|
||||||
|
@ -43,7 +43,7 @@ public class BooleanFormField extends SingleValueFormField {
|
||||||
return new Builder(this);
|
return new Builder(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder extends FormField.Builder<BooleanFormField, BooleanFormField.Builder> {
|
public static final class Builder extends SingleValueFormField.Builder<BooleanFormField, BooleanFormField.Builder> {
|
||||||
private Boolean value;
|
private Boolean value;
|
||||||
|
|
||||||
private Builder(BooleanFormField booleanFormField) {
|
private Builder(BooleanFormField booleanFormField) {
|
||||||
|
@ -57,6 +57,7 @@ public class BooleanFormField extends SingleValueFormField {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetInternal() {
|
protected void resetInternal() {
|
||||||
|
super.resetInternal();
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,11 +75,13 @@ public class BooleanFormField extends SingleValueFormField {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setValue(CharSequence value) {
|
public Builder setValue(CharSequence value) {
|
||||||
boolean valueBoolean = ParserUtils.parseXmlBoolean(value.toString());
|
rawValue = value.toString();
|
||||||
|
boolean valueBoolean = ParserUtils.parseXmlBoolean(rawValue);
|
||||||
return setValue(valueBoolean);
|
return setValue(valueBoolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setValue(boolean value) {
|
public Builder setValue(boolean value) {
|
||||||
|
rawValue = Boolean.toString(value);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2019-2020 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2019-2021 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* 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.
|
||||||
|
@ -271,6 +271,8 @@ public abstract class FormField implements XmlElement {
|
||||||
*/
|
*/
|
||||||
public abstract List<? extends CharSequence> getValues();
|
public abstract List<? extends CharSequence> getValues();
|
||||||
|
|
||||||
|
public abstract List<String> getRawValues();
|
||||||
|
|
||||||
public boolean hasValueSet() {
|
public boolean hasValueSet() {
|
||||||
List<?> values = getValues();
|
List<?> values = getValues();
|
||||||
return !values.isEmpty();
|
return !values.isEmpty();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus.
|
* Copyright 2020-2021 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.
|
||||||
|
@ -23,14 +23,18 @@ import java.util.List;
|
||||||
import org.jivesoftware.smack.util.CollectionUtil;
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
import org.jxmpp.jid.util.JidUtil;
|
||||||
|
|
||||||
public class JidMultiFormField extends FormField {
|
public final class JidMultiFormField extends FormField {
|
||||||
|
|
||||||
private final List<Jid> values;
|
private final List<Jid> values;
|
||||||
|
|
||||||
protected JidMultiFormField(Builder builder) {
|
private final List<String> rawValues;
|
||||||
|
|
||||||
|
JidMultiFormField(Builder builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
values = CollectionUtil.cloneAndSeal(builder.values);
|
values = CollectionUtil.cloneAndSeal(builder.values);
|
||||||
|
rawValues = CollectionUtil.cloneAndSeal(builder.rawValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,6 +42,11 @@ public class JidMultiFormField extends FormField {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getRawValues() {
|
||||||
|
return rawValues;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder asBuilder() {
|
public Builder asBuilder() {
|
||||||
return new Builder(this);
|
return new Builder(this);
|
||||||
}
|
}
|
||||||
|
@ -45,6 +54,8 @@ public class JidMultiFormField extends FormField {
|
||||||
public static final class Builder extends FormField.Builder<JidMultiFormField, JidMultiFormField.Builder> {
|
public static final class Builder extends FormField.Builder<JidMultiFormField, JidMultiFormField.Builder> {
|
||||||
private List<Jid> values;
|
private List<Jid> values;
|
||||||
|
|
||||||
|
private List<String> rawValues;
|
||||||
|
|
||||||
private Builder(JidMultiFormField jidMultiFormField) {
|
private Builder(JidMultiFormField jidMultiFormField) {
|
||||||
super(jidMultiFormField);
|
super(jidMultiFormField);
|
||||||
values = CollectionUtil.newListWith(jidMultiFormField.getValues());
|
values = CollectionUtil.newListWith(jidMultiFormField.getValues());
|
||||||
|
@ -57,18 +68,30 @@ public class JidMultiFormField extends FormField {
|
||||||
private void ensureValuesAreInitialized() {
|
private void ensureValuesAreInitialized() {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new ArrayList<>();
|
values = new ArrayList<>();
|
||||||
|
rawValues = new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetInternal() {
|
protected void resetInternal() {
|
||||||
values = null;
|
values = null;
|
||||||
|
rawValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addValue(Jid jid) {
|
public Builder addValue(Jid jid) {
|
||||||
|
return addValue(jid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addValue(Jid jid, String rawValue) {
|
||||||
|
if (rawValue == null) {
|
||||||
|
rawValue = jid.toString();
|
||||||
|
}
|
||||||
|
|
||||||
ensureValuesAreInitialized();
|
ensureValuesAreInitialized();
|
||||||
|
|
||||||
values.add(jid);
|
values.add(jid);
|
||||||
|
rawValues.add(rawValue);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +99,7 @@ public class JidMultiFormField extends FormField {
|
||||||
ensureValuesAreInitialized();
|
ensureValuesAreInitialized();
|
||||||
|
|
||||||
values.addAll(jids);
|
values.addAll(jids);
|
||||||
|
rawValues.addAll(JidUtil.toStringList(jids));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus.
|
* Copyright 2020-2021 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,7 @@ public class JidSingleFormField extends SingleValueFormField {
|
||||||
return new Builder(this);
|
return new Builder(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder extends FormField.Builder<JidSingleFormField, JidSingleFormField.Builder> {
|
public static final class Builder extends SingleValueFormField.Builder<JidSingleFormField, JidSingleFormField.Builder> {
|
||||||
private Jid value;
|
private Jid value;
|
||||||
|
|
||||||
private Builder(JidSingleFormField jidSingleFormField) {
|
private Builder(JidSingleFormField jidSingleFormField) {
|
||||||
|
@ -50,11 +50,21 @@ public class JidSingleFormField extends SingleValueFormField {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetInternal() {
|
protected void resetInternal() {
|
||||||
|
super.resetInternal();
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setValue(Jid value) {
|
public Builder setValue(Jid value) {
|
||||||
|
return setValue(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setValue(Jid value, String rawValue) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
if (rawValue != null) {
|
||||||
|
this.rawValue = rawValue;
|
||||||
|
} else {
|
||||||
|
this.rawValue = value.toString();
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus.
|
* Copyright 2020-2021 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,23 +19,35 @@ package org.jivesoftware.smackx.xdata;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
|
|
||||||
public abstract class SingleValueFormField extends FormField {
|
public abstract class SingleValueFormField extends FormField {
|
||||||
|
|
||||||
|
private final String rawValue;
|
||||||
|
|
||||||
protected SingleValueFormField(Builder<?, ?> builder) {
|
protected SingleValueFormField(Builder<?, ?> builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
|
rawValue = builder.rawValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final List<CharSequence> getValues() {
|
public final List<CharSequence> getValues() {
|
||||||
CharSequence value = getValue();
|
CharSequence value = getValue();
|
||||||
if (value == null) {
|
return CollectionUtil.emptyOrSingletonListFrom(value);
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Collections.singletonList(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract CharSequence getValue();
|
public abstract CharSequence getValue();
|
||||||
|
|
||||||
|
public final String getRawValue() {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final List<String> getRawValues() {
|
||||||
|
String rawValue = getRawValue();
|
||||||
|
return CollectionUtil.emptyOrSingletonListFrom(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populateExtraXmlChildElements() {
|
protected void populateExtraXmlChildElements() {
|
||||||
CharSequence value = getValue();
|
CharSequence value = getValue();
|
||||||
|
@ -45,4 +57,24 @@ public abstract class SingleValueFormField extends FormField {
|
||||||
|
|
||||||
extraXmlChildElements = Collections.singletonList(new Value(value));
|
extraXmlChildElements = Collections.singletonList(new Value(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract static class Builder<F extends SingleValueFormField, B extends Builder<F, B>>
|
||||||
|
extends FormField.Builder<F, B> {
|
||||||
|
|
||||||
|
protected Builder(String fieldName, Type type) {
|
||||||
|
super(fieldName, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Builder(FormField formField) {
|
||||||
|
super(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String rawValue;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void resetInternal() {
|
||||||
|
rawValue = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,13 @@ public class FillableForm extends FilledForm {
|
||||||
if (formField.isRequired()) {
|
if (formField.isRequired()) {
|
||||||
String fieldName = formField.getFieldName();
|
String fieldName = formField.getFieldName();
|
||||||
requiredFields.add(fieldName);
|
requiredFields.add(fieldName);
|
||||||
missingRequiredFields.add(fieldName);
|
|
||||||
|
if (formField.hasValueSet()) {
|
||||||
|
// This is a form field with a default value.
|
||||||
|
write(formField);
|
||||||
|
} else {
|
||||||
|
missingRequiredFields.add(fieldName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.requiredFields = Collections.unmodifiableSet(requiredFields);
|
this.requiredFields = Collections.unmodifiableSet(requiredFields);
|
||||||
|
@ -223,9 +229,6 @@ public class FillableForm extends FilledForm {
|
||||||
if (!getDataForm().hasField(fieldName)) {
|
if (!getDataForm().hasField(fieldName)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
if (filledFields.containsKey(fieldName)) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform validation, e.g. using XEP-0122.
|
// Perform validation, e.g. using XEP-0122.
|
||||||
// TODO: We could also perform list-* option validation, but this has to take xep122's <open/> into account.
|
// TODO: We could also perform list-* option validation, but this has to take xep122's <open/> into account.
|
||||||
|
|
|
@ -237,8 +237,9 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
case jid_multi:
|
case jid_multi:
|
||||||
JidMultiFormField.Builder jidMultiBuilder = FormField.jidMultiBuilder(fieldName);
|
JidMultiFormField.Builder jidMultiBuilder = FormField.jidMultiBuilder(fieldName);
|
||||||
for (FormField.Value value : values) {
|
for (FormField.Value value : values) {
|
||||||
Jid jid = JidCreate.from(value.getValue());
|
String rawValue = value.getValue().toString();
|
||||||
jidMultiBuilder.addValue(jid);
|
Jid jid = JidCreate.from(rawValue);
|
||||||
|
jidMultiBuilder.addValue(jid, rawValue);
|
||||||
}
|
}
|
||||||
builder = jidMultiBuilder;
|
builder = jidMultiBuilder;
|
||||||
break;
|
break;
|
||||||
|
@ -246,9 +247,9 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
ensureAtMostSingleValue(type, values);
|
ensureAtMostSingleValue(type, values);
|
||||||
JidSingleFormField.Builder jidSingleBuilder = FormField.jidSingleBuilder(fieldName);
|
JidSingleFormField.Builder jidSingleBuilder = FormField.jidSingleBuilder(fieldName);
|
||||||
if (!values.isEmpty()) {
|
if (!values.isEmpty()) {
|
||||||
CharSequence jidCharSequence = values.get(0).getValue();
|
String rawValue = values.get(0).getValue().toString();
|
||||||
Jid jid = JidCreate.from(jidCharSequence);
|
Jid jid = JidCreate.from(rawValue);
|
||||||
jidSingleBuilder.setValue(jid);
|
jidSingleBuilder.setValue(jid, rawValue);
|
||||||
}
|
}
|
||||||
builder = jidSingleBuilder;
|
builder = jidSingleBuilder;
|
||||||
break;
|
break;
|
||||||
|
@ -300,6 +301,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
|
|
||||||
private static FormField.Builder<?, ?> parseBooleanFormField(String fieldName, List<FormField.Value> values) throws SmackParsingException {
|
private static FormField.Builder<?, ?> parseBooleanFormField(String fieldName, List<FormField.Value> values) throws SmackParsingException {
|
||||||
BooleanFormField.Builder builder = FormField.booleanBuilder(fieldName);
|
BooleanFormField.Builder builder = FormField.booleanBuilder(fieldName);
|
||||||
|
ensureAtMostSingleValue(builder.getType(), values);
|
||||||
if (values.size() == 1) {
|
if (values.size() == 1) {
|
||||||
String value = values.get(0).getValue().toString();
|
String value = values.get(0).getValue().toString();
|
||||||
builder.setValue(value);
|
builder.setValue(value);
|
||||||
|
@ -321,7 +323,8 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
private static AbstractMultiFormField.Builder<?, ?> parseMultiKindFormField(AbstractMultiFormField.Builder<?, ?> builder,
|
private static AbstractMultiFormField.Builder<?, ?> parseMultiKindFormField(AbstractMultiFormField.Builder<?, ?> builder,
|
||||||
List<FormField.Value> values) {
|
List<FormField.Value> values) {
|
||||||
for (FormField.Value value : values) {
|
for (FormField.Value value : values) {
|
||||||
builder.addValue(value.getValue());
|
String rawValue = value.getValue().toString();
|
||||||
|
builder.addValue(rawValue);
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2014-2019 Florian Schmaus
|
* Copyright 2003-2007 Jive Software, 2014-2021 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.
|
||||||
|
@ -40,8 +40,8 @@ public class XHTMLExtensionProvider extends ExtensionElementProvider<XHTMLExtens
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
XmlPullParser.Event eventType = parser.getEventType();
|
XmlPullParser.Event eventType = parser.getEventType();
|
||||||
String name = parser.getName();
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String name = parser.getName();
|
||||||
if (name.equals(Message.BODY)) {
|
if (name.equals(Message.BODY)) {
|
||||||
xhtmlExtension.addBody(PacketParserUtils.parseElement(parser));
|
xhtmlExtension.addBody(PacketParserUtils.parseElement(parser));
|
||||||
}
|
}
|
||||||
|
|
|
@ -590,6 +590,14 @@ public class SmackIntegrationTestFramework {
|
||||||
if (e instanceof InterruptedException) {
|
if (e instanceof InterruptedException) {
|
||||||
throw (InterruptedException) e;
|
throw (InterruptedException) e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We handle NullPointerException as a non fatal exception, as they are mostly caused by an invalid reply where
|
||||||
|
// a extension element is missing. Consider for example
|
||||||
|
// assertEquals(StanzaError.Condition.foo, response.getError().getCondition())
|
||||||
|
// Otherwise NPEs could be caused by an internal bug in Smack, e.g. missing null handling.
|
||||||
|
if (e instanceof NullPointerException) {
|
||||||
|
return (NullPointerException) e;
|
||||||
|
}
|
||||||
if (e instanceof RuntimeException) {
|
if (e instanceof RuntimeException) {
|
||||||
throw (RuntimeException) e;
|
throw (RuntimeException) e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2021 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.muc;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.Form;
|
||||||
|
|
||||||
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
||||||
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.jid.parts.Localpart;
|
||||||
|
import org.jxmpp.jid.parts.Resourcepart;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
|
|
||||||
|
public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
final String randomString = StringUtils.insecureRandomString(6);
|
||||||
|
|
||||||
|
final MultiUserChatManager mucManagerOne;
|
||||||
|
final MultiUserChatManager mucManagerTwo;
|
||||||
|
final MultiUserChatManager mucManagerThree;
|
||||||
|
final DomainBareJid mucService;
|
||||||
|
|
||||||
|
public AbstractMultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment)
|
||||||
|
throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
|
InterruptedException, TestNotPossibleException {
|
||||||
|
super(environment);
|
||||||
|
mucManagerOne = MultiUserChatManager.getInstanceFor(conOne);
|
||||||
|
mucManagerTwo = MultiUserChatManager.getInstanceFor(conTwo);
|
||||||
|
mucManagerThree = MultiUserChatManager.getInstanceFor(conThree);
|
||||||
|
|
||||||
|
List<DomainBareJid> services = mucManagerOne.getMucServiceDomains();
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
throw new TestNotPossibleException("No MUC (XEP-45) service found");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mucService = services.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a random room name.
|
||||||
|
*
|
||||||
|
* @param prefix A prefix to add to the room name for descriptive purposes
|
||||||
|
* @return the bare JID of a random room
|
||||||
|
* @throws XmppStringprepException if the prefix isn't a valid XMPP Localpart
|
||||||
|
*/
|
||||||
|
public EntityBareJid getRandomRoom(String prefix) throws XmppStringprepException {
|
||||||
|
final String roomNameLocal = String.join("-", prefix, testRunId, StringUtils.insecureRandomString(6));
|
||||||
|
return JidCreate.entityBareFrom(Localpart.from(roomNameLocal), mucService.getDomain());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys a MUC room, ignoring any exceptions.
|
||||||
|
*
|
||||||
|
* @param muc The room to destroy (can be null).
|
||||||
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
|
* @throws SmackException.NotConnectedException if the XMPP connection is not connected.
|
||||||
|
* @throws XMPPException.XMPPErrorException if there was an XMPP error returned.
|
||||||
|
* @throws SmackException.NoResponseException if there was no response from the remote entity.
|
||||||
|
*/
|
||||||
|
static void tryDestroy(final MultiUserChat muc) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException {
|
||||||
|
if (muc == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
muc.destroy("test fixture teardown", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
|
||||||
|
MultiUserChat.MucCreateConfigFormHandle handle = muc.create(Resourcepart.from(resourceName));
|
||||||
|
if (handle != null) {
|
||||||
|
handle.makeInstant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createModeratedMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
|
||||||
|
muc.create(Resourcepart.from(resourceName));
|
||||||
|
Form configForm = muc.getConfigurationForm();
|
||||||
|
FillableForm answerForm = configForm.getFillableForm();
|
||||||
|
answerForm.setAnswer("muc#roomconfig_moderatedroom", true); //TODO Add this to the MucConfigFormManager?
|
||||||
|
muc.sendConfigurationForm(answerForm);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,66 +16,35 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.muc;
|
package org.jivesoftware.smackx.muc;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
|
||||||
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.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.MessageListener;
|
import org.jivesoftware.smack.MessageListener;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChat.MucCreateConfigFormHandle;
|
|
||||||
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
||||||
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
|
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
|
||||||
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.EntityFullJid;
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.jxmpp.jid.parts.Localpart;
|
|
||||||
import org.jxmpp.jid.parts.Resourcepart;
|
import org.jxmpp.jid.parts.Resourcepart;
|
||||||
import org.jxmpp.stringprep.XmppStringprepException;
|
|
||||||
|
|
||||||
public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
public class MultiUserChatIntegrationTest extends AbstractMultiUserChatIntegrationTest {
|
||||||
|
|
||||||
private final String randomString = StringUtils.insecureRandomString(6);
|
|
||||||
|
|
||||||
private final MultiUserChatManager mucManagerOne;
|
|
||||||
private final MultiUserChatManager mucManagerTwo;
|
|
||||||
private final MultiUserChatManager mucManagerThree;
|
|
||||||
private final DomainBareJid mucService;
|
|
||||||
|
|
||||||
public MultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment)
|
public MultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment)
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
InterruptedException, TestNotPossibleException {
|
InterruptedException, TestNotPossibleException {
|
||||||
super(environment);
|
super(environment);
|
||||||
mucManagerOne = MultiUserChatManager.getInstanceFor(conOne);
|
|
||||||
mucManagerTwo = MultiUserChatManager.getInstanceFor(conTwo);
|
|
||||||
mucManagerThree = MultiUserChatManager.getInstanceFor(conThree);
|
|
||||||
|
|
||||||
List<DomainBareJid> services = mucManagerOne.getMucServiceDomains();
|
|
||||||
if (services.isEmpty()) {
|
|
||||||
throw new TestNotPossibleException("No MUC (XEP-45) service found");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mucService = services.get(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,7 +60,7 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
*/
|
*/
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
public void mucJoinTest() throws Exception {
|
public void mucJoinTest() throws Exception {
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest-join-leave");
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest-join");
|
||||||
|
|
||||||
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
|
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
try {
|
try {
|
||||||
|
@ -122,7 +91,7 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
*/
|
*/
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
public void mucLeaveTest() throws Exception {
|
public void mucLeaveTest() throws Exception {
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest-join-leave");
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest-leave");
|
||||||
|
|
||||||
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
|
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
try {
|
try {
|
||||||
|
@ -137,13 +106,14 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
assertEquals(mucAddress + "/nick-one", reflectedLeavePresence.getFrom().toString());
|
assertEquals(mucAddress + "/nick-one", reflectedLeavePresence.getFrom().toString());
|
||||||
assertEquals(conOne.getUser().asEntityFullJidIfPossible().toString(), reflectedLeavePresence.getTo().toString());
|
assertEquals(conOne.getUser().asEntityFullJidIfPossible().toString(), reflectedLeavePresence.getTo().toString());
|
||||||
} finally {
|
} finally {
|
||||||
|
muc.join(Resourcepart.from("nick-one")); // We need to be in the room to destroy the room
|
||||||
tryDestroy(muc);
|
tryDestroy(muc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
public void mucTest() throws Exception {
|
public void mucTest() throws Exception {
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest-message");
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
@ -161,7 +131,7 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
mucAsSeenByTwo.join(Resourcepart.from("two-" + randomString));
|
mucAsSeenByTwo.join(Resourcepart.from("two-" + randomString));
|
||||||
mucAsSeenByOne.sendMessage(mucMessage);
|
mucAsSeenByOne.sendMessage(mucMessage);
|
||||||
try {
|
try {
|
||||||
|
@ -173,581 +143,9 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.1.3:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 9.6:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The service MUST then send updated presence from this individual to all occupants, indicating the addition of
|
|
||||||
* moderator status...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucRoleTestForReceivingModerator() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void moderatorGranted() {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
|
|
||||||
// This implicitly tests "The service MUST add the user to the moderator list and then inform the admin of
|
|
||||||
// success" in §9.6, since it'll throw on either an error IQ or on no response.
|
|
||||||
mucAsSeenByOne.grantModerator(nicknameTwo);
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.1.3:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 9.6:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The service MUST then send updated presence from this individual to all occupants, indicating the addition of
|
|
||||||
* moderator status...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucRoleTestForWitnessingModerator() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void moderatorGranted(EntityFullJid participant) {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByThree.join(nicknameThree);
|
|
||||||
|
|
||||||
mucAsSeenByOne.grantModerator(nicknameTwo);
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.1.3:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 9.7:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
|
||||||
* moderator status...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucRoleTestForRemovingModerator() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void moderatorRevoked() {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
|
|
||||||
mucAsSeenByOne.grantModerator(nicknameTwo);
|
|
||||||
mucAsSeenByOne.revokeModerator(nicknameTwo);
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.1.3:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 9.7:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
|
||||||
* moderator status...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucRoleTestForWitnessingModeratorRemoval() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void moderatorRevoked(EntityFullJid participant) {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByThree.join(nicknameThree);
|
|
||||||
|
|
||||||
mucAsSeenByOne.grantModerator(nicknameTwo);
|
|
||||||
mucAsSeenByOne.revokeModerator(nicknameTwo);
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user in an unmoderated room who undergoes an afilliation change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.1.3:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 8.4:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
|
||||||
* voice privileges...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucRoleTestForRevokingVoice() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void voiceRevoked() {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByOne.revokeVoice(nicknameTwo);
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.1.3:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 8.4:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
|
||||||
* voice privileges...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucRoleTestForWitnessingRevokingVoice() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void voiceRevoked(EntityFullJid participant) {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByThree.join(nicknameThree);
|
|
||||||
|
|
||||||
mucAsSeenByOne.revokeVoice(nicknameTwo);
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who undergoes an affiliation change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.2.2:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 10.6:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
|
||||||
* indicating the granting of admin status...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucAffiliationTestForReceivingAdmin() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
|
|
||||||
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void adminGranted() {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
|
|
||||||
// This implicitly tests "The service MUST add the user to the admin list and then inform the owner of success" in §10.6, since it'll throw on either an error IQ or on no response.
|
|
||||||
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who is present when another user undergoes an affiliation change receives that change as a
|
|
||||||
* presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.2.2:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that
|
|
||||||
* to all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 10.6:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
|
||||||
* indicating the granting of admin status...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucAffiliationTestForWitnessingAdmin() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void adminGranted(EntityFullJid participant) {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByThree.join(nicknameThree);
|
|
||||||
|
|
||||||
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.2.2:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that to
|
|
||||||
* all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 10.7:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
|
||||||
* indicating the loss of admin status by sending a presence element...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucAffiliationTestForRemovingAdmin() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void adminRevoked() {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
|
|
||||||
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
|
||||||
mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid());
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 5.2.2:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that to
|
|
||||||
* all occupants...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 10.7:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
|
||||||
* indicating the loss of admin status by sending a presence element...
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucAffiliationTestForWitnessingAdminRemoval() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
|
|
||||||
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
|
||||||
@Override
|
|
||||||
public void adminRevoked(EntityFullJid participant) {
|
|
||||||
resultSyncPoint.signal("done");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByThree.join(nicknameThree);
|
|
||||||
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
|
||||||
|
|
||||||
mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid());
|
|
||||||
try {
|
|
||||||
resultSyncPoint.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who gets kicked receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 8.2:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* The kick is performed based on the occupant's room nickname and is completed by setting the role of a
|
|
||||||
* participant or visitor to a value of "none".
|
|
||||||
*
|
|
||||||
* The service MUST remove the kicked occupant by sending a presence stanza of type "unavailable" to each kicked
|
|
||||||
* occupant, including status code 307 in the extended presence information, optionally along with the reason (if
|
|
||||||
* provided) and the roomnick or bare JID of the user who initiated the kick.
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucPresenceTestForGettingKicked() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
|
|
||||||
final ResultSyncPoint<Presence, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
mucAsSeenByTwo.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence));
|
|
||||||
|
|
||||||
mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test.");
|
|
||||||
try {
|
|
||||||
Presence kickPresence = resultSyncPoint.waitForResult(timeout);
|
|
||||||
MUCUser mucUser = MUCUser.from(kickPresence);
|
|
||||||
assertNotNull(mucUser);
|
|
||||||
assertAll(
|
|
||||||
() -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Missing self-presence status code in kick presence"),
|
|
||||||
() -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence"),
|
|
||||||
() -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence")
|
|
||||||
);
|
|
||||||
Jid itemJid = mucUser.getItem().getJid();
|
|
||||||
if (itemJid != null) {
|
|
||||||
assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence");
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a user who is present when another user gets kicked receives that change as a presence update
|
|
||||||
*
|
|
||||||
* <p>From XEP-0045 § 8.2:</p>
|
|
||||||
* <blockquote>
|
|
||||||
* ...the service MUST then inform all of the remaining occupants that the kicked occupant is no longer in the room
|
|
||||||
* by sending presence stanzas of type "unavailable" from the individual's roomnick (<room@service/nick>) to all
|
|
||||||
* the remaining occupants (just as it does when occupants exit the room of their own volition), including the
|
|
||||||
* status code and optionally the reason and actor.
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @throws Exception when errors occur
|
|
||||||
*/
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void mucPresenceTestForWitnessingKick() throws Exception {
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
|
||||||
|
|
||||||
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
|
||||||
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
|
||||||
|
|
||||||
createMUC(mucAsSeenByOne, "one-" + randomString);
|
|
||||||
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
|
||||||
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
|
||||||
mucAsSeenByTwo.join(nicknameTwo);
|
|
||||||
mucAsSeenByThree.join(nicknameThree);
|
|
||||||
|
|
||||||
final ResultSyncPoint<Presence, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
|
||||||
mucAsSeenByThree.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence));
|
|
||||||
|
|
||||||
mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test.");
|
|
||||||
try {
|
|
||||||
Presence kickPresence = resultSyncPoint.waitForResult(timeout);
|
|
||||||
MUCUser mucUser = MUCUser.from(kickPresence);
|
|
||||||
assertNotNull(mucUser);
|
|
||||||
assertAll(
|
|
||||||
() -> assertFalse(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Incorrect self-presence status code in kick presence"),
|
|
||||||
() -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence"),
|
|
||||||
() -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence")
|
|
||||||
);
|
|
||||||
Jid itemJid = mucUser.getItem().getJid();
|
|
||||||
if (itemJid != null) {
|
|
||||||
assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence");
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
tryDestroy(mucAsSeenByOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
* Asserts that a user is notified when a room is destroyed
|
||||||
*
|
*
|
||||||
* <p>From XEP-0045 § 10.9:</p>
|
* <p>From XEP-0045 § 10.9:</p>
|
||||||
* <blockquote>
|
* <blockquote>
|
||||||
|
@ -760,7 +158,7 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
public void mucDestroyTest() throws TimeoutException, Exception {
|
public void mucDestroyTest() throws TimeoutException, Exception {
|
||||||
|
|
||||||
EntityBareJid mucAddress = getRandomRoom("smack-inttest-join-leave");
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest-destroy");
|
||||||
|
|
||||||
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
|
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
muc.join(Resourcepart.from("nick-one"));
|
muc.join(Resourcepart.from("nick-one"));
|
||||||
|
@ -793,37 +191,6 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
assertNull(muc.getNickname());
|
assertNull(muc.getNickname());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a random room name
|
|
||||||
*
|
|
||||||
* @param prefix A prefix to add to the room name for descriptive purposes
|
|
||||||
*/
|
|
||||||
private EntityBareJid getRandomRoom(String prefix) throws XmppStringprepException {
|
|
||||||
final String roomNameLocal = String.join("-", prefix, testRunId, StringUtils.insecureRandomString(6));
|
|
||||||
return JidCreate.entityBareFrom(Localpart.from(roomNameLocal), mucService.getDomain());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys a MUC room, ignoring any exceptions.
|
|
||||||
*
|
|
||||||
* @param muc The room to destroy (can be null).
|
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
|
||||||
* @throws XMPPErrorException if there was an XMPP error returned.
|
|
||||||
* @throws NoResponseException if there was no response from the remote entity.
|
|
||||||
*/
|
|
||||||
static void tryDestroy(final MultiUserChat muc) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
|
||||||
if (muc == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
muc.destroy("test fixture teardown", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createMUC(MultiUserChat muc, String resourceName) throws NoResponseException, XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
|
|
||||||
MucCreateConfigFormHandle handle = muc.create(Resourcepart.from(resourceName));
|
|
||||||
if (handle != null) {
|
|
||||||
handle.makeInstant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,622 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2021 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.muc;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
|
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
||||||
|
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
||||||
|
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
|
||||||
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
import org.jxmpp.jid.EntityFullJid;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
import org.jxmpp.jid.parts.Resourcepart;
|
||||||
|
|
||||||
|
|
||||||
|
public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends AbstractMultiUserChatIntegrationTest{
|
||||||
|
|
||||||
|
public MultiUserChatRolesAffiliationsPrivilegesIntegrationTest(SmackIntegrationTestEnvironment environment)
|
||||||
|
throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
|
InterruptedException, TestNotPossibleException {
|
||||||
|
super(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who undergoes a role change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.1.3:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 9.6:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The service MUST then send updated presence from this individual to all occupants, indicating the addition of
|
||||||
|
* moderator status...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucRoleTestForReceivingModerator() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void moderatorGranted() {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
|
||||||
|
// This implicitly tests "The service MUST add the user to the moderator list and then inform the admin of
|
||||||
|
// success" in §9.6, since it'll throw on either an error IQ or on no response.
|
||||||
|
mucAsSeenByOne.grantModerator(nicknameTwo);
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.1.3:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 9.6:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The service MUST then send updated presence from this individual to all occupants, indicating the addition of
|
||||||
|
* moderator status...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucRoleTestForWitnessingModerator() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void moderatorGranted(EntityFullJid participant) {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByThree.join(nicknameThree);
|
||||||
|
|
||||||
|
mucAsSeenByOne.grantModerator(nicknameTwo);
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who undergoes a role change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.1.3:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 9.7:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
||||||
|
* moderator status...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucRoleTestForRemovingModerator() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void moderatorRevoked() {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
|
||||||
|
mucAsSeenByOne.grantModerator(nicknameTwo);
|
||||||
|
mucAsSeenByOne.revokeModerator(nicknameTwo);
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.1.3:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 9.7:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
||||||
|
* moderator status...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucRoleTestForWitnessingModeratorRemoval() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void moderatorRevoked(EntityFullJid participant) {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByThree.join(nicknameThree);
|
||||||
|
|
||||||
|
mucAsSeenByOne.grantModerator(nicknameTwo);
|
||||||
|
mucAsSeenByOne.revokeModerator(nicknameTwo);
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user in an unmoderated room who undergoes an afilliation change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.1.3:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 8.4:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
||||||
|
* voice privileges...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucRoleTestForRevokingVoice() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void voiceRevoked() {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByOne.revokeVoice(nicknameTwo);
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who is present when another user undergoes a role change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.1.3:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 8.4:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The service MUST then send updated presence from this individual to all occupants, indicating the removal of
|
||||||
|
* voice privileges...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucRoleTestForWitnessingRevokingVoice() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void voiceRevoked(EntityFullJid participant) {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByThree.join(nicknameThree);
|
||||||
|
|
||||||
|
mucAsSeenByOne.revokeVoice(nicknameTwo);
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who undergoes an affiliation change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.2.2:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 10.6:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
||||||
|
* indicating the granting of admin status...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucAffiliationTestForReceivingAdmin() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
|
||||||
|
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void adminGranted() {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
|
||||||
|
// This implicitly tests "The service MUST add the user to the admin list and then inform the owner of success" in §10.6, since it'll throw on either an error IQ or on no response.
|
||||||
|
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who is present when another user undergoes an affiliation change receives that change as a
|
||||||
|
* presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.2.2:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that
|
||||||
|
* to all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 10.6:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
||||||
|
* indicating the granting of admin status...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucAffiliationTestForWitnessingAdmin() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void adminGranted(EntityFullJid participant) {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByThree.join(nicknameThree);
|
||||||
|
|
||||||
|
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who undergoes an affiliation change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.2.2:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that to
|
||||||
|
* all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 10.7:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
||||||
|
* indicating the loss of admin status by sending a presence element...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucAffiliationTestForRemovingAdmin() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void adminRevoked() {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
|
||||||
|
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
||||||
|
mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid());
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who is present when another user undergoes an affiliation change receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 5.2.2:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that to
|
||||||
|
* all occupants...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 10.7:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* If the user is in the room, the service MUST then send updated presence from this individual to all occupants,
|
||||||
|
* indicating the loss of admin status by sending a presence element...
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucAffiliationTestForWitnessingAdminRemoval() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
|
||||||
|
mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() {
|
||||||
|
@Override
|
||||||
|
public void adminRevoked(EntityFullJid participant) {
|
||||||
|
resultSyncPoint.signal("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByThree.join(nicknameThree);
|
||||||
|
mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid());
|
||||||
|
|
||||||
|
mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid());
|
||||||
|
try {
|
||||||
|
resultSyncPoint.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who gets kicked receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 8.2:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* The kick is performed based on the occupant's room nickname and is completed by setting the role of a
|
||||||
|
* participant or visitor to a value of "none".
|
||||||
|
*
|
||||||
|
* The service MUST remove the kicked occupant by sending a presence stanza of type "unavailable" to each kicked
|
||||||
|
* occupant, including status code 307 in the extended presence information, optionally along with the reason (if
|
||||||
|
* provided) and the roomnick or bare JID of the user who initiated the kick.
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucPresenceTestForGettingKicked() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
|
||||||
|
final ResultSyncPoint<Presence, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
mucAsSeenByTwo.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence));
|
||||||
|
|
||||||
|
mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test.");
|
||||||
|
try {
|
||||||
|
Presence kickPresence = resultSyncPoint.waitForResult(timeout);
|
||||||
|
MUCUser mucUser = MUCUser.from(kickPresence);
|
||||||
|
assertNotNull(mucUser);
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Missing self-presence status code in kick presence"),
|
||||||
|
() -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence"),
|
||||||
|
() -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence")
|
||||||
|
);
|
||||||
|
Jid itemJid = mucUser.getItem().getJid();
|
||||||
|
if (itemJid != null) {
|
||||||
|
assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a user who is present when another user gets kicked receives that change as a presence update
|
||||||
|
*
|
||||||
|
* <p>From XEP-0045 § 8.2:</p>
|
||||||
|
* <blockquote>
|
||||||
|
* ...the service MUST then inform all of the remaining occupants that the kicked occupant is no longer in the room
|
||||||
|
* by sending presence stanzas of type "unavailable" from the individual's roomnick (<room@service/nick>) to all
|
||||||
|
* the remaining occupants (just as it does when occupants exit the room of their own volition), including the
|
||||||
|
* status code and optionally the reason and actor.
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @throws Exception when errors occur
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void mucPresenceTestForWitnessingKick() throws Exception {
|
||||||
|
EntityBareJid mucAddress = getRandomRoom("smack-inttest");
|
||||||
|
|
||||||
|
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
|
||||||
|
MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress);
|
||||||
|
|
||||||
|
createMuc(mucAsSeenByOne, "one-" + randomString);
|
||||||
|
final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString);
|
||||||
|
final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString);
|
||||||
|
mucAsSeenByTwo.join(nicknameTwo);
|
||||||
|
mucAsSeenByThree.join(nicknameThree);
|
||||||
|
|
||||||
|
final ResultSyncPoint<Presence, Exception> resultSyncPoint = new ResultSyncPoint<>();
|
||||||
|
mucAsSeenByThree.addParticipantListener(kickPresence -> resultSyncPoint.signal(kickPresence));
|
||||||
|
|
||||||
|
mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test.");
|
||||||
|
try {
|
||||||
|
Presence kickPresence = resultSyncPoint.waitForResult(timeout);
|
||||||
|
MUCUser mucUser = MUCUser.from(kickPresence);
|
||||||
|
assertNotNull(mucUser);
|
||||||
|
assertAll(
|
||||||
|
() -> assertFalse(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Incorrect self-presence status code in kick presence"),
|
||||||
|
() -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence"),
|
||||||
|
() -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence")
|
||||||
|
);
|
||||||
|
Jid itemJid = mucUser.getItem().getJid();
|
||||||
|
if (itemJid != null) {
|
||||||
|
assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
tryDestroy(mucAsSeenByOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
@ -63,7 +64,7 @@ public abstract class AbstractTwoUsersOmemoIntegrationTest extends AbstractOmemo
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public void cleanUp() throws IOException {
|
public void cleanUp() throws IOException, NotConnectedException, InterruptedException {
|
||||||
alice.stopStanzaAndPEPListeners();
|
alice.stopStanzaAndPEPListeners();
|
||||||
bob.stopStanzaAndPEPListeners();
|
bob.stopStanzaAndPEPListeners();
|
||||||
OmemoManagerSetupHelper.cleanUpPubSub(alice);
|
OmemoManagerSetupHelper.cleanUpPubSub(alice);
|
||||||
|
|
|
@ -22,8 +22,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.roster.Roster;
|
import org.jivesoftware.smack.roster.Roster;
|
||||||
|
@ -31,12 +33,9 @@ import org.jivesoftware.smack.roster.RosterEntry;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
@ -124,48 +123,10 @@ public class OmemoManagerSetupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanUpPubSub(OmemoManager omemoManager) throws IOException {
|
public static void cleanUpPubSub(OmemoManager omemoManager)
|
||||||
PubSubManager pm = PubSubManager.getInstanceFor(omemoManager.getConnection(), omemoManager.getOwnJid());
|
throws IOException, NotConnectedException, InterruptedException {
|
||||||
try {
|
List<Exception> exceptions = omemoManager.purgeEverything();
|
||||||
omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid());
|
assertTrue(exceptions.isEmpty(), "There where exceptions while purging OMEMO: " + exceptions);
|
||||||
} catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
OmemoCachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
|
|
||||||
.loadCachedDeviceList(omemoManager.getOwnDevice(), omemoManager.getOwnJid());
|
|
||||||
|
|
||||||
for (int id : deviceList.getAllDevices()) {
|
|
||||||
try {
|
|
||||||
pm.getLeafNode(OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(id)).deleteAllItems();
|
|
||||||
} catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException |
|
|
||||||
PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException |
|
|
||||||
PubSubException.NotAPubSubNodeException e) {
|
|
||||||
// Silent
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
pm.deleteNode(OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(id));
|
|
||||||
} catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException
|
|
||||||
| XMPPException.XMPPErrorException e) {
|
|
||||||
// Silent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
pm.getLeafNode(OmemoConstants.PEP_NODE_DEVICE_LIST).deleteAllItems();
|
|
||||||
} catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException |
|
|
||||||
PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException |
|
|
||||||
PubSubException.NotAPubSubNodeException e) {
|
|
||||||
// Silent
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
pm.deleteNode(OmemoConstants.PEP_NODE_DEVICE_LIST);
|
|
||||||
} catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException |
|
|
||||||
XMPPException.XMPPErrorException e) {
|
|
||||||
// Silent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanUpRoster(OmemoManager omemoManager) {
|
public static void cleanUpRoster(OmemoManager omemoManager) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus
|
* Copyright 2020-2021 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.
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
package org.jivesoftware.smack.full;
|
package org.jivesoftware.smack.full;
|
||||||
|
|
||||||
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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
@ -25,6 +26,8 @@ import java.io.StringWriter;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
|
||||||
|
import org.jivesoftware.smack.compression.CompressionModuleDescriptor;
|
||||||
import org.jivesoftware.smack.util.EqualsUtil;
|
import org.jivesoftware.smack.util.EqualsUtil;
|
||||||
import org.jivesoftware.smack.util.HashCode;
|
import org.jivesoftware.smack.util.HashCode;
|
||||||
|
|
||||||
|
@ -34,6 +37,7 @@ import org.jgrapht.graph.DirectedPseudograph;
|
||||||
import org.jgrapht.io.DOTImporter;
|
import org.jgrapht.io.DOTImporter;
|
||||||
import org.jgrapht.io.ImportException;
|
import org.jgrapht.io.ImportException;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
public class ModularXmppClientToServerConnectionStateGraphTest {
|
public class ModularXmppClientToServerConnectionStateGraphTest {
|
||||||
|
|
||||||
|
@ -80,4 +84,24 @@ public class ModularXmppClientToServerConnectionStateGraphTest {
|
||||||
assertEquals(expectedStateGraph, currentStateGraph);
|
assertEquals(expectedStateGraph, currentStateGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoUnknownStates() throws XmppStringprepException {
|
||||||
|
ModularXmppClientToServerConnectionConfiguration.builder()
|
||||||
|
.setUsernameAndPassword("user", "password")
|
||||||
|
.setXmppDomain("example.org")
|
||||||
|
.failOnUnknownStates() // This is the actual option that enableds this test.
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void throwsOnUnknownStates() throws XmppStringprepException {
|
||||||
|
assertThrows(IllegalStateException.class, () ->
|
||||||
|
ModularXmppClientToServerConnectionConfiguration.builder()
|
||||||
|
.setUsernameAndPassword("user", "password")
|
||||||
|
.setXmppDomain("example.org")
|
||||||
|
.removeModule(CompressionModuleDescriptor.class)
|
||||||
|
.failOnUnknownStates() // This is the actual option that enableds this test.
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,7 +340,6 @@ public class RTPBridge extends IQ {
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
|
|
||||||
XmlPullParser.Event eventType;
|
XmlPullParser.Event eventType;
|
||||||
String elementName;
|
|
||||||
|
|
||||||
if (!parser.getNamespace().equals(RTPBridge.NAMESPACE))
|
if (!parser.getNamespace().equals(RTPBridge.NAMESPACE))
|
||||||
// TODO: Should be SmackParseException.
|
// TODO: Should be SmackParseException.
|
||||||
|
@ -356,9 +355,9 @@ public class RTPBridge extends IQ {
|
||||||
// Start processing sub-elements
|
// Start processing sub-elements
|
||||||
while (!done) {
|
while (!done) {
|
||||||
eventType = parser.next();
|
eventType = parser.next();
|
||||||
elementName = parser.getName();
|
|
||||||
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String elementName = parser.getName();
|
||||||
if (elementName.equals("candidate")) {
|
if (elementName.equals("candidate")) {
|
||||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||||
if (parser.getAttributeName(i).equals("ip"))
|
if (parser.getAttributeName(i).equals("ip"))
|
||||||
|
|
|
@ -66,14 +66,13 @@ public abstract class JingleContentDescriptionProvider extends ExtensionElementP
|
||||||
public JingleContentDescription parse(XmlPullParser parser,
|
public JingleContentDescription parse(XmlPullParser parser,
|
||||||
int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException,
|
int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException,
|
||||||
IOException {
|
IOException {
|
||||||
boolean done = false;
|
|
||||||
JingleContentDescription desc = getInstance();
|
JingleContentDescription desc = getInstance();
|
||||||
|
|
||||||
while (!done) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event eventType = parser.next();
|
XmlPullParser.Event eventType = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String name = parser.getName();
|
||||||
if (name.equals(JingleContentDescription.JinglePayloadType.NODENAME)) {
|
if (name.equals(JingleContentDescription.JinglePayloadType.NODENAME)) {
|
||||||
desc.addJinglePayloadType(parsePayload(parser));
|
desc.addJinglePayloadType(parsePayload(parser));
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,8 +80,8 @@ public abstract class JingleContentDescriptionProvider extends ExtensionElementP
|
||||||
throw new IOException("Unknow element \"" + name + "\" in content.");
|
throw new IOException("Unknow element \"" + name + "\" in content.");
|
||||||
}
|
}
|
||||||
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
||||||
if (name.equals(JingleContentDescription.NODENAME)) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
done = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,14 +65,12 @@ public abstract class JingleDescriptionProvider extends ExtensionElementProvider
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public JingleDescription parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
public JingleDescription parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
||||||
boolean done = false;
|
|
||||||
JingleDescription desc = getInstance();
|
JingleDescription desc = getInstance();
|
||||||
|
|
||||||
while (!done) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event eventType = parser.next();
|
XmlPullParser.Event eventType = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String name = parser.getName();
|
||||||
if (name.equals(PayloadType.NODENAME)) {
|
if (name.equals(PayloadType.NODENAME)) {
|
||||||
desc.addPayloadType(parsePayload(parser));
|
desc.addPayloadType(parsePayload(parser));
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,8 +78,8 @@ public abstract class JingleDescriptionProvider extends ExtensionElementProvider
|
||||||
throw new IOException("Unknow element \"" + name + "\" in content.");
|
throw new IOException("Unknow element \"" + name + "\" in content.");
|
||||||
}
|
}
|
||||||
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
||||||
if (name.equals(JingleDescription.NODENAME)) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
done = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,10 +84,10 @@ public class JingleProvider extends IQProvider<Jingle> {
|
||||||
// Start processing sub-elements
|
// Start processing sub-elements
|
||||||
while (!done) {
|
while (!done) {
|
||||||
eventType = parser.next();
|
eventType = parser.next();
|
||||||
elementName = parser.getName();
|
|
||||||
namespace = parser.getNamespace();
|
|
||||||
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
elementName = parser.getName();
|
||||||
|
namespace = parser.getNamespace();
|
||||||
|
|
||||||
// Parse some well know subelements, depending on the namespaces
|
// Parse some well know subelements, depending on the namespaces
|
||||||
// and element names...
|
// and element names...
|
||||||
|
|
|
@ -54,14 +54,13 @@ public abstract class JingleTransportProvider extends ExtensionElementProvider<J
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public JingleTransport parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
public JingleTransport parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
||||||
boolean done = false;
|
|
||||||
JingleTransport trans = getInstance();
|
JingleTransport trans = getInstance();
|
||||||
|
|
||||||
while (!done) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event eventType = parser.next();
|
XmlPullParser.Event eventType = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
|
|
||||||
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String name = parser.getName();
|
||||||
if (name.equals(JingleTransportCandidate.NODENAME)) {
|
if (name.equals(JingleTransportCandidate.NODENAME)) {
|
||||||
JingleTransportCandidate jtc = parseCandidate(parser);
|
JingleTransportCandidate jtc = parseCandidate(parser);
|
||||||
if (jtc != null) trans.addCandidate(jtc);
|
if (jtc != null) trans.addCandidate(jtc);
|
||||||
|
@ -72,8 +71,8 @@ public abstract class JingleTransportProvider extends ExtensionElementProvider<J
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
||||||
if (name.equals(JingleTransport.NODENAME)) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
done = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,14 +71,13 @@ public class OfferConfirmation extends SimpleIQ {
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
parser.next();
|
parser.next();
|
||||||
String elementName = parser.getName();
|
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "user-jid".equals(parser.getName())) {
|
||||||
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "user-jid".equals(elementName)) {
|
|
||||||
confirmation.setUserJID(parser.nextText());
|
confirmation.setUserJID(parser.nextText());
|
||||||
}
|
}
|
||||||
else if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "session-id".equals(elementName)) {
|
else if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "session-id".equals(parser.getName())) {
|
||||||
confirmation.setSessionID(Long.valueOf(parser.nextText()));
|
confirmation.setSessionID(Long.valueOf(parser.nextText()));
|
||||||
}
|
}
|
||||||
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && "offer-confirmation".equals(elementName)) {
|
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && "offer-confirmation".equals(parser.getName())) {
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,14 +108,13 @@ public class QueueUpdate implements ExtensionElement {
|
||||||
int timeRemaining = -1;
|
int timeRemaining = -1;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
parser.next();
|
parser.next();
|
||||||
String elementName = parser.getName();
|
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "position".equals(parser.getName())) {
|
||||||
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "position".equals(elementName)) {
|
|
||||||
position = Integer.parseInt(parser.nextText());
|
position = Integer.parseInt(parser.nextText());
|
||||||
}
|
}
|
||||||
else if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "time".equals(elementName)) {
|
else if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT && "time".equals(parser.getName())) {
|
||||||
timeRemaining = Integer.parseInt(parser.nextText());
|
timeRemaining = Integer.parseInt(parser.nextText());
|
||||||
}
|
}
|
||||||
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && "queue-status".equals(elementName)) {
|
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && "queue-status".equals(parser.getName())) {
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,11 +179,10 @@ public class RoomInvitation implements ExtensionElement {
|
||||||
final RoomInvitation invitation = new RoomInvitation();
|
final RoomInvitation invitation = new RoomInvitation();
|
||||||
invitation.type = Type.valueOf(parser.getAttributeValue("", "type"));
|
invitation.type = Type.valueOf(parser.getAttributeValue("", "type"));
|
||||||
|
|
||||||
boolean done = false;
|
outerloop: while (true) {
|
||||||
while (!done) {
|
|
||||||
parser.next();
|
parser.next();
|
||||||
String elementName = parser.getName();
|
|
||||||
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT) {
|
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String elementName = parser.getName();
|
||||||
if ("session".equals(elementName)) {
|
if ("session".equals(elementName)) {
|
||||||
invitation.sessionID = parser.getAttributeValue("", "id");
|
invitation.sessionID = parser.getAttributeValue("", "id");
|
||||||
}
|
}
|
||||||
|
@ -203,8 +202,8 @@ public class RoomInvitation implements ExtensionElement {
|
||||||
invitation.room = JidCreate.entityBareFrom(roomString);
|
invitation.room = JidCreate.entityBareFrom(roomString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && ELEMENT_NAME.equals(elementName)) {
|
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == initialDepth) {
|
||||||
done = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return invitation;
|
return invitation;
|
||||||
|
|
|
@ -174,11 +174,10 @@ public class RoomTransfer implements ExtensionElement {
|
||||||
final RoomTransfer invitation = new RoomTransfer();
|
final RoomTransfer invitation = new RoomTransfer();
|
||||||
invitation.type = RoomTransfer.Type.valueOf(parser.getAttributeValue("", "type"));
|
invitation.type = RoomTransfer.Type.valueOf(parser.getAttributeValue("", "type"));
|
||||||
|
|
||||||
boolean done = false;
|
outerloop: while (true) {
|
||||||
while (!done) {
|
|
||||||
parser.next();
|
parser.next();
|
||||||
String elementName = parser.getName();
|
|
||||||
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT) {
|
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String elementName = parser.getName();
|
||||||
if ("session".equals(elementName)) {
|
if ("session".equals(elementName)) {
|
||||||
invitation.sessionID = parser.getAttributeValue("", "id");
|
invitation.sessionID = parser.getAttributeValue("", "id");
|
||||||
}
|
}
|
||||||
|
@ -195,8 +194,8 @@ public class RoomTransfer implements ExtensionElement {
|
||||||
invitation.room = parser.nextText();
|
invitation.room = parser.nextText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && ELEMENT_NAME.equals(elementName)) {
|
else if (parser.getEventType() == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == initialDepth) {
|
||||||
done = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return invitation;
|
return invitation;
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.util.logging.Logger;
|
||||||
import org.jivesoftware.smack.ConnectionListener;
|
import org.jivesoftware.smack.ConnectionListener;
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
@ -73,6 +74,7 @@ import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||||
import org.jivesoftware.smackx.pep.PepEventListener;
|
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||||
import org.jivesoftware.smackx.pep.PepManager;
|
import org.jivesoftware.smackx.pep.PepManager;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
|
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
@ -727,6 +729,50 @@ public final class OmemoManager extends Manager {
|
||||||
getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this));
|
getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Exception> purgeEverything() throws NotConnectedException, InterruptedException, IOException {
|
||||||
|
List<Exception> exceptions = new ArrayList<>(5);
|
||||||
|
PubSubManager pm = PubSubManager.getInstanceFor(getConnection(), getOwnJid());
|
||||||
|
try {
|
||||||
|
requestDeviceListUpdateFor(getOwnJid());
|
||||||
|
} catch (SmackException.NoResponseException | PubSubException.NotALeafNodeException
|
||||||
|
| XMPPException.XMPPErrorException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
OmemoCachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
|
||||||
|
.loadCachedDeviceList(getOwnDevice(), getOwnJid());
|
||||||
|
|
||||||
|
for (int id : deviceList.getAllDevices()) {
|
||||||
|
try {
|
||||||
|
pm.getLeafNode(OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(id)).deleteAllItems();
|
||||||
|
} catch (SmackException.NoResponseException | PubSubException.NotALeafNodeException
|
||||||
|
| XMPPException.XMPPErrorException | PubSubException.NotAPubSubNodeException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pm.deleteNode(OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(id));
|
||||||
|
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pm.getLeafNode(OmemoConstants.PEP_NODE_DEVICE_LIST).deleteAllItems();
|
||||||
|
} catch (SmackException.NoResponseException | PubSubException.NotALeafNodeException
|
||||||
|
| XMPPException.XMPPErrorException | PubSubException.NotAPubSubNodeException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pm.deleteNode(OmemoConstants.PEP_NODE_DEVICE_LIST);
|
||||||
|
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate the signedPreKey published in our OmemoBundle and republish it. This should be done every now and
|
* Rotate the signedPreKey published in our OmemoBundle and republish it. This should be done every now and
|
||||||
* then (7-14 days). The old signedPreKey should be kept for some more time (a month or so) to enable decryption
|
* then (7-14 days). The old signedPreKey should be kept for some more time (a month or so) to enable decryption
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.provider;
|
package org.jivesoftware.smackx.omemo.provider;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.BUNDLE;
|
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.IDENTITY_KEY;
|
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.IDENTITY_KEY;
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEYS;
|
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEYS;
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEY_ID;
|
import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEY_ID;
|
||||||
|
@ -43,7 +42,6 @@ import org.jivesoftware.smackx.omemo.element.OmemoBundleElement_VAxolotl;
|
||||||
public class OmemoBundleVAxolotlProvider extends ExtensionElementProvider<OmemoBundleElement_VAxolotl> {
|
public class OmemoBundleVAxolotlProvider extends ExtensionElementProvider<OmemoBundleElement_VAxolotl> {
|
||||||
@Override
|
@Override
|
||||||
public OmemoBundleElement_VAxolotl parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
public OmemoBundleElement_VAxolotl parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
||||||
boolean stop = false;
|
|
||||||
boolean inPreKeys = false;
|
boolean inPreKeys = false;
|
||||||
|
|
||||||
int signedPreKeyId = -1;
|
int signedPreKeyId = -1;
|
||||||
|
@ -52,11 +50,11 @@ public class OmemoBundleVAxolotlProvider extends ExtensionElementProvider<OmemoB
|
||||||
String identityKey = null;
|
String identityKey = null;
|
||||||
HashMap<Integer, String> preKeys = new HashMap<>();
|
HashMap<Integer, String> preKeys = new HashMap<>();
|
||||||
|
|
||||||
while (!stop) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.Event tag = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
String name = parser.getName();
|
||||||
final int attributeCount = parser.getAttributeCount();
|
final int attributeCount = parser.getAttributeCount();
|
||||||
// <signedPreKeyPublic>
|
// <signedPreKeyPublic>
|
||||||
if (name.equals(SIGNED_PRE_KEY_PUB)) {
|
if (name.equals(SIGNED_PRE_KEY_PUB)) {
|
||||||
|
@ -91,8 +89,8 @@ public class OmemoBundleVAxolotlProvider extends ExtensionElementProvider<OmemoB
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case END_ELEMENT:
|
case END_ELEMENT:
|
||||||
if (name.equals(BUNDLE)) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
stop = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Paul Schaub
|
* Copyright 2017 Paul Schaub, 2021 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,7 +18,6 @@ package org.jivesoftware.smackx.omemo.provider;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.DEVICE;
|
import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.DEVICE;
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.ID;
|
import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.ID;
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.LIST;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -41,12 +40,11 @@ public class OmemoDeviceListVAxolotlProvider extends ExtensionElementProvider<Om
|
||||||
@Override
|
@Override
|
||||||
public OmemoDeviceListElement_VAxolotl parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
public OmemoDeviceListElement_VAxolotl parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
||||||
Set<Integer> deviceListIds = new HashSet<>();
|
Set<Integer> deviceListIds = new HashSet<>();
|
||||||
boolean stop = false;
|
outerloop: while (true) {
|
||||||
while (!stop) {
|
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.Event tag = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
String name = parser.getName();
|
||||||
if (name.equals(DEVICE)) {
|
if (name.equals(DEVICE)) {
|
||||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||||
if (parser.getAttributeName(i).equals(ID)) {
|
if (parser.getAttributeName(i).equals(ID)) {
|
||||||
|
@ -57,8 +55,8 @@ public class OmemoDeviceListVAxolotlProvider extends ExtensionElementProvider<Om
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case END_ELEMENT:
|
case END_ELEMENT:
|
||||||
if (name.equals(LIST)) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
stop = true;
|
break outerloop;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.jivesoftware.smackx.omemo.provider;
|
package org.jivesoftware.smackx.omemo.provider;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoElement.ATTR_PAYLOAD;
|
import static org.jivesoftware.smackx.omemo.element.OmemoElement.ATTR_PAYLOAD;
|
||||||
import static org.jivesoftware.smackx.omemo.element.OmemoElement.NAME_ENCRYPTED;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -42,17 +41,16 @@ public class OmemoVAxolotlProvider extends ExtensionElementProvider<OmemoElement
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OmemoElement_VAxolotl parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
public OmemoElement_VAxolotl parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
||||||
boolean inEncrypted = true;
|
|
||||||
int sid = -1;
|
int sid = -1;
|
||||||
ArrayList<OmemoKeyElement> keys = new ArrayList<>();
|
ArrayList<OmemoKeyElement> keys = new ArrayList<>();
|
||||||
byte[] iv = null;
|
byte[] iv = null;
|
||||||
byte[] payload = null;
|
byte[] payload = null;
|
||||||
|
|
||||||
while (inEncrypted) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.Event tag = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case START_ELEMENT:
|
case START_ELEMENT:
|
||||||
|
String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case OmemoHeaderElement.ELEMENT:
|
case OmemoHeaderElement.ELEMENT:
|
||||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||||
|
@ -82,8 +80,8 @@ public class OmemoVAxolotlProvider extends ExtensionElementProvider<OmemoElement
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case END_ELEMENT:
|
case END_ELEMENT:
|
||||||
if (name.equals(NAME_ENCRYPTED)) {
|
if (parser.getDepth() == initialDepth) {
|
||||||
inEncrypted = false;
|
break outerloop;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -42,8 +42,8 @@ public class PubkeyElementProvider extends ExtensionElementProvider<PubkeyElemen
|
||||||
Date date = ParserUtils.getDateFromOptionalXep82String(dateString);
|
Date date = ParserUtils.getDateFromOptionalXep82String(dateString);
|
||||||
while (true) {
|
while (true) {
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.Event tag = parser.next();
|
||||||
String name = parser.getName();
|
|
||||||
if (tag == XmlPullParser.Event.START_ELEMENT) {
|
if (tag == XmlPullParser.Event.START_ELEMENT) {
|
||||||
|
String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case PubkeyElement.PubkeyDataElement.ELEMENT:
|
case PubkeyElement.PubkeyDataElement.ELEMENT:
|
||||||
String base64EncodedOpenPgpPubKey = parser.nextText();
|
String base64EncodedOpenPgpPubKey = parser.nextText();
|
||||||
|
|
|
@ -50,6 +50,7 @@ import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
||||||
import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore;
|
import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -156,9 +157,13 @@ public class OXInstantMessagingManagerTest extends SmackTestSuite {
|
||||||
// Check, if one of Bobs keys was used for decryption
|
// Check, if one of Bobs keys was used for decryption
|
||||||
assertNotNull(bobSelf.getSigningKeyRing().getPublicKey(metadata.getDecryptionFingerprint().getKeyId()));
|
assertNotNull(bobSelf.getSigningKeyRing().getPublicKey(metadata.getDecryptionFingerprint().getKeyId()));
|
||||||
|
|
||||||
|
// TODO: I observed this assertTrue() to fail sporadically. As a first attempt to diagnose this, a message was
|
||||||
|
// added to the assertion. However since most (all?) objects used in the message do not implement a proper
|
||||||
|
// toString() this is probably not really helpful as it is.
|
||||||
|
PGPPublicKeyRingCollection pubKeys = aliceForBob.getTrustedAnnouncedKeys();
|
||||||
// Check if one of Alice' keys was used for signing
|
// Check if one of Alice' keys was used for signing
|
||||||
assertTrue(metadata.containsVerifiedSignatureFrom(
|
assertTrue(metadata.containsVerifiedSignatureFrom(
|
||||||
aliceForBob.getTrustedAnnouncedKeys().iterator().next()));
|
pubKeys.iterator().next()), metadata + " did not contain one of alice' keys " + pubKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -9,7 +9,7 @@ apply plugin: 'scala'
|
||||||
apply plugin: 'com.github.alisiikh.scalastyle_2.12'
|
apply plugin: 'com.github.alisiikh.scalastyle_2.12'
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
scalaVersion = '2.12.1'
|
scalaVersion = '2.13.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -20,7 +20,7 @@ dependencies {
|
||||||
api project(':smack-omemo-signal')
|
api project(':smack-omemo-signal')
|
||||||
|
|
||||||
implementation "org.scala-lang:scala-library:$scalaVersion"
|
implementation "org.scala-lang:scala-library:$scalaVersion"
|
||||||
implementation "com.lihaoyi:ammonite_$scalaVersion:1.3.2"
|
implementation "com.lihaoyi:ammonite_$scalaVersion:2.4.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
scalaStyle {
|
scalaStyle {
|
||||||
|
|
Loading…
Reference in a new issue