diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..4045d19c0 --- /dev/null +++ b/.mailmap @@ -0,0 +1,27 @@ +Paul Schaub +Aditya Borikar +Anno van Vliet +Daryl E. Herzmann +Damian Minkov +Craig Hesling +Dave Stanley +Marcel Heckel +Candy Lohse +Luca Stucchi +Luke Granger-Brown +Florian Kimmann +Adam Stawicki +Andrey Sokolov +Andri Khrisharyadi +Andriy Tsykholyas +Fernando Ramirez +Marcel Heckel +Robin Collier +Thibaut Le Guilly +Thomas Pocreau +Vadim Fite +Vaibhav Ranglani +Xiaowei YAN + +Guus der Kinderen guus +Jesus Fuentes Jesus diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..2c4a2fd15 --- /dev/null +++ b/NOTICE @@ -0,0 +1,101 @@ + Smack + + An open-source XMPP library + maintained by Florian Schmaus + + https://igniterealtime.org/projects/smack + + +Authors: + +Abmar Barros +Aditya Borikar +Alexander Tovstonozhenko +Alex Wenckus +Andrew Wright +Andrey Prokopenko +Andrey Sokolov +Andrey Starodubtsev +Andri Khrisharyadi +Andriy Tsykholyas +Anno van Vliet +Bastien Rouiller +Benjamin JALON +Bill Lynch +Boris Grozev +Candy Lohse +Cem Yabansu +Chris Deering +Christoph Fiehe +Craig Hesling +Damian Minkov +Daniele Ricci +Daniel Henninger +Daniel Hintze +Daryl E. Herzmann +Dave Cridland +Dave Stanley +David Black +Derek DeMoro +Dmitry Deshevoy +Eng ChongMeng +Fernando Martinez Herrera +Fernando Ramirez +Florian Kimmann +Florian Schmaus +Francisco Vives +Gaston Dombiak +Georg Lukas +Gilles Cornu +Gligor Selimovic +Greg Thomas +Grigory Fedorov +Günther Niess +Guus der Kinderen +Henning Staib +Holger Bergunde +Hugues Bruant +Ingo Bauersachs +Ishan Khanna +Jae Jang +Jared DiCioccio +Jason Sipula +Jay Kline +Jeff Williams +Jesus Fuentes +John Haubrich +Júlio Cesar Bueno Cotta +Lars Noschinski +Luca Stucchi +Luke Granger-Brown +Marcel Heckel +Marilyn Daum +Matteo Campana +Matthew Wild +Matt Tucker +Michael Will +Miguel Hincapie +Mohsen Hariri +Oliver Mihatsch +Paul Schaub +Pete Matern +Piotr Nosek +Rajat Kumar Gupta +Robin Collier +Simon Schuster +Son Goku +Tairs Rzajevs +Thiago Camargo +Thibaut Le Guilly +Thomas Pocreau +Tim Jentz +Timothy Pitt +Tomáš Havlas +Tomas Nosek +Vadim Fite +Vaibhav Ranglani +V Lau +Vyacheslav Blinov +Wolf Posdorfer +Xiaowei YAN +Yash Thakkar \ No newline at end of file diff --git a/README.md b/README.md index 1e22423ce..4ffef04bd 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,15 @@ Start with having a look at the **[Documentation]** and the **[Javadoc]**. Instructions on how to use Smack in your Java or Android project are provided in the [Smack Readme and Upgrade Guide](https://igniterealtime.org/projects/smack/readme). +License +------- + +Most of Smack is governed by the Apache License 2.0 (SPDX License Identifier: Apache 2.0). This license requires that the contents of a NOICE text file are shown "…within a display generated by the Derivative Works, if and wherever such third-party notices normally appear.". + +Smack comes which such a NOTICE file. Moreover, since `smack-core` is licensed under the Apache License 2.0, the conditions apply to every project using Smack. The content of Smack's NOTICE file can conveniently be retrieved using `Smack.getNoticeStream()`. + +Some subprojects of Smack are governed by other licenses. Please refer to the individual subprojects. + Professional Services --------------------- diff --git a/resources/get-contributors.sh b/resources/get-contributors.sh new file mode 100755 index 000000000..c31236464 --- /dev/null +++ b/resources/get-contributors.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +git shortlog -s |\ + cut -f2- |\ + grep -v '(no author)' |\ + grep '\w \w.*' |\ + sort diff --git a/smack-core/build.gradle b/smack-core/build.gradle index 9a8bd757d..b0cd79641 100644 --- a/smack-core/build.gradle +++ b/smack-core/build.gradle @@ -33,6 +33,7 @@ dependencies { testFixturesApi "org.assertj:assertj-core:3.11.1" testFixturesApi "org.xmlunit:xmlunit-assertj:$xmlUnitVersion" testFixturesApi 'org.hamcrest:hamcrest-library:2.2' + testFixturesApi 'com.google.guava:guava:28.2-jre' } class CreateFileTask extends DefaultTask { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index a421d6cd1..e246210c0 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -172,8 +172,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { private static final AtomicInteger connectionCounter = new AtomicInteger(0); static { - // Ensure the SmackConfiguration class is loaded by calling a method in it. - SmackConfiguration.getVersion(); + Smack.ensureInitialized(); } protected enum SyncPointState { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index e6009754b..23e664e94 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -112,9 +112,7 @@ import org.minidns.util.InetAddressUtil; public abstract class ConnectionConfiguration { static { - // Ensure that Smack is initialized when ConnectionConfiguration is used, or otherwise e.g. - // SmackConfiguration.DEBUG may not be initialized yet. - SmackConfiguration.getVersion(); + Smack.ensureInitialized(); } private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName()); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/Smack.java b/smack-core/src/main/java/org/jivesoftware/smack/Smack.java new file mode 100644 index 000000000..690b9bb2d --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/Smack.java @@ -0,0 +1,53 @@ +/** + * + * Copyright 2020 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack; + +import java.io.InputStream; +import java.util.logging.Logger; + +public class Smack { + + private static final Logger LOGGER = Logger.getLogger(Smack.class.getName()); + + private static final String SMACK_ORG = "org.jivesoftware"; + + public static final String SMACK_PACKAGE = SMACK_ORG + ".smack"; + + /** + * Returns the Smack version information, eg "1.3.0". + * + * @return the Smack version information. + */ + public static String getVersion() { + return SmackInitialization.SMACK_VERSION; + } + + private static final String NOTICE_RESOURCE = SMACK_PACKAGE + "/NOTICE"; + + public static InputStream getNoticeStream() { + return ClassLoader.getSystemResourceAsStream(NOTICE_RESOURCE); + } + + public static void ensureInitialized() { + if (SmackConfiguration.isSmackInitialized()) { + return; + } + + String version = getVersion(); + LOGGER.finest("Smack " + version + " has been initialized"); + } +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index 75f23a97f..746c611a0 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -105,7 +105,10 @@ public final class SmackConfiguration { * Returns the Smack version information, eg "1.3.0". * * @return the Smack version information. + * @deprecated use {@link Smack#getVersion()} instead. */ + @Deprecated + // TODO: Remove in Smack 4.6 public static String getVersion() { return SmackInitialization.SMACK_VERSION; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java index 644f077ea..03d15f973 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java @@ -43,6 +43,7 @@ public abstract class IqBuilder, I extends IQ> return getThis(); } + @Override public abstract I build(); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/IqData.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/IqData.java index deba5486e..18173ffeb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/IqData.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/IqData.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,4 +41,9 @@ public final class IqData extends AbstractIqBuilder { public IqData getThis() { return this; } + + @Override + public Stanza build() { + throw new UnsupportedOperationException(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresenceBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresenceBuilder.java index 9a3ff66a0..1caab5c04 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresenceBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresenceBuilder.java @@ -37,6 +37,7 @@ public abstract class MessageOrPresenceBuilder> implements Stanz return getThis(); } + public abstract Stanza build(); + public abstract B getThis(); @Override diff --git a/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java b/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java index f9e2bb560..45260407e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java @@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.xml.namespace.QName; -import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.Smack; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Nonza; @@ -122,7 +122,7 @@ public final class ProviderManager { // registered providers do not get overwritten by a following Smack // initialization. This guarantees that Smack is initialized before a // new provider is registered - SmackConfiguration.getVersion(); + Smack.ensureInitialized(); } @SuppressWarnings("unchecked") diff --git a/smack-core/src/main/resources/org.jivesoftware.smack/NOTICE b/smack-core/src/main/resources/org.jivesoftware.smack/NOTICE new file mode 120000 index 000000000..1fb52812b --- /dev/null +++ b/smack-core/src/main/resources/org.jivesoftware.smack/NOTICE @@ -0,0 +1 @@ +../../../../../NOTICE \ No newline at end of file diff --git a/smack-core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java b/smack-core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java index 2d9de46c1..364c98fd1 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/SmackConfigurationTest.java @@ -49,8 +49,8 @@ public class SmackConfigurationTest { // *every* test, those tests are currently disabled. Hopefully this will change in the future. @Ignore @Test - public void smackconfigurationVersionShouldInitialzieSmacktTest() { - SmackConfiguration.getVersion(); + public void smackEnsureInitializedShouldInitialzieSmacktTest() { + Smack.ensureInitialized(); // Only a call to SmackConfiguration.getVersion() should cause Smack to become initialized. assertTrue(SmackConfiguration.isSmackInitialized()); diff --git a/smack-core/src/test/java/org/jivesoftware/smack/SmackTest.java b/smack-core/src/test/java/org/jivesoftware/smack/SmackTest.java new file mode 100644 index 000000000..7dd49cc39 --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/SmackTest.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2020 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack; + +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Set; + +import com.google.common.collect.Sets; +import org.junit.jupiter.api.Test; + +public class SmackTest { + + @Test + public void getNoticeStreamTest() throws IOException { + Set expectedStrings = Sets.newHashSet( + "Florian Schmaus" + , "Paul Schaub" + ); + int maxLineLength = 0; + + try (InputStream inputStream = Smack.getNoticeStream()) { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + + while (reader.ready()) { + String line = reader.readLine(); + + int lineLength = line.length(); + maxLineLength = Math.max(maxLineLength, lineLength); + + expectedStrings.removeIf(s -> s.equals(line)); + } + } + + assertTrue(expectedStrings.isEmpty()); + assertTrue(maxLineLength < 60); + } +} diff --git a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestSuite.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestSuite.java index 8a34b991b..f88346694 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestSuite.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/SmackTestSuite.java @@ -19,7 +19,7 @@ package org.jivesoftware.smack.test.util; import java.security.Security; import java.util.Base64; -import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.Smack; import org.jivesoftware.smack.util.stringencoder.Base64.Encoder; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -31,7 +31,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; public class SmackTestSuite { static { - SmackConfiguration.getVersion(); + Smack.ensureInitialized(); org.jivesoftware.smack.util.stringencoder.Base64.setEncoder(new Encoder() { @Override diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java index a41c9ab78..9822b59f9 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java @@ -43,7 +43,7 @@ import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; -import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.Smack; import org.jivesoftware.smack.provider.ProviderManager; /** @@ -232,7 +232,7 @@ public final class EnhancedDebuggerWindow { versionPanel.setLayout(new BoxLayout(versionPanel, BoxLayout.X_AXIS)); versionPanel.setMaximumSize(new Dimension(2000, 31)); versionPanel.add(new JLabel(" Smack version: ")); - JFormattedTextField field = new JFormattedTextField(SmackConfiguration.getVersion()); + JFormattedTextField field = new JFormattedTextField(Smack.getVersion()); field.setEditable(false); field.setBorder(null); versionPanel.add(field); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java index eb1d611de..a2c73552a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java @@ -26,8 +26,13 @@ import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.packet.StanzaBuilder; +import org.jivesoftware.smack.packet.StanzaFactory; +import org.jivesoftware.smack.packet.StanzaView; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.address.packet.MultipleAddresses; @@ -207,25 +212,44 @@ public class MultipleRecipientManager { return extension == null ? null : new MultipleRecipientInfo(extension); } - private static void sendToIndividualRecipients(XMPPConnection connection, Stanza packet, + private static void sendToIndividualRecipients(XMPPConnection connection, StanzaView stanza, Collection to, Collection cc, Collection bcc) throws NotConnectedException, InterruptedException { + final StanzaFactory stanzaFactory = connection.getStanzaFactory(); + final StanzaBuilder stanzaBuilder; + if (stanza instanceof Message) { + Message message = (Message) stanza; + stanzaBuilder = stanzaFactory.buildMessageStanzaFrom(message); + } else if (stanza instanceof Presence) { + Presence presence = (Presence) stanza; + stanzaBuilder = stanzaFactory.buildPresenceStanzaFrom(presence); + } else if (stanza instanceof IQ) { + throw new IllegalArgumentException("IQ stanzas have no supported fallback in case no XEP-0033 service is available"); + } else { + throw new AssertionError(); + } + + final int numRecipients = to.size() + cc.size() + bcc.size(); + final List recipients = new ArrayList<>(numRecipients); + if (to != null) { - for (Jid jid : to) { - packet.setTo(jid); - connection.sendStanza(new PacketCopy(packet)); - } + recipients.addAll(to); } if (cc != null) { - for (Jid jid : cc) { - packet.setTo(jid); - connection.sendStanza(new PacketCopy(packet)); - } + recipients.addAll(cc); } if (bcc != null) { - for (Jid jid : bcc) { - packet.setTo(jid); - connection.sendStanza(new PacketCopy(packet)); - } + recipients.addAll(bcc); + } + + final List stanzasToSend = new ArrayList<>(numRecipients); + for (Jid recipient : recipients) { + Stanza stanzaToSend = stanzaBuilder.to(recipient).build(); + stanzasToSend.add(stanzaToSend); + } + + // TODO: Use XMPPConnection.sendStanzas(Collection) once this method exists. + for (Stanza stanzaToSend : stanzasToSend) { + connection.sendStanza(stanzaToSend); } } @@ -289,43 +313,4 @@ public class MultipleRecipientManager { return sdm.findService(MultipleAddresses.NAMESPACE, true); } - /** - * Stanza that holds the XML stanza to send. This class is useful when the same packet - * is needed to be sent to different recipients. Since using the same stanza is not possible - * (i.e. cannot change the TO address of a queues stanza to be sent) then this class was - * created to keep the XML stanza to send. - */ - private static final class PacketCopy extends Stanza { - - private final String elementName; - private final CharSequence text; - - /** - * Create a copy of a stanza with the text to send. The passed text must be a valid text to - * send to the server, no validation will be done on the passed text. - * - * @param text the whole text of the stanza to send - */ - private PacketCopy(Stanza stanza) { - this.elementName = stanza.getElementName(); - this.text = stanza.toXML(); - } - - @Override - public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - return text; - } - - @Override - public String toString() { - return toXML().toString(); - } - - @Override - public String getElementName() { - return elementName; - } - - } - } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java index b111838d2..ec2a4f7d9 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java @@ -22,7 +22,7 @@ import java.util.WeakHashMap; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.Manager; -import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.Smack; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; @@ -152,7 +152,7 @@ public final class VersionManager extends Manager { private static Version generateVersionFrom(String name, String version, String os) { if (autoAppendSmackVersion) { - name += " (Smack " + SmackConfiguration.getVersion() + ')'; + name += " (Smack " + Smack.getVersion() + ')'; } return new Version(name, version, os); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java index f5ee3ec53..51f267694 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -149,6 +149,11 @@ public class MultiUserChat { private EntityFullJid myRoomJid; private StanzaCollector messageCollector; + /** + * Used to signal that the reflected self-presence was received and processed by us. + */ + private volatile boolean processedReflectedSelfPresence; + MultiUserChat(XMPPConnection connection, EntityBareJid room, MultiUserChatManager multiUserChatManager) { this.connection = connection; this.room = room; @@ -195,6 +200,7 @@ public class MultiUserChat { } final EntityFullJid myRoomJID = myRoomJid; final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID); + final MUCUser mucUser = MUCUser.from(packet); switch (presence.getType()) { case available: @@ -205,9 +211,8 @@ public class MultiUserChat { MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation(); MUCRole oldRole = mucExtension.getItem().getRole(); // Get the new occupant's affiliation & role - mucExtension = MUCUser.from(packet); - MUCAffiliation newAffiliation = mucExtension.getItem().getAffiliation(); - MUCRole newRole = mucExtension.getItem().getRole(); + MUCAffiliation newAffiliation = mucUser.getItem().getAffiliation(); + MUCRole newRole = mucUser.getItem().getRole(); // Fire role modification events checkRoleModifications(oldRole, newRole, isUserStatusModification, from); // Fire affiliation modification events @@ -216,19 +221,20 @@ public class MultiUserChat { newAffiliation, isUserStatusModification, from); - } - else { + } else if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) { + processedReflectedSelfPresence = true; + synchronized (this) { + notify(); + } + } else { // A new occupant has joined the room - if (!isUserStatusModification) { - for (ParticipantStatusListener listener : participantStatusListeners) { - listener.joined(from); - } + for (ParticipantStatusListener listener : participantStatusListeners) { + listener.joined(from); } } break; case unavailable: occupantsMap.remove(from); - MUCUser mucUser = MUCUser.from(packet); if (mucUser != null && mucUser.hasStatus()) { if (isUserStatusModification) { userHasLeft(); @@ -377,8 +383,9 @@ public class MultiUserChat { ) ); // @formatter:on + processedReflectedSelfPresence = false; StanzaCollector presenceStanzaCollector = null; - Presence presence; + final Presence reflectedSelfPresence; try { // This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC. StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence); @@ -386,7 +393,7 @@ public class MultiUserChat { selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter); // This stanza collector is used to reset the timeout of the selfPresenceCollector. presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration); - presence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout()); + reflectedSelfPresence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout()); } catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) { // Ensure that all callbacks are removed if there is an exception @@ -399,14 +406,24 @@ public class MultiUserChat { } } + synchronized (presenceListener) { + // Only continue after we have received *and* processed the reflected self-presence. Since presences are + // handled in an extra listener, we may return from enter() without having processed all presences of the + // participants, resulting in a e.g. to low participant counter after enter(). Hence we wait here until the + // processing is done. + while (!processedReflectedSelfPresence) { + presenceListener.wait(); + } + } + // This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may // performed roomnick rewriting - Resourcepart receivedNickname = presence.getFrom().getResourceOrThrow(); + Resourcepart receivedNickname = reflectedSelfPresence.getFrom().getResourceOrThrow(); setNickname(receivedNickname); // Update the list of joined rooms multiUserChatManager.addJoinedRoom(room); - return presence; + return reflectedSelfPresence; } private void setNickname(Resourcepart nickname) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java index 7249f9eea..df39053b5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java @@ -51,14 +51,9 @@ public class OptionsExtension extends NodeExtension { @Override protected void addXml(XmlStringBuilder xml) { - xml.rightAngleBracket(); - - xml.halfOpenElement(getElementName()); xml.attribute("jid", jid); - xml.optAttribute("node", getNode()); xml.optAttribute("subid", id); xml.closeEmptyElement(); - xml.closeElement(this); } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index c5b999a9b..76f6f2184 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -50,6 +50,7 @@ import java.util.logging.Logger; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; +import org.jivesoftware.smack.Smack; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NoResponseException; @@ -171,7 +172,7 @@ public class SmackIntegrationTestFramework { // Create a connection manager *after* we created the testRunId (in testRunResult). this.connectionManager = new XmppConnectionManager(this); - LOGGER.info("SmackIntegrationTestFramework [" + testRunResult.testRunId + ']' + ": Starting\nSmack version: " + SmackConfiguration.getVersion()); + LOGGER.info("SmackIntegrationTestFramework [" + testRunResult.testRunId + ']' + ": Starting\nSmack version: " + Smack.getVersion()); if (config.debugger != Configuration.Debugger.none) { // JUL Debugger will not print any information until configured to print log messages of // level FINE diff --git a/smack-java8-full/build.gradle b/smack-java8-full/build.gradle index 8ba7f227f..8aadd7927 100644 --- a/smack-java8-full/build.gradle +++ b/smack-java8-full/build.gradle @@ -15,7 +15,7 @@ dependencies { api project(':smack-websocket') api project(':smack-tcp') - testImplementation 'com.google.guava:guava:28.2-jre' + testImplementation(testFixtures(project(":smack-core"))) testImplementation 'org.jgrapht:jgrapht-io:1.3.1' } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java index 61c86b61f..ddef3aee9 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java @@ -295,14 +295,9 @@ public final class OpenPgpManager extends Manager { throwIfNoProviderSet(); OpenPgpStore store = provider.getStore(); - PGPKeyRing keys = store.generateKeyRing(ourJid); - try { - store.importSecretKey(ourJid, keys.getSecretKeys()); - store.importPublicKey(ourJid, keys.getPublicKeys()); - } catch (MissingUserIdOnKeyException e) { - // This should never throw, since we set our jid literally one line above this comment. - throw new AssertionError(e); - } + + PGPKeyRing keys = generateKeyRing(ourJid); + importKeyRing(ourJid, keys); OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keys.getSecretKeys()); @@ -311,6 +306,23 @@ public final class OpenPgpManager extends Manager { return fingerprint; } + public PGPKeyRing generateKeyRing(BareJid ourJid) + throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + throwIfNoProviderSet(); + PGPKeyRing keys = provider.getStore().generateKeyRing(ourJid); + return keys; + } + + private void importKeyRing(BareJid ourJid, PGPKeyRing keyRing) throws IOException, PGPException { + try { + provider.getStore().importSecretKey(ourJid, keyRing.getSecretKeys()); + provider.getStore().importPublicKey(ourJid, keyRing.getPublicKeys()); + } catch (MissingUserIdOnKeyException e) { + // This should never throw, since we set our jid literally one line above this comment. + throw new AssertionError(e); + } + } + /** * Return the upper-case hex encoded OpenPGP v4 fingerprint of our key pair. * diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java index 8573ae65a..6181d03a0 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016 Florian Schmaus + * Copyright 2016-2020 Florian Schmaus * * This file is part of smack-repl. * @@ -20,13 +20,13 @@ */ package org.igniterealtime.smack.smackrepl; -import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.Smack; import org.jivesoftware.smack.util.dns.javax.JavaxResolver; public class SmackRepl { public static void init() { - SmackConfiguration.getVersion(); + Smack.ensureInitialized(); // smack-repl also pulls in smack-resolver-minidns which has higher precedence the smack-resolver-javax but // won't work on Java SE platforms. Therefore explicitly setup JavaxResolver. JavaxResolver.setup(); diff --git a/smack-xmlparser-stax/src/main/java/org/jivesoftware/smack/xml/stax/StaxXmlPullParserFactory.java b/smack-xmlparser-stax/src/main/java/org/jivesoftware/smack/xml/stax/StaxXmlPullParserFactory.java index 6b442c5bb..7055d20de 100644 --- a/smack-xmlparser-stax/src/main/java/org/jivesoftware/smack/xml/stax/StaxXmlPullParserFactory.java +++ b/smack-xmlparser-stax/src/main/java/org/jivesoftware/smack/xml/stax/StaxXmlPullParserFactory.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2020-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,10 @@ public class StaxXmlPullParserFactory implements XmlPullParserFactory { // getText(). xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true); // Internal and external entity references are prohibited in XMPP (RFC 6120 § 11.1). + xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xmlInputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); + // We don't need to support DTDs in XMPP. + xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); } @Override