1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-09-11 01:59:38 +02:00

Compare commits

...

21 commits

Author SHA1 Message Date
Florian Schmaus
3003094130
Merge pull request #412 from Flowdalic/data-form-type-annotations
[xdata] Parse forms of any kind without field type annotations
2020-07-23 17:24:48 +02:00
Florian Schmaus
62b1320643
Merge pull request #410 from adiaholic/integrationTest
Add SimpleXmppConnectionIntegrationTest
2020-07-23 16:10:36 +02:00
Florian Schmaus
1bd097ed9b
Merge pull request #411 from Flowdalic/sasl
SASL / getFeature()
2020-07-23 16:09:57 +02:00
Florian Schmaus
103ffabc08 [xdata] Parse forms of any kind without field type annotations
We previously only looked in the registry for 'submit' type forms. But
also 'result' type forms may be send without field type
annotations. Same is true, but less likely, for 'form' type forms.
2020-07-23 15:47:17 +02:00
Florian Schmaus
329948b442 Add XMPP.(get|has)Feature(Class|QName) and deprecate (String, String) 2020-07-23 14:32:14 +02:00
Florian Schmaus
cbdde0f541
Merge pull request #358 from vanitasvitae/sce
XEP-0420: Stanza Content Encryption
2020-07-23 14:27:08 +02:00
Florian Schmaus
f1e10bc6bc
Merge pull request #409 from vanitasvitae/connectionListenerConnecting
ConnectionListener: Add connecting(XMPPConnection) method.
2020-07-23 14:19:14 +02:00
78f37a909e
Add support for XEP-0420: Stanza Content Encryption 2020-07-23 11:02:28 +02:00
Aditya Borikar
fcaeca48ec Add SimpleXmppConnectionIntegrationTest 2020-07-22 16:35:56 +05:30
Florian Schmaus
bcfe7b12a4 [tcp] Mark SM as disabled prior resource binding
Otherwise we may send a SM ack request with the bind IQ request,
causing a stream error:

D/SMACK: SENT (0):
    <iq id='SETVB-74' type='set'>
      <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
      </bind>
    </iq>
    <r xmlns='urn:xmpp:sm:3'/>
D/SMACK: RECV (0):
    <iq id='SETVB-74' type='result'>
      <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
        <jid>
          snakeman@wiuwiu.de/eHeTGlCq
        </jid>
      </bind>
    </iq>
    <stream:error>
      <unsupported-stanza-type xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
    </stream:error>
    </stream:stream>
2020-07-20 14:26:06 +02:00
Florian Schmaus
2edf27f5da
Merge pull request #406 from vanitasvitae/oxSecretKeyBackupRestore
OX: Improvements to Secret key backup restore function
2020-07-18 22:40:20 +02:00
Florian Schmaus
dad2a1ad2e Bump MiniDNS version to 1.0.0 2020-07-18 21:55:56 +02:00
Florian Schmaus
7d6374b04b Bump jXMPP version to 1.0.0 2020-07-18 21:55:47 +02:00
Florian Schmaus
2b423b911e Merge branch 'master' of github.com:igniterealtime/Smack 2020-07-18 21:47:06 +02:00
2f98bb25e2 OX: Fix incorrect documentation URL 2020-07-18 15:18:37 +02:00
15b8a81493 OX: Trust secret key upon backup import 2020-07-18 15:18:29 +02:00
bc599a6dd6
Add callback method for when Smack is connecting 2020-07-18 12:50:08 +02:00
a587827ef8
Remove unnecessary lines from OX backup restore routine 2020-07-15 18:08:18 +02:00
Florian Schmaus
d3a99a133a [core] Fix log/exception message of XmppElementUtil
The Class.toString() already prefixes the resulting string with "class
", no need to state it explicitly in the log message that this is a
class.
2020-07-13 08:50:10 +02:00
00acdfcb9e
Add QName field in OriginIdElement 2020-07-03 00:24:05 +02:00
Florian Schmaus
91337150e7 [sasl] Rename getChannelBindingName() to getGs2CbindFlag()
As this returns the value of the SCRAM gs2-cbind-flag.
2020-07-02 13:07:17 +02:00
42 changed files with 1427 additions and 60 deletions

View file

@ -122,8 +122,8 @@ allprojects {
// See also:
// - https://issues.apache.org/jira/browse/MNG-6232
// - https://issues.igniterealtime.org/browse/SMACK-858
jxmppVersion = '0.7.0-alpha6'
miniDnsVersion = '0.4.0-alpha6'
jxmppVersion = '1.0.0'
miniDnsVersion = '1.0.0'
smackMinAndroidSdk = 19
junitVersion = '5.6.2'
commonsIoVersion = '2.6'

View file

@ -123,6 +123,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
| [Consistent Color Generation](consistent_colors.md) | [XEP-0392](https://xmpp.org/extensions/xep-0392.html) | 0.6.0 | Generate consistent colors for identifiers like usernames to provide a consistent user experience. |
| [Message Markup](messagemarkup.md) | [XEP-0394](https://xmpp.org/extensions/xep-0394.html) | 0.1.0 | Style message bodies while keeping body and markup information separated. |
| DNS Queries over XMPP (DoX) | [XEP-0418](https://xmpp.org/extensions/xep-0418.html) | 0.1.0 | Send DNS queries and responses over XMPP. |
| Stanza Content Encryption | [XEP-0420](https://xmpp.org/extensions/xep-0420.html) | 0.3.0 | End-to-end encryption of arbitrary extension elements. Smack provides elements and providers to be used by encryption mechanisms. |
| Message Fastening | [XEP-0422](https://xmpp.org/extensions/xep-0422.html) | 0.1.1 | Mark payloads on a message to be logistically fastened to a previous message. |
| Message Retraction | [XEP-0424](https://xmpp.org/extensions/xep-0424.html) | 0.2.0 | Mark messages as retracted. |
| Fallback Indication | [XEP-0428](https://xmpp.org/extensions/xep-0428.html) | 0.1.0 | Declare body elements of a message as ignorable fallback for naive legacy clients. |

View file

@ -514,6 +514,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// Check if not already connected
throwAlreadyConnectedExceptionIfAppropriate();
// Notify connection listeners that we are trying to connect
callConnectionConnectingListener();
// Reset the connection state
initState();
closingStreamReceived = false;
@ -760,7 +763,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
user = response.getJid();
xmppServiceDomain = user.asDomainBareJid();
Session.Feature sessionFeature = getFeature(Session.ELEMENT, Session.NAMESPACE);
Session.Feature sessionFeature = getFeature(Session.Feature.class);
// Only bind the session if it's announced as stream feature by the server, is not optional and not disabled
// For more information see http://tools.ietf.org/html/draft-cridland-xmpp-session-01
if (sessionFeature != null && !sessionFeature.isOptional()) {
@ -1680,6 +1683,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
}
protected void callConnectionConnectingListener() {
for (ConnectionListener listener : connectionListeners) {
listener.connecting(this);
}
}
protected void callConnectionConnectedListener() {
for (ConnectionListener listener : connectionListeners) {
listener.connected(this);
@ -1904,14 +1913,13 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@SuppressWarnings("unchecked")
@Override
public <F extends FullyQualifiedElement> F getFeature(String element, String namespace) {
QName qname = new QName(namespace, element);
public <F extends FullyQualifiedElement> F getFeature(QName qname) {
return (F) streamFeatures.get(qname);
}
@Override
public boolean hasFeature(String element, String namespace) {
return getFeature(element, namespace) != null;
public boolean hasFeature(QName qname) {
return streamFeatures.containsKey(qname);
}
protected void addStreamFeature(FullyQualifiedElement feature) {

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software.
* Copyright 2003-2007 Jive Software, 2020 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,6 +28,16 @@ package org.jivesoftware.smack;
*/
public interface ConnectionListener {
/**
* Notification that the connection is in the process of connecting.
* This method is called when {@link AbstractXMPPConnection#connect()} is executed.
*
* @param connection connection
* @since 4.4
*/
default void connecting(XMPPConnection connection) {
}
/**
* Notification that the connection has been successfully connected to the remote endpoint (e.g. the XMPP server).
* <p>

View file

@ -356,7 +356,7 @@ public final class SASLAuthentication {
}
private List<String> getServerMechanisms() {
Mechanisms mechanisms = connection.getFeature(Mechanisms.ELEMENT, Mechanisms.NAMESPACE);
Mechanisms mechanisms = connection.getFeature(Mechanisms.class);
if (mechanisms == null) {
return Collections.emptyList();
}

View file

@ -18,6 +18,8 @@ package org.jivesoftware.smack;
import java.util.concurrent.TimeUnit;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
@ -36,6 +38,7 @@ import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaFactory;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.Predicate;
import org.jivesoftware.smack.util.XmppElementUtil;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityFullJid;
@ -576,8 +579,39 @@ public interface XMPPConnection {
* @param element TODO javadoc me please
* @param namespace TODO javadoc me please
* @return a stanza extensions of the feature or <code>null</code>
* @deprecated use {@link #getFeature(Class)} instead.
*/
<F extends FullyQualifiedElement> F getFeature(String element, String namespace);
// TODO: Remove in Smack 4.5.
@Deprecated
default <F extends FullyQualifiedElement> F getFeature(String element, String namespace) {
QName qname = new QName(namespace, element);
return getFeature(qname);
}
/**
* Get the feature stanza extensions for a given stream feature of the
* server, or <code>null</code> if the server doesn't support that feature.
*
* @param <F> {@link ExtensionElement} type of the feature.
* @param qname the qualified name of the XML element of feature.
* @return a stanza extensions of the feature or <code>null</code>
* @since 4.4
*/
<F extends FullyQualifiedElement> F getFeature(QName qname);
/**
* Get the feature stanza extensions for a given stream feature of the
* server, or <code>null</code> if the server doesn't support that feature.
*
* @param <F> {@link ExtensionElement} type of the feature.
* @param featureClass the class of the feature.
* @return a stanza extensions of the feature or <code>null</code>
* @since 4.4
*/
default <F extends FullyQualifiedElement> F getFeature(Class<F> featureClass) {
QName qname = XmppElementUtil.getQNameFor(featureClass);
return getFeature(qname);
}
/**
* Return true if the server supports the given stream feature.
@ -586,7 +620,18 @@ public interface XMPPConnection {
* @param namespace TODO javadoc me please
* @return true if the server supports the stream feature.
*/
boolean hasFeature(String element, String namespace);
default boolean hasFeature(String element, String namespace) {
QName qname = new QName(namespace, element);
return hasFeature(qname);
}
/**
* Return true if the server supports the given stream feature.
*
* @param qname the qualified name of the XML element of feature.
* @return true if the server supports the stream feature.
*/
boolean hasFeature(QName qname);
/**
* Send an IQ request asynchronously. The connection's default reply timeout will be used.

View file

@ -1,6 +1,6 @@
/**
*
* Copyright © 2014-2015 Florian Schmaus
* Copyright © 2014-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.
@ -19,6 +19,8 @@ package org.jivesoftware.smack.compress.packet;
import java.util.Collections;
import java.util.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.util.XmlStringBuilder;
@ -55,6 +57,7 @@ public class Compress implements Nonza {
public static class Feature implements ExtensionElement {
public static final String ELEMENT = "compression";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public final List<String> methods;

View file

@ -70,7 +70,7 @@ public class CompressionModule extends ModularXmppClientToServerConnectionModule
return new StateTransitionResult.TransitionImpossibleReason("Stream compression disabled by connection configuration");
}
Compress.Feature compressFeature = connectionInternal.connection.getFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE);
Compress.Feature compressFeature = connectionInternal.connection.getFeature(Compress.Feature.class);
if (compressFeature == null) {
return new StateTransitionResult.TransitionImpossibleReason("Stream compression not supported or enabled by service");
}

View file

@ -1,6 +1,6 @@
/**
*
* Copyright © 2014 Florian Schmaus
* Copyright © 2014-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.
@ -21,12 +21,15 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class Mechanisms implements ExtensionElement {
public static final String ELEMENT = "mechanisms";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public final List<String> mechanisms = new LinkedList<String>();

View file

@ -17,6 +17,8 @@
package org.jivesoftware.smack.packet;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.XmlStringBuilder;
/**
@ -44,6 +46,8 @@ public class Session extends SimpleIQ {
public static class Feature implements ExtensionElement {
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public static final String OPTIONAL_ELEMENT = "optional";
private final boolean optional;

View file

@ -1,6 +1,6 @@
/**
*
* Copyright © 2014-2019 Florian Schmaus
* Copyright © 2014-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.
@ -16,6 +16,8 @@
*/
package org.jivesoftware.smack.packet;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class StartTls implements Nonza {
@ -24,6 +26,7 @@ public class StartTls implements Nonza {
public static final String ELEMENT = "starttls";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-tls";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final boolean required;

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2014-2019 Florian Schmaus
* Copyright 2014-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.
@ -227,7 +227,7 @@ public abstract class ScramMechanism extends SASLMechanism {
authzidPortion = "a=" + authorizationId;
}
String cbName = getChannelBindingName();
String cbName = getGs2CbindFlag();
assert StringUtils.isNotEmpty(cbName);
return cbName + ',' + authzidPortion + ",";
@ -244,7 +244,13 @@ public abstract class ScramMechanism extends SASLMechanism {
return ByteUtils.concat(gs2Header, cbindData);
}
protected String getChannelBindingName() {
/**
* Get the SCRAM GSS-API Channel Binding Flag value.
*
* @return the gs2-cbind-flag value.
* @see <a href="https://tools.ietf.org/html/rfc5802#section-6">RFC 5802 § 6.</a>
*/
protected String getGs2CbindFlag() {
// Check if we are using TLS and if a "-PLUS" variant of this mechanism is enabled. Assuming that the "-PLUS"
// variants always have precedence before the non-"-PLUS" variants this means that the server did not announce
// the "-PLUS" variant, as otherwise we would have tried it.

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2016-2019 Florian Schmaus
* Copyright 2016-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.
@ -43,7 +43,7 @@ public abstract class ScramPlusMechanism extends ScramMechanism {
}
@Override
protected String getChannelBindingName() {
protected String getGs2CbindFlag() {
return "p=tls-server-end-point";
}

View file

@ -37,7 +37,7 @@ public class XmppElementUtil {
}
LOGGER.warning("The QNAME field of " + fullyQualifiedElement + " is not of type QNAME.");
} catch (NoSuchFieldException e) {
LOGGER.finer("The class " + fullyQualifiedElement + " has no static QNAME field. Consider adding one.");
LOGGER.finer("The " + fullyQualifiedElement + " has no static QNAME field. Consider adding one.");
// Proceed to fallback strategy.
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
throw new IllegalArgumentException(e);
@ -49,7 +49,7 @@ public class XmppElementUtil {
namespace = (String) fullyQualifiedElement.getField("NAMESPACE").get(null);
}
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
throw new IllegalArgumentException("The class" + fullyQualifiedElement + " has no ELEMENT, NAMESPACE or QNAME member. Consider adding QNAME", e);
throw new IllegalArgumentException("The " + fullyQualifiedElement + " has no ELEMENT, NAMESPACE or QNAME member. Consider adding QNAME", e);
}
return new QName(namespace, element);

View file

@ -16,6 +16,8 @@
*/
package org.jivesoftware.smackx.sid.element;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.util.XmlStringBuilder;
@ -25,6 +27,8 @@ import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
public class OriginIdElement extends StableAndUniqueIdElement {
public static final String ELEMENT = "origin-id";
public static final String NAMESPACE = StableUniqueStanzaIdManager.NAMESPACE;
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public OriginIdElement() {
super();

View file

@ -0,0 +1,29 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import org.jivesoftware.smack.packet.Element;
/**
* Interface that marks elements that may be used as affix elements inside a {@link ContentElement}.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html#affix_elements">
* XEP-0420: Stanza Content Encryption - §4. Affix Elements</a>
*/
public interface AffixElement extends Element {
}

View file

@ -0,0 +1,32 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import org.jivesoftware.smack.packet.ExtensionElement;
/**
* Affix element that is identified by element name and namespace.
* You should extend this interface with your custom affix extension elements
* and also provide a {@link org.jivesoftware.smackx.stanza_content_encryption.provider.AffixExtensionElementProvider}
* for them.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html#affix_elements">
* XEP-0420: Stanza Content Encryption - §4. Affix Elements</a>
*/
public interface AffixExtensionElement extends ExtensionElement, AffixElement {
}

View file

@ -0,0 +1,288 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.address.packet.MultipleAddresses;
import org.jivesoftware.smackx.hints.element.MessageProcessingHint;
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
import org.jxmpp.jid.Jid;
/**
* Extension element that holds the payload element, as well as a list of affix elements.
* In SCE, the XML representation of this element is what will be encrypted using the encryption mechanism of choice.
*/
public class ContentElement implements ExtensionElement {
private static final String NAMESPACE_UNVERSIONED = "urn:xmpp:sce";
public static final String NAMESPACE_0 = NAMESPACE_UNVERSIONED + ":0";
public static final String NAMESPACE = NAMESPACE_0;
public static final String ELEMENT = "content";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final PayloadElement payload;
private final List<AffixElement> affixElements;
ContentElement(PayloadElement payload, List<AffixElement> affixElements) {
this.payload = payload;
this.affixElements = Collections.unmodifiableList(affixElements);
}
/**
* Return the {@link PayloadElement} which holds the sensitive payload extensions.
*
* @return payload element
*/
public PayloadElement getPayload() {
return payload;
}
/**
* Return a list of affix elements.
* Those are elements that need to be verified upon reception by the encryption mechanisms implementation.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html#affix_elements">
* XEP-0420: Stanza Content Encryption - §4. Affix Elements</a>
*
* @return list of affix elements
*/
public List<AffixElement> getAffixElements() {
return affixElements;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this).rightAngleBracket();
xml.append(affixElements);
xml.append(payload);
return xml.closeElement(this);
}
@Override
public QName getQName() {
return QNAME;
}
/**
* Return a {@link Builder} that can be used to build the {@link ContentElement}.
* @return builder
*/
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private static final Set<String> BLACKLISTED_NAMESPACES = Collections.singleton(MessageProcessingHint.NAMESPACE);
private static final Set<QName> BLACKLISTED_QNAMES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
StanzaIdElement.QNAME,
MultipleAddresses.QNAME
)));
private FromAffixElement from = null;
private TimestampAffixElement timestamp = null;
private RandomPaddingAffixElement rpad = null;
private final List<AffixElement> otherAffixElements = new ArrayList<>();
private final List<ExtensionElement> payloadItems = new ArrayList<>();
private Builder() {
}
/**
* Add an affix element of type 'to' which addresses one recipient.
* The jid in the 'to' element SHOULD be a bare jid.
*
* @param jid jid
* @return builder
*/
public Builder addTo(Jid jid) {
return addTo(new ToAffixElement(jid));
}
/**
* Add an affix element of type 'to' which addresses one recipient.
*
* @param to affix element
* @return builder
*/
public Builder addTo(ToAffixElement to) {
this.otherAffixElements.add(Objects.requireNonNull(to, "'to' affix element MUST NOT be null."));
return this;
}
/**
* Set the senders jid as a 'from' affix element.
*
* @param jid jid of the sender
* @return builder
*/
public Builder setFrom(Jid jid) {
return setFrom(new FromAffixElement(jid));
}
/**
* Set the senders jid as a 'from' affix element.
*
* @param from affix element
* @return builder
*/
public Builder setFrom(FromAffixElement from) {
this.from = Objects.requireNonNull(from, "'form' affix element MUST NOT be null.");
return this;
}
/**
* Set the given date as a 'time' affix element.
*
* @param date timestamp as date
* @return builder
*/
public Builder setTimestamp(Date date) {
return setTimestamp(new TimestampAffixElement(date));
}
/**
* Set the timestamp of the message as a 'time' affix element.
*
* @param timestamp timestamp affix element
* @return builder
*/
public Builder setTimestamp(TimestampAffixElement timestamp) {
this.timestamp = Objects.requireNonNull(timestamp, "'time' affix element MUST NOT be null.");
return this;
}
/**
* Set some random length random content padding.
*
* @return builder
*/
public Builder setRandomPadding() {
this.rpad = new RandomPaddingAffixElement();
return this;
}
/**
* Set the given string as padding.
* The padding should be of length between 1 and 200 characters.
*
* @param padding padding string
* @return builder
*/
public Builder setRandomPadding(String padding) {
return setRandomPadding(new RandomPaddingAffixElement(padding));
}
/**
* Set a padding affix element.
*
* @param padding affix element
* @return builder
*/
public Builder setRandomPadding(RandomPaddingAffixElement padding) {
this.rpad = Objects.requireNonNull(padding, "'rpad' affix element MUST NOT be empty.");
return this;
}
/**
* Add an additional, SCE profile specific affix element.
*
* @param customAffixElement additional affix element
* @return builder
*/
public Builder addFurtherAffixElement(AffixElement customAffixElement) {
this.otherAffixElements.add(Objects.requireNonNull(customAffixElement,
"Custom affix element MUST NOT be null."));
return this;
}
/**
* Add a payload item as child element of the payload element.
* There are some items that are not allowed as payload.
* Adding those will throw an exception.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html#server-processed">
* XEP-0420: Stanza Content Encryption - §9. Server-processed Elements</a>
*
* @param payloadItem extension element
* @return builder
* @throws IllegalArgumentException in case an extension element from the blacklist is added.
*/
public Builder addPayloadItem(ExtensionElement payloadItem) {
Objects.requireNonNull(payloadItem, "Payload item MUST NOT be null.");
this.payloadItems.add(checkForIllegalPayloadsAndPossiblyThrow(payloadItem));
return this;
}
/**
* Construct a content element from this builder.
*
* @return content element
*/
public ContentElement build() {
List<AffixElement> allAffixElements = collectAffixElements();
PayloadElement payloadElement = new PayloadElement(payloadItems);
return new ContentElement(payloadElement, allAffixElements);
}
private static ExtensionElement checkForIllegalPayloadsAndPossiblyThrow(ExtensionElement payloadItem) {
QName qName = payloadItem.getQName();
if (BLACKLISTED_QNAMES.contains(qName)) {
throw new IllegalArgumentException("Element identified by " + qName +
" is not allowed as payload item. See https://xmpp.org/extensions/xep-0420.html#server-processed");
}
String namespace = payloadItem.getNamespace();
if (BLACKLISTED_NAMESPACES.contains(namespace)) {
throw new IllegalArgumentException("Elements of namespace '" + namespace +
"' are not allowed as payload items. See https://xmpp.org/extensions/xep-0420.html#server-processed");
}
return payloadItem;
}
private List<AffixElement> collectAffixElements() {
List<AffixElement> allAffixElements = new ArrayList<>(Arrays.asList(rpad, from, timestamp));
allAffixElements.addAll(otherAffixElements);
return allAffixElements;
}
}
}

View file

@ -0,0 +1,33 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import org.jxmpp.jid.Jid;
public class FromAffixElement extends JidAffixElement {
public static final String ELEMENT = "from";
public FromAffixElement(Jid jid) {
super(jid);
}
@Override
public String getElementName() {
return ELEMENT;
}
}

View file

@ -0,0 +1,57 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.Jid;
public abstract class JidAffixElement implements NamedElement, AffixElement {
public static final String ATTR_JID = "jid";
private final Jid jid;
public JidAffixElement(Jid jid) {
this.jid = Objects.requireNonNull(jid, "Value of 'jid' MUST NOT be null.");
}
public Jid getJid() {
return jid;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.attribute(ATTR_JID, getJid())
.closeEmptyElement();
}
@Override
public final boolean equals(Object obj) {
return EqualsUtil.equals(this, obj, (e, o) -> e.append(getJid(), o.getJid()).append(getElementName(), o.getElementName()));
}
@Override
public final int hashCode() {
return (getElementName() + getJid().toString()).hashCode();
}
}

View file

@ -0,0 +1,52 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class PayloadElement implements NamedElement {
public static final String ELEMENT = "payload";
private final List<ExtensionElement> payloadElements;
public PayloadElement(List<ExtensionElement> payloadElements) {
this.payloadElements = Collections.unmodifiableList(payloadElements);
}
public List<ExtensionElement> getItems() {
return payloadElements;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this).rightAngleBracket();
xml.append(payloadElements);
return xml.closeElement(this);
}
}

View file

@ -0,0 +1,75 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import java.security.SecureRandom;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.RandomUtil;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class RandomPaddingAffixElement implements NamedElement, AffixElement {
private static final int minPaddingLength = 1;
private static final int maxPaddingLength = 200;
public static final String ELEMENT = "rpad";
private final String padding;
public RandomPaddingAffixElement(String padding) {
this.padding = StringUtils.escapeForXmlText(
StringUtils.requireNotNullNorEmpty(padding, "Value of 'rpad' MUST NOT be null nor empty."))
.toString();
}
public RandomPaddingAffixElement() {
this(StringUtils.randomString(randomPaddingLength(), new SecureRandom()));
}
private static int randomPaddingLength() {
return minPaddingLength + RandomUtil.nextSecureRandomInt(maxPaddingLength - minPaddingLength);
}
public String getPadding() {
return padding;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this).rightAngleBracket()
.append(getPadding())
.closeElement(this);
}
@Override
public boolean equals(Object obj) {
return EqualsUtil.equals(this, obj, (e, o) -> e.append(getPadding(), o.getPadding()));
}
@Override
public int hashCode() {
return getPadding().hashCode();
}
}

View file

@ -0,0 +1,63 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import java.util.Date;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class TimestampAffixElement implements NamedElement, AffixElement {
public static final String ELEMENT = "time";
public static final String ATTR_STAMP = "stamp";
private final Date timestamp;
public TimestampAffixElement(Date timestamp) {
this.timestamp = Objects.requireNonNull(timestamp, "Date must not be null.");
}
public Date getTimestamp() {
return timestamp;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public CharSequence toXML(XmlEnvironment xmlEnvironment) {
return new XmlStringBuilder(this)
.attribute(ATTR_STAMP, getTimestamp())
.closeEmptyElement();
}
@Override
public boolean equals(Object obj) {
return EqualsUtil.equals(this, obj, (e, o) -> e.append(getTimestamp(), o.getTimestamp()));
}
@Override
public int hashCode() {
return timestamp.hashCode();
}
}

View file

@ -0,0 +1,34 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import org.jxmpp.jid.Jid;
public class ToAffixElement extends JidAffixElement {
public static final String ELEMENT = "to";
public ToAffixElement(Jid jid) {
super(jid);
}
@Override
public String getElementName() {
return ELEMENT;
}
}

View file

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0420: Stanza Content Encryption: Element classes.
*/
package org.jivesoftware.smackx.stanza_content_encryption.element;

View file

@ -0,0 +1,22 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0420: Stanza Content Encryption.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html">XEP-0420: Stanza Content Encryption</a>
*/
package org.jivesoftware.smackx.stanza_content_encryption;

View file

@ -0,0 +1,29 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.provider;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smackx.stanza_content_encryption.element.AffixExtensionElement;
/**
* Abstract class that needs to be extended by provider classes that parse out affix extension elements.
*
* @param <AE> affix extension element.
*/
public abstract class AffixExtensionElementProvider<AE extends AffixExtensionElement> extends ExtensionElementProvider<AE> {
}

View file

@ -0,0 +1,137 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.provider;
import java.io.IOException;
import java.util.Date;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.stanza_content_encryption.element.AffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.ContentElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.FromAffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.PayloadElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.RandomPaddingAffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.TimestampAffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.ToAffixElement;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
public class ContentElementProvider extends ExtensionElementProvider<ContentElement> {
@Override
public ContentElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
ContentElement.Builder builder = ContentElement.builder();
while (true) {
XmlPullParser.Event tag = parser.next();
if (tag == XmlPullParser.Event.START_ELEMENT) {
String name = parser.getName();
switch (name) {
case ToAffixElement.ELEMENT:
parseToAffix(parser, builder);
break;
case FromAffixElement.ELEMENT:
parseFromAffix(parser, builder);
break;
case TimestampAffixElement.ELEMENT:
parseTimestampAffix(parser, builder);
break;
case RandomPaddingAffixElement.ELEMENT:
parseRPadAffix(parser, builder);
break;
case PayloadElement.ELEMENT:
parsePayload(parser, xmlEnvironment, builder);
break;
default:
parseCustomAffix(parser, xmlEnvironment, builder);
break;
}
} else if (tag == XmlPullParser.Event.END_ELEMENT) {
if (parser.getDepth() == initialDepth) {
break;
}
}
}
return builder.build();
}
private static void parseCustomAffix(XmlPullParser parser, XmlEnvironment outerXmlEnvironment, ContentElement.Builder builder)
throws XmlPullParserException, IOException, SmackParsingException {
String name = parser.getName();
String namespace = parser.getNamespace();
AffixElement element = (AffixElement) PacketParserUtils.parseExtensionElement(name, namespace, parser, outerXmlEnvironment);
builder.addFurtherAffixElement(element);
}
private static void parsePayload(XmlPullParser parser, XmlEnvironment outerXmlEnvironment, ContentElement.Builder builder)
throws IOException, XmlPullParserException, SmackParsingException {
final int initialDepth = parser.getDepth();
while (true) {
XmlPullParser.Event tag = parser.next();
if (tag == XmlPullParser.Event.START_ELEMENT) {
String name = parser.getName();
String namespace = parser.getNamespace();
ExtensionElement element = PacketParserUtils.parseExtensionElement(name, namespace, parser, outerXmlEnvironment);
builder.addPayloadItem(element);
}
if (tag == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == initialDepth) {
return;
}
}
}
private static void parseRPadAffix(XmlPullParser parser, ContentElement.Builder builder)
throws IOException, XmlPullParserException {
builder.setRandomPadding(parser.nextText());
}
private static void parseTimestampAffix(XmlPullParser parser, ContentElement.Builder builder)
throws SmackParsingException.SmackTextParseException {
Date timestamp = ParserUtils.getDateFromXep82String(
parser.getAttributeValue("", TimestampAffixElement.ATTR_STAMP));
builder.setTimestamp(timestamp);
}
private static void parseFromAffix(XmlPullParser parser, ContentElement.Builder builder)
throws XmppStringprepException {
String jidString = parser.getAttributeValue("", FromAffixElement.ATTR_JID);
builder.setFrom(JidCreate.from(jidString));
}
private static void parseToAffix(XmlPullParser parser, ContentElement.Builder builder)
throws XmppStringprepException {
String jidString = parser.getAttributeValue("", ToAffixElement.ATTR_JID);
builder.addTo(JidCreate.from(jidString));
}
}

View file

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0420: Stanza Content Encryption: Provider classes.
*/
package org.jivesoftware.smackx.stanza_content_encryption.provider;

View file

@ -292,6 +292,13 @@
<className>org.jivesoftware.smackx.dox.provider.DnsIqProvider</className>
</iqProvider>
<!-- XEP-0420: Stanza Content Encryption (SCE) -->
<extensionProvider>
<elementName>content</elementName>
<namespace>urn:xmpp:sce:0</namespace>
<className>org.jivesoftware.smackx.stanza_content_encryption.provider.ContentElementProvider</className>
</extensionProvider>
<!-- XEP-0422: Message Fastening -->
<extensionProvider>
<elementName>apply-to</elementName>

View file

@ -0,0 +1,156 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.text.ParseException;
import java.util.Date;
import org.junit.jupiter.api.Test;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.util.XmppDateTime;
public class AffixElementsTest {
public static final EntityBareJid JID_HOUSTON = JidCreate.entityBareFromOrThrowUnchecked("missioncontrol@houston.nasa.gov");
public static final EntityBareJid JID_OPPORTUNITY = JidCreate.entityBareFromOrThrowUnchecked("opportunity@mars.planet");
/**
* Test serialization of 'to' affix element.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html#example-1">XEP-420 Example 1</a>
*/
@Test
public void testToAffixElement() {
ToAffixElement to = new ToAffixElement(JID_HOUSTON);
String expectedXml = "<to jid='missioncontrol@houston.nasa.gov'/>";
assertXmlSimilar(expectedXml, to.toXML());
assertEquals(JID_HOUSTON, to.getJid());
}
@Test
public void testToAffixElementEquals() {
ToAffixElement to1 = new ToAffixElement(JID_HOUSTON);
ToAffixElement to2 = new ToAffixElement(JID_HOUSTON);
assertEquals(to1, to2);
assertEquals(to1, to1);
assertEquals(to1.hashCode(), to2.hashCode());
assertFalse(to1.equals(null));
}
@Test
public void toElementNullArgThrows() {
assertThrows(IllegalArgumentException.class, () -> new ToAffixElement(null));
}
/**
* Test serialization of 'from' affix element.
*
* @see <a href="https://xmpp.org/extensions/xep-0420.html#example-1">XEP-420 Example 1</a>
*/
@Test
public void testFromAffixElement() {
FromAffixElement from = new FromAffixElement(JID_OPPORTUNITY);
String expectedXml = "<from jid='opportunity@mars.planet'/>";
assertXmlSimilar(expectedXml, from.toXML());
assertEquals(JID_OPPORTUNITY, from.getJid());
}
@Test
public void testFromAffixElementEquals() {
FromAffixElement from1 = new FromAffixElement(JID_HOUSTON);
FromAffixElement from2 = new FromAffixElement(JID_HOUSTON);
assertEquals(from1, from2);
assertEquals(from1, from1);
assertEquals(from1.hashCode(), from2.hashCode());
assertFalse(from1.equals(null));
}
@Test
public void fromElementNullArgThrows() {
assertThrows(IllegalArgumentException.class, () -> new FromAffixElement(null));
}
@Test
public void testTimestampAffixElement() throws ParseException {
Date date = XmppDateTime.parseDate("2004-01-25T05:05:00.000+00:00");
TimestampAffixElement timestamp = new TimestampAffixElement(date);
String expectedXml = "<time stamp='2004-01-25T05:05:00.000+00:00'/>";
assertXmlSimilar(expectedXml, timestamp.toXML());
assertEquals(date, timestamp.getTimestamp());
}
@Test
public void timestampElementNullArgThrows() {
assertThrows(IllegalArgumentException.class, () -> new TimestampAffixElement(null));
}
@Test
public void testTimestampElementEquals() throws ParseException {
TimestampAffixElement t1 = new TimestampAffixElement(XmppDateTime.parseDate("2004-01-25T05:05:00.000+00:00"));
TimestampAffixElement t2 = new TimestampAffixElement(t1.getTimestamp());
assertEquals(t1, t2);
assertEquals(t1, t1);
assertEquals(t1.hashCode(), t2.hashCode());
assertFalse(t1.equals(null));
}
@Test
public void testRandomPaddingElement() {
RandomPaddingAffixElement rpad = new RandomPaddingAffixElement();
assertNotNull(rpad.getPadding());
assertTrue(rpad.getPadding().length() < 200);
}
@Test
public void testRandomPaddingEquals() {
RandomPaddingAffixElement rpad1 = new RandomPaddingAffixElement();
RandomPaddingAffixElement rpad2 = new RandomPaddingAffixElement(rpad1.getPadding());
assertEquals(rpad1, rpad2);
assertEquals(rpad1, rpad1);
assertEquals(rpad1.hashCode(), rpad2.hashCode());
assertFalse(rpad1.equals(null));
}
@Test
public void testRandomPaddingSerialization() {
RandomPaddingAffixElement rpad = new RandomPaddingAffixElement();
String expectedXml = "<rpad>" + rpad.getPadding() + "</rpad>";
assertXmlSimilar(expectedXml, rpad.toXML());
}
@Test
public void rpadElementNullArgThrows() {
assertThrows(IllegalArgumentException.class, () -> new RandomPaddingAffixElement(null));
}
}

View file

@ -0,0 +1,81 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.element;
import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.text.ParseException;
import java.util.Collections;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.hints.element.StoreHint;
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
import org.junit.jupiter.api.Test;
import org.jxmpp.util.XmppDateTime;
public class ContentElementTest {
@Test
public void testContentElement() throws ParseException {
Message.Body body = new Message.Body("en", "My battery is low and its getting dark"); // :'(
ContentElement contentElement = ContentElement.builder()
.addPayloadItem(body)
.setFrom(AffixElementsTest.JID_OPPORTUNITY)
.addTo(AffixElementsTest.JID_HOUSTON)
.setTimestamp(XmppDateTime.parseXEP0082Date("2018-06-10T00:00:00.000+00:00"))
.setRandomPadding("RANDOMPADDING")
.build();
String expectedXml = "" +
"<content xmlns='urn:xmpp:sce:0'>" +
" <to jid='missioncontrol@houston.nasa.gov'/>" +
" <from jid='opportunity@mars.planet'/>" +
" <time stamp='2018-06-10T00:00:00.000+00:00'/>" +
" <rpad>RANDOMPADDING</rpad>" +
" <payload>" +
" <body xmlns='jabber:client' xml:lang='en'>My battery is low and its getting dark</body>" +
" </payload>" +
"</content>";
assertXmlSimilar(expectedXml, contentElement.toXML());
assertEquals(Collections.singletonList(body), contentElement.getPayload().getItems());
assertEquals(4, contentElement.getAffixElements().size());
assertTrue(contentElement.getAffixElements().contains(new ToAffixElement(AffixElementsTest.JID_HOUSTON)));
assertTrue(contentElement.getAffixElements().contains(new FromAffixElement(AffixElementsTest.JID_OPPORTUNITY)));
assertTrue(contentElement.getAffixElements().contains(
new TimestampAffixElement(XmppDateTime.parseXEP0082Date("2018-06-10T00:00:00.000+00:00"))));
assertTrue(contentElement.getAffixElements().contains(new RandomPaddingAffixElement("RANDOMPADDING")));
}
@Test
public void stanzaIdForbiddenInContentElementPayload() {
assertThrows(IllegalArgumentException.class,
() -> ContentElement.builder().addPayloadItem(new StanzaIdElement("alice@wonderland.lit")));
}
@Test
public void processingHintsForbiddenInContentElementPayload() {
assertThrows(IllegalArgumentException.class,
() -> ContentElement.builder().addPayloadItem(StoreHint.INSTANCE));
}
}

View file

@ -0,0 +1,84 @@
/**
*
* Copyright 2020 Paul Schaub
*
* 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.stanza_content_encryption.provider;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.stanza_content_encryption.element.ContentElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.FromAffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.RandomPaddingAffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.TimestampAffixElement;
import org.jivesoftware.smackx.stanza_content_encryption.element.ToAffixElement;
import org.junit.jupiter.api.Test;
import org.jxmpp.jid.impl.JidCreate;
public class ContentElementProviderTest {
@Test
public void testParsing() throws XmlPullParserException, IOException, SmackParsingException {
String xml = "" +
"<content xmlns='urn:xmpp:sce:0'>\n" +
" <payload>\n" +
" <body xmlns='jabber:client'>Have you seen that new movie?</body>\n" +
" <x xmlns='jabber:x:oob'>\n" +
" <url>https://en.wikipedia.org/wiki/Fight_Club#Plot</url>\n" +
" </x>\n" +
" </payload>\n" +
" <from jid='ladymacbeth@shakespear.lit/castle'/>\n" +
" <to jid='doctor@shakespeare.lit/pda'/>\n" +
" <time stamp='1993-10-12T03:13:10.000+00:00'/>\n" +
" <rpad>A98D7KJF1ASDVG232sdff341</rpad>\n" +
"</content>";
ContentElementProvider provider = new ContentElementProvider();
ContentElement contentElement = provider.parse(TestUtils.getParser(xml));
assertNotNull(contentElement);
assertEquals(4, contentElement.getAffixElements().size());
assertTrue(contentElement.getAffixElements().contains(
new FromAffixElement(JidCreate.from("ladymacbeth@shakespear.lit/castle"))));
assertTrue(contentElement.getAffixElements().contains(
new ToAffixElement(JidCreate.from("doctor@shakespeare.lit/pda"))));
assertTrue(contentElement.getAffixElements().contains(
new TimestampAffixElement(ParserUtils.getDateFromXep82String("1993-10-12T03:13:10.000+00:00"))));
assertTrue(contentElement.getAffixElements().contains(
new RandomPaddingAffixElement("A98D7KJF1ASDVG232sdff341")));
assertEquals(2, contentElement.getPayload().getItems().size());
assertTrue(contentElement.getPayload().getItems().get(0) instanceof Message.Body);
Message.Body body = (Message.Body) contentElement.getPayload().getItems().get(0);
assertEquals("Have you seen that new movie?", body.getMessage());
StandardExtensionElement oob = (StandardExtensionElement) contentElement.getPayload().getItems().get(1);
assertEquals("x", oob.getElementName());
assertEquals("jabber:x:oob", oob.getNamespace());
assertEquals("https://en.wikipedia.org/wiki/Fight_Club#Plot", oob.getFirstElement("url").getText());
}
}

View file

@ -339,7 +339,7 @@ public final class EntityCapsManager extends Manager {
}
private void processCapsStreamFeatureIfAvailable(XMPPConnection connection) {
CapsExtension capsExtension = connection.getFeature(
CapsExtension.ELEMENT, CapsExtension.NAMESPACE);
CapsExtension.class);
if (capsExtension == null) {
return;
}

View file

@ -339,8 +339,7 @@ public final class AccountManager extends Manager {
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
XMPPConnection connection = connection();
ExtensionElement extensionElement = connection.getFeature(Registration.Feature.ELEMENT,
Registration.Feature.NAMESPACE);
ExtensionElement extensionElement = connection.getFeature(Registration.Feature.class);
if (extensionElement != null) {
return true;
}

View file

@ -19,6 +19,8 @@ package org.jivesoftware.smackx.iqregister.packet;
import java.util.Map;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.IQ;
@ -104,6 +106,8 @@ public class Registration extends IQ {
public static final String ELEMENT = "register";
public static final String NAMESPACE = "http://jabber.org/features/iq-register";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
public static final Feature INSTANCE = new Registration.Feature();
private Feature() {

View file

@ -86,7 +86,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
dataForm.setTitle(parser.nextText());
break;
case "field":
FormField formField = parseField(parser, elementXmlEnvironment, formType, dataFormType);
FormField formField = parseField(parser, elementXmlEnvironment, formType);
TextSingleFormField hiddenFormTypeField = formField.asHiddenFormTypeFieldIfPossible();
if (hiddenFormTypeField != null) {
@ -99,11 +99,11 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
dataForm.addField(formField);
break;
case "item":
DataForm.Item item = parseItem(parser, elementXmlEnvironment, formType, dataFormType);
DataForm.Item item = parseItem(parser, elementXmlEnvironment, formType);
dataForm.addItem(item);
break;
case "reported":
DataForm.ReportedData reported = parseReported(parser, elementXmlEnvironment, formType, dataFormType);
DataForm.ReportedData reported = parseReported(parser, elementXmlEnvironment, formType);
dataForm.setReportedData(reported);
break;
// See XEP-133 Example 32 for a corner case where the data form contains this extension.
@ -133,7 +133,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
return dataForm.build();
}
private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType, DataForm.Type dataFormType)
private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType)
throws XmlPullParserException, IOException, SmackParsingException {
final int initialDepth = parser.getDepth();
@ -187,16 +187,13 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
}
if (type == null) {
if (dataFormType == DataForm.Type.submit) {
// If the data form is of type submit, and no type was explicitly provided, then we need to lookup the
// field's type in the registry.
type = FormFieldRegistry.lookup(formType, fieldName);
if (type == null) {
throw new SmackParsingException("Field of name '" + fieldName + "' (and FORM_TYPE '" + formType
+ "') not registered");
}
} else {
// As per XEP-0004, text-single is the default form field type.
// If no field type was explicitly provided, then we need to lookup the
// field's type in the registry.
type = FormFieldRegistry.lookup(formType, fieldName);
if (type == null) {
LOGGER.warning("The Field '" + fieldName + "' from FORM_TYPE '" + formType
+ "' is not registered. Field type is unknown, assuming text-single.");
// As per XEP-0004, text-single is the default form field type, which we use as emergency fallback here.
type = FormField.Type.text_single;
}
}
@ -304,7 +301,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
return builder;
}
private static DataForm.Item parseItem(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType, DataForm.Type dataFormType)
private static DataForm.Item parseItem(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType)
throws XmlPullParserException, IOException, SmackParsingException {
final int initialDepth = parser.getDepth();
List<FormField> fields = new ArrayList<>();
@ -315,7 +312,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
String name = parser.getName();
switch (name) {
case "field":
FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType, dataFormType);
FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType);
fields.add(field);
break;
}
@ -330,7 +327,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
return new DataForm.Item(fields);
}
private static DataForm.ReportedData parseReported(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType, DataForm.Type dataFormType)
private static DataForm.ReportedData parseReported(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType)
throws XmlPullParserException, IOException, SmackParsingException {
final int initialDepth = parser.getDepth();
List<FormField> fields = new ArrayList<>();
@ -341,7 +338,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
String name = parser.getName();
switch (name) {
case "field":
FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType, dataFormType);
FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType);
fields.add(field);
break;
}

View file

@ -0,0 +1,67 @@
/**
*
* Copyright 2020 Aditya Borikar
*
* 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.c2s;
import java.util.concurrent.TimeoutException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.jxmpp.jid.EntityFullJid;
public class SimpleXmppConnectionIntegrationTest extends AbstractSmackIntegrationTest {
public SimpleXmppConnectionIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
@SmackIntegrationTest
public void createConnectionTest() throws TimeoutException, Exception {
EntityFullJid userTwo = conTwo.getUser();
final String messageBody = testRunId + ": Hello from the other side!";
Message message = conTwo.getStanzaFactory().buildMessageStanza()
.to(userTwo)
.setBody(messageBody)
.build();
final SimpleResultSyncPoint messageReceived = new SimpleResultSyncPoint();
final StanzaListener stanzaListener = (Stanza stanza) -> {
if (((Message) stanza).getBody().equals(messageBody)) {
messageReceived.signal();
}
};
conTwo.addAsyncStanzaListener(stanzaListener, MessageWithBodiesFilter.INSTANCE);
try {
conOne.sendStanza(message);
messageReceived.waitForResult(timeout);
} finally {
conTwo.removeAsyncStanzaListener(stanzaListener);
}
}
}

View file

@ -20,7 +20,6 @@ import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEYS_NOTIFY;
import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.publishPublicKey;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@ -43,7 +42,6 @@ import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.ox.callback.backup.AskForBackupCodeCallback;
import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallback;
@ -75,12 +73,9 @@ import org.jivesoftware.smackx.pubsub.PubSubException;
import org.jivesoftware.smackx.pubsub.PubSubFeature;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
import org.pgpainless.key.OpenPgpV4Fingerprint;
@ -506,16 +501,11 @@ public final class OpenPgpManager extends Manager {
OpenPgpSecretKeyBackupPassphrase backupCode = codeCallback.askForBackupCode();
PGPSecretKeyRing secretKeys = SecretKeyBackupHelper.restoreSecretKeyBackup(backup, backupCode);
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(secretKeys);
provider.getStore().importSecretKey(getJidOrThrow(), secretKeys);
provider.getStore().importPublicKey(getJidOrThrow(), BCUtil.publicKeyRingFromSecretKeyRing(secretKeys));
ByteArrayOutputStream buffer = new ByteArrayOutputStream(2048);
for (PGPSecretKey sk : secretKeys) {
PGPPublicKey pk = sk.getPublicKey();
if (pk != null) pk.encode(buffer);
}
PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(buffer.toByteArray(), new BcKeyFingerprintCalculator());
provider.getStore().importPublicKey(getJidOrThrow(), publicKeys);
getOpenPgpSelf().trust(fingerprint);
return new OpenPgpV4Fingerprint(secretKeys);
}

View file

@ -111,7 +111,7 @@ public class OpenPgpPubSubUtil {
* Publish the users OpenPGP public key to the public key node if necessary.
* Also announce the key to other users by updating the metadata node.
*
* @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">XEP-0373 §4.1</a>
* @see <a href="https://xmpp.org/extensions/xep-0373.html#announcing-pubkey">XEP-0373 §4.1</a>
*
* @param pepManager The PEP manager.
* @param pubkeyElement {@link PubkeyElement} containing the public key

View file

@ -407,6 +407,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
LOGGER.fine("Stream resumption failed, continuing with normal stream establishment process: " + smResumptionFailed);
}
// We either failed to resume a previous stream management (SM) session, or we did not even try. In any case,
// mark SM as not enabled. Most importantly, we do this prior calling bindResourceAndEstablishSession(), as the
// bind IQ may trigger a SM ack request, which would be invalid in the pre resource bound state.
smEnabledSyncPoint = false;
List<Stanza> previouslyUnackedStanzas = new LinkedList<Stanza>();
if (unacknowledgedStanzas != null) {
// There was a previous connection with SM enabled but that was either not resumable or
@ -425,7 +430,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
// unacknowledgedStanzas and become duplicated on reconnect. See SMACK-706.
bindResourceAndEstablishSession(resource);
smEnabledSyncPoint = false;
if (isSmAvailable() && useSm) {
// Remove what is maybe left from previously stream managed sessions
serverHandledStanzasCount = 0;
@ -797,7 +801,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
return;
}
Compress.Feature compression = getFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE);
Compress.Feature compression = getFeature(Compress.Feature.class);
if (compression == null) {
// Server does not support compression
return;
@ -853,7 +857,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
@Override
protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException, SecurityRequiredByServerException {
StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
StartTls startTlsFeature = getFeature(StartTls.class);
if (startTlsFeature != null) {
if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
SecurityRequiredByServerException smackException = new SecurityRequiredByServerException();

View file

@ -819,7 +819,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
@Override
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext)
throws SecurityRequiredByClientException, SecurityRequiredByServerException {
StartTls startTlsFeature = connectionInternal.connection.getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
StartTls startTlsFeature = connectionInternal.connection.getFeature(StartTls.class);
SecurityMode securityMode = connectionInternal.connection.getConfiguration().getSecurityMode();
switch (securityMode) {