1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-09-10 17:49:38 +02: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

View file

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

View file

@ -149,6 +149,11 @@ public class MultiUserChat {
private EntityFullJid myRoomJid;
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) {
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) {

View file

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