diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java index 91c909aba..116c8375d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus + * Copyright 2017-2022 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,7 @@ package org.jivesoftware.smackx.jingle.element; import java.util.HashMap; import java.util.Map; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.FullyQualifiedElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.StringUtils; @@ -105,9 +106,17 @@ public class JingleReason implements FullyQualifiedElement { } protected final Reason reason; + private final String text; + private final ExtensionElement element; public JingleReason(Reason reason) { + this(reason, null, null); + } + + public JingleReason(Reason reason, String text, ExtensionElement element) { this.reason = reason; + this.text = text; + this.element = element; } @Override @@ -120,6 +129,26 @@ public class JingleReason implements FullyQualifiedElement { return NAMESPACE; } + /** + * An optional text that provides human-readable information about the reason for the action. + * + * @return a human-readable text with information regarding this reason or null. + * @since 4.4.5 + */ + public String getText() { + return text; + } + + /** + * An optional element that provides more detailed machine-readable information about the reason for the action. + * + * @return an elemnet with machine-readable information about this reason or null. + * @since 4.4.5 + */ + public ExtensionElement getElement() { + return element; + } + @Override public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) { XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment); @@ -142,7 +171,11 @@ public class JingleReason implements FullyQualifiedElement { private final String sessionId; public AlternativeSession(String sessionId) { - super(Reason.alternative_session); + this(sessionId, null, null); + } + + public AlternativeSession(String sessionId, String text, ExtensionElement element) { + super(Reason.alternative_session, text, element); if (StringUtils.isNullOrEmpty(sessionId)) { throw new NullPointerException("SessionID must not be null or empty."); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java index f6c352a75..861375995 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2021 Florian Schmaus + * Copyright 2017-2022 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,12 +19,14 @@ package org.jivesoftware.smackx.jingle.provider; import java.io.IOException; import java.util.logging.Logger; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.IqData; import org.jivesoftware.smack.packet.StandardExtensionElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.parsing.StandardExtensionElementProvider; import org.jivesoftware.smack.provider.IqProvider; +import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -76,16 +78,7 @@ public class JingleProvider extends IqProvider { builder.addJingleContent(content); break; case JingleReason.ELEMENT: - parser.next(); - String reasonString = parser.getName(); - JingleReason reason; - if (reasonString.equals("alternative-session")) { - parser.next(); - String sid = parser.nextText(); - reason = new JingleReason.AlternativeSession(sid); - } else { - reason = new JingleReason(Reason.fromString(reasonString)); - } + JingleReason reason = parseJingleReason(parser); builder.setReason(reason); break; default: @@ -178,4 +171,57 @@ public class JingleProvider extends IqProvider { return builder.build(); } + + public static JingleReason parseJingleReason(XmlPullParser parser) + throws XmlPullParserException, IOException, SmackParsingException { + ParserUtils.assertAtStartTag(parser); + final int initialDepth = parser.getDepth(); + final String jingleNamespace = parser.getNamespace(); + + JingleReason.Reason reason = null; + ExtensionElement element = null; + String text = null; + + // 'sid' is only set if the reason is 'alternative-session'. + String sid = null; + + outerloop: while (true) { + XmlPullParser.TagEvent event = parser.nextTag(); + switch (event) { + case START_ELEMENT: + String elementName = parser.getName(); + String namespace = parser.getNamespace(); + if (namespace.equals(jingleNamespace)) { + switch (elementName) { + case "text": + text = parser.nextText(); + break; + case "alternative-session": + parser.next(); + sid = parser.nextText(); + break; + default: + reason = Reason.fromString(elementName); + break; + } + } else { + element = PacketParserUtils.parseExtensionElement(elementName, namespace, parser, null); + } + break; + case END_ELEMENT: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + + JingleReason res; + if (sid != null) { + res = new JingleReason.AlternativeSession(sid, text, element); + } else { + res = new JingleReason(reason, text, element); + } + return res; + } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java index c17543148..1ca0fa71a 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/provider/JingleProviderTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus + * Copyright 2017-2022 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,30 @@ package org.jivesoftware.smackx.jingle.provider; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; -import org.jivesoftware.smack.util.PacketParserUtils; -import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.StandardExtensionElement; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.SmackTestUtil; import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.jingle.element.Jingle; import org.jivesoftware.smackx.jingle.element.JingleContentDescription; import org.jivesoftware.smackx.jingle.element.JingleContentTransport; +import org.jivesoftware.smackx.jingle.element.JingleReason; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; public class JingleProviderTest { - @Test - public void testParseUnknownJingleContentDescrption() throws Exception { + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testParseUnknownJingleContentDescrption(SmackTestUtil.XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { final String unknownJingleContentDescriptionNamespace = "urn:xmpp:jingle:unknown-description:5"; final String unknownJingleContentDescription = // @formatter:off @@ -50,8 +57,8 @@ public class JingleProviderTest { "" + ""; // @formatter:on - XmlPullParser parser = createTestJingle(unknownJingleContentDescription); - Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser); + CharSequence xml = createTestJingle(unknownJingleContentDescription); + Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind); JingleContentDescription jingleContentDescription = jingle.getSoleContentOrThrow().getDescription(); @@ -59,8 +66,10 @@ public class JingleProviderTest { assertEquals(unknownJingleContentDescriptionNamespace, parsedUnknownJingleContentDescriptionNamespace); } - @Test - public void testParseUnknownJingleContentTransport() throws Exception { + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testParseUnknownJingleContentTransport(SmackTestUtil.XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { final String unknownJingleContentTransportNamespace = "urn:xmpp:jingle:unknown-transport:foo:1"; final String unknownJingleContentTransport = // @formatter:off @@ -81,8 +90,8 @@ public class JingleProviderTest { " type='direct'/>" + ""; // @formatter:on - XmlPullParser parser = createTestJingle(unknownJingleContentTransport); - Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser); + CharSequence xml = createTestJingle(unknownJingleContentTransport); + Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind); JingleContentTransport jingleContentTransport = jingle.getSoleContentOrThrow().getTransport(); @@ -90,7 +99,38 @@ public class JingleProviderTest { assertEquals(unknownJingleContentTransportNamespace, parsedUnknownJingleContentTransportNamespace); } - private static XmlPullParser createTestJingle(String... childs) throws XmlPullParserException, IOException { + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testReasonElementWithExtraElement(SmackTestUtil.XmlPullParserKind parserKind) + throws XmlPullParserException, IOException, SmackParsingException { + String xml = "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind); + JingleReason jingleReason = jingle.getReason(); + + assertEquals(JingleReason.Reason.success, jingleReason.asEnum()); + + ExtensionElement element = jingleReason.getElement(); + // TODO: Use JUnit 5.8's assertInstanceOf when possible + // assertInstanceOf(StandardExtesionElement.class, extraElement); + assertTrue(element instanceof StandardExtensionElement); + StandardExtensionElement extraElement = (StandardExtensionElement) element; + assertEquals("https://example.org", extraElement.getNamespace()); + assertEquals("bar", extraElement.getAttributes().get("foo")); + } + + private static CharSequence createTestJingle(String... childs) throws XmlPullParserException, IOException { StringBuilder sb = new StringBuilder(); sb.append(// @formatter:off "" + "" + + "action='session-initiate' " + + "initiator='romeo@montague.example/dr4hcr0st3lup4c' " + + "sid='851ba2'>" + "" // @formatter:on ); @@ -114,9 +154,6 @@ public class JingleProviderTest { // @formatter:on ); - String jingleStanza = sb.toString(); - - XmlPullParser parser = PacketParserUtils.getParserFor(jingleStanza); - return parser; + return sb; } }