Merge branch '4.4'

This commit is contained in:
Florian Schmaus 2021-10-19 14:33:21 +02:00
commit e842195b71
28 changed files with 282 additions and 86 deletions

View File

@ -6,6 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2' classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2'
classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.0.0"
} }
} }
@ -450,6 +451,7 @@ subprojects {
apply plugin: 'signing' apply plugin: 'signing'
apply plugin: 'checkstyle' apply plugin: 'checkstyle'
apply plugin: 'org.kordamp.gradle.clirr' apply plugin: 'org.kordamp.gradle.clirr'
apply plugin: 'biz.aQute.bnd.builder'
checkstyle { checkstyle {
toolVersion = '8.27' toolVersion = '8.27'
@ -612,9 +614,15 @@ project(':smack-omemo').clirr.enabled = false
project(':smack-omemo-signal').clirr.enabled = false project(':smack-omemo-signal').clirr.enabled = false
subprojects*.jar { subprojects*.jar {
manifest { manifest {
from sharedManifest from sharedManifest
} }
bundle {
bnd(
'-removeheaders': 'Tool, Bnd-*',
'-exportcontents': '*',
)
}
} }
configure(subprojects - gplLicensedProjects) { configure(subprojects - gplLicensedProjects) {

View File

@ -43,6 +43,7 @@ import org.jivesoftware.smack.packet.StanzaError;
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.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.igniterealtime.jbosh.AbstractBody; import org.igniterealtime.jbosh.AbstractBody;
import org.igniterealtime.jbosh.BOSHClient; import org.igniterealtime.jbosh.BOSHClient;
@ -200,6 +201,13 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
+ getHost() + ":" + getPort() + "."; + getHost() + ":" + getPort() + ".";
throw new SmackException.SmackMessageException(errorMessage); throw new SmackException.SmackMessageException(errorMessage);
} }
try {
XmlPullParser parser = PacketParserUtils.getParserFor("<stream:stream xmlns='jabber:client'/>");
onStreamOpen(parser);
} catch (XmlPullParserException | IOException e) {
throw new AssertionError("Failed to setup stream environment", e);
}
} }
@Override @Override
@ -511,7 +519,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
parseAndProcessStanza(parser); parseAndProcessStanza(parser);
break; break;
case "features": case "features":
parseFeatures(parser); parseFeaturesAndNotify(parser);
break; break;
case "error": case "error":
// Some BOSH error isn't stream error. // Some BOSH error isn't stream error.

View File

@ -1,3 +1,8 @@
// Note that this is also declared in the main build.gradle for
// subprojects, but since evaluationDependsOnChildren is enabled we
// need to declare it here too to have bundle{bnd{...}} available
apply plugin: 'biz.aQute.bnd.builder'
description = """\ description = """\
Smack core components.""" Smack core components."""
@ -55,3 +60,11 @@ task createVersionResource(type: CreateFileTask) {
} }
compileJava.dependsOn(createVersionResource) compileJava.dependsOn(createVersionResource)
jar {
bundle {
bnd(
'DynamicImport-Package': '*',
)
}
}

View File

@ -405,7 +405,7 @@ public abstract class ConnectionConfiguration {
/** /**
* 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#required}.
* *
* @return the security mode. * @return the security mode.
*/ */
@ -960,7 +960,7 @@ public abstract class ConnectionConfiguration {
/** /**
* Sets the TLS security mode used when making the connection. By default, * Sets the TLS security mode used when making the connection. By default,
* the mode is {@link SecurityMode#ifpossible}. * the mode is {@link SecurityMode#required}.
* *
* @param securityMode the security mode. * @param securityMode the security mode.
* @return a reference to this builder. * @return a reference to this builder.

View File

@ -120,6 +120,16 @@ public abstract class XMPPException extends Exception {
return error; return error;
} }
/**
* Gets the stanza associated with this exception.
*
* @return the stanza from which this exception was created or {@code null} if the exception is not from a
* stanza.
*/
public Stanza getStanza() {
return stanza;
}
/** /**
* Get the request which triggered the error response causing this exception. * Get the request which triggered the error response causing this exception.
* *

View File

@ -202,7 +202,7 @@ public abstract class IQ extends Stanza implements IqView {
// Add the query section if there is one. // Add the query section if there is one.
IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder( IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(
new IQChildElementXmlStringBuilder(this)); new IQChildElementXmlStringBuilder(getChildElementName(), getChildElementNamespace(), null, xml.getXmlEnvironment()));
// TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change // TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change
// the logic. // the logic.
if (iqChildElement == null) { if (iqChildElement == null) {
@ -399,17 +399,16 @@ public abstract class IQ extends Stanza implements IqView {
private boolean isEmptyElement; private boolean isEmptyElement;
private IQChildElementXmlStringBuilder(IQ iq) { public IQChildElementXmlStringBuilder(ExtensionElement extensionElement,
this(iq.getChildElementName(), iq.getChildElementNamespace()); XmlEnvironment enclosingXmlEnvironment) {
this(extensionElement.getElementName(), extensionElement.getNamespace(), extensionElement.getLanguage(),
enclosingXmlEnvironment);
} }
public IQChildElementXmlStringBuilder(ExtensionElement pe) { private IQChildElementXmlStringBuilder(String elementName, String xmlNs, String xmlLang,
this(pe.getElementName(), pe.getNamespace()); XmlEnvironment enclosingXmlEnvironment) {
} super(elementName, xmlNs, xmlLang, enclosingXmlEnvironment);
this.element = elementName;
private IQChildElementXmlStringBuilder(String element, String namespace) {
prelude(element, namespace);
this.element = element;
} }
public void setEmptyElement() { public void setEmptyElement() {

View File

@ -60,4 +60,8 @@ public class SASLAnonymous extends SASLMechanism {
// SASL Anonymous is always successful :) // SASL Anonymous is always successful :)
} }
@Override
public boolean requiresPassword() {
return false;
}
} }

View File

@ -78,6 +78,7 @@ public class ParserUtils {
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
XmlPullParser.Event event = parser.getEventType(); XmlPullParser.Event event = parser.getEventType();
while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) { while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) {
assert event != XmlPullParser.Event.END_DOCUMENT;
event = parser.next(); event = parser.next();
} }
} }

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2014-2020 Florian Schmaus * Copyright 2014-2021 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -52,11 +52,13 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
} }
public XmlStringBuilder(XmlElement element, XmlEnvironment enclosingXmlEnvironment) { public XmlStringBuilder(XmlElement element, XmlEnvironment enclosingXmlEnvironment) {
sb = new LazyStringBuilder(); this(element.getElementName(), element.getNamespace(), element.getLanguage(), enclosingXmlEnvironment);
halfOpenElement(element); }
public XmlStringBuilder(String elementName, String xmlNs, String xmlLang, XmlEnvironment enclosingXmlEnvironment) {
sb = new LazyStringBuilder();
halfOpenElement(elementName);
String xmlNs = element.getNamespace();
String xmlLang = element.getLanguage();
if (enclosingXmlEnvironment == null) { if (enclosingXmlEnvironment == null) {
xmlnsAttribute(xmlNs); xmlnsAttribute(xmlNs);
xmllangAttribute(xmlLang); xmllangAttribute(xmlLang);

View File

@ -154,8 +154,7 @@ public final class CarbonManager extends Manager {
// because we also reset in authenticated() if the stream got not resumed, but for maximum correctness, // because we also reset in authenticated() if the stream got not resumed, but for maximum correctness,
// also reset here. // also reset here.
enabled_state = false; enabled_state = false;
boolean removed = connection().removeSyncStanzaListener(carbonsListener); connection().removeSyncStanzaListener(carbonsListener);
assert removed;
} }
@Override @Override
public void authenticated(XMPPConnection connection, boolean resumed) { public void authenticated(XMPPConnection connection, boolean resumed) {

View File

@ -153,7 +153,7 @@ public class DataPacketExtension implements ExtensionElement {
@Override @Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace));
xml.closeElement(this); xml.closeElement(this);
return xml; return xml;
} }

View File

@ -615,9 +615,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
if (annouceLocalStreamHost) { if (annouceLocalStreamHost) {
// add local proxy on first position if exists // add local proxy on first position if exists
List<StreamHost> localProxies = getLocalStreamHost(); List<StreamHost> localProxies = getLocalStreamHost();
if (localProxies != null) { streamHosts.addAll(localProxies);
streamHosts.addAll(localProxies);
}
} }
// query SOCKS5 proxies for network settings // query SOCKS5 proxies for network settings
@ -652,12 +650,14 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
/** /**
* Returns the stream host information of the local SOCKS5 proxy containing the IP address and * Returns the stream host information of the local SOCKS5 proxy containing the IP address and
* the port or null if local SOCKS5 proxy is not running. * the port. The returned list may be empty if the local SOCKS5 proxy is not running.
* *
* @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy * @return the stream host information of the local SOCKS5 proxy
* is not running
*/ */
public List<StreamHost> getLocalStreamHost() { public List<StreamHost> getLocalStreamHost() {
// Ensure that the local SOCKS5 proxy is running (if enabled).
Socks5Proxy.getSocks5Proxy();
List<StreamHost> streamHosts = new ArrayList<>(); List<StreamHost> streamHosts = new ArrayList<>();
XMPPConnection connection = connection(); XMPPConnection connection = connection();

View File

@ -258,6 +258,14 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView {
return features.contains(new Feature(feature)); return features.contains(new Feature(feature));
} }
public static boolean nullSafeContainsFeature(DiscoverInfo discoverInfo, CharSequence feature) {
if (discoverInfo == null) {
return false;
}
return discoverInfo.containsFeature(feature);
}
@Override @Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.optAttribute("node", getNode()); xml.optAttribute("node", getNode());

View File

@ -53,7 +53,7 @@ public class JingleUtil {
JingleContentDescription description, JingleContentDescription description,
JingleContentTransport transport) { JingleContentTransport transport) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setAction(JingleAction.session_initiate) jb.setAction(JingleAction.session_initiate)
.setSessionId(sessionId) .setSessionId(sessionId)
.setInitiator(connection.getUser()); .setInitiator(connection.getUser());
@ -118,7 +118,7 @@ public class JingleUtil {
JingleContentDescription description, JingleContentDescription description,
JingleContentTransport transport) { JingleContentTransport transport) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setResponder(connection.getUser()) jb.setResponder(connection.getUser())
.setAction(JingleAction.session_accept) .setAction(JingleAction.session_accept)
.setSessionId(sessionId); .setSessionId(sessionId);
@ -153,7 +153,7 @@ public class JingleUtil {
} }
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) { public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setAction(JingleAction.session_terminate) jb.setAction(JingleAction.session_terminate)
.setSessionId(sessionId) .setSessionId(sessionId)
.setReason(reason); .setReason(reason);
@ -232,7 +232,7 @@ public class JingleUtil {
public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId, public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId,
JingleContent.Creator contentCreator, String contentName) { JingleContent.Creator contentCreator, String contentName) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setAction(JingleAction.session_terminate) jb.setAction(JingleAction.session_terminate)
.setSessionId(sessionId); .setSessionId(sessionId);
@ -314,7 +314,7 @@ public class JingleUtil {
} }
public Jingle createSessionPing(FullJid recipient, String sessionId) { public Jingle createSessionPing(FullJid recipient, String sessionId) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setSessionId(sessionId) jb.setSessionId(sessionId)
.setAction(JingleAction.session_info); .setAction(JingleAction.session_info);
@ -343,7 +343,7 @@ public class JingleUtil {
public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId, public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
JingleContent.Creator contentCreator, String contentName, JingleContent.Creator contentCreator, String contentName,
JingleContentTransport transport) { JingleContentTransport transport) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setInitiator(initiator) jb.setInitiator(initiator)
.setSessionId(sessionId) .setSessionId(sessionId)
.setAction(JingleAction.transport_replace); .setAction(JingleAction.transport_replace);
@ -370,7 +370,7 @@ public class JingleUtil {
public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId, public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
JingleContent.Creator contentCreator, String contentName, JingleContent.Creator contentCreator, String contentName,
JingleContentTransport transport) { JingleContentTransport transport) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setAction(JingleAction.transport_accept) jb.setAction(JingleAction.transport_accept)
.setInitiator(initiator) .setInitiator(initiator)
.setSessionId(sessionId); .setSessionId(sessionId);
@ -397,7 +397,7 @@ public class JingleUtil {
public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId, public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId,
JingleContent.Creator contentCreator, String contentName, JingleContent.Creator contentCreator, String contentName,
JingleContentTransport transport) { JingleContentTransport transport) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection);
jb.setAction(JingleAction.transport_reject) jb.setAction(JingleAction.transport_reject)
.setInitiator(initiator) .setInitiator(initiator)
.setSessionId(sessionId); .setSessionId(sessionId);

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus * Copyright 2003-2007 Jive Software, 2014-2021 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,11 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IqBuilder;
import org.jivesoftware.smack.packet.IqData;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
@ -65,9 +69,9 @@ public final class Jingle extends IQ {
private final List<JingleContent> contents; private final List<JingleContent> contents;
private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason, private Jingle(Builder builder, String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
List<JingleContent> contents) { List<JingleContent> contents) {
super(ELEMENT, NAMESPACE); super(builder, ELEMENT, NAMESPACE);
this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null"); this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null");
this.action = Objects.requireNonNull(action, "Jingle action must not be null"); this.action = Objects.requireNonNull(action, "Jingle action must not be null");
this.initiator = initiator; this.initiator = initiator;
@ -169,11 +173,31 @@ public final class Jingle extends IQ {
return xml; return xml;
} }
/**
* Deprecated, do not use.
*
* @return a builder.
* @deprecated use {@link #builder(XMPPConnection)} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.6.
public static Builder getBuilder() { public static Builder getBuilder() {
return new Builder(); return builder(StandardStanzaIdSource.DEFAULT.getNewStanzaId());
} }
public static final class Builder { public static Builder builder(XMPPConnection connection) {
return new Builder(connection);
}
public static Builder builder(IqData iqData) {
return new Builder(iqData);
}
public static Builder builder(String stanzaId) {
return new Builder(stanzaId);
}
public static final class Builder extends IqBuilder<Builder, Jingle> {
private String sid; private String sid;
private JingleAction action; private JingleAction action;
@ -186,7 +210,16 @@ public final class Jingle extends IQ {
private List<JingleContent> contents; private List<JingleContent> contents;
private Builder() { Builder(IqData iqCommon) {
super(iqCommon);
}
Builder(XMPPConnection connection) {
super(connection);
}
Builder(String stanzaId) {
super(stanzaId);
} }
public Builder setSessionId(String sessionId) { public Builder setSessionId(String sessionId) {
@ -228,8 +261,14 @@ public final class Jingle extends IQ {
return this; return this;
} }
@Override
public Jingle build() { public Jingle build() {
return new Jingle(sid, action, initiator, responder, reason, contents); return new Jingle(this, sid, action, initiator, responder, reason, contents);
}
@Override
public Builder getThis() {
return this;
} }
} }
} }

View File

@ -145,6 +145,11 @@ public final class JingleContent implements XmlElement {
xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition); xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition);
xml.attribute(NAME_ATTRIBUTE_NAME, name); xml.attribute(NAME_ATTRIBUTE_NAME, name);
xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders); xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders);
if (description == null && transport == null) {
return xml.closeEmptyElement();
}
xml.rightAngleBracket(); xml.rightAngleBracket();
xml.optAppend(description); xml.optAppend(description);

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2017-2019 Florian Schmaus * Copyright 2017-2021 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,11 +19,12 @@ package org.jivesoftware.smackx.jingle.provider;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.packet.IqData;
import org.jivesoftware.smack.packet.StandardExtensionElement; import org.jivesoftware.smack.packet.StandardExtensionElement;
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.parsing.StandardExtensionElementProvider; import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.provider.IqProvider;
import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
@ -40,13 +41,13 @@ import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport;
import org.jxmpp.jid.FullJid; import org.jxmpp.jid.FullJid;
public class JingleProvider extends IQProvider<Jingle> { public class JingleProvider extends IqProvider<Jingle> {
private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName()); private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
@Override @Override
public Jingle parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { public Jingle parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
Jingle.Builder builder = Jingle.getBuilder(); Jingle.Builder builder = Jingle.builder(iqData);
String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME); String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
if (actionString != null) { if (actionString != null) {

View File

@ -36,6 +36,10 @@ public abstract class JingleTransportManager<D extends JingleContentTransport> i
} }
public XMPPConnection getConnection() { public XMPPConnection getConnection() {
return connection();
}
public XMPPConnection connection() {
return connection; return connection;
} }

View File

@ -148,7 +148,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders, public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders,
JingleContent.Creator contentCreator, String contentName, String streamId, JingleContent.Creator contentCreator, String contentName, String streamId,
String candidateId) { String candidateId) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection());
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info); jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
JingleContent.Builder cb = JingleContent.getBuilder(); JingleContent.Builder cb = JingleContent.getBuilder();
@ -165,7 +165,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
} }
public Jingle createCandidateError(FullJid remote, FullJid initiator, String sessionId, JingleContent.Senders senders, JingleContent.Creator creator, String name, String streamId) { public Jingle createCandidateError(FullJid remote, FullJid initiator, String sessionId, JingleContent.Senders senders, JingleContent.Creator creator, String name, String streamId) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection());
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info); jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
JingleContent.Builder cb = JingleContent.getBuilder(); JingleContent.Builder cb = JingleContent.getBuilder();
@ -184,7 +184,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
public Jingle createProxyError(FullJid remote, FullJid initiator, String sessionId, public Jingle createProxyError(FullJid remote, FullJid initiator, String sessionId,
JingleContent.Senders senders, JingleContent.Creator creator, JingleContent.Senders senders, JingleContent.Creator creator,
String name, String streamId) { String name, String streamId) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection());
jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator); jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator);
JingleContent.Builder cb = JingleContent.getBuilder(); JingleContent.Builder cb = JingleContent.getBuilder();
@ -202,7 +202,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
public Jingle createCandidateActivated(FullJid remote, FullJid initiator, String sessionId, public Jingle createCandidateActivated(FullJid remote, FullJid initiator, String sessionId,
JingleContent.Senders senders, JingleContent.Creator creator, JingleContent.Senders senders, JingleContent.Creator creator,
String name, String streamId, String candidateId) { String name, String streamId, String candidateId) {
Jingle.Builder jb = Jingle.getBuilder(); Jingle.Builder jb = Jingle.builder(connection());
jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info); jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info);
JingleContent.Builder cb = JingleContent.getBuilder(); JingleContent.Builder cb = JingleContent.getBuilder();

View File

@ -212,7 +212,12 @@ public class MultiUserChat {
switch (presence.getType()) { switch (presence.getType()) {
case available: case available:
Presence oldPresence = occupantsMap.put(from, presence); Presence oldPresence = occupantsMap.put(from, presence);
if (oldPresence != null) { if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
processedReflectedSelfPresence = true;
synchronized (this) {
notify();
}
} else if (oldPresence != null) {
// Get the previous occupant's affiliation & role // Get the previous occupant's affiliation & role
MUCUser mucExtension = MUCUser.from(oldPresence); MUCUser mucExtension = MUCUser.from(oldPresence);
MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation(); MUCAffiliation oldAffiliation = mucExtension.getItem().getAffiliation();
@ -228,11 +233,6 @@ public class MultiUserChat {
newAffiliation, newAffiliation,
isUserStatusModification, isUserStatusModification,
from); from);
} else if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
processedReflectedSelfPresence = true;
synchronized (this) {
notify();
}
} else { } else {
// A new occupant has joined the room // A new occupant has joined the room
for (ParticipantStatusListener listener : participantStatusListeners) { for (ParticipantStatusListener listener : participantStatusListeners) {
@ -259,23 +259,24 @@ public class MultiUserChat {
listener.left(from); listener.left(from);
} }
} }
}
Destroy destroy = mucUser.getDestroy(); Destroy destroy = mucUser.getDestroy();
// The room has been destroyed. // The room has been destroyed.
if (destroy != null) { if (destroy != null) {
EntityBareJid alternateMucJid = destroy.getJid(); EntityBareJid alternateMucJid = destroy.getJid();
final MultiUserChat alternateMuc; final MultiUserChat alternateMuc;
if (alternateMucJid == null) { if (alternateMucJid == null) {
alternateMuc = null; alternateMuc = null;
} else { } else {
alternateMuc = multiUserChatManager.getMultiUserChat(alternateMucJid); alternateMuc = multiUserChatManager.getMultiUserChat(alternateMucJid);
} }
for (UserStatusListener listener : userStatusListeners) { for (UserStatusListener listener : userStatusListeners) {
listener.roomDestroyed(alternateMuc, destroy.getReason()); listener.roomDestroyed(alternateMuc, destroy.getReason());
}
} }
} }
if (isUserStatusModification) { if (isUserStatusModification) {
for (UserStatusListener listener : userStatusListeners) { for (UserStatusListener listener : userStatusListeners) {
listener.removed(mucUser, presence); listener.removed(mucUser, presence);
@ -793,11 +794,14 @@ public class MultiUserChat {
StanzaFilter reflectedLeavePresenceFilter = new AndFilter(reflectedLeavePresenceFilters); StanzaFilter reflectedLeavePresenceFilter = new AndFilter(reflectedLeavePresenceFilters);
// Reset occupant information first so that we are assume that we left the room even if sendStanza() would Presence reflectedLeavePresence;
// throw. try {
userHasLeft(); reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow();
} finally {
Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow(); // Reset occupant information after we send the leave presence. This ensures that we only call userHasLeft()
// and reset the local MUC state after we successfully left the MUC (or if an exception occurred).
userHasLeft();
}
return reflectedLeavePresence; return reflectedLeavePresence;
} }
@ -2579,7 +2583,7 @@ public class MultiUserChat {
} }
public boolean serviceSupportsStableIds() { public boolean serviceSupportsStableIds() {
return mucServiceDiscoInfo.containsFeature(MultiUserChatConstants.STABLE_ID_FEATURE); return DiscoverInfo.nullSafeContainsFeature(mucServiceDiscoInfo, MultiUserChatConstants.STABLE_ID_FEATURE);
} }
@Override @Override

View File

@ -75,8 +75,7 @@ public class JingleContentTest extends SmackTestSuite {
assertEquals(content1.toXML().toString(), builder.build().toXML().toString()); assertEquals(content1.toXML().toString(), builder.build().toXML().toString());
String xml = String xml =
"<content xmlns='urn:xmpp:jingle:1' creator='initiator' disposition='session' name='A name' senders='both'>" + "<content xmlns='urn:xmpp:jingle:1' creator='initiator' disposition='session' name='A name' senders='both'/>";
"</content>";
assertEquals(xml, content1.toXML().toString()); assertEquals(xml, content1.toXML().toString());
} }
} }

View File

@ -38,7 +38,7 @@ public class JingleTest extends SmackTestSuite {
@Test @Test
public void emptyBuilderTest() { public void emptyBuilderTest() {
Jingle.Builder builder = Jingle.getBuilder(); Jingle.Builder builder = Jingle.builder("id");
assertThrows(IllegalArgumentException.class, () -> { assertThrows(IllegalArgumentException.class, () -> {
builder.build(); builder.build();
}); });
@ -48,7 +48,7 @@ public class JingleTest extends SmackTestSuite {
public void onlySessionIdBuilderTest() { public void onlySessionIdBuilderTest() {
String sessionId = "testSessionId"; String sessionId = "testSessionId";
Jingle.Builder builder = Jingle.getBuilder(); Jingle.Builder builder = Jingle.builder("id");
builder.setSessionId(sessionId); builder.setSessionId(sessionId);
assertThrows(IllegalArgumentException.class, () -> { assertThrows(IllegalArgumentException.class, () -> {
builder.build(); builder.build();
@ -59,7 +59,7 @@ public class JingleTest extends SmackTestSuite {
public void parserTest() throws XmppStringprepException { public void parserTest() throws XmppStringprepException {
String sessionId = "testSessionId"; String sessionId = "testSessionId";
Jingle.Builder builder = Jingle.getBuilder(); Jingle.Builder builder = Jingle.builder("id");
builder.setSessionId(sessionId); builder.setSessionId(sessionId);
builder.setAction(JingleAction.session_initiate); builder.setAction(JingleAction.session_initiate);

View File

@ -0,0 +1,48 @@
/**
*
* Copyright 2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.element;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.StreamOpen;
import org.junit.jupiter.api.Test;
public class JingleTest {
@Test
public void noRedundantNamespaceTest() {
Jingle.Builder jingleBuilder = Jingle.builder("test-id");
jingleBuilder.setSessionId("MySession");
jingleBuilder.setAction(JingleAction.content_accept);
JingleContent.Builder jingleContentBuilder = JingleContent.getBuilder();
jingleContentBuilder.setName("Hello world");
jingleContentBuilder.setCreator(JingleContent.Creator.initiator);
jingleBuilder.addJingleContent(jingleContentBuilder.build());
Jingle iq = jingleBuilder.build();
String actualXml = iq.toXML(StreamOpen.CLIENT_NAMESPACE).toString();
String expectedXml
= "<iq id='test-id' type='set'>"
+ "<jingle xmlns='urn:xmpp:jingle:1' action='content-accept' sid='MySession'>"
+ "<content creator='initiator' name='Hello world'/>"
+ "</jingle></iq>";
assertEquals(expectedXml, actualXml);
}
}

View File

@ -118,7 +118,7 @@ public class RoomInvitation implements ExtensionElement {
@Override @Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace));
xml.closeElement(this); xml.closeElement(this);
return xml; return xml;
} }

View File

@ -113,7 +113,7 @@ public class RoomTransfer implements ExtensionElement {
@Override @Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace));
xml.closeElement(this); xml.closeElement(this);
return xml; return xml;
} }

View File

@ -1,3 +1,8 @@
// Note that this is also declared in the main build.gradle for
// subprojects, but since evaluationDependsOnChildren is enabled we
// need to declare it here too to have bundle{bnd{...}} available
apply plugin: 'biz.aQute.bnd.builder'
description = """\ description = """\
Smack XML parser using Stax.""" Smack XML parser using Stax."""
@ -5,3 +10,13 @@ dependencies {
api project(':smack-xmlparser') api project(':smack-xmlparser')
//testCompile project(path: ":smack-xmlparser", configuration: "testRuntime") //testCompile project(path: ":smack-xmlparser", configuration: "testRuntime")
} }
jar {
bundle {
bnd(
// see http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.loader.html
'Require-Capability': 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)"',
'Provide-Capability': "osgi.serviceloader;osgi.serviceloader=org.jivesoftware.smack.xml.XmlPullParserFactory;register:=org.jivesoftware.smack.xml.stax.StaxXmlPullParserFactory",
)
}
}

View File

@ -1,3 +1,8 @@
// Note that this is also declared in the main build.gradle for
// subprojects, but since evaluationDependsOnChildren is enabled we
// need to declare it here too to have bundle{bnd{...}} available
apply plugin: 'biz.aQute.bnd.builder'
description = """\ description = """\
Smack XML parser using XPP3.""" Smack XML parser using XPP3."""
@ -11,3 +16,13 @@ dependencies {
api project(':smack-xmlparser') api project(':smack-xmlparser')
//testCompile project(path: ":smack-xmlparser", configuration: "testRuntime") //testCompile project(path: ":smack-xmlparser", configuration: "testRuntime")
} }
jar {
bundle {
bnd(
// see http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.loader.html
'Require-Capability': 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)"',
'Provide-Capability': "osgi.serviceloader;osgi.serviceloader=org.jivesoftware.smack.xml.XmlPullParserFactory;register:=org.jivesoftware.smack.xml.xpp3.Xpp3XmlPullParserFactory",
)
}
}

View File

@ -1,2 +1,16 @@
// Note that this is also declared in the main build.gradle for
// subprojects, but since evaluationDependsOnChildren is enabled we
// need to declare it here too to have bundle{bnd{...}} available
apply plugin: 'biz.aQute.bnd.builder'
description = """\ description = """\
Smack XML parser fundamentals""" Smack XML parser fundamentals"""
jar {
bundle {
bnd(
// see http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.loader.html
'Require-Capability': 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)"',
)
}
}