1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-22 20:12:07 +01:00

Compare commits

...

78 commits

Author SHA1 Message Date
Florian Schmaus
c6c904cc3e Add support for XEP-0350: Data Forms Geolocation Element
Fixes SMACK-871.
2019-06-12 17:13:05 +02:00
Florian Schmaus
fa0c16d75c Introduce EqualsUtil and HashCode.(Builder|Cache) 2019-06-12 14:51:17 +02:00
Florian Schmaus
92b02afbff Improve exceptions of Socks5Proxy 2019-06-11 12:47:40 +02:00
Florian Schmaus
818ee8a727 Make Objects.requireNonNull() throw IllegalArgumentException
and not NullPointerException. Altough this differs from
java.util.Objects behavior, throwing an IllegalArgumentException
appears more sensible and makes it easier to catch it in Smack's
parsing function.
2019-06-11 12:47:40 +02:00
Florian Schmaus
9bb36fc63c Make Socks5TestProxy a subclass of Socks5Proxy
to reduce the duplicated code. This also means we are now testing the
real implementation.
2019-06-11 12:47:39 +02:00
Florian Schmaus
6e1193edaf Use QName instead of XmppStringUtils.generateKey() 2019-06-11 12:47:39 +02:00
Florian Schmaus
7d59df9eed Change type of presence priority to 'Byte' 2019-06-11 12:47:39 +02:00
Florian Schmaus
e911874e72 Make MultiMap use generics where sensible
I wonder why I orginally did not do it that way…
2019-06-11 12:47:39 +02:00
Florian Schmaus
ce70308099 Introduce UInt(16|32) datatypes 2019-06-11 12:47:39 +02:00
Florian Schmaus
c0183775fe Add support for XEP-0221: Data Forms Media Element
Fixes SMACK-824.
2019-06-11 12:47:39 +02:00
Florian Schmaus
832b20a897 Add XmlPullParser.getAttributeValue(String) 2019-06-11 12:47:39 +02:00
Florian Schmaus
b834df65e9 Add NumberUtil.requireUShort16(int) 2019-06-11 12:47:39 +02:00
Florian Schmaus
b3b242f397 Add TODO comment to XmlStringBuilder 2019-06-11 12:47:39 +02:00
Florian Schmaus
1f8b7273a8 Improve junit test in Socks5ByteStreamManager
It could be that the test is flapping. This helps to see the origin of
the unexpected exception.
2019-06-11 12:47:39 +02:00
Florian Schmaus
4d36e3b521 Introduce FormFieldChildElement and make FormField immutable 2019-06-11 12:47:39 +02:00
Florian Schmaus
1a99801501 Fix typo in FormField's javadoc: s/fold/field/ 2019-06-05 10:54:17 +02:00
Florian Schmaus
3c306eaff9 Improve comment in SmackReactor 2019-06-05 10:54:07 +02:00
Florian Schmaus
9a2cca2bd3 Merge branch 'master' of github.com:Flowdalic/Smack 2019-06-03 17:41:49 +02:00
Florian Schmaus
b288768f77 Introduce util.InternetAddress
and use it where sensible. Also fixes a few unit tests along the way.
2019-06-03 17:41:10 +02:00
Florian Schmaus
4334ca33ff Add missing selector.wakeup() in SmackRactor.schedule()
Without this, newly scheduled runnables would potentially not be
scheduled in case the reactor thread was blocking in select(0)
indefinetly.

Thanks to Eng ChongMeng for reporting this.
2019-06-03 09:28:52 +02:00
Florian Schmaus
027cae3bd0 Remove unnecessary synchronization in SmackReater.schedule()
The DelayQueue 'scheduledActions' is already thread-safe.
2019-06-03 09:27:28 +02:00
Florian Schmaus
619b8e6f4a Add secure(OnlineAttackSafe|Unique|OfflineAttackSafe)RandomString()
and replace usages of java.util.UUID in Smack with
secureUniqueRandomString() because it uses a thread-local secure random
number generator.
2019-06-02 20:08:03 +02:00
Florian Schmaus
58fc39714f Use assertThrows() in Socks5ByteStreamManagerTest 2019-06-02 11:00:37 +02:00
Florian Schmaus
839e347676 Rename NumberUtil.checkIfInUInt32Range() to requireUInt32 2019-06-02 10:46:53 +02:00
Florian Schmaus
726a2de273 Deprecate ParserUtils.getQName() 2019-06-02 10:44:50 +02:00
Florian Schmaus
af6ee76f4c Remove empty lines in Socks5BytestreamManager 2019-06-02 10:41:39 +02:00
Florian Schmaus
49aa7ce21b Do not swallow exception in Socks5BytestreamManager 2019-06-02 10:41:27 +02:00
Florian Schmaus
6619372bb8 Enable parallel unit test execution 2019-06-02 10:40:44 +02:00
Florian Schmaus
9352225f44 Rework SOCKS5 unit tests so that they can be run in parallel
As result it is now also possible to start multiple local SOCKS5
proxies with different port, which is usually not necessary in real
life but useful for unit tests.
2019-06-02 10:38:19 +02:00
Florian Schmaus
d337474a86 Add DiscoverItems.Item.toString() 2019-05-23 23:06:39 +02:00
Florian Schmaus
02f7cfcf27 Improve session sweeping logic in AdHocCommandManager
Fixes SMACK-624.
2019-05-20 10:41:01 +02:00
Florian Schmaus
e8d9aed4be
Merge pull request #303 from adiaholic/SMACK-839
Generic Exception replaced with Type Specific Exceptions.
2019-05-20 09:44:04 +02:00
adiaholic
f91044657f Generic Exception replaced with Type Specific Exceptions.
'parseAndProcessStanza()' throws generic Exceptions.
Since there are plenty of exceptions that should not be
catched by smack, it's better to throw Type Specific Exceptions.
This Commit is was in response to SMACK-839.
2019-05-19 17:09:01 +05:30
Florian Schmaus
04238bd36a Add CloseableUtil.maybeClose(Closeable) 2019-05-18 13:25:31 +02:00
Florian Schmaus
02e2eba556 Socks5Proxy: Remove empty line 2019-05-18 13:24:48 +02:00
Florian Schmaus
48010f3b82 Socks5Proxy: Log if server thread termination was interrupted 2019-05-18 13:24:30 +02:00
Florian Schmaus
11ae6d6960 Socks5Proxy: Use CloseableUtil to close server socket 2019-05-18 13:24:05 +02:00
Florian Schmaus
ab99f629a3 Socks5Proxy: Include server socket in thread name 2019-05-18 13:23:24 +02:00
Florian Schmaus
f7762c5db7 Add "whitespace after comma" checkstyle rule 2019-05-17 21:56:46 +02:00
Florian Schmaus
db5f6f648c Bump AnimalSniffer to 1.5.0 2019-05-17 18:04:27 +02:00
Florian Schmaus
7e25c3ada5 Merge branch '4.3' 2019-05-16 20:52:27 +02:00
Florian Schmaus
2ceadc0de1 Introduce verificationSuccessful boolean 2019-05-16 16:51:31 +02:00
Florian Schmaus
f60e4055ec Use ACE representation of XMPP domain on certificate verification
This is based on the patch provided by Georg Lukas, modified to take
XMPP domain names that can not be represented as DNS names into
account. See c42d2eea24 (r269250137)

Fixes SMACK-870.
2019-05-16 14:14:10 +02:00
Florian Schmaus
381190a45c Add ConnectionConfiguration.getXmppServiceDomainAsDnsNameIfPossible()
in preperation of SMACK-870, so that the DNS name can be used for the
certificate verification.
2019-05-16 14:02:28 +02:00
Florian Schmaus
04f1d79d72 Try GC in MemoryLeakTestUtil.assertReferencesQueueSize()
It appears that we observe a partion GC run on some systems,
especially ones with few resources. Hopefully this increases the
chances to observe the expected GC affects so that the unit test
passes also on those systems.
2019-05-09 12:33:43 +02:00
Florian Schmaus
2dedd75cd7 Add comment about Thread.yield() in MemoryLeakTestUtil 2019-05-09 12:06:01 +02:00
Florian Schmaus
edcde28ecd Set UncaughtExceptionHandler for CACHED_EXECUTOR_SERVICE
In order to avoid uncaught exceptions from terminating the
program (SMACK-896).
2019-05-09 11:04:26 +02:00
Florian Schmaus
49f4de0cdb Replace 'key' with QName 2019-05-08 21:10:39 +02:00
Florian Schmaus
7c6d1f4340 Remove no-op configure block in build.gradle
Become a no-op configure block with
4108b9a83e.
2019-05-08 20:43:45 +02:00
Florian Schmaus
f6be434f66 Remove deprecated methods in StanzaCollector 2019-05-08 20:42:08 +02:00
Florian Schmaus
68b7eb26f3 Fix PacketWriterTest: Expect also InterruptedException
which is thrown in case the queue was shut down.
2019-05-08 15:19:48 +02:00
Florian Schmaus
32429bcb9c Add message to InterruptedException if blocking queue was shut down 2019-05-08 15:19:10 +02:00
Greg Thomas
07c069e1a1 Use the default integration test packages if the supplied list is empty 2019-05-08 15:10:03 +02:00
Florian Schmaus
5e1c3c7aa5 Use FileTagCharacter checkstyle module to check for tabs in source code
instead of RegexpSingleline.
2019-05-08 15:10:03 +02:00
Florian Schmaus
60db42e2f4
Merge pull request #299 from magnetsystems/autojoin-callback-on-reconnect
Autojoin callback on reconnect
2019-05-08 13:43:46 +02:00
Florian Schmaus
d20a2675a8 Make StanzaCollector final 2019-05-08 12:44:48 +02:00
Florian Schmaus
f4ee7dd541 Update PowerMock to 2.0.2
and set Mockito 2's mock-maker-inline as MockMaker to allow mocking of
final classes.
2019-05-08 12:44:48 +02:00
Florian Schmaus
f8de22478b Double the timeout to 120s in MemoryLeakTestUtil
Because an failing test on Travis was observered using a timeout of
60s:

org.jivesoftware.smackx.muc.MucMemoryLeakTest > mucMemoryLeakTest FAILED
    java.lang.AssertionError: No reference found after 60000ms
        at org.junit.Assert.fail(Assert.java:88)
        at org.junit.Assert.assertTrue(Assert.java:41)
        at org.junit.Assert.assertNotNull(Assert.java:712)
        at org.jivesoftware.smack.util.MemoryLeakTestUtil.assertReferencesQueueSize(MemoryLeakTestUtil.java:110)
        at org.jivesoftware.smack.util.MemoryLeakTestUtil.noResourceLeakTest(MemoryLeakTestUtil.java:103)
        at org.jivesoftware.smackx.muc.MucMemoryLeakTest.mucMemoryLeakTest(MucMemoryLeakTest.java:29)
2019-05-08 12:44:48 +02:00
Florian Schmaus
d2f5efcb20 Use StandardCharsets.(UTF_8|US_ASCII)
This also gets rid of a ton of UnsupportedEncodingException s.
2019-05-08 12:44:48 +02:00
Florian Schmaus
2a4d110b22 Add Socks5Exception and improve SOCKS5 bytestream exception messages
The exception message now also contains the stream hosts and their
exception.
2019-05-08 11:07:18 +02:00
Florian Schmaus
fd89a5e5a5 Add StreamHost.toString() 2019-05-08 09:24:23 +02:00
Florian Schmaus
7f0dc72dab Update errorprone(-plugin) and make Unused(Variable|Method) an error 2019-05-07 23:09:00 +02:00
Florian Schmaus
68d7d738b6 Remove all tabs and add checkstyle rule that enforces no-tabs
Fixes SMACK-866.
2019-05-07 21:24:00 +02:00
Florian Schmaus
575364cc1f Print exception's stacktrace in PacketWriterTest 2019-05-07 21:02:07 +02:00
Florian Schmaus
e1ed035beb Remove xmlunit-lagacy and add xmlunit-assertj 2019-05-07 19:06:16 +02:00
Florian Schmaus
95dbf5bb36 Change JUnit API from 4 to 5 in smack-experimental 2019-05-07 18:51:22 +02:00
Florian Schmaus
505493d889 Add XmlStringBuilder.optAttribute(String, Number) 2019-05-07 10:18:55 +02:00
Florian Schmaus
0e52560358 Add test fo smack-xmlparser
mostly to make jacoco happy, but it can not hurt anyway :).
2019-05-07 10:12:43 +02:00
Florian Schmaus
4a3dda93af
Merge pull request #315 from adiaholic/doc-fix-in-index
Minor changes in documentation.
2019-05-06 22:57:59 +02:00
Florian Schmaus
7980e2cedb
Merge pull request #318 from vanitasvitae/xhtml_fix
SMACK-868: Fix XHTMLText producing invalid XML
2019-05-06 22:56:01 +02:00
Florian Schmaus
4133eb175c Replace XPP3 by XmlPullParser interface wrapping StAX and XPP3
Introducing Smack's own XmlPullParser interface which tries to stay as
compatible as possible to XPP3. The interface is used to either wrap
StAX's XMLStreamReader if Smack is used on Java SE, and XPP3's
XmlPullParser if Smack is used on on Android.

Fixes SMACK-591.

Also introduce JUnit 5 and non-strict javadoc projects.
2019-05-06 22:10:50 +02:00
Eng ChongMeng
dd903bec95
SMACK-868: Fix XHTMLText producing invalid XML 2019-05-06 00:39:41 +02:00
adiaholic
1554a33518 Changed XEP-031 to XEP-0131 2019-05-04 14:18:46 +05:30
Florian Schmaus
b3646abecd XMPPTCPConnection: Move openStream() call from writer into reader thread
and also call notifyConnectionError() on exception thrown by
openStream().

In hindsight I wonder why openStream() was ever called in the writer
thread, as it only caused unnecessary synchronization overhead, as can
be seen by the initialOpenStreamSend synchronization point.
2019-04-30 13:55:55 +02:00
Florian Schmaus
ae2c57f56b Fix XML in MamResultProviderTest
Add missing whitespace between XML attributes.
2019-04-29 13:56:16 +02:00
adiaholic
3b657d36c4 Changed XEP-0116 to XEP-0166
Rearranged as per the expected XEP sequence
2019-04-29 13:32:26 +05:30
Florian Schmaus
4e7cd83220 Fix (Privacy|Time)ProviderTest
XML attributes must be separated by a whitespace.
2019-04-29 08:52:36 +02:00
V Lau
f1fb03d08c Add a success callback for auto-join on reconnect.
When auto-join on reconnection is success, invoke a callback.

Conform to the proper copyright format.
2019-04-09 14:21:11 -07:00
588 changed files with 8799 additions and 5100 deletions

View file

@ -1,7 +1,7 @@
language: android language: android
android: android:
components: components:
- android-16 - android-19
jdk: jdk:
- oraclejdk8 - oraclejdk8
- openjdk8 - openjdk8
@ -11,7 +11,7 @@ cache:
- $HOME/.m2 - $HOME/.m2
before_install: before_install:
- export GRADLE_VERSION=5.1.1 - export GRADLE_VERSION=5.2.1
- wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-all.zip - wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-all.zip
- unzip -q gradle-${GRADLE_VERSION}-all.zip - unzip -q gradle-${GRADLE_VERSION}-all.zip
- export PATH="$(pwd)/gradle-${GRADLE_VERSION}/bin:$PATH" - export PATH="$(pwd)/gradle-${GRADLE_VERSION}/bin:$PATH"

View file

@ -14,8 +14,8 @@ buildscript {
} }
plugins { plugins {
id 'ru.vyarus.animalsniffer' version '1.4.6' id 'ru.vyarus.animalsniffer' version '1.5.0'
id 'net.ltgt.errorprone' version '0.6' id 'net.ltgt.errorprone' version '0.8'
} }
apply plugin: 'org.kordamp.gradle.markdown' apply plugin: 'org.kordamp.gradle.markdown'
@ -24,6 +24,7 @@ apply from: 'version.gradle'
allprojects { allprojects {
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'eclipse' apply plugin: 'eclipse'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'jacoco' apply plugin: 'jacoco'
@ -80,6 +81,8 @@ allprojects {
':smack-omemo', ':smack-omemo',
':smack-omemo-signal', ':smack-omemo-signal',
':smack-openpgp', ':smack-openpgp',
':smack-xmlparser',
':smack-xmlparser-xpp3',
].collect{ project(it) } ].collect{ project(it) }
androidBootClasspathProjects = [ androidBootClasspathProjects = [
':smack-android', ':smack-android',
@ -97,13 +100,37 @@ allprojects {
':smack-omemo-signal', ':smack-omemo-signal',
':smack-omemo-signal-integration-test', ':smack-omemo-signal-integration-test',
].collect{ project(it) } ].collect{ project(it) }
// When this list is empty, then move the according javadoc
// tool Werror option into the global configure section.
nonStrictJavadocProjects = [
':smack-bosh',
':smack-core',
':smack-experimental',
':smack-extensions',
':smack-im',
':smack-integration-test',
':smack-jingle-old',
':smack-legacy',
':smack-omemo',
':smack-tcp',
].collect{ project(it) }
// Lazily evaluate the Android bootClasspath and offline // Lazily evaluate the Android bootClasspath and offline
// Javadoc using a closure, so that targets which do not // Javadoc using a closure, so that targets which do not
// require it are still able to succeed without an Android // require it are still able to succeed without an Android
// SDK. // SDK.
androidBootClasspath = { getAndroidRuntimeJar() } androidBootClasspath = { getAndroidRuntimeJar() }
androidJavadocOffline = { getAndroidJavadocOffline() } androidJavadocOffline = { getAndroidJavadocOffline() }
junitVersion = '5.2.0' junit4Projects = [
':smack-core',
':smack-extensions',
':smack-im',
':smack-integration-test',
':smack-omemo',
':smack-omemo-signal',
':smack-openpgp',
].collect { project(it) }
junitVersion = '5.4.2'
powerMockVersion = '2.0.2'
} }
group = 'org.igniterealtime.smack' group = 'org.igniterealtime.smack'
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
@ -114,6 +141,10 @@ allprojects {
} }
test { test {
useJUnitPlatform()
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
// Enable full stacktraces of failed tests. Especially handy // Enable full stacktraces of failed tests. Especially handy
// for environments like Travis. // for environments like Travis.
testLogging { testLogging {
@ -170,21 +201,25 @@ allprojects {
'-Xlint:-options', '-Xlint:-options',
'-Werror', '-Werror',
] ]
options.errorprone.errorproneArgs = [ options.errorprone {
// Disable errorprone checks error("UnusedVariable", "UnusedMethod")
'-Xep:TypeParameterUnusedInFormals:OFF', errorproneArgs = [
// Disable errorpone StringSplitter check, as it // Disable errorprone checks
// recommends using Splitter from Guava, which we don't '-Xep:TypeParameterUnusedInFormals:OFF',
// have (nor want to use in Smack). // Disable errorpone StringSplitter check, as it
'-Xep:StringSplitter:OFF', // recommends using Splitter from Guava, which we don't
'-Xep:JdkObsolete:OFF', // have (nor want to use in Smack).
// Disabled because sinttest re-uses BeforeClass from junit. '-Xep:StringSplitter:OFF',
// TODO: change sinttest so that it has it's own '-Xep:JdkObsolete:OFF',
// BeforeClass and re-enable this check. // Disabled because sinttest re-uses BeforeClass from junit.
'-Xep:JUnit4ClassAnnotationNonStatic:OFF', // TODO: change sinttest so that it has it's own
// Disabled but should be re-enabled at some point // BeforeClass and re-enable this check.
//'-Xep:InconsistentCapitalization:OFF', '-Xep:JUnit4ClassAnnotationNonStatic:OFF',
] // Disabled but should be re-enabled at some point
//'-Xep:InconsistentCapitalization:OFF',
'-Xep:MixedMutabilityReturnType:OFF',
]
}
} }
tasks.withType(ScalaCompile) { tasks.withType(ScalaCompile) {
@ -232,7 +267,11 @@ allprojects {
} }
dependencies { dependencies {
errorprone 'com.google.errorprone:error_prone_core:2.3.2' testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
errorprone 'com.google.errorprone:error_prone_core:2.3.3'
errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') errorproneJavac('com.google.errorprone:javac:9+181-r4173-1')
} }
@ -241,6 +280,15 @@ allprojects {
test { dependsOn javadoc } test { dependsOn javadoc }
} }
configure (junit4Projects) {
dependencies {
testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion"
testImplementation "org.powermock:powermock-module-junit4:$powerMockVersion"
testImplementation "org.powermock:powermock-module-junit4-rule:$powerMockVersion"
testImplementation "org.powermock:powermock-api-mockito2:$powerMockVersion"
}
}
gradle.taskGraph.whenReady { taskGraph -> gradle.taskGraph.whenReady { taskGraph ->
if (signingRequired if (signingRequired
&& taskGraph.allTasks.any { it instanceof Sign }) { && taskGraph.allTasks.any { it instanceof Sign }) {
@ -519,19 +567,21 @@ configure(integrationTestProjects + project(':smack-repl')) {
project(':smack-omemo').clirr.enabled = false project(':smack-omemo').clirr.enabled = false
project(':smack-omemo-signal').clirr.enabled = false project(':smack-omemo-signal').clirr.enabled = false
configure(
[ ':smack-omemo',
':smack-omemo-signal',
':smack-omemo-signal-integration-test',
].collect{ project(it) }) {
}
subprojects*.jar { subprojects*.jar {
manifest { manifest {
from sharedManifest from sharedManifest
} }
} }
configure(subprojects - nonStrictJavadocProjects) {
tasks.withType(Javadoc) {
// Abort on javadoc warnings.
// See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363)
// for information about the -Xwerror option.
options.addStringOption('Xwerror', '-quiet')
}
}
configure(subprojects - gplLicensedProjects) { configure(subprojects - gplLicensedProjects) {
checkstyle { checkstyle {
configProperties.checkstyleLicenseHeader = "header" configProperties.checkstyleLicenseHeader = "header"
@ -646,15 +696,7 @@ def getGitCommit() {
} }
def getAndroidRuntimeJar() { def getAndroidRuntimeJar() {
// We set a different Android API level compared to def androidApiLevel = ext.smackMinAndroidSdk
// smackMinAndroidSdk here. The runtime jar retrieved via this
// method is, compared to earlier Smack versions, not used to
// check for Android API compatibility. Instead it is used to for
// the eclipse classpath, for javadoc and when compiling the pure
// Android subprojects of Smack. Currently we require level 16
// here, because of the @TargetApi annotation found in
// AndroidUsingLinkProperties of minidns-android21.
def androidApiLevel = 16
def androidHome = getAndroidHome() def androidHome = getAndroidHome()
def androidJar = new File("$androidHome/platforms/android-${androidApiLevel}/android.jar") def androidJar = new File("$androidHome/platforms/android-${androidApiLevel}/android.jar")
if (androidJar.isFile()) { if (androidJar.isFile()) {

View file

@ -39,12 +39,7 @@
<property name="format" value="^\s+$"/> <property name="format" value="^\s+$"/>
<property name="message" value="Line containing only whitespace character(s)"/> <property name="message" value="Line containing only whitespace character(s)"/>
</module> </module>
<module name="RegexpSingleline"> <module name="FileTabCharacter"/>
<!-- We use {2,} instead of + here to address the typical case where a file was written
with tabs but javadoc is causing '\t *' -->
<property name="format" value="^\t+ {2,}"/>
<property name="message" value="Line containing space(s) after tab(s)"/>
</module>
<module name="RegexpSingleline"> <module name="RegexpSingleline">
<!-- <!--
Explaining the following Regex Explaining the following Regex
@ -147,6 +142,7 @@
, LITERAL_DO , LITERAL_DO
, LITERAL_FOR , LITERAL_FOR
, DO_WHILE , DO_WHILE
, COMMA
"/> "/>
</module> </module>
<module name="WhitespaceAround"> <module name="WhitespaceAround">

View file

@ -27,11 +27,11 @@ public MyExtension parse(XmlPullParser parser, int initialDepth) {
outerloop: while(true) { outerloop: while(true) {
// Make sure to have already parse all attributes of the outermost element, // Make sure to have already parse all attributes of the outermost element,
// i.e. 'attrFoo' of 'myExtension' in this example. Then advance the parser // i.e. 'attrFoo' of 'myExtension' in this example. Then advance the parser
int event = parser.next(); XmlPullParser.Event event = parser.next();
// Use switch/case of int instead of a if/else-if cascade // Use switch/case of int instead of a if/else-if cascade
switch (event) { switch (event) {
case XmlPullParser.START_TAG: case START_ELEMENT:
// Determine the name of the element which start tag we are seeing // Determine the name of the element which start tag we are seeing
String name = parser.getName(); String name = parser.getName();
// We can use switch/case of Strings since Java7, make use of its advantages // We can use switch/case of Strings since Java7, make use of its advantages
@ -52,12 +52,15 @@ public MyExtension parse(XmlPullParser parser, int initialDepth) {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
// The abort condition with the break labeled loop statement // The abort condition with the break labeled loop statement
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }

View file

@ -56,23 +56,25 @@ Smack Extensions and currently supported XEPs of smack-extensions
| [SI File Transfer](filetransfer.md) | [XEP-0096](https://xmpp.org/extensions/xep-0096.html) | n/a | Transfer files between two users over XMPP. | | [SI File Transfer](filetransfer.md) | [XEP-0096](https://xmpp.org/extensions/xep-0096.html) | n/a | Transfer files between two users over XMPP. |
| User Mood | [XEP-0107](https://xmpp.org/extensions/xep-0107.html) | 1.2.1 | Communicate the users current mood. | | User Mood | [XEP-0107](https://xmpp.org/extensions/xep-0107.html) | 1.2.1 | Communicate the users current mood. |
| [Entity Capabilities](caps.md) | [XEP-0115](https://xmpp.org/extensions/xep-0115.html) | n/a | Broadcasting and dynamic discovery of entity capabilities. | | [Entity Capabilities](caps.md) | [XEP-0115](https://xmpp.org/extensions/xep-0115.html) | n/a | Broadcasting and dynamic discovery of entity capabilities. |
| [Jingle](jingle.html) | [XEP-0116](https://xmpp.org/extensions/xep-0166.html) | n/a | Initiate and manage sessions between two XMPP entities. |
| Data Forms Validation | [XEP-0122](https://xmpp.org/extensions/xep-0122.html) | n/a | Enables an application to specify additional validation guidelines . | | Data Forms Validation | [XEP-0122](https://xmpp.org/extensions/xep-0122.html) | n/a | Enables an application to specify additional validation guidelines . |
| Service Administration | [XEP-0133](https://xmpp.org/extensions/xep-0133.html) | n/a | Recommended best practices for service-level administration of servers and components using Ad-Hoc Commands. | | Service Administration | [XEP-0133](https://xmpp.org/extensions/xep-0133.html) | n/a | Recommended best practices for service-level administration of servers and components using Ad-Hoc Commands. |
| Stream Compression | [XEP-0138](https://xmpp.org/extensions/xep-0138.html) | n/a | Support for optional compression of the XMPP stream. | Stream Compression | [XEP-0138](https://xmpp.org/extensions/xep-0138.html) | n/a | Support for optional compression of the XMPP stream.
| Data Forms Layout | [XEP-0141](https://xmpp.org/extensions/xep-0141.html) | n/a | Enables an application to specify form layouts. | | Data Forms Layout | [XEP-0141](https://xmpp.org/extensions/xep-0141.html) | n/a | Enables an application to specify form layouts. |
| Personal Eventing Protocol | [XEP-0163](https://xmpp.org/extensions/xep-0163.html) | n/a | Using the XMPP publish-subscribe protocol to broadcast state change events associated with an XMPP account. | | Personal Eventing Protocol | [XEP-0163](https://xmpp.org/extensions/xep-0163.html) | n/a | Using the XMPP publish-subscribe protocol to broadcast state change events associated with an XMPP account. |
| [Jingle](jingle.html) | [XEP-0166](https://xmpp.org/extensions/xep-0166.html) | n/a | Initiate and manage sessions between two XMPP entities. |
| Message Delivery Receipts | [XEP-0184](https://xmpp.org/extensions/xep-0184.html) | n/a | Extension for message delivery receipts. The sender can request notification that the message has been delivered. | | Message Delivery Receipts | [XEP-0184](https://xmpp.org/extensions/xep-0184.html) | n/a | Extension for message delivery receipts. The sender can request notification that the message has been delivered. |
| [Blocking Command](blockingcommand.md) | [XEP-0191](https://xmpp.org/extensions/xep-0191.html) | n/a | Communications blocking that is intended to be simpler than privacy lists (XEP-0016). | | [Blocking Command](blockingcommand.md) | [XEP-0191](https://xmpp.org/extensions/xep-0191.html) | n/a | Communications blocking that is intended to be simpler than privacy lists (XEP-0016). |
| XMPP Ping | [XEP-0199](https://xmpp.org/extensions/xep-0199.html) | n/a | Sending application-level pings over XML streams. | XMPP Ping | [XEP-0199](https://xmpp.org/extensions/xep-0199.html) | n/a | Sending application-level pings over XML streams.
| Entity Time | [XEP-0202](https://xmpp.org/extensions/xep-0202.html) | n/a | Allows entities to communicate their local time | | Entity Time | [XEP-0202](https://xmpp.org/extensions/xep-0202.html) | n/a | Allows entities to communicate their local time |
| Delayed Delivery | [XEP-0203](https://xmpp.org/extensions/xep-0203.html) | n/a | Extension for communicating the fact that an XML stanza has been delivered with a delay. | | Delayed Delivery | [XEP-0203](https://xmpp.org/extensions/xep-0203.html) | n/a | Extension for communicating the fact that an XML stanza has been delivered with a delay. |
| XMPP Over BOSH | [XEP-0206](https://xmpp.org/extensions/xep-0206.html) | n/a | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. | | XMPP Over BOSH | [XEP-0206](https://xmpp.org/extensions/xep-0206.html) | n/a | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. |
| Data Forms Media Element | [XEP-0221](https://xmpp.org/extensions/xep-0221.html) | n/a | Allows to include media data in XEP-0004 data forms. |
| Attention | [XEP-0224](https://xmpp.org/extensions/xep-0224.html) | n/a | Getting attention of another user. | | Attention | [XEP-0224](https://xmpp.org/extensions/xep-0224.html) | n/a | Getting attention of another user. |
| Bits of Binary | [XEP-0231](https://xmpp.org/extensions/xep-0231.html) | n/a | Including or referring to small bits of binary data in an XML stanza. | | Bits of Binary | [XEP-0231](https://xmpp.org/extensions/xep-0231.html) | n/a | Including or referring to small bits of binary data in an XML stanza. |
| Best Practices for Resource Locking | [XEP-0296](https://xmpp.org/extensions/xep-0296.html) | n/a | Specifies best practices to be followed by Jabber/XMPP clients about when to lock into, and unlock away from, resources. | | Best Practices for Resource Locking | [XEP-0296](https://xmpp.org/extensions/xep-0296.html) | n/a | Specifies best practices to be followed by Jabber/XMPP clients about when to lock into, and unlock away from, resources. |
| Last Message Correction | [XEP-0308](https://xmpp.org/extensions/xep-0308.html) | n/a | Provides a method for indicating that a message is a correction of the last sent message. | | Last Message Correction | [XEP-0308](https://xmpp.org/extensions/xep-0308.html) | n/a | Provides a method for indicating that a message is a correction of the last sent message. |
| Last User Interaction in Presence | [XEP-0319](https://xmpp.org/extensions/xep-0319.html) | n/a | Communicate time of last user interaction via XMPP presence notifications. | | Last User Interaction in Presence | [XEP-0319](https://xmpp.org/extensions/xep-0319.html) | n/a | Communicate time of last user interaction via XMPP presence notifications. |
| Data Forms Geolocation Element | [XEP-0350](https://xmpp.org/extensions/xep-0350.html) | n/a | Allows to include XEP-0080 gelocation data in XEP-0004 data forms. |
| [Group Chat Invitations](invitation.md) | n/a | n/a | Send invitations to other users to join a group chat room. | | [Group Chat Invitations](invitation.md) | n/a | n/a | Send invitations to other users to join a group chat room. |
| [Jive Properties](properties.md) | n/a | n/a | TODO | | [Jive Properties](properties.md) | n/a | n/a | TODO |

View file

@ -23,7 +23,7 @@ Whenever a packet extension is found in a packet, parsing will be
passed to the correct provider. Each provider must extend the passed to the correct provider. Each provider must extend the
ExtensionElementProvider abstract class. Each extension provider is ExtensionElementProvider abstract class. Each extension provider is
responsible for parsing the raw XML stream, via the responsible for parsing the raw XML stream, via the
[XML Pull Parser](http://www.xmlpull.org/), to contruct an object. Smack's `XmlPullParser` interface, to construct an object.
You can also create an introspection provider You can also create an introspection provider
(`provider.IntrospectionProvider.PacketExtensionIntrospectionProvider`). Here, (`provider.IntrospectionProvider.PacketExtensionIntrospectionProvider`). Here,
@ -161,9 +161,9 @@ public class MyIQProvider extends IQProvider<MyIQ> {
// Start parsing loop // Start parsing loop
outerloop: while(true) { outerloop: while(true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch(eventType) { switch(eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String elementName = parser.getName(); String elementName = parser.getName();
switch (elementName) { switch (elementName) {
case "user": case "user":
@ -175,12 +175,15 @@ public class MyIQProvider extends IQProvider<MyIQ> {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
// Abort condition: if the are on a end tag (closing element) of the same depth // Abort condition: if the are on a end tag (closing element) of the same depth
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
@ -225,9 +228,9 @@ _Disco Items IQProvider_
String node = ""; String node = "";
discoverItems.setNode(parser.getAttributeValue("", "node")); discoverItems.setNode(parser.getAttributeValue("", "node"));
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String elementName = parser.getName(); String elementName = parser.getName();
switch (elementName) { switch (elementName) {
case "item": case "item":
@ -239,7 +242,7 @@ _Disco Items IQProvider_
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
String elementName = parser.getName(); String elementName = parser.getName();
switch (elementName) { switch (elementName) {
case "item": case "item":
@ -295,17 +298,17 @@ _Subscription PacketExtensionProvider Implementation_
String state = parser.getAttributeValue(null, "subscription"); String state = parser.getAttributeValue(null, "subscription");
boolean isRequired = false; boolean isRequired = false;
int tag = parser.next(); XmlPullParser.Event tag = parser.next();
if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options")) { if ((tag == XmlPullParser.START_ELEMENT) && parser.getName().equals("subscribe-options")) {
tag = parser.next(); tag = parser.next();
if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required")) if ((tag == XmlPullParser.START_ELEMENT) && parser.getName().equals("required"))
isRequired = true; isRequired = true;
while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options"); while (parser.next() != XmlPullParser.END_ELEMENT && parser.getName() != "subscribe-options");
} }
while (parser.getEventType() != XmlPullParser.END_TAG) parser.next(); while (parser.getEventType() != XmlPullParser.END_ELEMENT) parser.next();
return new Subscription(jid, nodeId, subId, state == null ? null : Subscription.State.valueOf(state), isRequired); return new Subscription(jid, nodeId, subId, state == null ? null : Subscription.State.valueOf(state), isRequired);
} }
} }

View file

@ -169,14 +169,10 @@ recommended when using Smack.
</p> </p>
<p> <p>
If you dont' use a Smack tries to depend on as few as possible libraries. The only
dependency resolution system, like gradle or maven, then you will need requirement is <a href="http://jxmpp.org">jXMPP</a>. For DNS
to download at least resolution we recommend to
the <a href="http://www.extreme.indiana.edu/xgws/xsoap/xpp/mxp1/">Xml use <a href="http://minidns.org">MiniDNS</a>.
Pull Parser 3rd Edition (XPP3) library</a> or any other library that
implements the XmlPullParser interface
(like <a href="http://kxml.org/">kXML</a>) and the set of
<a href="http://jxmpp.org">jXMPP libraries</a>.
</p> </p>
<p> <p>

View file

@ -28,4 +28,7 @@ include 'smack-core',
'smack-omemo-signal', 'smack-omemo-signal',
'smack-omemo-signal-integration-test', 'smack-omemo-signal-integration-test',
'smack-repl', 'smack-repl',
'smack-openpgp' 'smack-openpgp',
'smack-xmlparser',
'smack-xmlparser-stax',
'smack-xmlparser-xpp3'

View file

@ -7,6 +7,7 @@ smack-extensions and smack-experimental."""
// Note that the test dependencies (junit, ) are inferred from the // Note that the test dependencies (junit, ) are inferred from the
// sourceSet.test of the core subproject // sourceSet.test of the core subproject
dependencies { dependencies {
api project(':smack-xmlparser-xpp3')
// Depend on minidns-android21 as optional dependency, even if may // Depend on minidns-android21 as optional dependency, even if may
// not need it. Can't hurt to have it in the programm path with // not need it. Can't hurt to have it in the programm path with
// the correct MiniDNS version as it won't hurt even if the // the correct MiniDNS version as it won't hurt even if the

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014-2018 Florian Schmaus * Copyright © 2014-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,8 @@
*/ */
package org.jivesoftware.smack.util.stringencoder.android; package org.jivesoftware.smack.util.stringencoder.android;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.stringencoder.StringEncoder; import org.jivesoftware.smack.util.stringencoder.StringEncoder;
import android.util.Base64; import android.util.Base64;
@ -39,21 +38,13 @@ public final class AndroidBase64UrlSafeEncoder implements StringEncoder<String>
@Override @Override
public String encode(String string) { public String encode(String string) {
try { return Base64.encodeToString(string.getBytes(StandardCharsets.UTF_8), BASE64_ENCODER_FLAGS);
return Base64.encodeToString(string.getBytes(StringUtils.UTF8), BASE64_ENCODER_FLAGS);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported", e);
}
} }
@Override @Override
public String decode(String string) { public String decode(String string) {
byte[] bytes = Base64.decode(string, BASE64_ENCODER_FLAGS); byte[] bytes = Base64.decode(string, BASE64_ENCODER_FLAGS);
try { return new String(bytes, StandardCharsets.UTF_8);
return new String(bytes, StringUtils.UTF8);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported", e);
}
} }
} }

View file

@ -20,7 +20,6 @@ package org.jivesoftware.smack.bosh;
import java.io.IOException; import java.io.IOException;
import java.io.PipedReader; import java.io.PipedReader;
import java.io.PipedWriter; import java.io.PipedWriter;
import java.io.StringReader;
import java.io.Writer; import java.io.Writer;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@ -44,6 +43,7 @@ import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.igniterealtime.jbosh.AbstractBody; import org.igniterealtime.jbosh.AbstractBody;
import org.igniterealtime.jbosh.BOSHClient; import org.igniterealtime.jbosh.BOSHClient;
@ -56,11 +56,8 @@ import org.igniterealtime.jbosh.BOSHException;
import org.igniterealtime.jbosh.BOSHMessageEvent; import org.igniterealtime.jbosh.BOSHMessageEvent;
import org.igniterealtime.jbosh.BodyQName; import org.igniterealtime.jbosh.BodyQName;
import org.igniterealtime.jbosh.ComposableBody; import org.igniterealtime.jbosh.ComposableBody;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
/** /**
* Creates a connection to an XMPP server via HTTP binding. * Creates a connection to an XMPP server via HTTP binding.
@ -479,14 +476,13 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
if (streamId == null) { if (streamId == null) {
streamId = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "authid")); streamId = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "authid"));
} }
final XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); final XmlPullParser parser = PacketParserUtils.getParserFor(body.toXML());
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(new StringReader(body.toXML())); XmlPullParser.Event eventType = parser.getEventType();
int eventType = parser.getEventType();
do { do {
eventType = parser.next(); eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
switch (name) { switch (name) {
case Message.ELEMENT: case Message.ELEMENT:
@ -528,9 +524,12 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
} }
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
while (eventType != XmlPullParser.END_DOCUMENT); while (eventType != XmlPullParser.Event.END_DOCUMENT);
} }
catch (Exception e) { catch (Exception e) {
if (isConnected()) { if (isConnected()) {

View file

@ -2,23 +2,27 @@ description = """\
Smack core components.""" Smack core components."""
ext { ext {
xmlUnitVersion = "2.6.0" xmlUnitVersion = '2.6.2'
powerMockVersion = "1.7.3"
} }
dependencies { dependencies {
compile 'xpp3:xpp3:1.1.4c' compile project(':smack-xmlparser')
compile "org.jxmpp:jxmpp-core:$jxmppVersion" compile "org.jxmpp:jxmpp-core:$jxmppVersion"
compile "org.jxmpp:jxmpp-jid:$jxmppVersion" compile "org.jxmpp:jxmpp-jid:$jxmppVersion"
compile "org.minidns:minidns-core:$miniDnsVersion" compile "org.minidns:minidns-core:$miniDnsVersion"
testCompile project(':smack-xmlparser-stax')
testCompile project(':smack-xmlparser-xpp3')
testCompile "org.jxmpp:jxmpp-jid:$jxmppVersion:tests" testCompile "org.jxmpp:jxmpp-jid:$jxmppVersion:tests"
testCompile "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testCompile "org.junit.vintage:junit-vintage-engine:$junitVersion"
testCompile "org.xmlunit:xmlunit-core:$xmlUnitVersion" testCompile "org.xmlunit:xmlunit-core:$xmlUnitVersion"
testCompile "org.xmlunit:xmlunit-legacy:$xmlUnitVersion" // Explictily add assertj-core which is a dependency of
testCompile "org.powermock:powermock-module-junit4:$powerMockVersion" // xmlunit-assertj, but gradle fails to resolves it with:
testCompile "org.powermock:powermock-module-junit4-rule:$powerMockVersion" // Execution failed for task ':smack-core:compileTestJava'.
testCompile "org.powermock:powermock-api-mockito2:$powerMockVersion" // > Could not resolve all files for configuration ':smack-core:testCompileClasspath'.
// > Could not find org.assertj:assertj-core:.
// Required by:
// project :smack-core > org.xmlunit:xmlunit-assertj:2.6.2
testCompile "org.assertj:assertj-core:3.11.1"
testCompile "org.xmlunit:xmlunit-assertj:$xmlUnitVersion"
testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2' testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2'
} }

View file

@ -16,9 +16,9 @@ package org.jivesoftware.smack.packet;
import org.jivesoftware.smack.provider.PrivacyProvider; import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.test.SmackTestCase; import org.jivesoftware.smack.test.SmackTestCase;
import org.xmlpull.v1.XmlPullParserFactory; import org.jivesoftware.smack.xml.XmlPullParserFactory;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import java.io.StringReader; import java.io.StringReader;

View file

@ -32,8 +32,8 @@ import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.TCPConnection; import org.jivesoftware.smack.TCPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.util.ConnectionUtils; import org.jivesoftware.smack.util.ConnectionUtils;
import org.xmlpull.v1.XmlPullParserFactory; import org.jivesoftware.smack.xml.XmlPullParserFactory;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
/** /**
* Base class for all the test cases which provides a pre-configured execution context. This * Base class for all the test cases which provides a pre-configured execution context. This
@ -414,9 +414,9 @@ public abstract class SmackTestCase extends TestCase {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(systemStream, "UTF-8"); parser.setInput(systemStream, "UTF-8");
int eventType = parser.getEventType(); XmlPullParser.Event eventType = parser.getEventType();
do { do {
if (eventType == XmlPullParser.START_TAG) { if (eventType == START_ELEMENT) {
if (parser.getName().equals("host")) { if (parser.getName().equals("host")) {
host = parser.nextText(); host = parser.nextText();
} }
@ -459,7 +459,7 @@ public abstract class SmackTestCase extends TestCase {
} }
eventType = parser.next(); eventType = parser.next();
} }
while (eventType != XmlPullParser.END_DOCUMENT); while (eventType != END_DOCUMENT);
parsedOK = true; parsedOK = true;
} }
catch (Exception e) { catch (Exception e) {

View file

@ -20,9 +20,9 @@ import java.io.StringReader;
import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.test.SmackTestCase; import org.jivesoftware.smack.test.SmackTestCase;
import org.xmlpull.v1.XmlPullParserFactory; import org.jivesoftware.smack.xml.XmlPullParserFactory;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
public class XMPPErrorTest extends SmackTestCase { public class XMPPErrorTest extends SmackTestCase {

View file

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
@ -64,6 +65,7 @@ import javax.net.ssl.X509TrustManager;
import javax.security.auth.callback.Callback; import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.PasswordCallback;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
@ -121,6 +123,8 @@ import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.dns.HostAddress; import org.jivesoftware.smack.util.dns.HostAddress;
import org.jivesoftware.smack.util.dns.SmackDaneProvider; import org.jivesoftware.smack.util.dns.SmackDaneProvider;
import org.jivesoftware.smack.util.dns.SmackDaneVerifier; import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityFullJid;
@ -130,8 +134,6 @@ import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
import org.jxmpp.util.XmppStringUtils; import org.jxmpp.util.XmppStringUtils;
import org.minidns.dnsname.DnsName; import org.minidns.dnsname.DnsName;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/** /**
@ -233,11 +235,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private XmlEnvironment incomingStreamXmlEnvironment; private XmlEnvironment incomingStreamXmlEnvironment;
final Map<String, NonzaCallback> nonzaCallbacks = new HashMap<>(); final Map<QName, NonzaCallback> nonzaCallbacks = new HashMap<>();
protected final Lock connectionLock = new ReentrantLock(); protected final Lock connectionLock = new ReentrantLock();
protected final Map<String, FullyQualifiedElement> streamFeatures = new HashMap<>(); protected final Map<QName, FullyQualifiedElement> streamFeatures = new HashMap<>();
/** /**
* The full JID of the authenticated user, as returned by the resource binding response of the server. * The full JID of the authenticated user, as returned by the resource binding response of the server.
@ -336,6 +338,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
Thread thread = new Thread(runnable); Thread thread = new Thread(runnable);
thread.setName("Smack Cached Executor"); thread.setName("Smack Cached Executor");
thread.setDaemon(true); thread.setDaemon(true);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.log(Level.WARNING, t + " encountered uncaught exception", e);
}
});
return thread; return thread;
} }
}); });
@ -381,8 +389,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected Exception currentConnectionException; protected Exception currentConnectionException;
private final Map<String, IQRequestHandler> setIqRequestHandler = new HashMap<>(); private final Map<QName, IQRequestHandler> setIqRequestHandler = new HashMap<>();
private final Map<String, IQRequestHandler> getIqRequestHandler = new HashMap<>(); private final Map<QName, IQRequestHandler> getIqRequestHandler = new HashMap<>();
/** /**
* Create a new XMPPConnection to an XMPP server. * Create a new XMPPConnection to an XMPP server.
@ -1213,7 +1221,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected final void parseAndProcessNonza(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException { protected final void parseAndProcessNonza(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
final String element = parser.getName(); final String element = parser.getName();
final String namespace = parser.getNamespace(); final String namespace = parser.getNamespace();
final String key = XmppStringUtils.generateKey(element, namespace); final QName key = new QName(namespace, element);
NonzaProvider<? extends Nonza> nonzaProvider = ProviderManager.getNonzaProvider(key); NonzaProvider<? extends Nonza> nonzaProvider = ProviderManager.getNonzaProvider(key);
if (nonzaProvider == null) { if (nonzaProvider == null) {
@ -1243,7 +1251,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
try { try {
stanza = PacketParserUtils.parseStanza(parser, incomingStreamXmlEnvironment); stanza = PacketParserUtils.parseStanza(parser, incomingStreamXmlEnvironment);
} }
catch (Exception e) { catch (XmlPullParserException | SmackParsingException | IOException e) {
CharSequence content = PacketParserUtils.parseContentDepth(parser, CharSequence content = PacketParserUtils.parseContentDepth(parser,
parserDepth); parserDepth);
UnparseableStanza message = new UnparseableStanza(content, e); UnparseableStanza message = new UnparseableStanza(content, e);
@ -1295,7 +1303,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
final IQ iq = (IQ) packet; final IQ iq = (IQ) packet;
if (iq.isRequestIQ()) { if (iq.isRequestIQ()) {
final IQ iqRequest = iq; final IQ iqRequest = iq;
final String key = XmppStringUtils.generateKey(iq.getChildElementName(), iq.getChildElementNamespace()); final QName key = iqRequest.getChildElementQName();
IQRequestHandler iqRequestHandler; IQRequestHandler iqRequestHandler;
final IQ.Type type = iq.getType(); final IQ.Type type = iq.getType();
switch (type) { switch (type) {
@ -1614,9 +1622,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
streamFeatures.clear(); streamFeatures.clear();
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
while (true) { while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initialDepth + 1) { if (eventType == XmlPullParser.Event.START_ELEMENT && parser.getDepth() == initialDepth + 1) {
FullyQualifiedElement streamFeature = null; FullyQualifiedElement streamFeature = null;
String name = parser.getName(); String name = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
@ -1647,7 +1655,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
addStreamFeature(streamFeature); addStreamFeature(streamFeature);
} }
} }
else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initialDepth) { else if (eventType == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == initialDepth) {
break; break;
} }
} }
@ -1686,7 +1694,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <F extends FullyQualifiedElement> F getFeature(String element, String namespace) { public <F extends FullyQualifiedElement> F getFeature(String element, String namespace) {
return (F) streamFeatures.get(XmppStringUtils.generateKey(element, namespace)); QName qname = new QName(namespace, element);
return (F) streamFeatures.get(qname);
} }
@Override @Override
@ -1695,7 +1704,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
protected void addStreamFeature(FullyQualifiedElement feature) { protected void addStreamFeature(FullyQualifiedElement feature) {
String key = XmppStringUtils.generateKey(feature.getElementName(), feature.getNamespace()); QName key = feature.getQName();
streamFeatures.put(key, feature); streamFeatures.put(key, feature);
} }
@ -1804,7 +1813,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public IQRequestHandler registerIQRequestHandler(final IQRequestHandler iqRequestHandler) { public IQRequestHandler registerIQRequestHandler(final IQRequestHandler iqRequestHandler) {
final String key = XmppStringUtils.generateKey(iqRequestHandler.getElement(), iqRequestHandler.getNamespace()); final QName key = iqRequestHandler.getQName();
switch (iqRequestHandler.getType()) { switch (iqRequestHandler.getType()) {
case set: case set:
synchronized (setIqRequestHandler) { synchronized (setIqRequestHandler) {
@ -1827,7 +1836,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) { public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) {
final String key = XmppStringUtils.generateKey(element, namespace); final QName key = new QName(namespace, element);
switch (type) { switch (type) {
case set: case set:
synchronized (setIqRequestHandler) { synchronized (setIqRequestHandler) {
@ -2044,13 +2053,13 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
try { try {
Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class); Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class);
String pkcs11Config = "name = SmartCard\nlibrary = " + config.getPKCS11Library(); String pkcs11Config = "name = SmartCard\nlibrary = " + config.getPKCS11Library();
ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StringUtils.UTF8)); ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8));
Provider p = (Provider) c.newInstance(config); Provider p = (Provider) c.newInstance(config);
Security.addProvider(p); Security.addProvider(p);
ks = KeyStore.getInstance("PKCS11",p); ks = KeyStore.getInstance("PKCS11", p);
pcb = new PasswordCallback("PKCS11 Password: ",false); pcb = new PasswordCallback("PKCS11 Password: ", false);
callbackHandler.handle(new Callback[] {pcb}); callbackHandler.handle(new Callback[] {pcb});
ks.load(null,pcb.getPassword()); ks.load(null, pcb.getPassword());
} }
catch (Exception e) { catch (Exception e) {
LOGGER.log(Level.WARNING, "Exception", e); LOGGER.log(Level.WARNING, "Exception", e);
@ -2058,8 +2067,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
} }
else if ("Apple".equals(keyStoreType)) { else if ("Apple".equals(keyStoreType)) {
ks = KeyStore.getInstance("KeychainStore","Apple"); ks = KeyStore.getInstance("KeychainStore", "Apple");
ks.load(null,null); ks.load(null, null);
// pcb = new PasswordCallback("Apple Keychain",false); // pcb = new PasswordCallback("Apple Keychain",false);
// pcb.setPassword(null); // pcb.setPassword(null);
} }

View file

@ -25,6 +25,8 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
@ -46,6 +48,7 @@ import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
import org.minidns.dnsname.DnsName; import org.minidns.dnsname.DnsName;
import org.minidns.dnsname.InvalidDnsNameException;
import org.minidns.util.InetAddressUtil; import org.minidns.util.InetAddressUtil;
/** /**
@ -81,6 +84,8 @@ public abstract class ConnectionConfiguration {
SmackConfiguration.getVersion(); SmackConfiguration.getVersion();
} }
private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName());
/** /**
* The XMPP domain of the XMPP Service. Usually servers use the same service name as the name * The XMPP domain of the XMPP Service. Usually servers use the same service name as the name
* of the server. However, there are some servers like google where host would be * of the server. However, there are some servers like google where host would be
@ -88,6 +93,8 @@ public abstract class ConnectionConfiguration {
*/ */
protected final DomainBareJid xmppServiceDomain; protected final DomainBareJid xmppServiceDomain;
protected final DnsName xmppServiceDomainDnsName;
protected final InetAddress hostAddress; protected final InetAddress hostAddress;
protected final DnsName host; protected final DnsName host;
protected final int port; protected final int port;
@ -149,7 +156,7 @@ public abstract class ConnectionConfiguration {
private final boolean compressionEnabled; private final boolean compressionEnabled;
protected ConnectionConfiguration(Builder<?,?> builder) { protected ConnectionConfiguration(Builder<?, ?> builder) {
authzid = builder.authzid; authzid = builder.authzid;
username = builder.username; username = builder.username;
password = builder.password; password = builder.password;
@ -162,6 +169,19 @@ public abstract class ConnectionConfiguration {
if (xmppServiceDomain == null) { if (xmppServiceDomain == null) {
throw new IllegalArgumentException("Must define the XMPP domain"); throw new IllegalArgumentException("Must define the XMPP domain");
} }
DnsName xmppServiceDomainDnsName;
try {
xmppServiceDomainDnsName = DnsName.from(xmppServiceDomain);
} catch (InvalidDnsNameException e) {
LOGGER.log(Level.INFO,
"Could not transform XMPP service domain '" + xmppServiceDomain
+ "' to a DNS name. TLS X.509 certificate validiation may not be possible.",
e);
xmppServiceDomainDnsName = null;
}
this.xmppServiceDomainDnsName = xmppServiceDomainDnsName;
hostAddress = builder.hostAddress; hostAddress = builder.hostAddress;
host = builder.host; host = builder.host;
port = builder.port; port = builder.port;
@ -225,6 +245,17 @@ public abstract class ConnectionConfiguration {
return xmppServiceDomain; return xmppServiceDomain;
} }
/**
* Returns the XMPP service domain as DNS name if possible. Note that since not every XMPP address domainpart is a
* valid DNS name, this method may return <code>null</code>.
*
* @return the XMPP service domain as DNS name or <code>null</code>.
* @since 4.3.4
*/
public DnsName getXmppServiceDomainAsDnsNameIfPossible() {
return xmppServiceDomainDnsName;
}
/** /**
* Returns the TLS security mode used when making the connection. By default, * Returns the TLS security mode used when making the connection. By default,
* the mode is {@link SecurityMode#ifpossible}. * the mode is {@link SecurityMode#ifpossible}.

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Florian Schmaus * Copyright 2018-2019 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,18 +19,18 @@ package org.jivesoftware.smack;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.FailedNonzaException; import org.jivesoftware.smack.XMPPException.FailedNonzaException;
import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.util.XmppElementUtil; import org.jivesoftware.smack.util.XmppElementUtil;
import org.jxmpp.util.XmppStringUtils;
public class NonzaCallback { public class NonzaCallback {
protected final AbstractXMPPConnection connection; protected final AbstractXMPPConnection connection;
protected final Map<String, GenericElementListener<? extends Nonza>> filterAndListeners; protected final Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners;
private NonzaCallback(Builder builder) { private NonzaCallback(Builder builder) {
this.connection = builder.connection; this.connection = builder.connection;
@ -39,7 +39,7 @@ public class NonzaCallback {
} }
void onNonzaReceived(Nonza nonza) { void onNonzaReceived(Nonza nonza) {
String key = XmppStringUtils.generateKey(nonza.getElementName(), nonza.getNamespace()); QName key = nonza.getQName();
GenericElementListener<? extends Nonza> nonzaListener = filterAndListeners.get(key); GenericElementListener<? extends Nonza> nonzaListener = filterAndListeners.get(key);
nonzaListener.processElement(nonza); nonzaListener.processElement(nonza);
@ -47,8 +47,8 @@ public class NonzaCallback {
public void cancel() { public void cancel() {
synchronized (connection.nonzaCallbacks) { synchronized (connection.nonzaCallbacks) {
for (Map.Entry<String, GenericElementListener<? extends Nonza>> entry : filterAndListeners.entrySet()) { for (Map.Entry<QName, GenericElementListener<? extends Nonza>> entry : filterAndListeners.entrySet()) {
String filterKey = entry.getKey(); QName filterKey = entry.getKey();
NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey); NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey);
if (equals(installedCallback)) { if (equals(installedCallback)) {
connection.nonzaCallbacks.remove(filterKey); connection.nonzaCallbacks.remove(filterKey);
@ -63,7 +63,7 @@ public class NonzaCallback {
} }
synchronized (connection.nonzaCallbacks) { synchronized (connection.nonzaCallbacks) {
for (String key : filterAndListeners.keySet()) { for (QName key : filterAndListeners.keySet()) {
connection.nonzaCallbacks.put(key, this); connection.nonzaCallbacks.put(key, this);
} }
} }
@ -78,8 +78,8 @@ public class NonzaCallback {
Builder builder) { Builder builder) {
super(builder); super(builder);
final String successNonzaKey = XmppElementUtil.getKeyFor(successNonzaClass); final QName successNonzaKey = XmppElementUtil.getQNameFor(successNonzaClass);
final String failedNonzaKey = XmppElementUtil.getKeyFor(failedNonzaClass); final QName failedNonzaKey = XmppElementUtil.getQNameFor(failedNonzaClass);
final GenericElementListener<SN> successListener = new GenericElementListener<SN>(successNonzaClass) { final GenericElementListener<SN> successListener = new GenericElementListener<SN>(successNonzaClass) {
@Override @Override
@ -139,14 +139,14 @@ public class NonzaCallback {
public static final class Builder { public static final class Builder {
private final AbstractXMPPConnection connection; private final AbstractXMPPConnection connection;
private Map<String, GenericElementListener<? extends Nonza>> filterAndListeners = new HashMap<>(); private Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners = new HashMap<>();
Builder(AbstractXMPPConnection connection) { Builder(AbstractXMPPConnection connection) {
this.connection = connection; this.connection = connection;
} }
public <N extends Nonza> Builder listenFor(Class<? extends N> nonza, GenericElementListener<? extends N> nonzaListener) { public <N extends Nonza> Builder listenFor(Class<? extends N> nonza, GenericElementListener<? extends N> nonzaListener) {
String key = XmppElementUtil.getKeyFor(nonza); QName key = XmppElementUtil.getQNameFor(nonza);
filterAndListeners.put(key, nonzaListener); filterAndListeners.put(key, nonzaListener);
return this; return this;
} }

View file

@ -29,6 +29,7 @@ import javax.net.ssl.HostnameVerifier;
import org.jivesoftware.smack.compression.XMPPInputOutputStream; import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory; import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory; import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
import org.jivesoftware.smack.parsing.ExceptionThrowingCallbackWithHint; import org.jivesoftware.smack.parsing.ExceptionThrowingCallbackWithHint;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback; import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
@ -380,4 +381,5 @@ public final class SmackConfiguration {
public static int getDefaultConcurrencyLevelLimit() { public static int getDefaultConcurrencyLevelLimit() {
return defaultConcurrencyLevelLimit; return defaultConcurrencyLevelLimit;
} }
} }

View file

@ -20,6 +20,7 @@ package org.jivesoftware.smack;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -44,10 +45,9 @@ import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism;
import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism; import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.FileUtils; import org.jivesoftware.smack.util.FileUtils;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
public final class SmackInitialization { public final class SmackInitialization {
@ -69,7 +69,7 @@ public final class SmackInitialization {
String smackVersion; String smackVersion;
BufferedReader reader = null; BufferedReader reader = null;
try { try {
reader = new BufferedReader(new InputStreamReader(FileUtils.getStreamForClasspathFile("org.jivesoftware.smack/version", null), StringUtils.UTF8)); reader = new BufferedReader(new InputStreamReader(FileUtils.getStreamForClasspathFile("org.jivesoftware.smack/version", null), StandardCharsets.UTF_8));
smackVersion = reader.readLine(); smackVersion = reader.readLine();
} catch (Exception e) { } catch (Exception e) {
LOGGER.log(Level.SEVERE, "Could not determine Smack version", e); LOGGER.log(Level.SEVERE, "Could not determine Smack version", e);
@ -141,12 +141,10 @@ public final class SmackInitialization {
public static void processConfigFile(InputStream cfgFileStream, public static void processConfigFile(InputStream cfgFileStream,
Collection<Exception> exceptions, ClassLoader classLoader) throws Exception { Collection<Exception> exceptions, ClassLoader classLoader) throws Exception {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); XmlPullParser parser = PacketParserUtils.getParserFor(cfgFileStream);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); XmlPullParser.Event eventType = parser.getEventType();
parser.setInput(cfgFileStream, "UTF-8");
int eventType = parser.getEventType();
do { do {
if (eventType == XmlPullParser.START_TAG) { if (eventType == XmlPullParser.Event.START_ELEMENT) {
if (parser.getName().equals("startupClasses")) { if (parser.getName().equals("startupClasses")) {
parseClassesToLoad(parser, false, exceptions, classLoader); parseClassesToLoad(parser, false, exceptions, classLoader);
} }
@ -156,7 +154,7 @@ public final class SmackInitialization {
} }
eventType = parser.next(); eventType = parser.next();
} }
while (eventType != XmlPullParser.END_DOCUMENT); while (eventType != XmlPullParser.Event.END_DOCUMENT);
CloseableUtil.maybeClose(cfgFileStream, LOGGER); CloseableUtil.maybeClose(cfgFileStream, LOGGER);
} }
@ -164,12 +162,10 @@ public final class SmackInitialization {
Collection<Exception> exceptions, ClassLoader classLoader) Collection<Exception> exceptions, ClassLoader classLoader)
throws Exception { throws Exception {
final String startName = parser.getName(); final String startName = parser.getName();
int eventType; XmlPullParser.Event eventType;
String name;
outerloop: do { outerloop: do {
eventType = parser.next(); eventType = parser.next();
name = parser.getName(); if (eventType == XmlPullParser.Event.START_ELEMENT && "className".equals(parser.getName())) {
if (eventType == XmlPullParser.START_TAG && "className".equals(name)) {
String classToLoad = parser.nextText(); String classToLoad = parser.nextText();
if (SmackConfiguration.isDisabledSmackClass(classToLoad)) { if (SmackConfiguration.isDisabledSmackClass(classToLoad)) {
continue outerloop; continue outerloop;
@ -188,7 +184,7 @@ public final class SmackInitialization {
} }
} }
} }
while (!(eventType == XmlPullParser.END_TAG && startName.equals(name))); while (!(eventType == XmlPullParser.Event.END_ELEMENT && startName.equals(parser.getName())));
} }
private static void loadSmackClass(String className, boolean optional, ClassLoader classLoader) throws Exception { private static void loadSmackClass(String className, boolean optional, ClassLoader classLoader) throws Exception {

View file

@ -149,9 +149,8 @@ public class SmackReactor {
long releaseTimeEpoch = System.currentTimeMillis() + unit.toMillis(delay); long releaseTimeEpoch = System.currentTimeMillis() + unit.toMillis(delay);
Date releaseTimeDate = new Date(releaseTimeEpoch); Date releaseTimeDate = new Date(releaseTimeEpoch);
ScheduledAction scheduledAction = new ScheduledAction(runnable, releaseTimeDate, this); ScheduledAction scheduledAction = new ScheduledAction(runnable, releaseTimeDate, this);
synchronized (scheduledActions) { scheduledActions.add(scheduledAction);
scheduledActions.add(scheduledAction); selector.wakeup();
}
return scheduledAction; return scheduledAction;
} }
@ -209,7 +208,8 @@ public class SmackReactor {
long selectWait; long selectWait;
if (nextScheduledAction == null) { if (nextScheduledAction == null) {
// There is no next scheduled action, wait indefinitely in select(). // There is no next scheduled action, wait indefinitely in select() or until another thread invokes
// selector.wakeup().
selectWait = 0; selectWait = 0;
} else { } else {
selectWait = nextScheduledAction.getTimeToDueMillis(); selectWait = nextScheduledAction.getTimeToDueMillis();

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software, 2016-2018 Florian Schmaus. * Copyright 2003-2007 Jive Software, 2016-2019 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.
@ -41,7 +41,7 @@ import org.jivesoftware.smack.packet.Stanza;
* @see XMPPConnection#createStanzaCollector(StanzaFilter) * @see XMPPConnection#createStanzaCollector(StanzaFilter)
* @author Matt Tucker * @author Matt Tucker
*/ */
public class StanzaCollector implements AutoCloseable { public final class StanzaCollector implements AutoCloseable {
private final StanzaFilter packetFilter; private final StanzaFilter packetFilter;
@ -69,7 +69,7 @@ public class StanzaCollector implements AutoCloseable {
* @param connection the connection the collector is tied to. * @param connection the connection the collector is tied to.
* @param configuration the configuration used to construct this collector * @param configuration the configuration used to construct this collector
*/ */
protected StanzaCollector(XMPPConnection connection, Configuration configuration) { StanzaCollector(XMPPConnection connection, Configuration configuration) {
this.connection = connection; this.connection = connection;
this.packetFilter = configuration.packetFilter; this.packetFilter = configuration.packetFilter;
this.resultQueue = new ArrayDeque<>(configuration.size); this.resultQueue = new ArrayDeque<>(configuration.size);
@ -98,18 +98,6 @@ public class StanzaCollector implements AutoCloseable {
} }
} }
/**
* Returns the stanza filter associated with this stanza collector. The packet
* filter is used to determine what packets are queued as results.
*
* @return the stanza filter.
* @deprecated use {@link #getStanzaFilter()} instead.
*/
@Deprecated
public StanzaFilter getPacketFilter() {
return getStanzaFilter();
}
/** /**
* Returns the stanza filter associated with this stanza collector. The stanza * Returns the stanza filter associated with this stanza collector. The stanza
* filter is used to determine what stanzas are queued as results. * filter is used to determine what stanzas are queued as results.
@ -329,6 +317,21 @@ public class StanzaCollector implements AutoCloseable {
return resultQueue.size(); return resultQueue.size();
} }
private String stringCache;
@Override
public String toString() {
if (stringCache == null) {
StringBuilder sb = new StringBuilder(128);
sb.append("Stanza Collector filter='").append(packetFilter).append('\'');
if (request != null) {
sb.append(" request='").append(request).append('\'');
}
stringCache = sb.toString();
}
return stringCache;
}
synchronized void notifyConnectionError(Exception exception) { synchronized void notifyConnectionError(Exception exception) {
connectionException = exception; connectionException = exception;
notifyAll(); notifyAll();
@ -380,19 +383,6 @@ public class StanzaCollector implements AutoCloseable {
private Configuration() { private Configuration() {
} }
/**
* Set the stanza filter used by this collector. If <code>null</code>, then all packets will
* get collected by this collector.
*
* @param packetFilter
* @return a reference to this configuration.
* @deprecated use {@link #setStanzaFilter(StanzaFilter)} instead.
*/
@Deprecated
public Configuration setPacketFilter(StanzaFilter packetFilter) {
return setStanzaFilter(packetFilter);
}
/** /**
* Set the stanza filter used by this collector. If <code>null</code>, then all stanzas will * Set the stanza filter used by this collector. If <code>null</code>, then all stanzas will
* get collected by this collector. * get collected by this collector.

View file

@ -20,7 +20,7 @@ import org.jivesoftware.smack.compress.packet.Compressed;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.NonzaProvider; import org.jivesoftware.smack.provider.NonzaProvider;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public final class CompressedProvider extends NonzaProvider<Compressed> { public final class CompressedProvider extends NonzaProvider<Compressed> {

View file

@ -27,8 +27,8 @@ import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.NonzaProvider; import org.jivesoftware.smack.provider.NonzaProvider;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
public final class FailureProvider extends NonzaProvider<Failure> { public final class FailureProvider extends NonzaProvider<Failure> {
@ -46,9 +46,9 @@ public final class FailureProvider extends NonzaProvider<Failure> {
XmlEnvironment failureXmlEnvironment = XmlEnvironment.from(parser, xmlEnvironment); XmlEnvironment failureXmlEnvironment = XmlEnvironment.from(parser, xmlEnvironment);
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
switch (namespace) { switch (namespace) {
@ -72,11 +72,12 @@ public final class FailureProvider extends NonzaProvider<Failure> {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default: // fall out
} }
} }

View file

@ -0,0 +1,70 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.datatypes;
public abstract class Scalar extends java.lang.Number {
/**
*
*/
private static final long serialVersionUID = 1L;
private final java.lang.Number number;
protected Scalar(java.lang.Number number) {
this.number = number;
}
public final Number number() {
return number;
}
@Override
public final int intValue() {
return number.intValue();
}
@Override
public final long longValue() {
return number.longValue();
}
@Override
public final float floatValue() {
return number.floatValue();
}
@Override
public final double doubleValue() {
return number.doubleValue();
}
@Override
public final int hashCode() {
return number.hashCode();
}
@Override
public final boolean equals(Object other) {
return number.equals(other);
}
@Override
public final String toString() {
return number.toString();
}
}

View file

@ -0,0 +1,42 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.datatypes;
import org.jivesoftware.smack.util.NumberUtil;
/**
* A number representing an unsigned 16-bit integer. Can be used for values with the XML schema type "xs:unsingedShort".
*/
public final class UInt16 extends Scalar {
private static final long serialVersionUID = 1L;
private final int number;
private UInt16(int number) {
super(NumberUtil.requireUShort16(number));
this.number = number;
}
public int nativeRepresentation() {
return number;
}
public static UInt16 from(int number) {
return new UInt16(number);
}
}

View file

@ -0,0 +1,42 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.datatypes;
import org.jivesoftware.smack.util.NumberUtil;
/**
* A number representing an unsigned 32-bit integer. Can be used for values with the XML schema type "xs:unsignedInt".
*/
public final class UInt32 extends Scalar {
private static final long serialVersionUID = 1L;
private final long number;
private UInt32(long number) {
super(NumberUtil.requireUInt32(number));
this.number = number;
}
public long nativeRepresentation() {
return number;
}
public static UInt32 from(long number) {
return new UInt32(number);
}
}

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 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.
*/
/**
* Custom datatypes for Integers.
*/
package org.jivesoftware.smack.datatypes;

View file

@ -57,10 +57,10 @@ import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPConnection { public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPConnection {
@ -310,10 +310,10 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
// Skip the enclosing stream open what is guaranteed to be there. // Skip the enclosing stream open what is guaranteed to be there.
parser.next(); parser.next();
int event = parser.getEventType(); XmlPullParser.Event event = parser.getEventType();
outerloop: while (true) { outerloop: while (true) {
switch (event) { switch (event) {
case XmlPullParser.START_TAG: case START_ELEMENT:
final String name = parser.getName(); final String name = parser.getName();
// Note that we don't handle "stream" here as it's done in the splitter. // Note that we don't handle "stream" here as it's done in the splitter.
switch (name) { switch (name) {
@ -353,8 +353,9 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
break; break;
} }
break; break;
case XmlPullParser.END_DOCUMENT: case END_DOCUMENT:
break outerloop; break outerloop;
default: // fall out
} }
event = parser.next(); event = parser.next();
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2015 Florian Schmaus * Copyright 2015-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
*/ */
package org.jivesoftware.smack.iqrequest; package org.jivesoftware.smack.iqrequest;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
/** /**
@ -50,4 +52,8 @@ public interface IQRequestHandler {
String getElement(); String getElement();
String getNamespace(); String getNamespace();
default QName getQName() {
return new QName(getNamespace(), getElement());
}
} }

View file

@ -52,7 +52,7 @@ public class DefaultExtensionElement implements ExtensionElement {
private String elementName; private String elementName;
private String namespace; private String namespace;
private Map<String,String> map; private Map<String, String> map;
/** /**
* Creates a new generic stanza extension. * Creates a new generic stanza extension.
@ -107,7 +107,7 @@ public class DefaultExtensionElement implements ExtensionElement {
if (map == null) { if (map == null) {
return Collections.emptySet(); return Collections.emptySet();
} }
return Collections.unmodifiableSet(new HashMap<String,String>(map).keySet()); return Collections.unmodifiableSet(new HashMap<String, String>(map).keySet());
} }
/** /**
@ -131,7 +131,7 @@ public class DefaultExtensionElement implements ExtensionElement {
*/ */
public synchronized void setValue(String name, String value) { public synchronized void setValue(String name, String value) {
if (map == null) { if (map == null) {
map = new HashMap<String,String>(); map = new HashMap<String, String>();
} }
map.put(name, value); map.put(name, value);
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Florian Schmaus * Copyright 2018-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import javax.xml.namespace.QName;
public interface FullyQualifiedElement extends NamedElement { public interface FullyQualifiedElement extends NamedElement {
/** /**
@ -25,4 +27,9 @@ public interface FullyQualifiedElement extends NamedElement {
*/ */
String getNamespace(); String getNamespace();
default QName getQName() {
String namespaceURI = getNamespace();
String localPart = getElementName();
return new QName(namespaceURI, localPart);
}
} }

View file

@ -20,6 +20,8 @@ package org.jivesoftware.smack.packet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
@ -46,6 +48,7 @@ public abstract class IQ extends Stanza {
public static final String IQ_ELEMENT = "iq"; public static final String IQ_ELEMENT = "iq";
public static final String QUERY_ELEMENT = "query"; public static final String QUERY_ELEMENT = "query";
private final QName childElementQName;
private final String childElementName; private final String childElementName;
private final String childElementNamespace; private final String childElementNamespace;
@ -56,6 +59,7 @@ public abstract class IQ extends Stanza {
type = iq.getType(); type = iq.getType();
this.childElementName = iq.childElementName; this.childElementName = iq.childElementName;
this.childElementNamespace = iq.childElementNamespace; this.childElementNamespace = iq.childElementNamespace;
this.childElementQName = iq.childElementQName;
} }
protected IQ(String childElementName) { protected IQ(String childElementName) {
@ -65,6 +69,11 @@ public abstract class IQ extends Stanza {
protected IQ(String childElementName, String childElementNamespace) { protected IQ(String childElementName, String childElementNamespace) {
this.childElementName = childElementName; this.childElementName = childElementName;
this.childElementNamespace = childElementNamespace; this.childElementNamespace = childElementNamespace;
if (childElementName == null) {
childElementQName = null;
} else {
childElementQName = new QName(childElementNamespace, childElementName);
}
} }
/** /**
@ -115,6 +124,10 @@ public abstract class IQ extends Stanza {
return !isRequestIQ(); return !isRequestIQ();
} }
public final QName getChildElementQName() {
return childElementQName;
}
public final String getChildElementName() { public final String getChildElementName() {
return childElementName; return childElementName;
} }

View file

@ -24,6 +24,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.TypedCloneable;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
@ -561,32 +563,22 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
return subject; return subject;
} }
private final HashCode.Cache hashCodeCache = new HashCode.Cache();
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; return hashCodeCache.getHashCode(c ->
int result = 1; c.append(language)
if (language != null) { .append(subject)
result = prime * result + this.language.hashCode(); );
}
result = prime * result + this.subject.hashCode();
return result;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) { return EqualsUtil.equals(this, obj, (e, o) ->
return true; e.append(language, o.language)
} .append(subject, o.subject)
if (obj == null) { );
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Subject other = (Subject) obj;
// simplified comparison because language and subject are always set
return this.language.equals(other.language) && this.subject.equals(other.subject);
} }
@Override @Override
@ -670,31 +662,22 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
return message; return message;
} }
private final HashCode.Cache hashCodeCache = new HashCode.Cache();
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; return hashCodeCache.getHashCode(c ->
int result = 1; c.append(language)
if (language != null) { .append(message)
result = prime * result + this.language.hashCode(); );
}
result = prime * result + this.message.hashCode();
return result;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) { return EqualsUtil.equals(this, obj, (e, o) ->
return true; e.append(language, o.language)
} .append(message, o.message)
if (obj == null) { );
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Body other = (Body) obj;
// simplified comparison because language and message are always set
return Objects.equals(this.language, other.language) && this.message.equals(other.message);
} }
@Override @Override

View file

@ -150,7 +150,7 @@ public interface Packet extends TopLevelStreamElement {
/** /**
* Returns the first extension of this stanza that has the given namespace. * Returns the first extension of this stanza that has the given namespace.
* <p> * <p>
* When possible, use {@link #getExtension(String,String)} instead. * When possible, use {@link #getExtension(String, String)} instead.
* </p> * </p>
* *
* @param namespace the namespace of the extension that is desired. * @param namespace the namespace of the extension that is desired.

View file

@ -68,12 +68,12 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
private String status = null; private String status = null;
/** /**
* The priority of the presence. The magic value {@link Integer#MIN_VALUE} is used to indicate that the original * The priority of the presence. It is <code>null</code> to indicate that the original
* presence stanza did not had an explicit priority set. In which case the priority defaults to 0. * presence stanza did not had an explicit priority set. In which case the priority defaults to 0.
* *
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3.</a> * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3.</a>
*/ */
private int priority = Integer.MIN_VALUE; private Byte priority;
private Mode mode = null; private Mode mode = null;
@ -203,13 +203,13 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
} }
/** /**
* Returns the priority of the presence, or Integer.MIN_VALUE if no priority has been set. * Returns the priority of the presence.
* *
* @return the priority. * @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a> * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/ */
public int getPriority() { public int getPriority() {
if (priority == Integer.MIN_VALUE) { if (priority == null) {
return 0; return 0;
} }
return priority; return priority;
@ -227,6 +227,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
throw new IllegalArgumentException("Priority value " + priority + throw new IllegalArgumentException("Priority value " + priority +
" is not valid. Valid range is -128 through 127."); " is not valid. Valid range is -128 through 127.");
} }
setPriority((byte) priority);
}
public void setPriority(byte priority) {
this.priority = priority; this.priority = priority;
} }
@ -264,7 +268,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
if (!StringUtils.isNullOrEmpty(status)) { if (!StringUtils.isNullOrEmpty(status)) {
sb.append("status=").append(status).append(','); sb.append("status=").append(status).append(',');
} }
if (priority != Integer.MIN_VALUE) { if (priority != null) {
sb.append("prio=").append(priority).append(','); sb.append("prio=").append(priority).append(',');
} }
sb.append(']'); sb.append(']');
@ -282,9 +286,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
buf.rightAngleBracket(); buf.rightAngleBracket();
buf.optElement("status", status); buf.optElement("status", status);
if (priority != Integer.MIN_VALUE) { buf.optElement("priority", priority);
buf.element("priority", Integer.toString(priority));
}
if (mode != null && mode != Mode.available) { if (mode != null && mode != Mode.available) {
buf.element("show", mode); buf.element("show", mode);
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2015 Florian Schmaus. * Copyright 2015-2019 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.
@ -21,13 +21,13 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.util.XmppStringUtils;
/** /**
* An {@link ExtensionElement} modeling the often required and used XML features when using XMPP. It * An {@link ExtensionElement} modeling the often required and used XML features when using XMPP. It
* is therefore suitable for most use cases. Use * is therefore suitable for most use cases. Use
@ -47,7 +47,7 @@ public final class StandardExtensionElement implements ExtensionElement {
private final String namespace; private final String namespace;
private final Map<String, String> attributes; private final Map<String, String> attributes;
private final String text; private final String text;
private final MultiMap<String, StandardExtensionElement> elements; private final MultiMap<QName, StandardExtensionElement> elements;
private XmlStringBuilder xmlCache; private XmlStringBuilder xmlCache;
@ -65,7 +65,7 @@ public final class StandardExtensionElement implements ExtensionElement {
} }
private StandardExtensionElement(String name, String namespace, Map<String, String> attributes, String text, private StandardExtensionElement(String name, String namespace, Map<String, String> attributes, String text,
MultiMap<String, StandardExtensionElement> elements) { MultiMap<QName, StandardExtensionElement> elements) {
this.name = StringUtils.requireNotNullNorEmpty(name, "Name must not be null nor empty"); this.name = StringUtils.requireNotNullNorEmpty(name, "Name must not be null nor empty");
this.namespace = StringUtils.requireNotNullNorEmpty(namespace, "Namespace must not be null nor empty"); this.namespace = StringUtils.requireNotNullNorEmpty(namespace, "Namespace must not be null nor empty");
if (attributes == null) { if (attributes == null) {
@ -100,7 +100,7 @@ public final class StandardExtensionElement implements ExtensionElement {
if (elements == null) { if (elements == null) {
return null; return null;
} }
String key = XmppStringUtils.generateKey(element, namespace); QName key = new QName(namespace, element);
return elements.getFirst(key); return elements.getFirst(key);
} }
@ -112,7 +112,7 @@ public final class StandardExtensionElement implements ExtensionElement {
if (elements == null) { if (elements == null) {
return null; return null;
} }
String key = XmppStringUtils.generateKey(element, namespace); QName key = new QName(namespace, element);
return elements.getAll(key); return elements.getAll(key);
} }
@ -145,7 +145,7 @@ public final class StandardExtensionElement implements ExtensionElement {
xml.optEscape(text); xml.optEscape(text);
if (elements != null) { if (elements != null) {
for (Map.Entry<String, StandardExtensionElement> entry : elements.entrySet()) { for (Map.Entry<QName, StandardExtensionElement> entry : elements.entrySet()) {
xml.append(entry.getValue().toXML(getNamespace())); xml.append(entry.getValue().toXML(getNamespace()));
} }
} }
@ -165,7 +165,7 @@ public final class StandardExtensionElement implements ExtensionElement {
private Map<String, String> attributes; private Map<String, String> attributes;
private String text; private String text;
private MultiMap<String, StandardExtensionElement> elements; private MultiMap<QName, StandardExtensionElement> elements;
private Builder(String name, String namespace) { private Builder(String name, String namespace) {
this.name = name; this.name = name;
@ -200,7 +200,8 @@ public final class StandardExtensionElement implements ExtensionElement {
if (elements == null) { if (elements == null) {
elements = new MultiMap<>(); elements = new MultiMap<>();
} }
String key = XmppStringUtils.generateKey(element.getElementName(), element.getNamespace());
QName key = element.getQName();
elements.put(key, element); elements.put(key, element);
return this; return this;
} }

View file

@ -23,6 +23,8 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.id.StanzaIdUtil; import org.jivesoftware.smack.packet.id.StanzaIdUtil;
import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.PacketUtil; import org.jivesoftware.smack.util.PacketUtil;
@ -31,7 +33,6 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
import org.jxmpp.util.XmppStringUtils;
/** /**
* Base class for XMPP Stanzas, which are called Stanza in older versions of Smack (i.e. &lt; 4.1). * Base class for XMPP Stanzas, which are called Stanza in older versions of Smack (i.e. &lt; 4.1).
@ -56,7 +57,7 @@ public abstract class Stanza implements TopLevelStreamElement {
protected static final String DEFAULT_LANGUAGE = protected static final String DEFAULT_LANGUAGE =
java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US); java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US);
private final MultiMap<String, ExtensionElement> packetExtensions = new MultiMap<>(); private final MultiMap<QName, ExtensionElement> extensionElements = new MultiMap<>();
private String id = null; private String id = null;
private Jid to; private Jid to;
@ -306,9 +307,9 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return a list of all extension elements of this stanza. * @return a list of all extension elements of this stanza.
*/ */
public List<ExtensionElement> getExtensions() { public List<ExtensionElement> getExtensions() {
synchronized (packetExtensions) { synchronized (extensionElements) {
// No need to create a new list, values() will already create a new one for us // No need to create a new list, values() will already create a new one for us
return packetExtensions.values(); return extensionElements.values();
} }
} }
@ -326,14 +327,14 @@ public abstract class Stanza implements TopLevelStreamElement {
public List<ExtensionElement> getExtensions(String elementName, String namespace) { public List<ExtensionElement> getExtensions(String elementName, String namespace) {
requireNotNullNorEmpty(elementName, "elementName must not be null nor empty"); requireNotNullNorEmpty(elementName, "elementName must not be null nor empty");
requireNotNullNorEmpty(namespace, "namespace must not be null nor empty"); requireNotNullNorEmpty(namespace, "namespace must not be null nor empty");
String key = XmppStringUtils.generateKey(elementName, namespace); QName key = new QName(namespace, elementName);
return packetExtensions.getAll(key); return extensionElements.getAll(key);
} }
/** /**
* Returns the first extension of this stanza that has the given namespace. * Returns the first extension of this stanza that has the given namespace.
* <p> * <p>
* When possible, use {@link #getExtension(String,String)} instead. * When possible, use {@link #getExtension(String, String)} instead.
* </p> * </p>
* *
* @param namespace the namespace of the extension that is desired. * @param namespace the namespace of the extension that is desired.
@ -359,10 +360,10 @@ public abstract class Stanza implements TopLevelStreamElement {
if (namespace == null) { if (namespace == null) {
return null; return null;
} }
String key = XmppStringUtils.generateKey(elementName, namespace); QName key = new QName(namespace, elementName);
ExtensionElement packetExtension; ExtensionElement packetExtension;
synchronized (packetExtensions) { synchronized (extensionElements) {
packetExtension = packetExtensions.getFirst(key); packetExtension = extensionElements.getFirst(key);
} }
if (packetExtension == null) { if (packetExtension == null) {
return null; return null;
@ -377,9 +378,9 @@ public abstract class Stanza implements TopLevelStreamElement {
*/ */
public void addExtension(ExtensionElement extension) { public void addExtension(ExtensionElement extension) {
if (extension == null) return; if (extension == null) return;
String key = XmppStringUtils.generateKey(extension.getElementName(), extension.getNamespace()); QName key = extension.getQName();
synchronized (packetExtensions) { synchronized (extensionElements) {
packetExtensions.put(key, extension); extensionElements.put(key, extension);
} }
} }
@ -393,7 +394,7 @@ public abstract class Stanza implements TopLevelStreamElement {
*/ */
public ExtensionElement overrideExtension(ExtensionElement extension) { public ExtensionElement overrideExtension(ExtensionElement extension) {
if (extension == null) return null; if (extension == null) return null;
synchronized (packetExtensions) { synchronized (extensionElements) {
// Note that we need to use removeExtension(String, String) here. If would use // Note that we need to use removeExtension(String, String) here. If would use
// removeExtension(ExtensionElement) then we would remove based on the equality of ExtensionElement, which // removeExtension(ExtensionElement) then we would remove based on the equality of ExtensionElement, which
// is not what we want in this case. // is not what we want in this case.
@ -429,9 +430,9 @@ public abstract class Stanza implements TopLevelStreamElement {
if (elementName == null) { if (elementName == null) {
return hasExtension(namespace); return hasExtension(namespace);
} }
String key = XmppStringUtils.generateKey(elementName, namespace); QName key = new QName(namespace, elementName);
synchronized (packetExtensions) { synchronized (extensionElements) {
return packetExtensions.containsKey(key); return extensionElements.containsKey(key);
} }
} }
@ -442,8 +443,8 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return true if a stanza extension exists, false otherwise. * @return true if a stanza extension exists, false otherwise.
*/ */
public boolean hasExtension(String namespace) { public boolean hasExtension(String namespace) {
synchronized (packetExtensions) { synchronized (extensionElements) {
for (ExtensionElement packetExtension : packetExtensions.values()) { for (ExtensionElement packetExtension : extensionElements.values()) {
if (packetExtension.getNamespace().equals(namespace)) { if (packetExtension.getNamespace().equals(namespace)) {
return true; return true;
} }
@ -460,9 +461,9 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return the removed stanza extension or null. * @return the removed stanza extension or null.
*/ */
public ExtensionElement removeExtension(String elementName, String namespace) { public ExtensionElement removeExtension(String elementName, String namespace) {
String key = XmppStringUtils.generateKey(elementName, namespace); QName key = new QName(namespace, elementName);
synchronized (packetExtensions) { synchronized (extensionElements) {
return packetExtensions.remove(key); return extensionElements.remove(key);
} }
} }
@ -473,9 +474,9 @@ public abstract class Stanza implements TopLevelStreamElement {
* @return the removed stanza extension or null. * @return the removed stanza extension or null.
*/ */
public ExtensionElement removeExtension(ExtensionElement extension) { public ExtensionElement removeExtension(ExtensionElement extension) {
String key = XmppStringUtils.generateKey(extension.getElementName(), extension.getNamespace()); QName key = extension.getQName();
synchronized (packetExtensions) { synchronized (extensionElements) {
List<ExtensionElement> list = packetExtensions.getAll(key); List<ExtensionElement> list = extensionElements.getAll(key);
boolean removed = list.remove(extension); boolean removed = list.remove(extension);
if (removed) { if (removed) {
return extension; return extension;

View file

@ -19,7 +19,7 @@ package org.jivesoftware.smack.packet;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public class XmlEnvironment { public class XmlEnvironment {

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2015 Florian Schmaus. * Copyright 2015-2019 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.
@ -26,8 +26,8 @@ import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
/** /**
* The parser for {@link StandardExtensionElement}s. * The parser for {@link StandardExtensionElement}s.
@ -47,7 +47,7 @@ public class StandardExtensionElementProvider extends ExtensionElementProvider<S
String name = parser.getName(); String name = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
StandardExtensionElement.Builder builder = StandardExtensionElement.builder(name, namespace); StandardExtensionElement.Builder builder = StandardExtensionElement.builder(name, namespace);
final int namespaceCount = parser.getNamespaceCount(initialDepth); final int namespaceCount = parser.getNamespaceCount();
final int attributeCount = parser.getAttributeCount(); final int attributeCount = parser.getAttributeCount();
final Map<String, String> attributes = new LinkedHashMap<>(namespaceCount + attributeCount); final Map<String, String> attributes = new LinkedHashMap<>(namespaceCount + attributeCount);
for (int i = 0; i < namespaceCount; i++) { for (int i = 0; i < namespaceCount; i++) {
@ -77,19 +77,22 @@ public class StandardExtensionElementProvider extends ExtensionElementProvider<S
builder.addAttributes(attributes); builder.addAttributes(attributes);
outerloop: while (true) { outerloop: while (true) {
int event = parser.next(); XmlPullParser.Event event = parser.next();
switch (event) { switch (event) {
case XmlPullParser.START_TAG: case START_ELEMENT:
builder.addElement(parse(parser, parser.getDepth(), xmlEnvironment)); builder.addElement(parse(parser, parser.getDepth(), xmlEnvironment));
break; break;
case XmlPullParser.TEXT: case TEXT_CHARACTERS:
builder.setText(parser.getText()); builder.setText(parser.getText());
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (initialDepth == parser.getDepth()) { if (initialDepth == parser.getDepth()) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }

View file

@ -20,12 +20,12 @@ import java.io.IOException;
import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.packet.Bind;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class BindIQProvider extends IQProvider<Bind> { public class BindIQProvider extends IQProvider<Bind> {
@ -34,9 +34,9 @@ public class BindIQProvider extends IQProvider<Bind> {
String name; String name;
Bind bind = null; Bind bind = null;
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
name = parser.getName(); name = parser.getName();
switch (name) { switch (name) {
case "resource": case "resource":
@ -49,11 +49,14 @@ public class BindIQProvider extends IQProvider<Bind> {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
return bind; return bind;

View file

@ -24,8 +24,8 @@ import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
public class BodyElementProvider extends ExtensionElementProvider<Message.Body> { public class BodyElementProvider extends ExtensionElementProvider<Message.Body> {

View file

@ -27,8 +27,8 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
/** /**
* *
@ -97,14 +97,14 @@ public abstract class EmbeddedExtensionProvider<PE extends ExtensionElement> ext
} }
List<ExtensionElement> extensions = new ArrayList<>(); List<ExtensionElement> extensions = new ArrayList<>();
int event; XmlPullParser.Event event;
do { do {
event = parser.next(); event = parser.next();
if (event == XmlPullParser.START_TAG) if (event == XmlPullParser.Event.START_ELEMENT)
PacketParserUtils.addExtensionElement(extensions, parser, xmlEnvironment); PacketParserUtils.addExtensionElement(extensions, parser, xmlEnvironment);
} }
while (!(event == XmlPullParser.END_TAG && parser.getDepth() == initialDepth)); while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == initialDepth));
return createReturnExtension(name, namespace, attMap, extensions); return createReturnExtension(name, namespace, attMap, extensions);
} }

View file

@ -24,8 +24,8 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
public class IntrospectionProvider{ public class IntrospectionProvider{
@ -81,9 +81,9 @@ public class IntrospectionProvider{
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);
Object object = objectClass.getConstructor().newInstance(); Object object = objectClass.getConstructor().newInstance();
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
String stringValue = parser.nextText(); String stringValue = parser.nextText();
Class<?> propertyType = object.getClass().getMethod( Class<?> propertyType = object.getClass().getMethod(
@ -97,11 +97,14 @@ public class IntrospectionProvider{
propertyType).invoke(object, value); propertyType).invoke(object, value);
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
ParserUtils.assertAtEndTag(parser); ParserUtils.assertAtEndTag(parser);

View file

@ -26,8 +26,8 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
/** /**
* Smack provider are the parsers used to deserialize raw XMPP into the according Java {@link Element}s. * Smack provider are the parsers used to deserialize raw XMPP into the according Java {@link Element}s.

View file

@ -26,9 +26,8 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
/** /**
* Loads the {@link IQProvider} and {@link ExtensionElementProvider} information from a standard provider file in preparation * Loads the {@link IQProvider} and {@link ExtensionElementProvider} information from a standard provider file in preparation
@ -54,12 +53,10 @@ public class ProviderFileLoader implements ProviderLoader {
public ProviderFileLoader(InputStream providerStream, ClassLoader classLoader) { public ProviderFileLoader(InputStream providerStream, ClassLoader classLoader) {
// Load processing providers. // Load processing providers.
try (InputStream is = providerStream) { try (InputStream is = providerStream) {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); XmlPullParser parser = PacketParserUtils.getParserFor(is);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); XmlPullParser.Event eventType = parser.getEventType();
parser.setInput(is, "UTF-8");
int eventType = parser.getEventType();
do { do {
if (eventType == XmlPullParser.START_TAG) { if (eventType == XmlPullParser.Event.START_ELEMENT) {
final String typeName = parser.getName(); final String typeName = parser.getName();
try { try {
@ -134,7 +131,7 @@ public class ProviderFileLoader implements ProviderLoader {
} }
eventType = parser.next(); eventType = parser.next();
} }
while (eventType != XmlPullParser.END_DOCUMENT); while (eventType != XmlPullParser.Event.END_DOCUMENT);
} }
catch (Exception e) { catch (Exception e) {
LOGGER.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e); LOGGER.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e);

View file

@ -22,6 +22,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
@ -29,8 +31,6 @@ import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmppElementUtil; import org.jivesoftware.smack.util.XmppElementUtil;
import org.jxmpp.util.XmppStringUtils;
/** /**
* Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of * Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of
* providers exist:<ul> * providers exist:<ul>
@ -112,10 +112,10 @@ import org.jxmpp.util.XmppStringUtils;
*/ */
public final class ProviderManager { public final class ProviderManager {
private static final Map<String, ExtensionElementProvider<ExtensionElement>> extensionProviders = new ConcurrentHashMap<String, ExtensionElementProvider<ExtensionElement>>(); private static final Map<QName, ExtensionElementProvider<ExtensionElement>> extensionProviders = new ConcurrentHashMap<>();
private static final Map<String, IQProvider<IQ>> iqProviders = new ConcurrentHashMap<String, IQProvider<IQ>>(); private static final Map<QName, IQProvider<IQ>> iqProviders = new ConcurrentHashMap<>();
private static final Map<String, ExtensionElementProvider<ExtensionElement>> streamFeatureProviders = new ConcurrentHashMap<String, ExtensionElementProvider<ExtensionElement>>(); private static final Map<QName, ExtensionElementProvider<ExtensionElement>> streamFeatureProviders = new ConcurrentHashMap<>();
private static final Map<String, NonzaProvider<? extends Nonza>> nonzaProviders = new ConcurrentHashMap<>(); private static final Map<QName, NonzaProvider<? extends Nonza>> nonzaProviders = new ConcurrentHashMap<>();
static { static {
// Ensure that Smack is initialized by calling getVersion, so that user // Ensure that Smack is initialized by calling getVersion, so that user
@ -168,7 +168,7 @@ public final class ProviderManager {
* @return the IQ provider. * @return the IQ provider.
*/ */
public static IQProvider<IQ> getIQProvider(String elementName, String namespace) { public static IQProvider<IQ> getIQProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
return iqProviders.get(key); return iqProviders.get(key);
} }
@ -199,7 +199,7 @@ public final class ProviderManager {
Object provider) { Object provider) {
validate(elementName, namespace); validate(elementName, namespace);
// First remove existing providers // First remove existing providers
String key = removeIQProvider(elementName, namespace); QName key = removeIQProvider(elementName, namespace);
if (provider instanceof IQProvider) { if (provider instanceof IQProvider) {
iqProviders.put(key, (IQProvider<IQ>) provider); iqProviders.put(key, (IQProvider<IQ>) provider);
} else { } else {
@ -214,10 +214,10 @@ public final class ProviderManager {
* *
* @param elementName the XML element name. * @param elementName the XML element name.
* @param namespace the XML namespace. * @param namespace the XML namespace.
* @return the key of the removed IQ Provider * @return the QName of the removed provider
*/ */
public static String removeIQProvider(String elementName, String namespace) { public static QName removeIQProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
iqProviders.remove(key); iqProviders.remove(key);
return key; return key;
} }
@ -242,7 +242,7 @@ public final class ProviderManager {
* @return the extension provider. * @return the extension provider.
*/ */
public static ExtensionElementProvider<ExtensionElement> getExtensionProvider(String elementName, String namespace) { public static ExtensionElementProvider<ExtensionElement> getExtensionProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
return extensionProviders.get(key); return extensionProviders.get(key);
} }
@ -260,7 +260,7 @@ public final class ProviderManager {
Object provider) { Object provider) {
validate(elementName, namespace); validate(elementName, namespace);
// First remove existing providers // First remove existing providers
String key = removeExtensionProvider(elementName, namespace); QName key = removeExtensionProvider(elementName, namespace);
if (provider instanceof ExtensionElementProvider) { if (provider instanceof ExtensionElementProvider) {
extensionProviders.put(key, (ExtensionElementProvider<ExtensionElement>) provider); extensionProviders.put(key, (ExtensionElementProvider<ExtensionElement>) provider);
} else { } else {
@ -275,10 +275,10 @@ public final class ProviderManager {
* *
* @param elementName the XML element name. * @param elementName the XML element name.
* @param namespace the XML namespace. * @param namespace the XML namespace.
* @return the key of the removed stanza extension provider * @return the QName of the removed stanza extension provider
*/ */
public static String removeExtensionProvider(String elementName, String namespace) { public static QName removeExtensionProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
extensionProviders.remove(key); extensionProviders.remove(key);
return key; return key;
} }
@ -297,48 +297,48 @@ public final class ProviderManager {
} }
public static ExtensionElementProvider<ExtensionElement> getStreamFeatureProvider(String elementName, String namespace) { public static ExtensionElementProvider<ExtensionElement> getStreamFeatureProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
return streamFeatureProviders.get(key); return streamFeatureProviders.get(key);
} }
public static void addStreamFeatureProvider(String elementName, String namespace, ExtensionElementProvider<ExtensionElement> provider) { public static void addStreamFeatureProvider(String elementName, String namespace, ExtensionElementProvider<ExtensionElement> provider) {
validate(elementName, namespace); validate(elementName, namespace);
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
streamFeatureProviders.put(key, provider); streamFeatureProviders.put(key, provider);
} }
public static void removeStreamFeatureProvider(String elementName, String namespace) { public static void removeStreamFeatureProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
streamFeatureProviders.remove(key); streamFeatureProviders.remove(key);
} }
public static NonzaProvider<? extends Nonza> getNonzaProvider(String elementName, String namespace) { public static NonzaProvider<? extends Nonza> getNonzaProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
return getNonzaProvider(key); return getNonzaProvider(key);
} }
public static NonzaProvider<? extends Nonza> getNonzaProvider(String key) { public static NonzaProvider<? extends Nonza> getNonzaProvider(QName key) {
return nonzaProviders.get(key); return nonzaProviders.get(key);
} }
public static void addNonzaProvider(NonzaProvider<? extends Nonza> nonzaProvider) { public static void addNonzaProvider(NonzaProvider<? extends Nonza> nonzaProvider) {
Class<? extends Nonza> nonzaClass = nonzaProvider.getElementClass(); Class<? extends Nonza> nonzaClass = nonzaProvider.getElementClass();
String key = XmppElementUtil.getKeyFor(nonzaClass); QName key = XmppElementUtil.getQNameFor(nonzaClass);
nonzaProviders.put(key, nonzaProvider); nonzaProviders.put(key, nonzaProvider);
} }
public static void removeNonzaProvider(Class<? extends Nonza> nonzaClass) { public static void removeNonzaProvider(Class<? extends Nonza> nonzaClass) {
String key = XmppElementUtil.getKeyFor(nonzaClass); QName key = XmppElementUtil.getQNameFor(nonzaClass);
nonzaProviders.remove(key); nonzaProviders.remove(key);
} }
public static void removeNonzaProvider(String elementName, String namespace) { public static void removeNonzaProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace); QName key = getQName(elementName, namespace);
nonzaProviders.remove(key); nonzaProviders.remove(key);
} }
private static String getKey(String elementName, String namespace) { private static QName getQName(String elementName, String namespace) {
return XmppStringUtils.generateKey(elementName, namespace); return new QName(namespace, elementName);
} }
private static void validate(String elementName, String namespace) { private static void validate(String elementName, String namespace) {

View file

@ -19,7 +19,7 @@ package org.jivesoftware.smack.provider;
import org.jivesoftware.smack.packet.TlsProceed; import org.jivesoftware.smack.packet.TlsProceed;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public final class TlsFailureProvider extends NonzaProvider<TlsProceed> { public final class TlsFailureProvider extends NonzaProvider<TlsProceed> {

View file

@ -19,7 +19,7 @@ package org.jivesoftware.smack.provider;
import org.jivesoftware.smack.packet.TlsFailure; import org.jivesoftware.smack.packet.TlsFailure;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.xmlpull.v1.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
public final class TlsProceedProvider extends NonzaProvider<TlsFailure> { public final class TlsProceedProvider extends NonzaProvider<TlsFailure> {

View file

@ -22,8 +22,7 @@ import java.io.OutputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets;
import org.jivesoftware.smack.util.StringUtils;
/** /**
* Socket factory for socks4 proxy. * Socket factory for socks4 proxy.
@ -73,7 +72,6 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
of all zero bits. of all zero bits.
*/ */
index = 0;
buf[index++] = 4; buf[index++] = 4;
buf[index++] = 1; buf[index++] = 1;
@ -87,7 +85,7 @@ public class Socks4ProxySocketConnection implements ProxySocketConnection {
} }
if (user != null) { if (user != null) {
byte[] userBytes = user.getBytes(StringUtils.UTF8); byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
System.arraycopy(userBytes, 0, buf, index, user.length()); System.arraycopy(userBytes, 0, buf, index, user.length());
index += user.length(); index += user.length();
} }

View file

@ -21,10 +21,10 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.StringUtils;
/** /**
* Socket factory for Socks5 proxy. * Socket factory for Socks5 proxy.
@ -133,11 +133,11 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
index = 0; index = 0;
buf[index++] = 1; buf[index++] = 1;
buf[index++] = (byte) user.length(); buf[index++] = (byte) user.length();
byte[] userBytes = user.getBytes(StringUtils.UTF8); byte[] userBytes = user.getBytes(StandardCharsets.UTF_8);
System.arraycopy(userBytes, 0, buf, index, System.arraycopy(userBytes, 0, buf, index,
user.length()); user.length());
index += user.length(); index += user.length();
byte[] passwordBytes = passwd.getBytes(StringUtils.UTF8); byte[] passwordBytes = passwd.getBytes(StandardCharsets.UTF_8);
buf[index++] = (byte) passwordBytes.length; buf[index++] = (byte) passwordBytes.length;
System.arraycopy(passwordBytes, 0, buf, index, System.arraycopy(passwordBytes, 0, buf, index,
passwd.length()); passwd.length());
@ -204,7 +204,7 @@ public class Socks5ProxySocketConnection implements ProxySocketConnection {
buf[index++] = 1; // CONNECT buf[index++] = 1; // CONNECT
buf[index++] = 0; buf[index++] = 0;
byte[] hostb = host.getBytes(StringUtils.UTF8); byte[] hostb = host.getBytes(StandardCharsets.UTF_8);
int len = hostb.length; int len = hostb.length;
buf[index++] = 3; // DOMAINNAME buf[index++] = 3; // DOMAINNAME
buf[index++] = (byte) len; buf[index++] = (byte) len;

View file

@ -31,13 +31,13 @@ public class SASLErrorException extends XMPPException {
private final SASLFailure saslFailure; private final SASLFailure saslFailure;
private final String mechanism; private final String mechanism;
private final Map<String,String> texts; private final Map<String, String> texts;
public SASLErrorException(String mechanism, SASLFailure saslFailure) { public SASLErrorException(String mechanism, SASLFailure saslFailure) {
this(mechanism, saslFailure, new HashMap<String, String>()); this(mechanism, saslFailure, new HashMap<String, String>());
} }
public SASLErrorException(String mechanism, SASLFailure saslFailure, Map<String,String> texts) { public SASLErrorException(String mechanism, SASLFailure saslFailure, Map<String, String> texts) {
super("SASLError using " + mechanism + ": " + saslFailure.getSASLErrorString()); super("SASLError using " + mechanism + ": " + saslFailure.getSASLErrorString());
this.mechanism = mechanism; this.mechanism = mechanism;
this.saslFailure = saslFailure; this.saslFailure = saslFailure;
@ -52,7 +52,7 @@ public class SASLErrorException extends XMPPException {
return mechanism; return mechanism;
} }
public Map<String,String> getTexts() { public Map<String, String> getTexts() {
return texts; return texts;
} }
} }

View file

@ -16,7 +16,7 @@
*/ */
package org.jivesoftware.smack.sasl.core; package org.jivesoftware.smack.sasl.core;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collections; import java.util.Collections;
@ -114,14 +114,8 @@ public abstract class ScramMechanism extends SASLMechanism {
@Override @Override
protected byte[] evaluateChallenge(byte[] challenge) throws SmackSaslException { protected byte[] evaluateChallenge(byte[] challenge) throws SmackSaslException {
String challengeString; // TODO: Where is it specified that this is an UTF-8 encoded string?
try { String challengeString = new String(challenge, StandardCharsets.UTF_8);
// TODO: Where is it specified that this is an UTF-8 encoded string?
challengeString = new String(challenge, StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
switch (state) { switch (state) {
case AUTH_TEXT_SENT: case AUTH_TEXT_SENT:
@ -386,14 +380,9 @@ public abstract class ScramMechanism extends SASLMechanism {
* @throws SmackSaslException if a SASL related error occurs. * @throws SmackSaslException if a SASL related error occurs.
*/ */
private byte[] hi(String normalizedPassword, byte[] salt, int iterations) throws SmackSaslException { private byte[] hi(String normalizedPassword, byte[] salt, int iterations) throws SmackSaslException {
byte[] key; // According to RFC 5802 § 2.2, the resulting string of the normalization is also in UTF-8.
try { byte[] key = normalizedPassword.getBytes(StandardCharsets.UTF_8);
// According to RFC 5802 § 2.2, the resulting string of the normalization is also in UTF-8.
key = normalizedPassword.getBytes(StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new AssertionError();
}
// U1 := HMAC(str, salt + INT(1)) // U1 := HMAC(str, salt + INT(1))
byte[] u = hmac(key, ByteUtils.concat(salt, ONE)); byte[] u = hmac(key, ByteUtils.concat(salt, ONE));
byte[] res = u.clone(); byte[] res = u.clone();

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2014-2018 Florian Schmaus * Copyright 2014-2019 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.
@ -110,7 +110,7 @@ public class ArrayBlockingQueueWithShutdown<E> extends AbstractQueue<E> implemen
private void checkNotShutdown() throws InterruptedException { private void checkNotShutdown() throws InterruptedException {
if (isShutdown) { if (isShutdown) {
throw new InterruptedException(); throw new InterruptedException("Queue was already shut down");
} }
} }

View file

@ -16,7 +16,7 @@
*/ */
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
public interface CallbackRecipient<V,E> { public interface CallbackRecipient<V, E> {
CallbackRecipient<V, E> onSuccess(SuccessCallback<V> successCallback); CallbackRecipient<V, E> onSuccess(SuccessCallback<V> successCallback);

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Florian Schmaus * Copyright 2018-2019 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,6 +23,10 @@ import java.util.logging.Logger;
public class CloseableUtil { public class CloseableUtil {
public static void maybeClose(Closeable closable) {
maybeClose(closable, null);
}
public static void maybeClose(Closeable closable, Logger logger) { public static void maybeClose(Closeable closable, Logger logger) {
if (closable == null) { if (closable == null) {
return; return;
@ -31,7 +35,9 @@ public class CloseableUtil {
try { try {
closable.close(); closable.close();
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.WARNING, "Could not close " + closable, e); if (logger != null) {
logger.log(Level.WARNING, "Could not close " + closable, e);
}
} }
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2015-2018 Florian Schmaus * Copyright 2015-2019 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.
@ -49,4 +49,10 @@ public class CollectionUtil {
public interface Predicate<T> { public interface Predicate<T> {
boolean test(T t); boolean test(T t);
} }
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
ArrayList<T> arrayList = new ArrayList<>(collection.size());
arrayList.addAll(collection);
return arrayList;
}
} }

View file

@ -0,0 +1,243 @@
/**
*
* Copyright 2019 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util;
public final class EqualsUtil {
private EqualsUtil() {
}
public static <T> boolean equals(T thisObject, Object other, EqualsComperator<T> equalsComperator) {
if (other == null) {
return false;
}
if (thisObject == other) {
return true;
}
@SuppressWarnings("unchecked")
Class<T> thisObjectClass = (Class<T>) thisObject.getClass();
if (thisObjectClass != other.getClass()) {
return false;
}
EqualsUtil.Builder equalsBuilder = new EqualsUtil.Builder();
equalsComperator.compare(equalsBuilder, thisObjectClass.cast(other));
return equalsBuilder.isEquals;
}
@FunctionalInterface
public interface EqualsComperator<T> {
void compare(EqualsUtil.Builder equalsBuilder, T other);
}
public static final class Builder {
private boolean isEquals = true;
private Builder() {
}
private void nullSafeCompare(Object left, Object right, Runnable runnable) {
if (!isEquals) {
return;
}
if (left == right) {
return;
}
if (left == null || right == null) {
isEquals = false;
return;
}
runnable.run();
}
public <O> Builder append(O left, O right) {
if (!isEquals) {
return this;
}
if (left == right) {
return this;
}
if (left == null || right == null) {
isEquals = false;
return this;
}
isEquals = left.equals(right);
return this;
}
public Builder append(boolean left, boolean right) {
if (!isEquals) {
return this;
}
isEquals = left == right;
return this;
}
public Builder append(boolean[] left, boolean[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
public Builder append(byte left, byte right) {
if (!isEquals) {
return this;
}
isEquals = left == right;
return this;
}
public Builder append(byte[] left, byte[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
public Builder append(char left, char right) {
if (!isEquals) {
return this;
}
isEquals = left == right;
return this;
}
public Builder append(char[] left, char[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
public Builder append(double left, double right) {
if (!isEquals) {
return this;
}
return append(Double.doubleToLongBits(left), Double.doubleToLongBits(right));
}
public Builder append(double[] left, double[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
public Builder append(float left, float right) {
if (!isEquals) {
return this;
}
return append(Float.floatToIntBits(left), Float.floatToIntBits(right));
}
public Builder append(float[] left, float[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
public Builder append(int left, int right) {
if (!isEquals) {
return this;
}
isEquals = left == right;
return this;
}
public Builder append(int[] left, int[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
public Builder append(long left, long right) {
if (!isEquals) {
return this;
}
isEquals = left == right;
return this;
}
public Builder append(long[] left, long[] right) {
nullSafeCompare(left, right, () -> {
if (left.length != right.length) {
isEquals = false;
return;
}
for (int i = 0; i < left.length && isEquals; i++) {
append(left[i], right[i]);
}
});
return this;
}
}
}

View file

@ -34,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class EventManger<K, R, E extends Exception> { public class EventManger<K, R, E extends Exception> {
private final Map<K,Reference<R>> events = new ConcurrentHashMap<>(); private final Map<K, Reference<R>> events = new ConcurrentHashMap<>();
/** /**
* Perform an action and wait for an event. * Perform an action and wait for an event.

View file

@ -30,6 +30,7 @@ import java.io.Reader;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -90,7 +91,7 @@ public final class FileUtils {
public static boolean addLines(String uriString, Set<String> set) throws MalformedURLException, IOException { public static boolean addLines(String uriString, Set<String> set) throws MalformedURLException, IOException {
URI uri = URI.create(uriString); URI uri = URI.create(uriString);
InputStream is = getStreamForUri(uri, null); InputStream is = getStreamForUri(uri, null);
InputStreamReader sr = new InputStreamReader(is, StringUtils.UTF8); InputStreamReader sr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(sr); BufferedReader br = new BufferedReader(sr);
try { try {
String line; String line;

View file

@ -0,0 +1,211 @@
/**
*
* Copyright 2019 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util;
public class HashCode {
private static final int MULTIPLIER_VALUE = 37;
public static class Cache {
private boolean calculated;
private int hashcode;
public int getHashCode(Calculator hashCodeCalculator) {
if (calculated) {
return hashcode;
}
HashCode.Builder hashCodeBuilder = new HashCode.Builder();
hashCodeCalculator.calculateHash(hashCodeBuilder);
calculated = true;
hashcode = hashCodeBuilder.hashcode;
return hashcode;
}
}
@FunctionalInterface
public interface Calculator {
void calculateHash(HashCode.Builder hashCodeBuilder);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private int hashcode = 17;
private void applyHash() {
applyHash(0);
}
private void applyHash(int hash) {
hashcode = MULTIPLIER_VALUE * hashcode + hash;
}
public Builder append(Object object) {
if (object == null) {
applyHash();
return this;
}
if (object.getClass().isArray()) {
if (object instanceof int[]) {
append((int[]) object);
} else if (object instanceof long[]) {
append((long[]) object);
} else if (object instanceof boolean[]) {
append((boolean[]) object);
} else if (object instanceof double[]) {
append((double[]) object);
} else if (object instanceof float[]) {
append((float[]) object);
} else if (object instanceof short[]) {
append((short[]) object);
} else if (object instanceof char[]) {
append((char[]) object);
} else if (object instanceof byte[]) {
append((byte[]) object);
} else {
append((Object[]) object);
}
}
applyHash(object.hashCode());
return this;
}
public Builder append(boolean value) {
applyHash(value ? 0 : 1);
return this;
}
public Builder append(boolean[] array) {
if (array == null) {
applyHash();
return this;
}
for (boolean bool : array) {
append(bool);
}
return this;
}
public Builder append(byte value) {
applyHash(value);
return this;
}
public Builder append(byte[] array) {
if (array == null) {
applyHash();
return this;
}
for (byte b : array) {
append(b);
}
return this;
}
public Builder append(char value) {
applyHash(value);
return this;
}
public Builder append(char[] array) {
if (array == null) {
applyHash();
return this;
}
for (char c : array) {
append(c);
}
return this;
}
public Builder append(double value) {
return append(Double.doubleToLongBits(value));
}
public Builder append(double[] array) {
if (array == null) {
applyHash();
return this;
}
for (double d : array) {
append(d);
}
return this;
}
public Builder append(float value) {
return append(Float.floatToIntBits(value));
}
public Builder append(float[] array) {
if (array == null) {
applyHash();
return this;
}
for (float f : array) {
append(f);
}
return this;
}
public Builder append(long value) {
applyHash((int) (value ^ (value >>> 32)));
return this;
}
public Builder append(long[] array) {
if (array == null) {
applyHash();
return this;
}
for (long l : array) {
append(l);
}
return this;
}
public Builder append(Object[] array) {
if (array == null) {
applyHash();
return this;
}
for (Object element : array) {
append(element);
}
return this;
}
public int build() {
return hashcode;
}
}
}

View file

@ -0,0 +1,213 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.minidns.dnslabel.DnsLabel;
import org.minidns.dnsname.DnsName;
import org.minidns.dnsname.InvalidDnsNameException;
import org.minidns.util.InetAddressUtil;
/**
* An internet address, can be given as IP or as DNS name.
* <p>
* This type is meant for strings that hold an internet address. The original string used to construct this type is
* stored and returning in the {@link #toString()} method.
* </p>
*
* @since 4.4.0
*/
public abstract class InternetAddress implements CharSequence {
protected final String originalString;
protected InternetAddress(String originalString) {
this.originalString = Objects.requireNonNull(originalString, "The 'originalString' argument must not be null");
}
public abstract InetAddress asInetAddress() throws UnknownHostException;
@Override
public String toString() {
return originalString;
}
@Override
public int length() {
return originalString.length();
}
@Override
public char charAt(int index) {
return originalString.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return originalString.subSequence(start, end);
}
public static InternetAddress from(String address) {
final InternetAddress internetAddress;
if (InetAddressUtil.isIpV4Address(address)) {
internetAddress = new InternetAddress.Ipv4(address);
} else if (InetAddressUtil.isIpV6Address(address)) {
internetAddress = new InternetAddress.Ipv6(address);
} else if (address.contains(".")) {
InternetAddress domainNameInternetAddress;
try {
DnsName dnsName = DnsName.from(address);
domainNameInternetAddress = new InternetAddress.DomainName(address, dnsName);
} catch (InvalidDnsNameException e) {
domainNameInternetAddress = new InternetAddress.InvalidDomainName(address, e);
}
internetAddress = domainNameInternetAddress;
} else {
DnsLabel dnsLabel = DnsLabel.from(address);
internetAddress = new InternetAddress.DomainNameLabel(address, dnsLabel);
}
return internetAddress;
}
public static InternetAddress from(InetAddress inetAddress) {
if (inetAddress instanceof Inet4Address) {
return new InternetAddress.Ipv4(inetAddress.getHostAddress(), (Inet4Address) inetAddress);
} else if (inetAddress instanceof Inet6Address) {
return new InternetAddress.Ipv6(inetAddress.getHostAddress(), (Inet6Address) inetAddress);
} else {
throw new IllegalArgumentException("Unknown type " + inetAddress.getClass() + " of " + inetAddress);
}
}
private static class InetAddressInternetAddress extends InternetAddress {
private final InetAddress inetAddress;
protected InetAddressInternetAddress(String originalString, InetAddress inetAddress) {
super(originalString);
this.inetAddress = inetAddress;
}
@Override
public InetAddress asInetAddress() {
return inetAddress;
}
}
public static final class Ipv4 extends InetAddressInternetAddress {
private final Inet4Address inet4Address;
private Ipv4(String originalString) {
this(originalString, InetAddressUtil.ipv4From(originalString));
}
private Ipv4(String originalString, Inet4Address inet4Address) {
super(originalString, inet4Address);
this.inet4Address = inet4Address;
}
public Inet4Address getInet4Address() {
return inet4Address;
}
}
public static final class Ipv6 extends InetAddressInternetAddress {
private Inet6Address inet6Address;
private Ipv6(String originalString) {
this(originalString, InetAddressUtil.ipv6From(originalString));
}
private Ipv6(String originalString, Inet6Address inet6Address) {
super(originalString, inet6Address);
this.inet6Address = inet6Address;
}
public Inet6Address getInet6Address() {
return inet6Address;
}
}
private static class NonNumericInternetAddress extends InternetAddress {
private boolean attemptedToResolveInetAddress;
private InetAddress inetAddress;
protected NonNumericInternetAddress(String originalString) {
super(originalString);
}
@Override
public InetAddress asInetAddress() throws UnknownHostException {
if (inetAddress != null || attemptedToResolveInetAddress) {
return inetAddress;
}
attemptedToResolveInetAddress = true;
inetAddress = InetAddress.getByName(originalString);
return inetAddress;
}
}
public static final class DomainName extends NonNumericInternetAddress {
private final DnsName dnsName;
private DomainName(String originalString, DnsName dnsName) {
super(originalString);
this.dnsName = dnsName;
}
public DnsName getDnsName() {
return dnsName;
}
}
public static final class DomainNameLabel extends NonNumericInternetAddress {
private final DnsLabel dnsLabel;
private DomainNameLabel(String originalString, DnsLabel dnsLabel) {
super(originalString);
this.dnsLabel = dnsLabel;
}
public DnsLabel getDnsLabel() {
return dnsLabel;
}
}
public static final class InvalidDomainName extends NonNumericInternetAddress {
private final InvalidDnsNameException invalidDnsNameException;
private InvalidDomainName(String originalString, InvalidDnsNameException invalidDnsNameException) {
super(originalString);
this.invalidDnsNameException = invalidDnsNameException;
}
public InvalidDnsNameException getInvalidDnsNameException() {
return invalidDnsNameException;
}
}
}

View file

@ -22,6 +22,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
/** /**
@ -33,7 +34,7 @@ import java.util.Set;
* @param <K> the type of the keys the map uses. * @param <K> the type of the keys the map uses.
* @param <V> the type of the values the map uses. * @param <V> the type of the values the map uses.
*/ */
public class MultiMap<K,V> { public class MultiMap<K, V> {
/** /**
* The constant value {@value}. * The constant value {@value}.
@ -57,7 +58,11 @@ public class MultiMap<K,V> {
* @param size the initial capacity. * @param size the initial capacity.
*/ */
public MultiMap(int size) { public MultiMap(int size) {
map = new LinkedHashMap<>(size); this(new LinkedHashMap<>(size));
}
private MultiMap(Map<K, List<V>> map) {
this.map = map;
} }
public int size() { public int size() {
@ -72,11 +77,11 @@ public class MultiMap<K,V> {
return map.isEmpty(); return map.isEmpty();
} }
public boolean containsKey(Object key) { public boolean containsKey(K key) {
return map.containsKey(key); return map.containsKey(key);
} }
public boolean containsValue(Object value) { public boolean containsValue(V value) {
for (List<V> list : map.values()) { for (List<V> list : map.values()) {
if (list.contains(value)) { if (list.contains(value)) {
return true; return true;
@ -91,7 +96,7 @@ public class MultiMap<K,V> {
* @param key * @param key
* @return the first value or null. * @return the first value or null.
*/ */
public V getFirst(Object key) { public V getFirst(K key) {
List<V> res = getAll(key); List<V> res = getAll(key);
if (res.isEmpty()) { if (res.isEmpty()) {
return null; return null;
@ -109,7 +114,7 @@ public class MultiMap<K,V> {
* @param key * @param key
* @return all values for the given key. * @return all values for the given key.
*/ */
public List<V> getAll(Object key) { public List<V> getAll(K key) {
List<V> res = map.get(key); List<V> res = map.get(key);
if (res == null) { if (res == null) {
res = Collections.emptyList(); res = Collections.emptyList();
@ -138,7 +143,7 @@ public class MultiMap<K,V> {
* @param key * @param key
* @return the first value of the given key or null. * @return the first value of the given key or null.
*/ */
public V remove(Object key) { public V remove(K key) {
List<V> res = map.remove(key); List<V> res = map.remove(key);
if (res == null) { if (res == null) {
return null; return null;
@ -157,7 +162,7 @@ public class MultiMap<K,V> {
* @param value * @param value
* @return true if the mapping was removed, false otherwise. * @return true if the mapping was removed, false otherwise.
*/ */
public boolean removeOne(Object key, V value) { public boolean removeOne(K key, V value) {
List<V> list = map.get(key); List<V> list = map.get(key);
if (list == null) { if (list == null) {
return false; return false;
@ -235,6 +240,18 @@ public class MultiMap<K,V> {
return entrySet; return entrySet;
} }
public MultiMap<K, V> asUnmodifiableMultiMap() {
LinkedHashMap<K, List<V>> mapCopy = new LinkedHashMap<>(map.size());
for (Entry<K, List<V>> entry : map.entrySet()) {
K key = entry.getKey();
List<V> values = entry.getValue();
mapCopy.put(key, Collections.unmodifiableList(values));
}
return new MultiMap<K, V>(Collections.unmodifiableMap(mapCopy));
}
private static final class SimpleMapEntry<K, V> implements Map.Entry<K, V> { private static final class SimpleMapEntry<K, V> implements Map.Entry<K, V> {
private final K key; private final K key;

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2015 Florian Schmaus * Copyright © 2015-2019 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.
@ -22,13 +22,41 @@ public class NumberUtil {
* Checks if the given long is within the range of an unsigned 32-bit integer, the XML type "xs:unsignedInt". * Checks if the given long is within the range of an unsigned 32-bit integer, the XML type "xs:unsignedInt".
* *
* @param value * @param value
* @deprecated use {@link #requireUInt32(long)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.5.
public static void checkIfInUInt32Range(long value) { public static void checkIfInUInt32Range(long value) {
requireUInt32(value);
}
/**
* Checks if the given long is within the range of an unsigned 32-bit integer, the XML type "xs:unsignedInt".
*
* @param value
*/
public static long requireUInt32(long value) {
if (value < 0) { if (value < 0) {
throw new IllegalArgumentException("unsigned 32-bit integers can't be negative"); throw new IllegalArgumentException("unsigned 32-bit integers can't be negative");
} }
if (value > ((1L << 32) - 1)) { if (value > ((1L << 32) - 1)) {
throw new IllegalArgumentException("unsigned 32-bit integers can't be greater then 2^32 - 1"); throw new IllegalArgumentException("unsigned 32-bit integers can't be greater then 2^32 - 1");
} }
return value;
}
/**
* Checks if the given int is within the range of an unsigned 16-bit integer, the XML type "xs:unsignedShort".
*
* @param value
*/
public static int requireUShort16(int value) {
if (value < 0) {
throw new IllegalArgumentException("unsigned 16-bit integers can't be negative");
}
if (value > ((1 << 16) - 1)) {
throw new IllegalArgumentException("unsigned 16-bit integers can't be greater than 2^16 - 1");
}
return value;
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2015-2018 Florian Schmaus * Copyright 2015-2019 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.
@ -20,9 +20,26 @@ import java.util.Collection;
public class Objects { public class Objects {
public static <T> T requireNonNull(T obj, String message) { /**
* Checks that the specified object reference is not <code>null</code> and throws a customized
* {@link IllegalArgumentException} if it is.
* <p>
* Note that unlike <code>java.util.Objects</code>, this method throws an {@link IllegalArgumentException} instead
* of an {@link NullPointerException}.
* </p>
*
* @param <T> the type of the reference.
* @param obj the object reference to check for nullity.
* @param message detail message to be used in the event that a {@link IllegalArgumentException} is thrown.
* @return <code>obj</code> if not null.
* @throws IllegalArgumentException in case <code>obj</code> is <code>null</code>.
*/
public static <T> T requireNonNull(T obj, String message) throws IllegalArgumentException {
if (obj == null) { if (obj == null) {
throw new NullPointerException(message); if (message == null) {
message = "Can not provide null argument";
}
throw new IllegalArgumentException(message);
} }
return obj; return obj;
} }

View file

@ -17,18 +17,19 @@
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.compress.packet.Compress; import org.jivesoftware.smack.compress.packet.Compress;
import org.jivesoftware.smack.packet.EmptyResultIQ; import org.jivesoftware.smack.packet.EmptyResultIQ;
import org.jivesoftware.smack.packet.ErrorIQ; import org.jivesoftware.smack.packet.ErrorIQ;
@ -49,11 +50,12 @@ import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.xmlpull.v1.XmlPullParser; import org.jxmpp.stringprep.XmppStringprepException;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
/** /**
* Utility class that helps to parse packets. Any parsing packets method that must be shared * Utility class that helps to parse packets. Any parsing packets method that must be shared
@ -64,51 +66,22 @@ import org.xmlpull.v1.XmlPullParserFactory;
public class PacketParserUtils { public class PacketParserUtils {
private static final Logger LOGGER = Logger.getLogger(PacketParserUtils.class.getName()); private static final Logger LOGGER = Logger.getLogger(PacketParserUtils.class.getName());
public static final String FEATURE_XML_ROUNDTRIP = "http://xmlpull.org/v1/doc/features.html#xml-roundtrip";
private static final XmlPullParserFactory XML_PULL_PARSER_FACTORY;
/**
* True if the XmlPullParser supports the XML_ROUNDTRIP feature.
*/
public static final boolean XML_PULL_PARSER_SUPPORTS_ROUNDTRIP;
static {
// Ensure that Smack is initialized.
SmackConfiguration.getVersion();
XmlPullParser xmlPullParser;
boolean roundtrip = false;
try {
XML_PULL_PARSER_FACTORY = XmlPullParserFactory.newInstance();
xmlPullParser = XML_PULL_PARSER_FACTORY.newPullParser();
try {
xmlPullParser.setFeature(FEATURE_XML_ROUNDTRIP, true);
// We could successfully set the feature
roundtrip = true;
} catch (XmlPullParserException e) {
// Doesn't matter if FEATURE_XML_ROUNDTRIP isn't available
LOGGER.log(Level.FINEST, "XmlPullParser does not support XML_ROUNDTRIP", e);
}
}
catch (XmlPullParserException e) {
// Something really bad happened
throw new AssertionError(e);
}
XML_PULL_PARSER_SUPPORTS_ROUNDTRIP = roundtrip;
}
// TODO: Rename argument name from 'stanza' to 'element'. // TODO: Rename argument name from 'stanza' to 'element'.
public static XmlPullParser getParserFor(String stanza) throws XmlPullParserException, IOException { public static XmlPullParser getParserFor(String stanza) throws XmlPullParserException, IOException {
return getParserFor(new StringReader(stanza)); return getParserFor(new StringReader(stanza));
} }
public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
return SmackXmlParser.newXmlParser(inputStreamReader);
}
public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException { public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException {
XmlPullParser parser = newXmppParser(reader); XmlPullParser parser = SmackXmlParser.newXmlParser(reader);
// Wind the parser forward to the first start tag // Wind the parser forward to the first start tag
int event = parser.getEventType(); XmlPullParser.Event event = parser.getEventType();
while (event != XmlPullParser.START_TAG) { while (event != XmlPullParser.Event.START_ELEMENT) {
if (event == XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.Event.END_DOCUMENT) {
throw new IllegalArgumentException("Document contains no start tag"); throw new IllegalArgumentException("Document contains no start tag");
} }
event = parser.next(); event = parser.next();
@ -116,28 +89,8 @@ public class PacketParserUtils {
return parser; return parser;
} }
public static XmlPullParser getParserFor(String stanza, String startTag)
throws XmlPullParserException, IOException {
XmlPullParser parser = getParserFor(stanza);
while (true) {
int event = parser.getEventType();
String name = parser.getName();
if (event == XmlPullParser.START_TAG && name.equals(startTag)) {
break;
}
else if (event == XmlPullParser.END_DOCUMENT) {
throw new IllegalArgumentException("Could not find start tag '" + startTag
+ "' in stanza: " + stanza);
}
parser.next();
}
return parser;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <S extends Stanza> S parseStanza(String stanza) throws Exception { public static <S extends Stanza> S parseStanza(String stanza) throws XmlPullParserException, SmackParsingException, IOException {
return (S) parseStanza(getParserFor(stanza), null); return (S) parseStanza(getParserFor(stanza), null);
} }
@ -149,9 +102,11 @@ public class PacketParserUtils {
* @param parser * @param parser
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return a stanza which is either a Message, IQ or Presence. * @return a stanza which is either a Message, IQ or Presence.
* @throws Exception * @throws XmlPullParserException
* @throws SmackParsingException
* @throws IOException
*/ */
public static Stanza parseStanza(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws Exception { public static Stanza parseStanza(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, SmackParsingException, IOException {
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);
final String name = parser.getName(); final String name = parser.getName();
switch (name) { switch (name) {
@ -166,53 +121,6 @@ public class PacketParserUtils {
} }
} }
/**
* Creates a new XmlPullParser suitable for parsing XMPP. This means in particular that
* FEATURE_PROCESS_NAMESPACES is enabled.
* <p>
* Note that not all XmlPullParser implementations will return a String on
* <code>getText()</code> if the parser is on START_TAG or END_TAG. So you must not rely on this
* behavior when using the parser.
* </p>
*
* @return A suitable XmlPullParser for XMPP parsing
* @throws XmlPullParserException
*/
public static XmlPullParser newXmppParser() throws XmlPullParserException {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
if (XML_PULL_PARSER_SUPPORTS_ROUNDTRIP) {
try {
parser.setFeature(FEATURE_XML_ROUNDTRIP, true);
}
catch (XmlPullParserException e) {
LOGGER.log(Level.SEVERE,
"XmlPullParser does not support XML_ROUNDTRIP, although it was first determined to be supported",
e);
}
}
return parser;
}
/**
* Creates a new XmlPullParser suitable for parsing XMPP. This means in particular that
* FEATURE_PROCESS_NAMESPACES is enabled.
* <p>
* Note that not all XmlPullParser implementations will return a String on
* <code>getText()</code> if the parser is on START_TAG or END_TAG. So you must not rely on this
* behavior when using the parser.
* </p>
*
* @param reader
* @return A suitable XmlPullParser for XMPP parsing
* @throws XmlPullParserException
*/
public static XmlPullParser newXmppParser(Reader reader) throws XmlPullParserException {
XmlPullParser parser = newXmppParser();
parser.setInput(reader);
return parser;
}
public static Message parseMessage(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException { public static Message parseMessage(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException {
return parseMessage(parser, null); return parseMessage(parser, null);
} }
@ -249,9 +157,9 @@ public class PacketParserUtils {
// in arbitrary sub-elements. // in arbitrary sub-elements.
String thread = null; String thread = null;
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String elementName = parser.getName(); String elementName = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
switch (elementName) { switch (elementName) {
@ -276,11 +184,12 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default: // fall out
} }
} }
@ -295,9 +204,9 @@ public class PacketParserUtils {
/** /**
* Returns the textual content of an element as String. After this method returns the parser * Returns the textual content of an element as String. After this method returns the parser
* position will be END_TAG, following the established pull parser calling convention. * position will be END_ELEMENT, following the established pull parser calling convention.
* <p> * <p>
* The parser must be positioned on a START_TAG of an element which MUST NOT contain Mixed * The parser must be positioned on a START_ELEMENT of an element which MUST NOT contain Mixed
* Content (as defined in XML 3.2.2), or else an XmlPullParserException will be thrown. * Content (as defined in XML 3.2.2), or else an XmlPullParserException will be thrown.
* </p> * </p>
* This method is used for the parts where the XMPP specification requires elements that contain * This method is used for the parts where the XMPP specification requires elements that contain
@ -309,41 +218,36 @@ public class PacketParserUtils {
* @throws IOException * @throws IOException
*/ */
public static String parseElementText(XmlPullParser parser) throws XmlPullParserException, IOException { public static String parseElementText(XmlPullParser parser) throws XmlPullParserException, IOException {
assert (parser.getEventType() == XmlPullParser.START_TAG); assert (parser.getEventType() == XmlPullParser.Event.START_ELEMENT);
String res; String res;
if (parser.isEmptyElementTag()) { // Advance to the text of the Element
res = ""; XmlPullParser.Event event = parser.next();
} if (event != XmlPullParser.Event.TEXT_CHARACTERS) {
else { if (event == XmlPullParser.Event.END_ELEMENT) {
// Advance to the text of the Element // Assume this is the end tag of the start tag at the
int event = parser.next(); // beginning of this method. Typical examples where this
if (event != XmlPullParser.TEXT) { // happens are body elements containing the empty string,
if (event == XmlPullParser.END_TAG) { // ie. <body></body>, which appears to be valid XMPP, or a
// Assume this is the end tag of the start tag at the // least it's not explicitly forbidden by RFC 6121 5.2.3
// beginning of this method. Typical examples where this return "";
// happens are body elements containing the empty string, } else {
// ie. <body></body>, which appears to be valid XMPP, or a
// least it's not explicitly forbidden by RFC 6121 5.2.3
return "";
} else {
throw new XmlPullParserException(
"Non-empty element tag not followed by text, while Mixed Content (XML 3.2.2) is disallowed");
}
}
res = parser.getText();
event = parser.next();
if (event != XmlPullParser.END_TAG) {
throw new XmlPullParserException( throw new XmlPullParserException(
"Non-empty element tag contains child-elements, while Mixed Content (XML 3.2.2) is disallowed"); "Non-empty element tag not followed by text, while Mixed Content (XML 3.2.2) is disallowed");
} }
} }
res = parser.getText();
event = parser.next();
if (event != XmlPullParser.Event.END_ELEMENT) {
throw new XmlPullParserException(
"Non-empty element tag contains child-elements, while Mixed Content (XML 3.2.2) is disallowed");
}
return res; return res;
} }
/** /**
* Returns the current element as string. * Returns the current element as string.
* <p> * <p>
* The parser must be positioned on START_TAG. * The parser must be positioned on START_ELEMENT.
* </p> * </p>
* Note that only the outermost namespace attributes ("xmlns") will be returned, not nested ones. * Note that only the outermost namespace attributes ("xmlns") will be returned, not nested ones.
* *
@ -359,37 +263,10 @@ public class PacketParserUtils {
public static CharSequence parseElement(XmlPullParser parser, public static CharSequence parseElement(XmlPullParser parser,
boolean fullNamespaces) throws XmlPullParserException, boolean fullNamespaces) throws XmlPullParserException,
IOException { IOException {
assert (parser.getEventType() == XmlPullParser.START_TAG); assert (parser.getEventType() == XmlPullParser.Event.START_ELEMENT);
return parseContentDepth(parser, parser.getDepth(), fullNamespaces); return parseContentDepth(parser, parser.getDepth(), fullNamespaces);
} }
/**
* Returns the content of a element.
* <p>
* The parser must be positioned on the START_TAG of the element which content is going to get
* returned. If the current element is the empty element, then the empty string is returned. If
* it is a element which contains just text, then just the text is returned. If it contains
* nested elements (and text), then everything from the current opening tag to the corresponding
* closing tag of the same depth is returned as String.
* </p>
* Note that only the outermost namespace attributes ("xmlns") will be returned, not nested ones.
*
* @param parser the XML pull parser
* @return the content of a tag
* @throws XmlPullParserException if parser encounters invalid XML
* @throws IOException if an IO error occurs
*/
public static CharSequence parseContent(XmlPullParser parser)
throws XmlPullParserException, IOException {
assert (parser.getEventType() == XmlPullParser.START_TAG);
if (parser.isEmptyElementTag()) {
return "";
}
// Advance the parser, since we want to parse the content of the current element
parser.next();
return parseContentDepth(parser, parser.getDepth(), false);
}
public static CharSequence parseContentDepth(XmlPullParser parser, int depth) public static CharSequence parseContentDepth(XmlPullParser parser, int depth)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
return parseContentDepth(parser, depth, false); return parseContentDepth(parser, depth, false);
@ -402,7 +279,7 @@ public class PacketParserUtils {
* parent elements will be added to child elements that don't define a different namespace. * parent elements will be added to child elements that don't define a different namespace.
* <p> * <p>
* This method is able to parse the content with MX- and KXmlParser. KXmlParser does not support * This method is able to parse the content with MX- and KXmlParser. KXmlParser does not support
* xml-roundtrip. i.e. return a String on getText() on START_TAG and END_TAG. We check for the * xml-roundtrip. i.e. return a String on getText() on START_ELEMENT and END_ELEMENT. We check for the
* XML_ROUNDTRIP feature. If it's not found we are required to work around this limitation, which * XML_ROUNDTRIP feature. If it's not found we are required to work around this limitation, which
* results in only partial support for XML namespaces ("xmlns"): Only the outermost namespace of * results in only partial support for XML namespaces ("xmlns"): Only the outermost namespace of
* elements will be included in the resulting String, if <code>fullNamespaces</code> is set to false. * elements will be included in the resulting String, if <code>fullNamespaces</code> is set to false.
@ -419,7 +296,7 @@ public class PacketParserUtils {
* @throws IOException * @throws IOException
*/ */
public static CharSequence parseContentDepth(XmlPullParser parser, int depth, boolean fullNamespaces) throws XmlPullParserException, IOException { public static CharSequence parseContentDepth(XmlPullParser parser, int depth, boolean fullNamespaces) throws XmlPullParserException, IOException {
if (parser.getFeature(FEATURE_XML_ROUNDTRIP)) { if (parser.supportsRoundtrip()) {
return parseContentDepthWithRoundtrip(parser, depth, fullNamespaces); return parseContentDepthWithRoundtrip(parser, depth, fullNamespaces);
} else { } else {
return parseContentDepthWithoutRoundtrip(parser, depth, fullNamespaces); return parseContentDepthWithoutRoundtrip(parser, depth, fullNamespaces);
@ -429,15 +306,21 @@ public class PacketParserUtils {
private static CharSequence parseContentDepthWithoutRoundtrip(XmlPullParser parser, int depth, private static CharSequence parseContentDepthWithoutRoundtrip(XmlPullParser parser, int depth,
boolean fullNamespaces) throws XmlPullParserException, IOException { boolean fullNamespaces) throws XmlPullParserException, IOException {
XmlStringBuilder xml = new XmlStringBuilder(); XmlStringBuilder xml = new XmlStringBuilder();
int event = parser.getEventType(); XmlPullParser.Event event = parser.getEventType();
boolean isEmptyElement = false;
// XmlPullParser reports namespaces in nested elements even if *only* the outer ones defines // XmlPullParser reports namespaces in nested elements even if *only* the outer ones defines
// it. This 'flag' ensures that when a namespace is set for an element, it won't be set again // it. This 'flag' ensures that when a namespace is set for an element, it won't be set again
// in a nested element. It's an ugly workaround that has the potential to break things. // in a nested element. It's an ugly workaround that has the potential to break things.
String namespaceElement = null; String namespaceElement = null;
boolean startElementJustSeen = false;
outerloop: while (true) { outerloop: while (true) {
switch (event) { switch (event) {
case XmlPullParser.START_TAG: case START_ELEMENT:
if (startElementJustSeen) {
xml.rightAngleBracket();
}
else {
startElementJustSeen = true;
}
xml.halfOpenElement(parser.getName()); xml.halfOpenElement(parser.getName());
if (namespaceElement == null || fullNamespaces) { if (namespaceElement == null || fullNamespaces) {
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
@ -449,18 +332,11 @@ public class PacketParserUtils {
for (int i = 0; i < parser.getAttributeCount(); i++) { for (int i = 0; i < parser.getAttributeCount(); i++) {
xml.attribute(parser.getAttributeName(i), parser.getAttributeValue(i)); xml.attribute(parser.getAttributeName(i), parser.getAttributeValue(i));
} }
if (parser.isEmptyElementTag()) {
xml.closeEmptyElement();
isEmptyElement = true;
}
else {
xml.rightAngleBracket();
}
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (isEmptyElement) { if (startElementJustSeen) {
// Do nothing as the element was already closed, just reset the flag xml.closeEmptyElement();
isEmptyElement = false; startElementJustSeen = false;
} }
else { else {
xml.closeElement(parser.getName()); xml.closeElement(parser.getName());
@ -474,31 +350,58 @@ public class PacketParserUtils {
break outerloop; break outerloop;
} }
break; break;
case XmlPullParser.TEXT: case TEXT_CHARACTERS:
if (startElementJustSeen) {
startElementJustSeen = false;
xml.rightAngleBracket();
}
xml.escape(parser.getText()); xml.escape(parser.getText());
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
event = parser.next(); event = parser.next();
} }
return xml; return xml;
} }
@SuppressWarnings("UnusedVariable")
private static CharSequence parseContentDepthWithRoundtrip(XmlPullParser parser, int depth, boolean fullNamespaces) private static CharSequence parseContentDepthWithRoundtrip(XmlPullParser parser, int depth, boolean fullNamespaces)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
StringBuilder sb = new StringBuilder(); XmlStringBuilder sb = new XmlStringBuilder();
int event = parser.getEventType(); XmlPullParser.Event event = parser.getEventType();
boolean startElementJustSeen = false;
outerloop: while (true) { outerloop: while (true) {
// Only append the text if the parser is not on on an empty element' start tag. Empty elements are reported switch (event) {
// twice, so in order to prevent duplication we only add their text when we are on their end tag. case START_ELEMENT:
if (!(event == XmlPullParser.START_TAG && parser.isEmptyElementTag())) { if (startElementJustSeen) {
sb.rightAngleBracket();
}
startElementJustSeen = true;
break;
case END_ELEMENT:
boolean isEmptyElement = false;
if (startElementJustSeen) {
isEmptyElement = true;
startElementJustSeen = false;
}
if (!isEmptyElement) {
String text = parser.getText();
sb.append(text);
}
if (parser.getDepth() <= depth) {
break outerloop;
}
break;
default:
startElementJustSeen = false;
CharSequence text = parser.getText(); CharSequence text = parser.getText();
if (event == XmlPullParser.TEXT) { if (event == XmlPullParser.Event.TEXT_CHARACTERS) {
text = StringUtils.escapeForXmlText(text); text = StringUtils.escapeForXml(text);
} }
sb.append(text); sb.append(text);
} break;
if (event == XmlPullParser.END_TAG && parser.getDepth() <= depth) {
break outerloop;
} }
event = parser.next(); event = parser.next();
} }
@ -536,16 +439,14 @@ public class PacketParserUtils {
String language = ParserUtils.getXmlLang(parser); String language = ParserUtils.getXmlLang(parser);
if (language != null && !"".equals(language.trim())) { if (language != null && !"".equals(language.trim())) {
// CHECKSTYLE:OFF presence.setLanguage(language);
presence.setLanguage(language);
// CHECKSTYLE:ON
} }
// Parse sub-elements // Parse sub-elements
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String elementName = parser.getName(); String elementName = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
switch (elementName) { switch (elementName) {
@ -553,7 +454,7 @@ public class PacketParserUtils {
presence.setStatus(parser.nextText()); presence.setStatus(parser.nextText());
break; break;
case "priority": case "priority":
int priority = Integer.parseInt(parser.nextText()); Byte priority = ParserUtils.getByteAttributeFromNextText(parser);
presence.setPriority(priority); presence.setPriority(priority);
break; break;
case "show": case "show":
@ -587,11 +488,14 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
return presence; return presence;
@ -607,9 +511,12 @@ public class PacketParserUtils {
* @param parser the XML parser, positioned at the start of an IQ packet. * @param parser the XML parser, positioned at the start of an IQ packet.
* @param outerXmlEnvironment the outer XML environment (optional). * @param outerXmlEnvironment the outer XML environment (optional).
* @return an IQ object. * @return an IQ object.
* @throws Exception * @throws XmlPullParserException
* @throws XmppStringprepException
* @throws IOException
* @throws SmackParsingException
*/ */
public static IQ parseIQ(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws Exception { public static IQ parseIQ(XmlPullParser parser, XmlEnvironment outerXmlEnvironment) throws XmlPullParserException, XmppStringprepException, IOException, SmackParsingException {
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
@ -622,10 +529,10 @@ public class PacketParserUtils {
final IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type")); final IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String elementName = parser.getName(); String elementName = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
switch (elementName) { switch (elementName) {
@ -640,7 +547,7 @@ public class PacketParserUtils {
iqPacket = provider.parse(parser, outerXmlEnvironment); iqPacket = provider.parse(parser, outerXmlEnvironment);
} }
// Note that if we reach this code, it is guranteed that the result IQ contained a child element // Note that if we reach this code, it is guranteed that the result IQ contained a child element
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_TAG first. // (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_ELEMENT first.
else { else {
// No Provider found for the IQ stanza, parse it to an UnparsedIQ instance // No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
// so that the content of the IQ can be examined later on // so that the content of the IQ can be examined later on
@ -649,11 +556,14 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
// Decide what to do when an IQ packet was not understood // Decide what to do when an IQ packet was not understood
@ -694,15 +604,15 @@ public class PacketParserUtils {
List<String> mechanisms = new ArrayList<String>(); List<String> mechanisms = new ArrayList<String>();
boolean done = false; boolean done = false;
while (!done) { while (!done) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) { if (eventType == XmlPullParser.Event.START_ELEMENT) {
String elementName = parser.getName(); String elementName = parser.getName();
if (elementName.equals("mechanism")) { if (elementName.equals("mechanism")) {
mechanisms.add(parser.nextText()); mechanisms.add(parser.nextText());
} }
} }
else if (eventType == XmlPullParser.END_TAG) { else if (eventType == XmlPullParser.Event.END_ELEMENT) {
if (parser.getName().equals("mechanisms")) { if (parser.getName().equals("mechanisms")) {
done = true; done = true;
} }
@ -721,14 +631,14 @@ public class PacketParserUtils {
*/ */
public static Compress.Feature parseCompressionFeature(XmlPullParser parser) public static Compress.Feature parseCompressionFeature(XmlPullParser parser)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
assert (parser.getEventType() == XmlPullParser.START_TAG); assert (parser.getEventType() == XmlPullParser.Event.START_ELEMENT);
String name; String name;
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
List<String> methods = new LinkedList<>(); List<String> methods = new LinkedList<>();
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
name = parser.getName(); name = parser.getName();
switch (name) { switch (name) {
case "method": case "method":
@ -736,7 +646,7 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
name = parser.getName(); name = parser.getName();
switch (name) { switch (name) {
case Compress.Feature.ELEMENT: case Compress.Feature.ELEMENT:
@ -744,9 +654,13 @@ public class PacketParserUtils {
break outerloop; break outerloop;
} }
} }
break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
assert (parser.getEventType() == XmlPullParser.END_TAG); assert (parser.getEventType() == XmlPullParser.Event.END_ELEMENT);
assert (parser.getDepth() == initialDepth); assert (parser.getDepth() == initialDepth);
return new Compress.Feature(methods); return new Compress.Feature(methods);
} }
@ -782,9 +696,9 @@ public class PacketParserUtils {
String condition = null; String condition = null;
Map<String, String> descriptiveTexts = null; Map<String, String> descriptiveTexts = null;
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
if (name.equals("text")) { if (name.equals("text")) {
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts); descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
@ -794,11 +708,14 @@ public class PacketParserUtils {
condition = parser.getName(); condition = parser.getName();
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
return new SASLFailure(condition, descriptiveTexts); return new SASLFailure(condition, descriptiveTexts);
@ -826,9 +743,9 @@ public class PacketParserUtils {
String conditionText = null; String conditionText = null;
XmlEnvironment streamErrorXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); XmlEnvironment streamErrorXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
switch (namespace) { switch (namespace) {
@ -841,8 +758,9 @@ public class PacketParserUtils {
// If it's not a text element, that is qualified by the StreamError.NAMESPACE, // If it's not a text element, that is qualified by the StreamError.NAMESPACE,
// then it has to be the stream error code // then it has to be the stream error code
condition = StreamError.Condition.fromString(name); condition = StreamError.Condition.fromString(name);
if (!parser.isEmptyElementTag()) { conditionText = parser.nextText();
conditionText = parser.nextText(); if (conditionText.isEmpty()) {
conditionText = null;
} }
break; break;
} }
@ -852,11 +770,14 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break; break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
return new StreamError(condition, conditionText, descriptiveTexts, extensions); return new StreamError(condition, conditionText, descriptiveTexts, extensions);
@ -888,9 +809,9 @@ public class PacketParserUtils {
builder.setErrorGenerator(parser.getAttributeValue("", "by")); builder.setErrorGenerator(parser.getAttributeValue("", "by"));
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
String namespace = parser.getNamespace(); String namespace = parser.getNamespace();
switch (namespace) { switch (namespace) {
@ -901,8 +822,9 @@ public class PacketParserUtils {
break; break;
default: default:
builder.setCondition(StanzaError.Condition.fromString(name)); builder.setCondition(StanzaError.Condition.fromString(name));
if (!parser.isEmptyElementTag()) { String conditionText = parser.nextText();
builder.setConditionText(parser.nextText()); if (!conditionText.isEmpty()) {
builder.setConditionText(conditionText);
} }
break; break;
} }
@ -911,10 +833,14 @@ public class PacketParserUtils {
PacketParserUtils.addExtensionElement(extensions, parser, name, namespace, stanzaErrorXmlEnvironment); PacketParserUtils.addExtensionElement(extensions, parser, name, namespace, stanzaErrorXmlEnvironment);
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
builder.setExtensions(extensions).setDescriptiveTexts(descriptiveTexts); builder.setExtensions(extensions).setDescriptiveTexts(descriptiveTexts);
@ -949,14 +875,14 @@ public class PacketParserUtils {
public static StartTls parseStartTlsFeature(XmlPullParser parser) public static StartTls parseStartTlsFeature(XmlPullParser parser)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
assert (parser.getEventType() == XmlPullParser.START_TAG); assert (parser.getEventType() == XmlPullParser.Event.START_ELEMENT);
assert (parser.getNamespace().equals(StartTls.NAMESPACE)); assert (parser.getNamespace().equals(StartTls.NAMESPACE));
int initalDepth = parser.getDepth(); int initalDepth = parser.getDepth();
boolean required = false; boolean required = false;
outerloop: while (true) { outerloop: while (true) {
int event = parser.next(); XmlPullParser.Event event = parser.next();
switch (event) { switch (event) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
switch (name) { switch (name) {
case "required": case "required":
@ -964,13 +890,17 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initalDepth) { if (parser.getDepth() == initalDepth) {
break outerloop; break outerloop;
} }
break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }
assert (parser.getEventType() == XmlPullParser.END_TAG); assert (parser.getEventType() == XmlPullParser.Event.END_ELEMENT);
return new StartTls(required); return new StartTls(required);
} }
@ -978,14 +908,11 @@ public class PacketParserUtils {
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
boolean optional = false; boolean optional = false;
if (parser.isEmptyElementTag()) {
return new Session.Feature(optional);
}
outerloop: while (true) { outerloop: while (true) {
int event = parser.next(); XmlPullParser.Event event = parser.next();
switch (event) { switch (event) {
case XmlPullParser.START_TAG: case START_ELEMENT:
String name = parser.getName(); String name = parser.getName();
switch (name) { switch (name) {
case Session.Feature.OPTIONAL_ELEMENT: case Session.Feature.OPTIONAL_ELEMENT:
@ -993,10 +920,14 @@ public class PacketParserUtils {
break; break;
} }
break; break;
case XmlPullParser.END_TAG: case END_ELEMENT:
if (parser.getDepth() == initialDepth) { if (parser.getDepth() == initialDepth) {
break outerloop; break outerloop;
} }
break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
} }
} }

View file

@ -23,12 +23,15 @@ import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.datatypes.UInt32;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.parsing.SmackParsingException.SmackTextParseException; import org.jivesoftware.smack.parsing.SmackParsingException.SmackTextParseException;
import org.jivesoftware.smack.parsing.SmackParsingException.SmackUriSyntaxParsingException; import org.jivesoftware.smack.parsing.SmackParsingException.SmackUriSyntaxParsingException;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityFullJid;
@ -38,8 +41,6 @@ import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
import org.jxmpp.util.XmppDateTime; import org.jxmpp.util.XmppDateTime;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class ParserUtils { public class ParserUtils {
@ -49,7 +50,7 @@ public class ParserUtils {
public static final String JID = "jid"; public static final String JID = "jid";
public static void assertAtStartTag(XmlPullParser parser) throws XmlPullParserException { public static void assertAtStartTag(XmlPullParser parser) throws XmlPullParserException {
assert (parser.getEventType() == XmlPullParser.START_TAG); assert (parser.getEventType() == XmlPullParser.Event.START_ELEMENT);
} }
public static void assertAtStartTag(XmlPullParser parser, String name) throws XmlPullParserException { public static void assertAtStartTag(XmlPullParser parser, String name) throws XmlPullParserException {
@ -58,13 +59,13 @@ public class ParserUtils {
} }
public static void assertAtEndTag(XmlPullParser parser) throws XmlPullParserException { public static void assertAtEndTag(XmlPullParser parser) throws XmlPullParserException {
assert (parser.getEventType() == XmlPullParser.END_TAG); assert (parser.getEventType() == XmlPullParser.Event.END_ELEMENT);
} }
public static void forwardToEndTagOfDepth(XmlPullParser parser, int depth) public static void forwardToEndTagOfDepth(XmlPullParser parser, int depth)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
int event = parser.getEventType(); XmlPullParser.Event event = parser.getEventType();
while (!(event == XmlPullParser.END_TAG && parser.getDepth() == depth)) { while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) {
event = parser.next(); event = parser.next();
} }
} }
@ -178,6 +179,11 @@ public class ParserUtils {
} }
} }
public static Byte getByteAttributeFromNextText(XmlPullParser parser) throws IOException, XmlPullParserException {
String nextText = parser.nextText();
return Byte.valueOf(nextText);
}
public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage) public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage)
throws IOException { throws IOException {
Integer res = getIntegerAttribute(parser, name); Integer res = getIntegerAttribute(parser, name);
@ -205,6 +211,14 @@ public class ParserUtils {
} }
} }
public static UInt16 getUInt16Attribute(XmlPullParser parser, String name) {
Integer integer = getIntegerAttribute(parser, name);
if (integer == null) {
return null;
}
return UInt16.from(integer);
}
public static int getIntegerFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException { public static int getIntegerFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
String intString = parser.nextText(); String intString = parser.nextText();
return Integer.valueOf(intString); return Integer.valueOf(intString);
@ -227,6 +241,14 @@ public class ParserUtils {
} }
} }
public static UInt32 getUInt32Attribute(XmlPullParser parser, String name) {
Long l = getLongAttribute(parser, name);
if (l == null) {
return null;
}
return UInt32.from(l);
}
public static double getDoubleFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException { public static double getDoubleFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
String doubleString = parser.nextText(); String doubleString = parser.nextText();
return Double.valueOf(doubleString); return Double.valueOf(doubleString);
@ -323,13 +345,16 @@ public class ParserUtils {
return parser.getAttributeValue("http://www.w3.org/XML/1998/namespace", "lang"); return parser.getAttributeValue("http://www.w3.org/XML/1998/namespace", "lang");
} }
/**
* Get the QName of the current element.
*
* @param parser the parser.
* @return the Qname.
* @deprecated use {@link XmlPullParser#getQName()} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5
public static QName getQName(XmlPullParser parser) { public static QName getQName(XmlPullParser parser) {
String elementName = parser.getName(); return parser.getQName();
String prefix = parser.getPrefix();
if (prefix == null) {
prefix = XMLConstants.DEFAULT_NS_PREFIX;
}
String namespace = parser.getNamespace();
return new QName(namespace, elementName, prefix);
} }
} }

View file

@ -17,7 +17,9 @@
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import java.io.UnsupportedEncodingException; import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.Random; import java.util.Random;
@ -29,7 +31,23 @@ public class StringUtils {
public static final String MD5 = "MD5"; public static final String MD5 = "MD5";
public static final String SHA1 = "SHA-1"; public static final String SHA1 = "SHA-1";
/**
* Deprecated, do not use.
*
* @deprecated use StandardCharsets.UTF_8 instead.
*/
// TODO: Remove in Smack 4.5.
@Deprecated
public static final String UTF8 = "UTF-8"; public static final String UTF8 = "UTF-8";
/**
* Deprecated, do not use.
*
* @deprecated use StandardCharsets.US_ASCII instead.
*/
// TODO: Remove in Smack 4.5.
@Deprecated
public static final String USASCII = "US-ASCII"; public static final String USASCII = "US-ASCII";
public static final String QUOTE_ENCODE = "&quot;"; public static final String QUOTE_ENCODE = "&quot;";
@ -244,22 +262,13 @@ public class StringUtils {
} }
public static byte[] toUtf8Bytes(String string) { public static byte[] toUtf8Bytes(String string) {
try { return string.getBytes(StandardCharsets.UTF_8);
return string.getBytes(StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 encoding not supported by platform", e);
}
} }
/** /**
* Array of numbers and letters of mixed case. Numbers appear in the list * 24 upper case characters from the latin alphabet and numbers without '0' and 'O'.
* twice so that there is a more equal chance that a number will be picked.
* We can use the array to get a random number or letter by picking a random
* array index.
*/ */
private static final char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + private static final char[] UNAMBIGUOUS_NUMBERS_AND_LETTER = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
"ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
/** /**
* Returns a random String of numbers and letters (lower and upper case) * Returns a random String of numbers and letters (lower and upper case)
@ -278,6 +287,74 @@ public class StringUtils {
return randomString(length, RandomUtil.RANDOM.get()); return randomString(length, RandomUtil.RANDOM.get());
} }
public static String secureOnlineAttackSafeRandomString() {
// 34^10 = 2.06e15 possible combinations. Which is enough to protect against online brute force attacks.
// See also https://www.grc.com/haystack.htm
final int REQUIRED_LENGTH = 10;
return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTER, REQUIRED_LENGTH);
}
public static String secureUniqueRandomString() {
// 34^13 = 8.11e19 possible combinations, which is > 2^64.
final int REQUIRED_LENGTH = 13;
return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTER, REQUIRED_LENGTH);
}
/**
* Generate a secure random string with is human readable. The resulting string consists of 24 upper case characters
* from the Latin alphabet and numbers without '0' and 'O', grouped into 4-characters chunks, e.g.
* "TWNK-KD5Y-MT3T-E1GS-DRDB-KVTW". The characters are randomly selected by a cryptographically secure pseudorandom
* number generator (CSPRNG).
* <p>
* The string can be used a backup "code" for secrets, and is in fact the same as the one backup code specified in
* XEP-0373 and the one used by the <a href="https://github.com/open-keychain/open-keychain/wiki/Backups">Backup
* Format v2 of OpenKeychain</a>.
* </p>
*
* @see <a href="https://xmpp.org/extensions/xep-0373.html#backup-encryption"> XEP-0373 §5.4 Encrypting the Secret
* Key Backup</a>
* @return a human readable secure random string.
*/
public static String secureOfflineAttackSafeRandomString() {
// 34^24 = 2^122.10 possible combinations. Which is enough to protect against offline brute force attacks.
// See also https://www.grc.com/haystack.htm
final int REQUIRED_LENGTH = 24;
return randomString(RandomUtil.SECURE_RANDOM.get(), UNAMBIGUOUS_NUMBERS_AND_LETTER, REQUIRED_LENGTH);
}
private static final int RANDOM_STRING_CHUNK_SIZE = 4;
private static String randomString(Random random, char[] alphabet, int numRandomChars) {
// The buffer most hold the size of the requested number of random chars and the chunk separators ('-').
int bufferSize = numRandomChars + ((numRandomChars - 1) / RANDOM_STRING_CHUNK_SIZE);
CharBuffer charBuffer = CharBuffer.allocate(bufferSize);
try {
randomString(charBuffer, random, alphabet, numRandomChars);
} catch (IOException e) {
// This should never happen if we calcuate the buffer size correctly.
throw new AssertionError(e);
}
return charBuffer.flip().toString();
}
private static void randomString(Appendable appendable, Random random, char[] alphabet, int numRandomChars)
throws IOException {
for (int randomCharNum = 1; randomCharNum <= numRandomChars; randomCharNum++) {
int randomIndex = random.nextInt(alphabet.length);
char randomChar = alphabet[randomIndex];
appendable.append(randomChar);
if (randomCharNum % RANDOM_STRING_CHUNK_SIZE == 0 && randomCharNum < numRandomChars) {
appendable.append('-');
}
}
}
public static String randomString(final int length) { public static String randomString(final int length) {
return randomString(length, RandomUtil.SECURE_RANDOM.get()); return randomString(length, RandomUtil.SECURE_RANDOM.get());
} }
@ -287,24 +364,14 @@ public class StringUtils {
return ""; return "";
} }
byte[] randomBytes = new byte[length];
random.nextBytes(randomBytes);
char[] randomChars = new char[length]; char[] randomChars = new char[length];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
randomChars[i] = getPrintableChar(randomBytes[i]); int index = random.nextInt(UNAMBIGUOUS_NUMBERS_AND_LETTER.length);
randomChars[i] = UNAMBIGUOUS_NUMBERS_AND_LETTER[index];
} }
return new String(randomChars); return new String(randomChars);
} }
private static char getPrintableChar(byte indexByte) {
assert (numbersAndLetters.length < Byte.MAX_VALUE * 2);
// Convert indexByte as it where an unsigned byte by promoting it to int
// and masking it with 0xff. Yields results from 0 - 254.
int index = indexByte & 0xff;
return numbersAndLetters[index % numbersAndLetters.length];
}
/** /**
* Returns true if CharSequence is not null and is not empty, false otherwise. * Returns true if CharSequence is not null and is not empty, false otherwise.
* Examples: * Examples:

View file

@ -64,7 +64,7 @@ public class TLSUtils {
* *
* @return the given builder * @return the given builder
*/ */
public static <B extends ConnectionConfiguration.Builder<B,?>> B setTLSOnly(B builder) { public static <B extends ConnectionConfiguration.Builder<B, ?>> B setTLSOnly(B builder) {
builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1 }); builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1 });
return builder; return builder;
} }
@ -84,7 +84,7 @@ public class TLSUtils {
* *
* @return the given builder * @return the given builder
*/ */
public static <B extends ConnectionConfiguration.Builder<B,?>> B setSSLv3AndTLSOnly(B builder) { public static <B extends ConnectionConfiguration.Builder<B, ?>> B setSSLv3AndTLSOnly(B builder) {
builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1, PROTO_SSL3 }); builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1, PROTO_SSL3 });
return builder; return builder;
} }
@ -103,7 +103,7 @@ public class TLSUtils {
* @throws KeyManagementException * @throws KeyManagementException
* @return the given builder. * @return the given builder.
*/ */
public static <B extends ConnectionConfiguration.Builder<B,?>> B acceptAllCertificates(B builder) throws NoSuchAlgorithmException, KeyManagementException { public static <B extends ConnectionConfiguration.Builder<B, ?>> B acceptAllCertificates(B builder) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext context = SSLContext.getInstance(TLS); SSLContext context = SSLContext.getInstance(TLS);
context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom()); context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom());
builder.setCustomSSLContext(context); builder.setCustomSSLContext(context);
@ -130,7 +130,7 @@ public class TLSUtils {
* @param <B> Type of the ConnectionConfiguration builder. * @param <B> Type of the ConnectionConfiguration builder.
* @return the given builder. * @return the given builder.
*/ */
public static <B extends ConnectionConfiguration.Builder<B,?>> B disableHostnameVerificationForTlsCertificates(B builder) { public static <B extends ConnectionConfiguration.Builder<B, ?>> B disableHostnameVerificationForTlsCertificates(B builder) {
builder.setHostnameVerifier(DOES_NOT_VERIFY_VERIFIER); builder.setHostnameVerifier(DOES_NOT_VERIFY_VERIFIER);
return builder; return builder;
} }

View file

@ -269,10 +269,20 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
public XmlStringBuilder attribute(String name, Enum<?> value) { public XmlStringBuilder attribute(String name, Enum<?> value) {
assert value != null; assert value != null;
// TODO: Should use toString() instead of name().
attribute(name, value.name()); attribute(name, value.name());
return this; return this;
} }
public <E extends Enum<?>> XmlStringBuilder attribute(String name, E value, E implicitDefault) {
if (value == null || value == implicitDefault) {
return this;
}
attribute(name, value.toString());
return this;
}
public XmlStringBuilder attribute(String name, int value) { public XmlStringBuilder attribute(String name, int value) {
assert name != null; assert name != null;
return attribute(name, String.valueOf(value)); return attribute(name, String.valueOf(value));
@ -327,6 +337,13 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
return this; return this;
} }
public XmlStringBuilder optAttribute(String name, Number number) {
if (number != null) {
attribute(name, number.toString());
}
return this;
}
/** /**
* Add the given attribute if {@code value => 0}. * Add the given attribute if {@code value => 0}.
* *

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Florian Schmaus * Copyright 2018-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,13 @@
*/ */
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import org.jivesoftware.smack.packet.FullyQualifiedElement; import javax.xml.namespace.QName;
import org.jxmpp.util.XmppStringUtils; import org.jivesoftware.smack.packet.FullyQualifiedElement;
public class XmppElementUtil { public class XmppElementUtil {
public static String getKeyFor(Class<? extends FullyQualifiedElement> fullyQualifiedElement) { public static QName getQNameFor(Class<? extends FullyQualifiedElement> fullyQualifiedElement) {
String element, namespace; String element, namespace;
try { try {
element = (String) fullyQualifiedElement.getField("ELEMENT").get(null); element = (String) fullyQualifiedElement.getField("ELEMENT").get(null);
@ -32,14 +32,7 @@ public class XmppElementUtil {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }
String key = XmppStringUtils.generateKey(element, namespace); return new QName(namespace, element);
return key;
} }
public static String getKeyFor(FullyQualifiedElement fullyQualifiedElement) {
String element = fullyQualifiedElement.getElementName();
String namespace = fullyQualifiedElement.getNamespace();
String key = XmppStringUtils.generateKey(element, namespace);
return key;
}
} }

View file

@ -19,9 +19,7 @@ package org.jivesoftware.smack.util.stringencoder;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import org.jivesoftware.smack.util.StringUtils;
/** /**
* Base32 string encoding is useful for when filenames case-insensitive filesystems are encoded. * Base32 string encoding is useful for when filenames case-insensitive filesystems are encoded.
@ -55,13 +53,8 @@ public class Base32 {
public static String decode(String str) { public static String decode(String str) {
ByteArrayOutputStream bs = new ByteArrayOutputStream(); ByteArrayOutputStream bs = new ByteArrayOutputStream();
byte[] raw; byte[] raw = str.getBytes(StandardCharsets.UTF_8);
try {
raw = str.getBytes(StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
for (int i = 0; i < raw.length; i++) { for (int i = 0; i < raw.length; i++) {
char c = (char) raw[i]; char c = (char) raw[i];
if (!Character.isWhitespace(c)) { if (!Character.isWhitespace(c)) {
@ -114,24 +107,12 @@ public class Base32 {
} }
} }
String res; String res = new String(bs.toByteArray(), StandardCharsets.UTF_8);
try {
res = new String(bs.toByteArray(), StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
return res; return res;
} }
public static String encode(String str) { public static String encode(String str) {
byte[] b; byte[] b = str.getBytes(StandardCharsets.UTF_8);
try {
b = str.getBytes(StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
for (int i = 0; i < (b.length + 4) / 5; i++) { for (int i = 0; i < (b.length + 4) / 5; i++) {
@ -174,13 +155,7 @@ public class Base32 {
os.write(c); os.write(c);
} }
} }
String res; String res = new String(os.toByteArray(), StandardCharsets.UTF_8);
try {
res = new String(os.toByteArray(), StringUtils.UTF8);
}
catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
return res; return res;
} }

View file

@ -16,10 +16,9 @@
*/ */
package org.jivesoftware.smack.util.stringencoder; package org.jivesoftware.smack.util.stringencoder;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
public class Base64 { public class Base64 {
@ -31,11 +30,7 @@ public class Base64 {
} }
public static final String encode(String string) { public static final String encode(String string) {
try { return encodeToString(string.getBytes(StandardCharsets.UTF_8));
return encodeToString(string.getBytes(StringUtils.UTF8));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported", e);
}
} }
public static final String encodeToString(byte[] input) { public static final String encodeToString(byte[] input) {
@ -56,11 +51,7 @@ public class Base64 {
public static final String decodeToString(String string) { public static final String decodeToString(String string) {
byte[] bytes = decode(string); byte[] bytes = decode(string);
try { return new String(bytes, StandardCharsets.UTF_8);
return new String(bytes, StringUtils.UTF8);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported", e);
}
} }
// TODO: We really should not mask the IllegalArgumentException. But some unit test depend on this behavior, like // TODO: We really should not mask the IllegalArgumentException. But some unit test depend on this behavior, like
@ -74,12 +65,7 @@ public class Base64 {
} }
public static final byte[] decode(byte[] input) { public static final byte[] decode(byte[] input) {
String string; String string = new String(input, StandardCharsets.US_ASCII);
try {
string = new String(input, StringUtils.USASCII);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
return decode(string); return decode(string);
} }

View file

@ -30,7 +30,7 @@ public class StanzaCollectorTest {
@Test @Test
public void verifyRollover() throws InterruptedException { public void verifyRollover() throws InterruptedException {
TestStanzaCollector collector = new TestStanzaCollector(null, new OKEverything(), 5); StanzaCollector collector = createTestStanzaCollector(null, new OKEverything(), 5);
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
Stanza testPacket = new TestPacket(i); Stanza testPacket = new TestPacket(i);
@ -70,7 +70,7 @@ public class StanzaCollectorTest {
@Test @Test
public void verifyThreadSafety() throws InterruptedException { public void verifyThreadSafety() throws InterruptedException {
final int insertCount = 500; final int insertCount = 500;
final TestStanzaCollector collector = new TestStanzaCollector(null, new OKEverything(), insertCount); final StanzaCollector collector = createTestStanzaCollector(null, new OKEverything(), insertCount);
final AtomicInteger consumer1Dequeued = new AtomicInteger(); final AtomicInteger consumer1Dequeued = new AtomicInteger();
final AtomicInteger consumer2Dequeued = new AtomicInteger(); final AtomicInteger consumer2Dequeued = new AtomicInteger();
@ -170,10 +170,8 @@ public class StanzaCollectorTest {
} }
static class TestStanzaCollector extends StanzaCollector { private static StanzaCollector createTestStanzaCollector(XMPPConnection connection, StanzaFilter packetFilter, int size) {
protected TestStanzaCollector(XMPPConnection conection, StanzaFilter packetFilter, int size) { return new StanzaCollector(connection, StanzaCollector.newConfiguration().setStanzaFilter(packetFilter).setSize(size));
super(conection, StanzaCollector.newConfiguration().setStanzaFilter(packetFilter).setSize(size));
}
} }
static class TestPacket extends Stanza { static class TestPacket extends Stanza {

View file

@ -16,7 +16,7 @@
*/ */
package org.jivesoftware.smack.compress.packet; package org.jivesoftware.smack.compress.packet;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import java.io.IOException; import java.io.IOException;
@ -35,7 +35,7 @@ public class FailureTest {
final String expectedXml = "<failure xmlns='http://jabber.org/protocol/compress'><processing-failed/></failure>"; final String expectedXml = "<failure xmlns='http://jabber.org/protocol/compress'><processing-failed/></failure>";
assertXMLEqual(expectedXml, xml.toString()); assertXmlSimilar(expectedXml, xml.toString());
} }
@Test @Test
@ -53,6 +53,6 @@ public class FailureTest {
+ "</error>" + "</error>"
+ "</failure>"; + "</failure>";
assertXMLEqual(expectedXml, xml.toString()); assertXmlSimilar(expectedXml, xml.toString());
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Florian Schmaus * Copyright 2018-2019 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.
@ -22,9 +22,9 @@ import org.jivesoftware.smack.compress.packet.Failure;
import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StanzaError.Condition; import org.jivesoftware.smack.packet.StanzaError.Condition;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.junit.Test; import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
public class FailureProviderTest { public class FailureProviderTest {

View file

@ -16,7 +16,7 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -26,8 +26,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.test.util.XmlUnitUtils;
import org.junit.Test; import org.junit.Test;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -49,7 +47,7 @@ public class MessageTest {
Message messageTypeInConstructor = new Message(null, Message.Type.chat); Message messageTypeInConstructor = new Message(null, Message.Type.chat);
messageTypeInConstructor.setStanzaId(null); messageTypeInConstructor.setStanzaId(null);
assertEquals(type, messageTypeInConstructor.getType()); assertEquals(type, messageTypeInConstructor.getType());
assertXMLEqual(control, messageTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, messageTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
controlBuilder = new StringBuilder(); controlBuilder = new StringBuilder();
controlBuilder.append("<message") controlBuilder.append("<message")
@ -62,7 +60,7 @@ public class MessageTest {
Message messageTypeSet = getNewMessage(); Message messageTypeSet = getNewMessage();
messageTypeSet.setType(type2); messageTypeSet.setType(type2);
assertEquals(type2, messageTypeSet.getType()); assertEquals(type2, messageTypeSet.getType());
assertXMLEqual(control, messageTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, messageTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
@ -87,7 +85,7 @@ public class MessageTest {
message.setSubject(messageSubject); message.setSubject(messageSubject);
assertEquals(messageSubject, message.getSubject()); assertEquals(messageSubject, message.getSubject());
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -106,7 +104,7 @@ public class MessageTest {
message.setBody(messageBody); message.setBody(messageBody);
assertEquals(messageBody, message.getBody()); assertEquals(messageBody, message.getBody());
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -139,7 +137,7 @@ public class MessageTest {
message.addBody(null, messageBody1); message.addBody(null, messageBody1);
message.addBody(lang2, messageBody2); message.addBody(lang2, messageBody2);
message.addBody(lang3, messageBody3); message.addBody(lang3, messageBody3);
XmlUnitUtils.assertSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE)); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE));
Collection<String> languages = message.getBodyLanguages(); Collection<String> languages = message.getBodyLanguages();
List<String> controlLanguages = new ArrayList<>(); List<String> controlLanguages = new ArrayList<>();
@ -183,7 +181,7 @@ public class MessageTest {
message.setThread(messageThread); message.setThread(messageThread);
assertEquals(messageThread, message.getThread()); assertEquals(messageThread, message.getThread());
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -201,7 +199,7 @@ public class MessageTest {
Message message = getNewMessage(); Message message = getNewMessage();
message.setLanguage(lang); message.setLanguage(lang);
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
private static Message getNewMessage() { private static Message getNewMessage() {

View file

@ -16,14 +16,15 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException; import java.io.IOException;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
public class PresenceTest { public class PresenceTest {
@ -43,7 +44,7 @@ public class PresenceTest {
Presence presenceTypeInConstructor = new Presence(type); Presence presenceTypeInConstructor = new Presence(type);
presenceTypeInConstructor.setStanzaId(null); presenceTypeInConstructor.setStanzaId(null);
assertEquals(type, presenceTypeInConstructor.getType()); assertEquals(type, presenceTypeInConstructor.getType());
assertXMLEqual(control, presenceTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceTypeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
controlBuilder = new StringBuilder(); controlBuilder = new StringBuilder();
controlBuilder.append("<presence") controlBuilder.append("<presence")
@ -56,12 +57,14 @@ public class PresenceTest {
Presence presenceTypeSet = getNewPresence(); Presence presenceTypeSet = getNewPresence();
presenceTypeSet.setType(type2); presenceTypeSet.setType(type2);
assertEquals(type2, presenceTypeSet.getType()); assertEquals(type2, presenceTypeSet.getType());
assertXMLEqual(control, presenceTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceTypeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test(expected = NullPointerException.class) @Test
public void setNullPresenceTypeTest() { public void setNullPresenceTypeTest() {
getNewPresence().setType(null); assertThrows(IllegalArgumentException.class, () ->
getNewPresence().setType(null)
);
} }
@Test @Test
@ -90,7 +93,7 @@ public class PresenceTest {
presence.setStatus(status); presence.setStatus(status);
assertEquals(status, presence.getStatus()); assertEquals(status, presence.getStatus());
assertXMLEqual(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -109,12 +112,14 @@ public class PresenceTest {
presence.setPriority(priority); presence.setPriority(priority);
assertEquals(priority, presence.getPriority()); assertEquals(priority, presence.getPriority());
assertXMLEqual(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test(expected = IllegalArgumentException.class) @Test
public void setIllegalPriorityTest() { public void setIllegalPriorityTest() {
getNewPresence().setPriority(Integer.MIN_VALUE); assertThrows(IllegalArgumentException.class, () ->
getNewPresence().setPriority(Integer.MIN_VALUE)
);
} }
@Test @Test
@ -142,7 +147,7 @@ public class PresenceTest {
mode1); mode1);
presenceModeInConstructor.setStanzaId(null); presenceModeInConstructor.setStanzaId(null);
assertEquals(mode1, presenceModeInConstructor.getMode()); assertEquals(mode1, presenceModeInConstructor.getMode());
assertXMLEqual(control, presenceModeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceModeInConstructor.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
controlBuilder = new StringBuilder(); controlBuilder = new StringBuilder();
controlBuilder.append("<presence>") controlBuilder.append("<presence>")
@ -155,7 +160,7 @@ public class PresenceTest {
Presence presenceModeSet = getNewPresence(); Presence presenceModeSet = getNewPresence();
presenceModeSet.setMode(mode2); presenceModeSet.setMode(mode2);
assertEquals(mode2, presenceModeSet.getMode()); assertEquals(mode2, presenceModeSet.getMode());
assertXMLEqual(control, presenceModeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presenceModeSet.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@Test @Test
@ -183,7 +188,7 @@ public class PresenceTest {
Presence presence = getNewPresence(); Presence presence = getNewPresence();
presence.setLanguage(lang); presence.setLanguage(lang);
assertXMLEqual(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
private static Presence getNewPresence() { private static Presence getNewPresence() {

View file

@ -18,19 +18,25 @@ package org.jivesoftware.smack.packet;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.io.IOException;
import org.jivesoftware.smack.packet.StreamError.Condition; import org.jivesoftware.smack.packet.StreamError.Condition;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest;
import org.xmlpull.v1.XmlPullParser; import org.junit.jupiter.params.provider.EnumSource;
public class StreamErrorTest { public class StreamErrorTest {
@Test @ParameterizedTest
public void testParsingOfSimpleStreamError() { @EnumSource(SmackTestUtil.XmlPullParserKind.class)
StreamError error = null; public void testParsingOfSimpleStreamError(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
final String xml = final String xml =
// Usually the stream:stream element has more attributes (to, version, ...) // Usually the stream:stream element has more attributes (to, version, ...)
// We omit those, since they are not relevant for testing // We omit those, since they are not relevant for testing
@ -39,19 +45,17 @@ public class StreamErrorTest {
"<conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams' /> +" + "<conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams' /> +" +
"</stream:error>" + "</stream:error>" +
"</stream:stream>"; "</stream:stream>";
try {
XmlPullParser parser = PacketParserUtils.getParserFor(xml, "error"); XmlPullParser parser = SmackTestUtil.getParserFor(xml, "error", parserKind);
error = PacketParserUtils.parseStreamError(parser); StreamError error = PacketParserUtils.parseStreamError(parser);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(error); assertNotNull(error);
assertEquals(Condition.conflict, error.getCondition()); assertEquals(Condition.conflict, error.getCondition());
} }
@Test @ParameterizedTest
public void testParsingOfStreamErrorWithText() { @EnumSource(SmackTestUtil.XmlPullParserKind.class)
StreamError error = null; public void testParsingOfStreamErrorWithText(SmackTestUtil.XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException {
final String xml = final String xml =
// Usually the stream:stream element has more attributes (to, version, ...) // Usually the stream:stream element has more attributes (to, version, ...)
// We omit those, since they are not relevant for testing // We omit those, since they are not relevant for testing
@ -63,20 +67,19 @@ public class StreamErrorTest {
"</text>" + "</text>" +
"</stream:error>" + "</stream:error>" +
"</stream:stream>"; "</stream:stream>";
try {
XmlPullParser parser = PacketParserUtils.getParserFor(xml, "error"); XmlPullParser parser = SmackTestUtil.getParserFor(xml, "error", parserKind);
error = PacketParserUtils.parseStreamError(parser); StreamError error = PacketParserUtils.parseStreamError(parser);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(error); assertNotNull(error);
assertEquals(Condition.conflict, error.getCondition()); assertEquals(Condition.conflict, error.getCondition());
assertEquals("Replaced by new connection", error.getDescriptiveText()); assertEquals("Replaced by new connection", error.getDescriptiveText());
} }
@Test @ParameterizedTest
public void testParsingOfStreamErrorWithTextAndOptionalElement() { @EnumSource(SmackTestUtil.XmlPullParserKind.class)
StreamError error = null; public void testParsingOfStreamErrorWithTextAndOptionalElement(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
final String xml = final String xml =
// Usually the stream:stream element has more attributes (to, version, ...) // Usually the stream:stream element has more attributes (to, version, ...)
// We omit those, since they are not relevant for testing // We omit those, since they are not relevant for testing
@ -91,12 +94,10 @@ public class StreamErrorTest {
"</appSpecificElement>" + "</appSpecificElement>" +
"</stream:error>" + "</stream:error>" +
"</stream:stream>"; "</stream:stream>";
try {
XmlPullParser parser = PacketParserUtils.getParserFor(xml, "error"); XmlPullParser parser = SmackTestUtil.getParserFor(xml, "error", parserKind);
error = PacketParserUtils.parseStreamError(parser); StreamError error = PacketParserUtils.parseStreamError(parser);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(error); assertNotNull(error);
assertEquals(Condition.conflict, error.getCondition()); assertEquals(Condition.conflict, error.getCondition());
assertEquals("Replaced by new connection", error.getDescriptiveText()); assertEquals("Replaced by new connection", error.getDescriptiveText());
@ -104,21 +105,20 @@ public class StreamErrorTest {
assertNotNull(appSpecificElement); assertNotNull(appSpecificElement);
} }
@Test @ParameterizedTest
public void testStreamErrorXmlNotWellFormed() { @EnumSource(SmackTestUtil.XmlPullParserKind.class)
StreamError error = null; public void testStreamErrorXmlNotWellFormed(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
final String xml = final String xml =
// Usually the stream:stream element has more attributes (to, version, ...) // Usually the stream:stream element has more attributes (to, version, ...)
// We omit those, since they are not relevant for testing // We omit those, since they are not relevant for testing
"<stream:stream from='im.example.com' id='++TR84Sm6A3hnt3Q065SnAbbk3Y=' xmlns:stream='http://etherx.jabber.org/streams'>" + "<stream:stream from='im.example.com' id='++TR84Sm6A3hnt3Q065SnAbbk3Y=' xmlns:stream='http://etherx.jabber.org/streams'>" +
"<stream:error><xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>" + "<stream:error><xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>" +
"</stream:stream>"; "</stream:stream>";
try {
XmlPullParser parser = PacketParserUtils.getParserFor(xml, "error"); XmlPullParser parser = SmackTestUtil.getParserFor(xml, "error", parserKind);
error = PacketParserUtils.parseStreamError(parser); StreamError error = PacketParserUtils.parseStreamError(parser);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(error); assertNotNull(error);
assertEquals(Condition.not_well_formed, error.getCondition()); assertEquals(Condition.not_well_formed, error.getCondition());
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014 Florian Schmaus * Copyright © 2014-2019 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,7 +19,7 @@ package org.jivesoftware.smack.packet;
public class TestIQ extends SimpleIQ { public class TestIQ extends SimpleIQ {
public TestIQ() { public TestIQ() {
this(null, null); this("https://igniterealtime.org/projects/smack", "test-iq");
} }
public TestIQ(String element, String namespace) { public TestIQ(String element, String namespace) {

View file

@ -27,11 +27,11 @@ import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
public class ParsingExceptionTest { public class ParsingExceptionTest {
@ -39,6 +39,7 @@ public class ParsingExceptionTest {
"<extension2 xmlns='namespace'>" + "<extension2 xmlns='namespace'>" +
"<bar node='testNode'>" + "<bar node='testNode'>" +
"<i id='testid1'>" + "<i id='testid1'>" +
"text content" +
"</i>" + "</i>" +
"</bar>" + "</bar>" +
"</extension2>"; "</extension2>";
@ -57,8 +58,7 @@ public class ParsingExceptionTest {
public void consumeUnparsedInput() throws Exception { public void consumeUnparsedInput() throws Exception {
final String MESSAGE_EXCEPTION_ELEMENT = final String MESSAGE_EXCEPTION_ELEMENT =
"<" + ThrowException.ELEMENT + " xmlns='" + ThrowException.NAMESPACE + "'>" + "<" + ThrowException.ELEMENT + " xmlns='" + ThrowException.NAMESPACE + "'>" +
"<nothingInHere>" + "<nothingInHere/>" +
"</nothingInHere>" +
"</" + ThrowException.ELEMENT + ">"; "</" + ThrowException.ELEMENT + ">";
XmlPullParser parser = TestUtils.getMessageParser( XmlPullParser parser = TestUtils.getMessageParser(
"<message from='user@server.example' to='francisco@denmark.lit' id='foo'>" + "<message from='user@server.example' to='francisco@denmark.lit' id='foo'>" +

View file

@ -22,10 +22,10 @@ import java.util.Collection;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.FileUtils; import org.jivesoftware.smack.util.FileUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
public class ProviderConfigTest { public class ProviderConfigTest {

View file

@ -21,9 +21,9 @@ import static org.junit.Assert.assertTrue;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.junit.Test; import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
public class ProviderManagerTest { public class ProviderManagerTest {

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014-2017 Florian Schmaus * Copyright © 2014-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,10 +16,10 @@
*/ */
package org.jivesoftware.smack.sasl; package org.jivesoftware.smack.sasl;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -39,14 +39,14 @@ public class DigestMd5SaslTest extends AbstractSaslTest {
super(saslMechanism); super(saslMechanism);
} }
protected void runTest(boolean useAuthzid) throws SmackException, InterruptedException, XmppStringprepException, UnsupportedEncodingException { protected void runTest(boolean useAuthzid) throws SmackException, InterruptedException, XmppStringprepException {
EntityBareJid authzid = null; EntityBareJid authzid = null;
if (useAuthzid) { if (useAuthzid) {
authzid = JidCreate.entityBareFrom("shazbat@xmpp.org"); authzid = JidCreate.entityBareFrom("shazbat@xmpp.org");
} }
saslMechanism.authenticate("florian", "irrelevant", JidCreate.domainBareFrom("xmpp.org"), "secret", authzid, null); saslMechanism.authenticate("florian", "irrelevant", JidCreate.domainBareFrom("xmpp.org"), "secret", authzid, null);
byte[] response = saslMechanism.evaluateChallenge(challengeBytes); byte[] response = saslMechanism.evaluateChallenge(challengeBytes);
String responseString = new String(response, StringUtils.UTF8); String responseString = new String(response, StandardCharsets.UTF_8);
String[] responseParts = responseString.split(","); String[] responseParts = responseString.split(",");
Map<String, String> responsePairs = new HashMap<String, String>(); Map<String, String> responsePairs = new HashMap<String, String>();
for (String part : responseParts) { for (String part : responseParts) {

View file

@ -18,6 +18,7 @@ package org.jivesoftware.smack.test.util;
import java.util.Base64; import java.util.Base64;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.util.stringencoder.Base64.Encoder; import org.jivesoftware.smack.util.stringencoder.Base64.Encoder;
/** /**
@ -27,6 +28,7 @@ import org.jivesoftware.smack.util.stringencoder.Base64.Encoder;
public class SmackTestSuite { public class SmackTestSuite {
static { static {
SmackConfiguration.getVersion();
org.jivesoftware.smack.util.stringencoder.Base64.setEncoder(new Encoder() { org.jivesoftware.smack.util.stringencoder.Base64.setEncoder(new Encoder() {
@Override @Override

View file

@ -0,0 +1,165 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.test.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.function.Predicate;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.Provider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smack.xml.XmlPullParserFactory;
import org.jivesoftware.smack.xml.stax.StaxXmlPullParserFactory;
import org.jivesoftware.smack.xml.xpp3.Xpp3XmlPullParserFactory;
public class SmackTestUtil {
@SuppressWarnings("ImmutableEnumChecker")
public enum XmlPullParserKind {
StAX(StaxXmlPullParserFactory.class),
XPP3(Xpp3XmlPullParserFactory.class),
;
public final XmlPullParserFactory factory;
XmlPullParserKind(Class<? extends XmlPullParserFactory> factoryClass) {
try {
factory = factoryClass.getDeclaredConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
throw new AssertionError(e);
}
}
}
public static <E extends Element, P extends Provider<E>> E parse(CharSequence xml, Class<P> providerClass, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
P provider = providerClassToProvider(providerClass);
return parse(xml, provider, parserKind);
}
public static <E extends Element, P extends Provider<E>> E parse(InputStream inputStream, Class<P> providerClass, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
P provider = providerClassToProvider(providerClass);
return parse(inputStream, provider, parserKind);
}
public static <E extends Element, P extends Provider<E>> E parse(Reader reader, Class<P> providerClass, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
P provider = providerClassToProvider(providerClass);
return parse(reader, provider, parserKind);
}
public static <E extends Element> E parse(CharSequence xml, Provider<E> provider, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
String xmlString = xml.toString();
Reader reader = new StringReader(xmlString);
return parse(reader, provider, parserKind);
}
public static <E extends Element> E parse(InputStream inputStream, Provider<E> provider, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
return parse(inputStreamReader, provider, parserKind);
}
public static <E extends Element> E parse(Reader reader, Provider<E> provider, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
XmlPullParser parser = getParserFor(reader, parserKind);
E element = provider.parse(parser);
return element;
}
public static XmlPullParser getParserFor(String xml, XmlPullParserKind parserKind) throws XmlPullParserException, IOException {
Reader reader = new StringReader(xml);
return getParserFor(reader, parserKind);
}
public static XmlPullParser getParserFor(InputStream inputStream, XmlPullParserKind parserKind) throws XmlPullParserException, IOException {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
return getParserFor(inputStreamReader, parserKind);
}
public static XmlPullParser getParserFor(Reader reader, XmlPullParserKind parserKind) throws XmlPullParserException, IOException {
XmlPullParser parser = parserKind.factory.newXmlPullParser(reader);
forwardParserToStartElement(parser);
return parser;
}
public static XmlPullParser getParserFor(String xml, QName startTagQName, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
XmlPullParser parser = getParserFor(xml, parserKind);
forwardParserToStartElement(parser, (p) -> p.getQName().equals(startTagQName));
return parser;
}
public static XmlPullParser getParserFor(String xml, String startTagLocalpart, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
XmlPullParser parser = getParserFor(xml, parserKind);
forwardParserToStartElement(parser, (p) -> p.getName().equals(startTagLocalpart));
return parser;
}
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
P provider;
// TODO: Consider adding a shortcut in case there is a static INSTANCE field holding an instance of the
// requested provider.
try {
provider = providerClass.getDeclaredConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new AssertionError(e);
}
return provider;
}
private static void forwardParserToStartElement(XmlPullParser parser) throws XmlPullParserException, IOException {
forwardParserToStartElement(parser, p -> true);
}
private static void forwardParserToStartElement(XmlPullParser parser,
Predicate<XmlPullParser> doneForwarding) throws XmlPullParserException, IOException {
outerloop: while (true) {
XmlPullParser.Event event = parser.getEventType();
switch (event) {
case START_ELEMENT:
if (doneForwarding.test(parser)) {
break outerloop;
}
break;
case END_DOCUMENT:
throw new IllegalArgumentException("Not matching START_ELEMENT found");
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
}
parser.next();
}
}
}

View file

@ -20,14 +20,11 @@ import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smack.util.ParserUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
// TODO: Remove this class and replace it with SmackTestUtil.
public final class TestUtils { public final class TestUtils {
private TestUtils() { private TestUtils() {
} }
@ -52,12 +49,12 @@ public final class TestUtils {
return getParser(new StringReader(string), startTag); return getParser(new StringReader(string), startTag);
} }
public static XmlPullParser getParser(Reader reader, String startTag) { private static XmlPullParser getParser(Reader reader, String startTag) {
XmlPullParser parser; XmlPullParser parser;
try { try {
parser = PacketParserUtils.newXmppParser(reader); parser = SmackXmlParser.newXmlParser(reader);
if (startTag == null) { if (startTag == null) {
while (parser.getEventType() != XmlPullParser.START_TAG) { while (parser.getEventType() != XmlPullParser.Event.START_ELEMENT) {
parser.next(); parser.next();
} }
return parser; return parser;
@ -65,7 +62,7 @@ public final class TestUtils {
boolean found = false; boolean found = false;
while (!found) { while (!found) {
if ((parser.next() == XmlPullParser.START_TAG) && parser.getName().equals(startTag)) if ((parser.next() == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(startTag))
found = true; found = true;
} }
@ -79,17 +76,4 @@ public final class TestUtils {
return parser; return parser;
} }
public static <EE extends ExtensionElement> EE parseExtensionElement(String elementString)
throws Exception {
return parseExtensionElement(getParser(elementString), null);
}
@SuppressWarnings("unchecked")
public static <EE extends ExtensionElement> EE parseExtensionElement(XmlPullParser parser, XmlEnvironment outerXmlEnvironment)
throws Exception {
ParserUtils.assertAtStartTag(parser);
final String elementName = parser.getName();
final String namespace = parser.getNamespace();
return (EE) PacketParserUtils.parseExtensionElement(elementName, namespace, parser, outerXmlEnvironment);
}
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2014-2018 Florian Schmaus * Copyright 2014-2019 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,24 +16,36 @@
*/ */
package org.jivesoftware.smack.test.util; package org.jivesoftware.smack.test.util;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import java.io.StringReader;
import java.io.IOException; import javax.xml.transform.stream.StreamSource;
import org.custommonkey.xmlunit.Diff; import org.xmlunit.assertj.CompareAssert;
import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; import org.xmlunit.assertj.XmlAssert;
import org.xml.sax.SAXException; import org.xmlunit.diff.DefaultNodeMatcher;
import org.xmlunit.diff.ElementSelectors;
import org.xmlunit.input.NormalizedSource;
// TODO: Rename this class to XmlAssertUtil
public class XmlUnitUtils { public class XmlUnitUtils {
// TOOD: Remove this method. public static void assertXmlNotSimilar(CharSequence xmlOne, CharSequence xmlTwo) {
public static void assertSimilar(CharSequence expected, CharSequence actual) throws SAXException, IOException { normalizedCompare(xmlOne, xmlTwo).areNotSimilar();
assertXmlSimilar(expected, actual);
} }
public static void assertXmlSimilar(CharSequence expected, CharSequence actual) throws SAXException, IOException { public static void assertXmlSimilar(CharSequence expected, CharSequence actual) {
Diff diff = new Diff(expected.toString(), actual.toString()); normalizedCompare(expected, actual).areSimilar();
diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); }
assertXMLEqual(diff, true);
private static CompareAssert normalizedCompare(CharSequence expectedCharSequence, CharSequence actualCharSequence) {
String expectedString = expectedCharSequence.toString();
String actualString = actualCharSequence.toString();
NormalizedSource expected = new NormalizedSource(new StreamSource(new StringReader(expectedString)));
NormalizedSource actual = new NormalizedSource(new StreamSource(new StringReader(actualString)));
return XmlAssert.assertThat(expected).and(actual)
.ignoreChildNodesOrder()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndAllAttributes, ElementSelectors.byNameAndText))
.normalizeWhitespace();
} }
} }

View file

@ -16,8 +16,8 @@
*/ */
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.fail;
import java.lang.ref.PhantomReference; import java.lang.ref.PhantomReference;
import java.lang.ref.Reference; import java.lang.ref.Reference;
@ -51,6 +51,7 @@ public class MemoryLeakTestUtil {
private static final Logger LOGGER = Logger.getLogger(MemoryLeakTestUtil.class.getName()); private static final Logger LOGGER = Logger.getLogger(MemoryLeakTestUtil.class.getName());
@SuppressWarnings("UnusedVariable")
public static <M extends Manager> void noResourceLeakTest(Function<DummyConnection, M> managerSupplier) public static <M extends Manager> void noResourceLeakTest(Function<DummyConnection, M> managerSupplier)
throws XmppStringprepException, IllegalArgumentException, InterruptedException { throws XmppStringprepException, IllegalArgumentException, InterruptedException {
final int numConnections = 10; final int numConnections = 10;
@ -103,10 +104,27 @@ public class MemoryLeakTestUtil {
} }
private static void assertReferencesQueueSize(ReferenceQueue<?> referenceQueue, int expectedSize) throws IllegalArgumentException, InterruptedException { private static void assertReferencesQueueSize(ReferenceQueue<?> referenceQueue, int expectedSize) throws IllegalArgumentException, InterruptedException {
final int timeout = 60000; final int timeout = 120000;
final int maxAttempts = 3;
for (int itemsRemoved = 0; itemsRemoved < expectedSize; ++itemsRemoved) { for (int itemsRemoved = 0; itemsRemoved < expectedSize; ++itemsRemoved) {
Reference<?> reference = referenceQueue.remove(timeout); int attempt = 0;
assertNotNull("No reference found after " + timeout + "ms", reference); Reference<?> reference = null;
do {
reference = referenceQueue.remove(timeout);
if (reference != null) {
break;
}
attempt++;
String message = "No reference to a gc'ed object found after " + timeout + "ms in the " + attempt
+ ". attempt.";
if (attempt >= maxAttempts) {
fail(message);
}
LOGGER.warning(message);
triggerGarbageCollection();
} while (true);
reference.clear(); reference.clear();
} }
@ -119,6 +137,7 @@ public class MemoryLeakTestUtil {
assertNull(reference); assertNull(reference);
} }
@SuppressWarnings("UnusedVariable")
private static void triggerGarbageCollection() { private static void triggerGarbageCollection() {
Object object = new Object(); Object object = new Object();
WeakReference<Object> weakReference = new WeakReference<>(object); WeakReference<Object> weakReference = new WeakReference<>(object);
@ -130,6 +149,8 @@ public class MemoryLeakTestUtil {
throw new AssertionError("No observed gargabe collection after " + gcCalls + " calls of System.gc()"); throw new AssertionError("No observed gargabe collection after " + gcCalls + " calls of System.gc()");
} }
System.gc(); System.gc();
// TODO: Would a Thread.yield() here improve the chances of a full GC? It appears that on some systems we
// observe a partial GC here.
gcCalls++; gcCalls++;
} while (weakReference.get() != null); } while (weakReference.get() != null);

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2016 Florian Schmaus * Copyright 2016-2019 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.
@ -27,7 +27,7 @@ public class NetworkUtil {
private static final Logger LOGGER = Logger.getLogger(NetworkUtil.class.getName()); private static final Logger LOGGER = Logger.getLogger(NetworkUtil.class.getName());
public static ServerSocket getSocketOnLoopback() { public static ServerSocket getSocketOnLoopback() throws IOException {
final InetAddress loopbackAddress = InetAddress.getLoopbackAddress(); final InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
final int portMin = 1024; final int portMin = 1024;
final int portMax = (1 << 16) - 1; final int portMax = (1 << 16) - 1;
@ -40,13 +40,12 @@ public class NetworkUtil {
break; break;
} catch (BindException e) { } catch (BindException e) {
LOGGER.log(Level.FINEST, "Could not bind port " + port + ", trying next", e); LOGGER.log(Level.FINEST, "Could not bind port " + port + ", trying next", e);
} catch (IOException e) {
throw new IllegalStateException(e);
} }
} }
if (serverSocket == null) { if (serverSocket == null) {
throw new IllegalStateException(); throw new IOException("Could not bind any port between " + portMin + " and " + portMax
+ " on loopback address" + loopbackAddress);
} }
return serverSocket; return serverSocket;

View file

@ -16,8 +16,8 @@
*/ */
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlNotSimilar;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLNotEqual; import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -29,6 +29,8 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@ -42,15 +44,17 @@ import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.sasl.SASLError; import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements; import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.test.util.XmlUnitUtils; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import com.jamesmurty.utils.XMLBuilder; import com.jamesmurty.utils.XMLBuilder;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class PacketParserUtilsTest { public class PacketParserUtilsTest {
@ -59,8 +63,9 @@ public class PacketParserUtilsTest {
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
} }
@Test @ParameterizedTest
public void singleMessageBodyTest() throws Exception { @EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void singleMessageBodyTest(SmackTestUtil.XmlPullParserKind parserKind) throws Exception {
String defaultLanguage = Stanza.getDefaultLanguage(); String defaultLanguage = Stanza.getDefaultLanguage();
String otherLanguage = determineNonDefaultLanguage(); String otherLanguage = determineNonDefaultLanguage();
@ -79,13 +84,13 @@ public class PacketParserUtilsTest {
.asString(outputProperties); .asString(outputProperties);
Message message = PacketParserUtils Message message = PacketParserUtils
.parseMessage(PacketParserUtils.getParserFor(control)); .parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertEquals(defaultLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody());
assertTrue(message.getBodyLanguages().isEmpty()); assertTrue(message.getBodyLanguages().isEmpty());
assertEquals(defaultLanguage, message.getBody(defaultLanguage)); assertEquals(defaultLanguage, message.getBody(defaultLanguage));
assertNull(message.getBody(otherLanguage)); assertNull(message.getBody(otherLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has non-default language, body has no language // message has non-default language, body has no language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -99,13 +104,13 @@ public class PacketParserUtilsTest {
.t(otherLanguage) .t(otherLanguage)
.asString(outputProperties); .asString(outputProperties);
message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(control)); message = PacketParserUtils.parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertEquals(otherLanguage, message.getBody()); assertEquals(otherLanguage, message.getBody());
assertTrue(message.getBodyLanguages().isEmpty()); assertTrue(message.getBodyLanguages().isEmpty());
assertEquals(otherLanguage, message.getBody(otherLanguage)); assertEquals(otherLanguage, message.getBody(otherLanguage));
assertNull(message.getBody(defaultLanguage)); assertNull(message.getBody(defaultLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has no language, body has no language // message has no language, body has no language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -118,13 +123,13 @@ public class PacketParserUtilsTest {
.t(defaultLanguage) .t(defaultLanguage)
.asString(outputProperties); .asString(outputProperties);
message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(control)); message = PacketParserUtils.parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertEquals(defaultLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody());
assertTrue(message.getBodyLanguages().isEmpty()); assertTrue(message.getBodyLanguages().isEmpty());
assertEquals(defaultLanguage, message.getBody(null)); assertEquals(defaultLanguage, message.getBody(null));
assertNull(message.getBody(otherLanguage)); assertNull(message.getBody(otherLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has no language, body has default language // message has no language, body has default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -138,14 +143,14 @@ public class PacketParserUtilsTest {
.t(defaultLanguage) .t(defaultLanguage)
.asString(outputProperties); .asString(outputProperties);
message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(control)); message = PacketParserUtils.parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertNull(message.getBody()); assertNull(message.getBody());
assertFalse(message.getBodyLanguages().isEmpty()); assertFalse(message.getBodyLanguages().isEmpty());
assertEquals(defaultLanguage, message.getBody(defaultLanguage)); assertEquals(defaultLanguage, message.getBody(defaultLanguage));
assertNull(message.getBody(otherLanguage)); assertNull(message.getBody(otherLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has no language, body has non-default language // message has no language, body has non-default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -159,14 +164,14 @@ public class PacketParserUtilsTest {
.t(otherLanguage) .t(otherLanguage)
.asString(outputProperties); .asString(outputProperties);
message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(control)); message = PacketParserUtils.parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertNull(message.getBody()); assertNull(message.getBody());
assertFalse(message.getBodyLanguages().isEmpty()); assertFalse(message.getBodyLanguages().isEmpty());
assertTrue(message.getBodyLanguages().contains(otherLanguage)); assertTrue(message.getBodyLanguages().contains(otherLanguage));
assertEquals(otherLanguage, message.getBody(otherLanguage)); assertEquals(otherLanguage, message.getBody(otherLanguage));
assertNull(message.getBody(defaultLanguage)); assertNull(message.getBody(defaultLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has default language, body has non-default language // message has default language, body has non-default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -181,14 +186,14 @@ public class PacketParserUtilsTest {
.t(otherLanguage) .t(otherLanguage)
.asString(outputProperties); .asString(outputProperties);
message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(control)); message = PacketParserUtils.parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertNull(message.getBody()); assertNull(message.getBody());
assertFalse(message.getBodyLanguages().isEmpty()); assertFalse(message.getBodyLanguages().isEmpty());
assertTrue(message.getBodyLanguages().contains(otherLanguage)); assertTrue(message.getBodyLanguages().contains(otherLanguage));
assertEquals(otherLanguage, message.getBody(otherLanguage)); assertEquals(otherLanguage, message.getBody(otherLanguage));
assertNull(message.getBody(defaultLanguage)); assertNull(message.getBody(defaultLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has non-default language, body has default language // message has non-default language, body has default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -203,14 +208,14 @@ public class PacketParserUtilsTest {
.t(defaultLanguage) .t(defaultLanguage)
.asString(outputProperties); .asString(outputProperties);
message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(control)); message = PacketParserUtils.parseMessage(SmackTestUtil.getParserFor(control, parserKind));
assertNull(message.getBody()); assertNull(message.getBody());
assertFalse(message.getBodyLanguages().isEmpty()); assertFalse(message.getBodyLanguages().isEmpty());
assertTrue(message.getBodyLanguages().contains(defaultLanguage)); assertTrue(message.getBodyLanguages().contains(defaultLanguage));
assertEquals(defaultLanguage, message.getBody(defaultLanguage)); assertEquals(defaultLanguage, message.getBody(defaultLanguage));
assertNull(message.getBody(otherLanguage)); assertNull(message.getBody(otherLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
} }
@ -239,7 +244,7 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().isEmpty()); assertTrue(message.getSubjectLanguages().isEmpty());
assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
assertNull(message.getSubject(otherLanguage)); assertNull(message.getSubject(otherLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has non-default language, subject has no language // message has non-default language, subject has no language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -258,7 +263,7 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().isEmpty()); assertTrue(message.getSubjectLanguages().isEmpty());
assertEquals(otherLanguage, message.getSubject(otherLanguage)); assertEquals(otherLanguage, message.getSubject(otherLanguage));
assertNull(message.getSubject(defaultLanguage)); assertNull(message.getSubject(defaultLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has no language, subject has no language // message has no language, subject has no language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -276,7 +281,7 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().isEmpty()); assertTrue(message.getSubjectLanguages().isEmpty());
assertEquals(defaultLanguage, message.getSubject(null)); assertEquals(defaultLanguage, message.getSubject(null));
assertNull(message.getSubject(otherLanguage)); assertNull(message.getSubject(otherLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has no language, subject has default language // message has no language, subject has default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -295,7 +300,7 @@ public class PacketParserUtilsTest {
assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
assertNull(message.getSubject(otherLanguage)); assertNull(message.getSubject(otherLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has no language, subject has non-default language // message has no language, subject has non-default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -315,7 +320,7 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().contains(otherLanguage)); assertTrue(message.getSubjectLanguages().contains(otherLanguage));
assertEquals(otherLanguage, message.getSubject(otherLanguage)); assertEquals(otherLanguage, message.getSubject(otherLanguage));
assertNull(message.getSubject(defaultLanguage)); assertNull(message.getSubject(defaultLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has default language, subject has non-default language // message has default language, subject has non-default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -336,7 +341,7 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().contains(otherLanguage)); assertTrue(message.getSubjectLanguages().contains(otherLanguage));
assertEquals(otherLanguage, message.getSubject(otherLanguage)); assertEquals(otherLanguage, message.getSubject(otherLanguage));
assertNull(message.getSubject(defaultLanguage)); assertNull(message.getSubject(defaultLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has non-default language, subject has default language // message has non-default language, subject has default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -357,7 +362,7 @@ public class PacketParserUtilsTest {
assertTrue(message.getSubjectLanguages().contains(defaultLanguage)); assertTrue(message.getSubjectLanguages().contains(defaultLanguage));
assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
assertNull(message.getSubject(otherLanguage)); assertNull(message.getSubject(otherLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
} }
@ -393,7 +398,7 @@ public class PacketParserUtilsTest {
assertEquals(2, message.getBodies().size()); assertEquals(2, message.getBodies().size());
assertEquals(1, message.getBodyLanguages().size()); assertEquals(1, message.getBodyLanguages().size());
assertTrue(message.getBodyLanguages().contains(otherLanguage)); assertTrue(message.getBodyLanguages().contains(otherLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
// message has non-default language, first body no language, second body default language // message has non-default language, first body no language, second body default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -419,7 +424,7 @@ public class PacketParserUtilsTest {
assertEquals(2, message.getBodies().size()); assertEquals(2, message.getBodies().size());
assertEquals(1, message.getBodyLanguages().size()); assertEquals(1, message.getBodyLanguages().size());
assertTrue(message.getBodyLanguages().contains(defaultLanguage)); assertTrue(message.getBodyLanguages().contains(defaultLanguage));
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
} }
// TODO: Re-enable once we throw an exception on duplicate body elements. // TODO: Re-enable once we throw an exception on duplicate body elements.
@ -452,7 +457,7 @@ public class PacketParserUtilsTest {
assertEquals(defaultLanguage, message.getBody(defaultLanguage)); assertEquals(defaultLanguage, message.getBody(defaultLanguage));
assertEquals(1, message.getBodies().size()); assertEquals(1, message.getBodies().size());
assertEquals(0, message.getBodyLanguages().size()); assertEquals(0, message.getBodyLanguages().size());
assertXMLNotEqual(control, message.toXML().toString()); assertXmlNotSimilar(control, message.toXML().toString());
} }
@Ignore @Ignore
@ -510,7 +515,7 @@ public class PacketParserUtilsTest {
assertEquals(otherLanguage, message.getBody(otherLanguage)); assertEquals(otherLanguage, message.getBody(otherLanguage));
assertEquals(2, message.getBodies().size()); assertEquals(2, message.getBodies().size());
assertEquals(1, message.getBodyLanguages().size()); assertEquals(1, message.getBodyLanguages().size());
assertXMLEqual(control, message.toXML().toString()); assertXmlSimilar(control, message.toXML().toString());
} }
@Test @Test
@ -544,7 +549,7 @@ public class PacketParserUtilsTest {
assertEquals(2, message.getSubjects().size()); assertEquals(2, message.getSubjects().size());
assertEquals(1, message.getSubjectLanguages().size()); assertEquals(1, message.getSubjectLanguages().size());
assertTrue(message.getSubjectLanguages().contains(otherLanguage)); assertTrue(message.getSubjectLanguages().contains(otherLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has default language, first subject no language, second subject default language // message has default language, first subject no language, second subject default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -568,7 +573,7 @@ public class PacketParserUtilsTest {
assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
assertEquals(1, message.getSubjects().size()); assertEquals(1, message.getSubjects().size());
assertEquals(0, message.getSubjectLanguages().size()); assertEquals(0, message.getSubjectLanguages().size());
assertXMLNotEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlNotSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has non-default language, first subject no language, second subject default language // message has non-default language, first subject no language, second subject default language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -593,7 +598,7 @@ public class PacketParserUtilsTest {
assertEquals(2, message.getSubjects().size()); assertEquals(2, message.getSubjects().size());
assertEquals(1, message.getSubjectLanguages().size()); assertEquals(1, message.getSubjectLanguages().size());
assertTrue(message.getSubjectLanguages().contains(defaultLanguage)); assertTrue(message.getSubjectLanguages().contains(defaultLanguage));
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
/* /*
// message has no language, first subject no language, second subject default language // message has no language, first subject no language, second subject default language
@ -641,7 +646,7 @@ public class PacketParserUtilsTest {
assertEquals(otherLanguage, message.getSubject(otherLanguage)); assertEquals(otherLanguage, message.getSubject(otherLanguage));
assertEquals(2, message.getSubjects().size()); assertEquals(2, message.getSubjects().size());
assertEquals(1, message.getSubjectLanguages().size()); assertEquals(1, message.getSubjectLanguages().size());
assertXMLEqual(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// message has no language, first subject no language, second subject no language // message has no language, first subject no language, second subject no language
control = XMLBuilder.create("message") control = XMLBuilder.create("message")
@ -708,13 +713,18 @@ public class PacketParserUtilsTest {
.t("Good Message Body") .t("Good Message Body")
.asString(outputProperties); .asString(outputProperties);
// XPP3 writes "end tag", StAX writes "end-tag".
Supplier<Stream<String>> expectedContentOfExceptionMessage = () -> Stream.of("end tag", "end-tag");
String invalidControl = validControl.replace("Good Message Body", "Bad </span> Body"); String invalidControl = validControl.replace("Good Message Body", "Bad </span> Body");
try { try {
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl)); PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl));
fail("Exception should be thrown"); fail("Exception should be thrown");
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
assertTrue(e.getMessage().contains("end tag name </span>")); String exceptionMessage = e.getMessage();
boolean expectedContentFound = expectedContentOfExceptionMessage.get().anyMatch((expected) -> exceptionMessage.contains(expected));
assertTrue(expectedContentFound);
} }
invalidControl = validControl.replace("Good Message Body", "Bad </body> Body"); invalidControl = validControl.replace("Good Message Body", "Bad </body> Body");
@ -723,7 +733,9 @@ public class PacketParserUtilsTest {
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl)); PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl));
fail("Exception should be thrown"); fail("Exception should be thrown");
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
assertTrue(e.getMessage().contains("end tag name </body>")); String exceptionMessage = e.getMessage();
boolean expectedContentFound = expectedContentOfExceptionMessage.get().anyMatch((expected) -> exceptionMessage.contains(expected));
assertTrue(expectedContentFound);
} }
invalidControl = validControl.replace("Good Message Body", "Bad </message> Body"); invalidControl = validControl.replace("Good Message Body", "Bad </message> Body");
@ -732,9 +744,10 @@ public class PacketParserUtilsTest {
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl)); PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl));
fail("Exception should be thrown"); fail("Exception should be thrown");
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
assertTrue(e.getMessage().contains("end tag name </message>")); String exceptionMessage = e.getMessage();
boolean expectedContentFound = expectedContentOfExceptionMessage.get().anyMatch((expected) -> exceptionMessage.contains(expected));
assertTrue(expectedContentFound);
} }
} }
@Test @Test
@ -763,58 +776,52 @@ public class PacketParserUtilsTest {
.asString(outputProperties); .asString(outputProperties);
Stanza message = PacketParserUtils.parseStanza(control); Stanza message = PacketParserUtils.parseStanza(control);
XmlUnitUtils.assertSimilar(control, message.toXML()); assertXmlSimilar(control, message.toXML());
} }
@Test @Test
public void validateSimplePresence() throws Exception { public void validateSimplePresence() throws Exception {
// CHECKSTYLE:OFF String stanza = "<presence from='juliet@example.com/balcony' to='romeo@example.net'/>";
String stanza = "<presence from='juliet@example.com/balcony' to='romeo@example.net'/>";
Presence presence = PacketParserUtils.parsePresence(PacketParserUtils.getParserFor(stanza)); Presence presence = PacketParserUtils.parsePresence(PacketParserUtils.getParserFor(stanza));
assertXMLEqual(stanza, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(stanza, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
// CHECKSTYLE:ON
} }
@Test @Test
public void validatePresenceProbe() throws Exception { public void validatePresenceProbe() throws Exception {
// CHECKSTYLE:OFF String stanza = "<presence from='mercutio@example.com' id='xv291f38' to='juliet@example.com' type='unsubscribed'/>";
String stanza = "<presence from='mercutio@example.com' id='xv291f38' to='juliet@example.com' type='unsubscribed'/>";
Presence presence = PacketParserUtils.parsePresence(PacketParserUtils.getParserFor(stanza)); Presence presence = PacketParserUtils.parsePresence(PacketParserUtils.getParserFor(stanza));
assertXMLEqual(stanza, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(stanza, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
assertEquals(Presence.Type.unsubscribed, presence.getType()); assertEquals(Presence.Type.unsubscribed, presence.getType());
// CHECKSTYLE:ON
} }
@Test @Test
public void validatePresenceOptionalElements() throws Exception { public void validatePresenceOptionalElements() throws Exception {
// CHECKSTYLE:OFF String stanza = "<presence xml:lang='en' type='unsubscribed'>"
String stanza = "<presence xml:lang='en' type='unsubscribed'>" + "<show>dnd</show>"
+ "<show>dnd</show>" + "<status>Wooing Juliet</status>"
+ "<status>Wooing Juliet</status>" + "<priority>1</priority>"
+ "<priority>1</priority>" + "</presence>";
+ "</presence>";
Presence presence = PacketParserUtils.parsePresence(PacketParserUtils.getParserFor(stanza)); Presence presence = PacketParserUtils.parsePresence(PacketParserUtils.getParserFor(stanza));
assertXMLEqual(stanza, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); assertXmlSimilar(stanza, presence.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
assertEquals(Presence.Type.unsubscribed, presence.getType()); assertEquals(Presence.Type.unsubscribed, presence.getType());
assertEquals("dnd", presence.getMode().name()); assertEquals("dnd", presence.getMode().name());
assertEquals("en", presence.getLanguage()); assertEquals("en", presence.getLanguage());
assertEquals("Wooing Juliet", presence.getStatus()); assertEquals("Wooing Juliet", presence.getStatus());
assertEquals(1, presence.getPriority()); assertEquals(1, presence.getPriority());
// CHECKSTYLE:ON
} }
@Test // @Test
public void parseContentDepthTest() throws Exception { // public void parseContentDepthTest() throws Exception {
final String stanza = "<iq type='result' to='foo@bar.com' from='baz.com' id='42'/>"; // final String stanza = "<iq type='result' to='foo@bar.com' from='baz.com' id='42'/>";
XmlPullParser parser = TestUtils.getParser(stanza, "iq"); // XmlPullParser parser = TestUtils.getParser(stanza, "iq");
CharSequence content = PacketParserUtils.parseContent(parser); // CharSequence content = PacketParserUtils.parseContent(parser);
assertEquals("", content.toString()); // assertEquals("", content.toString());
} // }
@Test @Test
public void parseElementMultipleNamespace() public void parseElementMultipleNamespace()
@ -830,7 +837,7 @@ public class PacketParserUtilsTest {
// @formatter:on // @formatter:on
XmlPullParser parser = TestUtils.getParser(stanza, "outer"); XmlPullParser parser = TestUtils.getParser(stanza, "outer");
CharSequence result = PacketParserUtils.parseElement(parser, true); CharSequence result = PacketParserUtils.parseElement(parser, true);
assertXMLEqual(stanza, result.toString()); assertXmlSimilar(stanza, result.toString());
} }
@Test @Test
@ -843,7 +850,7 @@ public class PacketParserUtilsTest {
// @formatter:on // @formatter:on
XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT); XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT);
SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser); SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser);
assertXMLEqual(saslFailureString, saslFailure.toString()); assertXmlSimilar(saslFailureString, saslFailure.toString());
} }
@Test @Test
@ -865,7 +872,7 @@ public class PacketParserUtilsTest {
// @formatter:on // @formatter:on
XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT); XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT);
SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser); SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser);
XmlUnitUtils.assertSimilar(saslFailureString, saslFailure.toXML(StreamOpen.CLIENT_NAMESPACE)); assertXmlSimilar(saslFailureString, saslFailure.toXML(StreamOpen.CLIENT_NAMESPACE));
} }
@SuppressWarnings("ReferenceEquality") @SuppressWarnings("ReferenceEquality")
@ -909,7 +916,7 @@ public class PacketParserUtilsTest {
.element("internal-server-error", StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE).up() .element("internal-server-error", StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE).up()
.element("text", StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE).t(text).up() .element("text", StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE).t(text).up()
.asString(); .asString();
XmlUnitUtils.assertSimilar(errorXml, error.toXML(StreamOpen.CLIENT_NAMESPACE)); assertXmlSimilar(errorXml, error.toXML(StreamOpen.CLIENT_NAMESPACE));
} }
@Test @Test

View file

@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import org.junit.Test; import org.junit.Test;
@ -31,11 +31,9 @@ import org.junit.Test;
public class StringUtilsTest { public class StringUtilsTest {
@Test @Test
public void testEscapeForXml() { public void testEscapeForXml() {
String input = null;
assertNull(StringUtils.escapeForXml(null)); assertNull(StringUtils.escapeForXml(null));
input = "<b>"; String input = "<b>";
assertCharSequenceEquals("&lt;b&gt;", StringUtils.escapeForXml(input)); assertCharSequenceEquals("&lt;b&gt;", StringUtils.escapeForXml(input));
input = "\""; input = "\"";
@ -74,15 +72,15 @@ public class StringUtilsTest {
} }
@Test @Test
public void testEncodeHex() throws UnsupportedEncodingException { public void testEncodeHex() {
String input = ""; String input = "";
String output = ""; String output = "";
assertEquals(new String(StringUtils.encodeHex(input.getBytes(StringUtils.UTF8))), assertEquals(new String(StringUtils.encodeHex(input.getBytes(StandardCharsets.UTF_8))),
output); output);
input = "foo bar 123"; input = "foo bar 123";
output = "666f6f2062617220313233"; output = "666f6f2062617220313233";
assertEquals(new String(StringUtils.encodeHex(input.getBytes(StringUtils.UTF8))), assertEquals(new String(StringUtils.encodeHex(input.getBytes(StandardCharsets.UTF_8))),
output); output);
} }

View file

@ -0,0 +1,231 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.xml;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.stream.Stream;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
public class XmlPullParserTest {
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testSimpleEmptyElement(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
Reader reader = new StringReader("<empty-element/>");
XmlPullParser parser = parserKind.factory.newXmlPullParser(reader);
assertEquals(XmlPullParser.Event.START_DOCUMENT, parser.getEventType());
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
QName qname = parser.getQName();
assertEquals(qname.getLocalPart(), "empty-element");
assertEquals(qname.getPrefix(), XMLConstants.DEFAULT_NS_PREFIX);
assertEquals(qname.getNamespaceURI(), XMLConstants.NULL_NS_URI);
assertEquals(XmlPullParser.Event.END_ELEMENT, parser.next());
assertEquals(XmlPullParser.Event.END_DOCUMENT, parser.next());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testQNameSimpleElement(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String simpleElement = "<outer-element xmlns='outer-namespace'><inner-element/></outer-element>";
XmlPullParser parser = SmackTestUtil.getParserFor(simpleElement, parserKind);
QName qname = parser.getQName();
assertEquals("outer-element", qname.getLocalPart());
assertEquals("outer-namespace", qname.getNamespaceURI());
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
qname = parser.getQName();
assertEquals("inner-element", qname.getLocalPart());
assertEquals("outer-namespace", qname.getNamespaceURI());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testQNamePrefixElement(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String prefixElement = "<outer-element xmlns='outer-namespace' xmlns:inner-prefix='inner-namespace'><inner-prefix:inner-element></outer-element>";
XmlPullParser parser = SmackTestUtil.getParserFor(prefixElement, parserKind);
QName qname = parser.getQName();
assertEquals("outer-element", qname.getLocalPart());
assertEquals("outer-namespace", qname.getNamespaceURI());
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
qname = parser.getQName();
assertEquals("inner-element", qname.getLocalPart());
assertEquals("inner-namespace", qname.getNamespaceURI());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testAttributesElementWithOneAttribute(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String elementWithOneAttribute = "<element attribute-one='attribute-one-value'/>";
XmlPullParser parser = SmackTestUtil.getParserFor(elementWithOneAttribute, parserKind);
assertAttributeHolds(parser, 0, "attribute-one", "", "");
assertThrows(NullPointerException.class, () ->
assertAttributeHolds(parser, 1, "attribute-one", "", ""));
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testAttributesNamespacedElementWithOneAttribute(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String namespacedElementWithOneAttribute = "<element xmlns='element-namespace' attribute-one='attribute-one-value'/>";
XmlPullParser parser = SmackTestUtil.getParserFor(namespacedElementWithOneAttribute, parserKind);
assertAttributeHolds(parser, 0, "attribute-one", "", "");
assertThrows(NullPointerException.class, () ->
assertAttributeHolds(parser, 1, "attribute-one", "", ""));
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testAttributesNamespacedElementWithOneNamespacedAttribute(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String namespacedElementWithOneNamespacedAttribute = "<element xmlns='element-namespace' xmlns:attribute-namespace='attribute-namespace-value' attribute-namespace:attribute-one='attribute-one-value'/>";
XmlPullParser parser = SmackTestUtil.getParserFor(namespacedElementWithOneNamespacedAttribute, parserKind);
assertAttributeHolds(parser, 0, "attribute-one", "attribute-namespace", "attribute-namespace-value");
assertThrows(NullPointerException.class, () ->
assertAttributeHolds(parser, 1, "attribute-one", "", ""));
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testNamespacedAttributes(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String element = "<element xmlns:attr='attribute-namespace' attr:attributeOneName='attributeOneValue'/>";
XmlPullParser parser = SmackTestUtil.getParserFor(element, parserKind);
assertEquals(1, parser.getAttributeCount());
assertEquals("attributeOneName", parser.getAttributeName(0));
assertEquals("attr", parser.getAttributePrefix(0));
assertEquals("attribute-namespace", parser.getAttributeNamespace(0));
QName attributeZeroQname = parser.getAttributeQName(0);
assertEquals("attributeOneName", attributeZeroQname.getLocalPart());
assertEquals("attr", attributeZeroQname.getPrefix());
assertEquals("attribute-namespace", attributeZeroQname.getNamespaceURI());
// Test how parser handle non-existent attributes.
assertNull(parser.getAttributeName(1));
assertNull(parser.getAttributePrefix(1));
assertNull(parser.getAttributeNamespace(1));
QName attributeOneQname = parser.getAttributeQName(1);
assertNull(attributeOneQname);
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testAttributeType(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String element = "<element xmlns:attr='attribute-namespace' attr:attributeOneName='attributeOneValue'/>";
XmlPullParser parser = SmackTestUtil.getParserFor(element, parserKind);
assertEquals("CDATA", parser.getAttributeType(0));
assertNull(parser.getAttributeType(1));
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testNextText(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
XmlPullParser parser;
String simpleElement = "<element>Element text</element>";
parser = SmackTestUtil.getParserFor(simpleElement, parserKind);
assertEquals("Element text", parser.nextText());
String complexElement = "<outer-elment><element1>Element 1 &apos; text</element1><element2>Element 2 text</element2></outer-element>";
parser = SmackTestUtil.getParserFor(complexElement, parserKind);
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
assertEquals("element1", parser.getName());
assertEquals(0, parser.getAttributeCount());
assertEquals("Element 1 ' text", parser.nextText());
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
assertEquals("element2", parser.getName());
assertEquals(0, parser.getAttributeCount());
assertEquals("Element 2 text", parser.nextText());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testNextTextMixedContent(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String element = "<element>Mixed content element text<inner-element>Inner element text</inner-element></element>";
XmlPullParser parser = SmackTestUtil.getParserFor(element, parserKind);
assertThrows(XmlPullParserException.class, () -> parser.nextText());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testNextTextOnEndElement(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String element = "<element>Element text</element>";
XmlPullParser parser = SmackTestUtil.getParserFor(element, parserKind);
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.getEventType());
assertEquals(XmlPullParser.Event.TEXT_CHARACTERS, parser.next());
assertEquals(XmlPullParser.Event.END_ELEMENT, parser.next());
assertThrows(XmlPullParserException.class, () -> parser.nextText());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testNextTextOnEmptyElement(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException {
String[] emptyElementStream = Stream.of().toArray(String[]::new);
for (String emptyElement : emptyElementStream) {
XmlPullParser parser = SmackTestUtil.getParserFor(emptyElement, parserKind);
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.getEventType());
assertEquals("", parser.nextText());
}
}
private static void assertAttributeHolds(XmlPullParser parser, int attributeIndex, String expectedLocalpart,
String expectedPrefix, String expectedNamespace) {
QName qname = parser.getAttributeQName(attributeIndex);
String qnameNamespaceUri = qname.getNamespaceURI();
assertEquals(expectedLocalpart, qname.getLocalPart());
assertEquals(expectedPrefix, qname.getPrefix());
assertEquals(expectedNamespace, qnameNamespaceUri);
assertEquals(qname.getLocalPart(), parser.getAttributeName(attributeIndex));
assertEquals(qname.getPrefix(), parser.getAttributePrefix(attributeIndex));
final String expectedGetAttributeNamespace;
if (qnameNamespaceUri.equals(XMLConstants.NULL_NS_URI)) {
expectedGetAttributeNamespace = null;
}
else {
expectedGetAttributeNamespace = qnameNamespaceUri;
}
assertEquals(expectedGetAttributeNamespace, parser.getAttributeNamespace(attributeIndex));
}
}

View file

@ -257,11 +257,9 @@ public class EnhancedDebugger extends SmackDebugger {
new DefaultTableModel( new DefaultTableModel(
new Object[] {"Hide", "Timestamp", "", "", "Message", "Id", "Type", "To", "From"}, new Object[] {"Hide", "Timestamp", "", "", "Message", "Id", "Type", "To", "From"},
0) { 0) {
// CHECKSTYLE:OFF private static final long serialVersionUID = 8136121224474217264L;
private static final long serialVersionUID = 8136121224474217264L; @Override
@Override
public boolean isCellEditable(int rowIndex, int mColIndex) { public boolean isCellEditable(int rowIndex, int mColIndex) {
// CHECKSTYLE:ON
return false; return false;
} }
@ -689,11 +687,9 @@ public class EnhancedDebugger extends SmackDebugger {
new DefaultTableModel(new Object[][] { {"IQ", 0, 0}, {"Message", 0, 0}, new DefaultTableModel(new Object[][] { {"IQ", 0, 0}, {"Message", 0, 0},
{"Presence", 0, 0}, {"Other", 0, 0}, {"Total", 0, 0}}, {"Presence", 0, 0}, {"Other", 0, 0}, {"Total", 0, 0}},
new Object[] {"Type", "Received", "Sent"}) { new Object[] {"Type", "Received", "Sent"}) {
// CHECKSTYLE:OFF private static final long serialVersionUID = -6793886085109589269L;
private static final long serialVersionUID = -6793886085109589269L; @Override
@Override
public boolean isCellEditable(int rowIndex, int mColIndex) { public boolean isCellEditable(int rowIndex, int mColIndex) {
// CHECKSTYLE:ON
return false; return false;
} }
}; };

View file

@ -346,6 +346,7 @@ public final class EnhancedDebuggerWindow {
* *
* @param evt the event that indicates that the root window is closing * @param evt the event that indicates that the root window is closing
*/ */
@SuppressWarnings("UnusedVariable")
private synchronized void rootWindowClosing(WindowEvent evt) { private synchronized void rootWindowClosing(WindowEvent evt) {
// Notify to all the debuggers to stop debugging // Notify to all the debuggers to stop debugging
for (EnhancedDebugger debugger : debuggers) { for (EnhancedDebugger debugger : debuggers) {

View file

@ -21,15 +21,14 @@ import java.io.IOException;
import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.carbons.packet.CarbonExtension; import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Direction; import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Direction;
import org.jivesoftware.smackx.forward.packet.Forwarded; import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.forward.provider.ForwardedProvider; import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/** /**
* This class implements the {@link ExtensionElementProvider} to parse * This class implements the {@link ExtensionElementProvider} to parse
* carbon copied messages from a packet. It will return a {@link CarbonExtension} stanza extension. * carbon copied messages from a packet. It will return a {@link CarbonExtension} stanza extension.
@ -48,11 +47,11 @@ public class CarbonManagerProvider extends ExtensionElementProvider<CarbonExtens
boolean done = false; boolean done = false;
while (!done) { while (!done) {
int eventType = parser.next(); XmlPullParser.Event eventType = parser.next();
if (eventType == XmlPullParser.START_TAG && parser.getName().equals("forwarded")) { if (eventType == XmlPullParser.Event.START_ELEMENT && parser.getName().equals("forwarded")) {
fwd = FORWARDED_PROVIDER.parse(parser); fwd = FORWARDED_PROVIDER.parse(parser);
} }
else if (eventType == XmlPullParser.END_TAG && dir == Direction.valueOf(parser.getName())) else if (eventType == XmlPullParser.Event.END_ELEMENT && dir == Direction.valueOf(parser.getName()))
done = true; done = true;
} }
if (fwd == null) { if (fwd == null) {

View file

@ -42,6 +42,7 @@ import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements; import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements;
import org.jivesoftware.smackx.chat_markers.filter.ChatMarkersFilter; import org.jivesoftware.smackx.chat_markers.filter.ChatMarkersFilter;
import org.jivesoftware.smackx.chat_markers.filter.EligibleForChatMarkerFilter; import org.jivesoftware.smackx.chat_markers.filter.EligibleForChatMarkerFilter;

View file

@ -20,6 +20,7 @@ import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.chat_markers.ChatMarkersState; import org.jivesoftware.smackx.chat_markers.ChatMarkersState;
/** /**

View file

@ -18,6 +18,7 @@ package org.jivesoftware.smackx.chat_markers.filter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements; import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements;
/** /**

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.chat_markers.filter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.chatstates.ChatState; import org.jivesoftware.smackx.chatstates.ChatState;
import org.jivesoftware.smackx.chatstates.ChatStateManager; import org.jivesoftware.smackx.chatstates.ChatStateManager;

Some files were not shown because too many files have changed in this diff Show more