1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-12-22 10:37:59 +01:00

Merge branch '4.4'

This commit is contained in:
Florian Schmaus 2020-11-09 11:08:47 +01:00
commit 71f5cfe3da
28 changed files with 380 additions and 102 deletions

27
.mailmap Normal file
View file

@ -0,0 +1,27 @@
Paul Schaub <vanitasvitae@fsfe.org>
Aditya Borikar <adityaborikar2@gmail.com>
Anno van Vliet <annovanvliet@gmail.com>
Daryl E. Herzmann <akrherz@iastate.edu>
Damian Minkov <damencho@jitsi.org>
Craig Hesling <craig@hesling.com>
Dave Stanley <dave.stanley@gossinteractive.com>
Marcel Heckel <marcel.heckel@ivi.fraunhofer.de>
Candy Lohse <candy.lohse@ivi.fraunhofer.de>
Luca Stucchi <stucchi@skebby.it>
Luke Granger-Brown <git@lukegb.com>
Florian Kimmann <florian.kimmann@tu-dortmund.de>
Adam Stawicki <adamstawicki91@gmail.homas>
Andrey Sokolov <andrey.sokolov@billing.ru>
Andri Khrisharyadi <andri.khrisharyadi@gmail.com>
Andriy Tsykholyas <andriy.tsykholyas@gmail.com>
Fernando Ramirez <f.e.ramirez94@gmail.com>
Marcel Heckel <marcel.heckel@ivi.fraunhofer.de>
Robin Collier <rcollier@b35dd754-fafc-0310-a699-88a17e54d16e>
Thibaut Le Guilly <leguilly.thibaut@gmail.com>
Thomas Pocreau <thomas.pocreau@iadvize.com>
Vadim Fite <vadim.fite@quickblox.com>
Vaibhav Ranglani <vaibhav.ranglani@talentica.com>
Xiaowei YAN <xwyan@minigui.org>
Guus der Kinderen <guus.der.kinderen@gmail.com> guus <guus@b35dd754-fafc-0310-a699-88a17e54d16e>
Jesus Fuentes <fuentes.jesus1010010@gmail.com> Jesus <fuentes31j@gmail.com>

101
NOTICE Normal file
View file

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

View file

@ -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). 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 Professional Services
--------------------- ---------------------

7
resources/get-contributors.sh Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
git shortlog -s |\
cut -f2- |\
grep -v '(no author)' |\
grep '\w \w.*' |\
sort

View file

@ -33,6 +33,7 @@ dependencies {
testFixturesApi "org.assertj:assertj-core:3.11.1" testFixturesApi "org.assertj:assertj-core:3.11.1"
testFixturesApi "org.xmlunit:xmlunit-assertj:$xmlUnitVersion" testFixturesApi "org.xmlunit:xmlunit-assertj:$xmlUnitVersion"
testFixturesApi 'org.hamcrest:hamcrest-library:2.2' testFixturesApi 'org.hamcrest:hamcrest-library:2.2'
testFixturesApi 'com.google.guava:guava:28.2-jre'
} }
class CreateFileTask extends DefaultTask { class CreateFileTask extends DefaultTask {

View file

@ -172,8 +172,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private static final AtomicInteger connectionCounter = new AtomicInteger(0); private static final AtomicInteger connectionCounter = new AtomicInteger(0);
static { static {
// Ensure the SmackConfiguration class is loaded by calling a method in it. Smack.ensureInitialized();
SmackConfiguration.getVersion();
} }
protected enum SyncPointState { protected enum SyncPointState {

View file

@ -112,9 +112,7 @@ import org.minidns.util.InetAddressUtil;
public abstract class ConnectionConfiguration { public abstract class ConnectionConfiguration {
static { static {
// Ensure that Smack is initialized when ConnectionConfiguration is used, or otherwise e.g. Smack.ensureInitialized();
// SmackConfiguration.DEBUG may not be initialized yet.
SmackConfiguration.getVersion();
} }
private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName()); private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName());

View file

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

View file

@ -105,7 +105,10 @@ public final class SmackConfiguration {
* Returns the Smack version information, eg "1.3.0". * Returns the Smack version information, eg "1.3.0".
* *
* @return the Smack version information. * @return the Smack version information.
* @deprecated use {@link Smack#getVersion()} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.6
public static String getVersion() { public static String getVersion() {
return SmackInitialization.SMACK_VERSION; return SmackInitialization.SMACK_VERSION;
} }

View file

@ -43,6 +43,7 @@ public abstract class IqBuilder<IB extends IqBuilder<IB, I>, I extends IQ>
return getThis(); return getThis();
} }
@Override
public abstract I build(); public abstract I build();
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2019 Florian Schmaus * Copyright 2019-2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,4 +41,9 @@ public final class IqData extends AbstractIqBuilder<IqData> {
public IqData getThis() { public IqData getThis() {
return this; return this;
} }
@Override
public Stanza build() {
throw new UnsupportedOperationException();
}
} }

View file

@ -37,6 +37,7 @@ public abstract class MessageOrPresenceBuilder<MP extends MessageOrPresence<? ex
super(stanzaId); super(stanzaId);
} }
@Override
public abstract MP build(); public abstract MP build();
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2019 Florian Schmaus * Copyright 2019-2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -184,6 +184,8 @@ public abstract class StanzaBuilder<B extends StanzaBuilder<B>> implements Stanz
return getThis(); return getThis();
} }
public abstract Stanza build();
public abstract B getThis(); public abstract B getThis();
@Override @Override

View file

@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.xml.namespace.QName; 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.ExtensionElement;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Nonza;
@ -122,7 +122,7 @@ public final class ProviderManager {
// registered providers do not get overwritten by a following Smack // registered providers do not get overwritten by a following Smack
// initialization. This guarantees that Smack is initialized before a // initialization. This guarantees that Smack is initialized before a
// new provider is registered // new provider is registered
SmackConfiguration.getVersion(); Smack.ensureInitialized();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View file

@ -0,0 +1 @@
../../../../../NOTICE

View file

@ -49,8 +49,8 @@ public class SmackConfigurationTest {
// *every* test, those tests are currently disabled. Hopefully this will change in the future. // *every* test, those tests are currently disabled. Hopefully this will change in the future.
@Ignore @Ignore
@Test @Test
public void smackconfigurationVersionShouldInitialzieSmacktTest() { public void smackEnsureInitializedShouldInitialzieSmacktTest() {
SmackConfiguration.getVersion(); Smack.ensureInitialized();
// Only a call to SmackConfiguration.getVersion() should cause Smack to become initialized. // Only a call to SmackConfiguration.getVersion() should cause Smack to become initialized.
assertTrue(SmackConfiguration.isSmackInitialized()); assertTrue(SmackConfiguration.isSmackInitialized());

View file

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

View file

@ -19,7 +19,7 @@ package org.jivesoftware.smack.test.util;
import java.security.Security; import java.security.Security;
import java.util.Base64; import java.util.Base64;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.Smack;
import org.jivesoftware.smack.util.stringencoder.Base64.Encoder; import org.jivesoftware.smack.util.stringencoder.Base64.Encoder;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
@ -31,7 +31,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class SmackTestSuite { public class SmackTestSuite {
static { static {
SmackConfiguration.getVersion(); Smack.ensureInitialized();
org.jivesoftware.smack.util.stringencoder.Base64.setEncoder(new Encoder() { org.jivesoftware.smack.util.stringencoder.Base64.setEncoder(new Encoder() {
@Override @Override

View file

@ -43,7 +43,7 @@ import javax.swing.JPopupMenu;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.Smack;
import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.provider.ProviderManager;
/** /**
@ -232,7 +232,7 @@ public final class EnhancedDebuggerWindow {
versionPanel.setLayout(new BoxLayout(versionPanel, BoxLayout.X_AXIS)); versionPanel.setLayout(new BoxLayout(versionPanel, BoxLayout.X_AXIS));
versionPanel.setMaximumSize(new Dimension(2000, 31)); versionPanel.setMaximumSize(new Dimension(2000, 31));
versionPanel.add(new JLabel(" Smack version: ")); versionPanel.add(new JLabel(" Smack version: "));
JFormattedTextField field = new JFormattedTextField(SmackConfiguration.getVersion()); JFormattedTextField field = new JFormattedTextField(Smack.getVersion());
field.setEditable(false); field.setEditable(false);
field.setBorder(null); field.setBorder(null);
versionPanel.add(field); versionPanel.add(field);

View file

@ -26,8 +26,13 @@ import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza; 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.smack.util.StringUtils;
import org.jivesoftware.smackx.address.packet.MultipleAddresses; import org.jivesoftware.smackx.address.packet.MultipleAddresses;
@ -207,25 +212,44 @@ public class MultipleRecipientManager {
return extension == null ? null : new MultipleRecipientInfo(extension); return extension == null ? null : new MultipleRecipientInfo(extension);
} }
private static void sendToIndividualRecipients(XMPPConnection connection, Stanza packet, private static void sendToIndividualRecipients(XMPPConnection connection, StanzaView stanza,
Collection<? extends Jid> to, Collection<? extends Jid> cc, Collection<? extends Jid> bcc) throws NotConnectedException, InterruptedException { Collection<? extends Jid> to, Collection<? extends Jid> cc, Collection<? extends Jid> 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<Jid> recipients = new ArrayList<>(numRecipients);
if (to != null) { if (to != null) {
for (Jid jid : to) { recipients.addAll(to);
packet.setTo(jid);
connection.sendStanza(new PacketCopy(packet));
}
} }
if (cc != null) { if (cc != null) {
for (Jid jid : cc) { recipients.addAll(cc);
packet.setTo(jid);
connection.sendStanza(new PacketCopy(packet));
}
} }
if (bcc != null) { if (bcc != null) {
for (Jid jid : bcc) { recipients.addAll(bcc);
packet.setTo(jid); }
connection.sendStanza(new PacketCopy(packet));
} final List<Stanza> stanzasToSend = new ArrayList<>(numRecipients);
for (Jid recipient : recipients) {
Stanza stanzaToSend = stanzaBuilder.to(recipient).build();
stanzasToSend.add(stanzaToSend);
}
// TODO: Use XMPPConnection.sendStanzas(Collection<? extends Stanza>) once this method exists.
for (Stanza stanzaToSend : stanzasToSend) {
connection.sendStanza(stanzaToSend);
} }
} }
@ -289,43 +313,4 @@ public class MultipleRecipientManager {
return sdm.findService(MultipleAddresses.NAMESPACE, true); 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;
}
}
} }

View file

@ -22,7 +22,7 @@ import java.util.WeakHashMap;
import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager; 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.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; 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) { private static Version generateVersionFrom(String name, String version, String os) {
if (autoAppendSmackVersion) { if (autoAppendSmackVersion) {
name += " (Smack " + SmackConfiguration.getVersion() + ')'; name += " (Smack " + Smack.getVersion() + ')';
} }
return new Version(name, version, os); return new Version(name, version, os);
} }

View file

@ -149,6 +149,11 @@ public class MultiUserChat {
private EntityFullJid myRoomJid; private EntityFullJid myRoomJid;
private StanzaCollector messageCollector; private StanzaCollector messageCollector;
/**
* Used to signal that the reflected self-presence was received <b>and</b> processed by us.
*/
private volatile boolean processedReflectedSelfPresence;
MultiUserChat(XMPPConnection connection, EntityBareJid room, MultiUserChatManager multiUserChatManager) { MultiUserChat(XMPPConnection connection, EntityBareJid room, MultiUserChatManager multiUserChatManager) {
this.connection = connection; this.connection = connection;
this.room = room; this.room = room;
@ -195,6 +200,7 @@ public class MultiUserChat {
} }
final EntityFullJid myRoomJID = myRoomJid; final EntityFullJid myRoomJID = myRoomJid;
final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID); final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID);
final MUCUser mucUser = MUCUser.from(packet);
switch (presence.getType()) { switch (presence.getType()) {
case available: case available:
@ -205,9 +211,8 @@ public class MultiUserChat {
MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation(); MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation();
MUCRole oldRole = mucExtension.getItem().getRole(); MUCRole oldRole = mucExtension.getItem().getRole();
// Get the new occupant's affiliation & role // Get the new occupant's affiliation & role
mucExtension = MUCUser.from(packet); MUCAffiliation newAffiliation = mucUser.getItem().getAffiliation();
MUCAffiliation newAffiliation = mucExtension.getItem().getAffiliation(); MUCRole newRole = mucUser.getItem().getRole();
MUCRole newRole = mucExtension.getItem().getRole();
// Fire role modification events // Fire role modification events
checkRoleModifications(oldRole, newRole, isUserStatusModification, from); checkRoleModifications(oldRole, newRole, isUserStatusModification, from);
// Fire affiliation modification events // Fire affiliation modification events
@ -216,19 +221,20 @@ public class MultiUserChat {
newAffiliation, newAffiliation,
isUserStatusModification, isUserStatusModification,
from); from);
} } else if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
else { processedReflectedSelfPresence = true;
synchronized (this) {
notify();
}
} else {
// A new occupant has joined the room // A new occupant has joined the room
if (!isUserStatusModification) { for (ParticipantStatusListener listener : participantStatusListeners) {
for (ParticipantStatusListener listener : participantStatusListeners) { listener.joined(from);
listener.joined(from);
}
} }
} }
break; break;
case unavailable: case unavailable:
occupantsMap.remove(from); occupantsMap.remove(from);
MUCUser mucUser = MUCUser.from(packet);
if (mucUser != null && mucUser.hasStatus()) { if (mucUser != null && mucUser.hasStatus()) {
if (isUserStatusModification) { if (isUserStatusModification) {
userHasLeft(); userHasLeft();
@ -377,8 +383,9 @@ public class MultiUserChat {
) )
); );
// @formatter:on // @formatter:on
processedReflectedSelfPresence = false;
StanzaCollector presenceStanzaCollector = null; StanzaCollector presenceStanzaCollector = null;
Presence presence; final Presence reflectedSelfPresence;
try { try {
// This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC. // 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); StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence);
@ -386,7 +393,7 @@ public class MultiUserChat {
selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter); selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter);
// This stanza collector is used to reset the timeout of the selfPresenceCollector. // This stanza collector is used to reset the timeout of the selfPresenceCollector.
presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration); presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration);
presence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout()); reflectedSelfPresence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout());
} }
catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) { catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
// Ensure that all callbacks are removed if there is an exception // 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 // 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 // performed roomnick rewriting
Resourcepart receivedNickname = presence.getFrom().getResourceOrThrow(); Resourcepart receivedNickname = reflectedSelfPresence.getFrom().getResourceOrThrow();
setNickname(receivedNickname); setNickname(receivedNickname);
// Update the list of joined rooms // Update the list of joined rooms
multiUserChatManager.addJoinedRoom(room); multiUserChatManager.addJoinedRoom(room);
return presence; return reflectedSelfPresence;
} }
private void setNickname(Resourcepart nickname) { private void setNickname(Resourcepart nickname) {

View file

@ -51,14 +51,9 @@ public class OptionsExtension extends NodeExtension {
@Override @Override
protected void addXml(XmlStringBuilder xml) { protected void addXml(XmlStringBuilder xml) {
xml.rightAngleBracket();
xml.halfOpenElement(getElementName());
xml.attribute("jid", jid); xml.attribute("jid", jid);
xml.optAttribute("node", getNode());
xml.optAttribute("subid", id); xml.optAttribute("subid", id);
xml.closeEmptyElement(); xml.closeEmptyElement();
xml.closeElement(this);
} }
} }

View file

@ -50,6 +50,7 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.Smack;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
@ -171,7 +172,7 @@ public class SmackIntegrationTestFramework {
// Create a connection manager *after* we created the testRunId (in testRunResult). // Create a connection manager *after* we created the testRunId (in testRunResult).
this.connectionManager = new XmppConnectionManager(this); 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) { if (config.debugger != Configuration.Debugger.none) {
// JUL Debugger will not print any information until configured to print log messages of // JUL Debugger will not print any information until configured to print log messages of
// level FINE // level FINE

View file

@ -15,7 +15,7 @@ dependencies {
api project(':smack-websocket') api project(':smack-websocket')
api project(':smack-tcp') 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' testImplementation 'org.jgrapht:jgrapht-io:1.3.1'
} }

View file

@ -295,14 +295,9 @@ public final class OpenPgpManager extends Manager {
throwIfNoProviderSet(); throwIfNoProviderSet();
OpenPgpStore store = provider.getStore(); OpenPgpStore store = provider.getStore();
PGPKeyRing keys = store.generateKeyRing(ourJid);
try { PGPKeyRing keys = generateKeyRing(ourJid);
store.importSecretKey(ourJid, keys.getSecretKeys()); importKeyRing(ourJid, keys);
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);
}
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keys.getSecretKeys()); OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keys.getSecretKeys());
@ -311,6 +306,23 @@ public final class OpenPgpManager extends Manager {
return fingerprint; 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. * Return the upper-case hex encoded OpenPGP v4 fingerprint of our key pair.
* *

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2016 Florian Schmaus * Copyright 2016-2020 Florian Schmaus
* *
* This file is part of smack-repl. * This file is part of smack-repl.
* *
@ -20,13 +20,13 @@
*/ */
package org.igniterealtime.smack.smackrepl; package org.igniterealtime.smack.smackrepl;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.Smack;
import org.jivesoftware.smack.util.dns.javax.JavaxResolver; import org.jivesoftware.smack.util.dns.javax.JavaxResolver;
public class SmackRepl { public class SmackRepl {
public static void init() { 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 // 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. // won't work on Java SE platforms. Therefore explicitly setup JavaxResolver.
JavaxResolver.setup(); JavaxResolver.setup();

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2019 Florian Schmaus * Copyright 2020-2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -34,7 +34,10 @@ public class StaxXmlPullParserFactory implements XmlPullParserFactory {
// getText(). // getText().
xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true); xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
// Internal and external entity references are prohibited in XMPP (RFC 6120 § 11.1). // 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); xmlInputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
// We don't need to support DTDs in XMPP.
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
} }
@Override @Override