diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java index 9e670acb3..ac789adcc 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java @@ -360,8 +360,7 @@ public final class FileTransferNegotiator extends Manager { } private static DataForm createDefaultInitiationForm() { - DataForm.Builder form = DataForm.builder() - .setType(DataForm.Type.form); + DataForm.Builder form = DataForm.builder(DataForm.Type.form); ListSingleFormField.Builder fieldBuilder = FormField.listSingleBuilder(STREAM_DATA_FIELD_NAME); if (!IBB_ONLY) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java index 0457e69f3..37a37741a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2020-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. @@ -353,6 +353,7 @@ public final class DataForm implements ExtensionElement { } public static final class Builder { + // TODO: Make this field final once setType() is gone. private Type type; private String title; private List instructions; @@ -381,6 +382,15 @@ public final class DataForm implements ExtensionElement { extensionElements = CollectionUtil.newListWith(dataForm.getExtensionElements()); } + /** + * Deprecated do not use. + * + * @param type the type. + * @return a reference to this builder. + * @deprecated use {@link DataForm#builder(Type)} instead. + */ + @Deprecated + // TODO: Remove in Smack 4.5 and then make this.type final. public Builder setType(Type type) { this.type = Objects.requireNonNull(type); return this; @@ -538,6 +548,8 @@ public final class DataForm implements ExtensionElement { private final List fields; + private Map fieldMap; + public ReportedData(List fields) { this.fields = Collections.unmodifiableList(fields); } @@ -561,6 +573,18 @@ public final class DataForm implements ExtensionElement { return fields; } + public FormField getField(String name) { + if (fieldMap == null) { + fieldMap = new HashMap<>(fields.size()); + for (FormField field : fields) { + String fieldName = field.getFieldName(); + fieldMap.put(fieldName, field); + } + } + + return fieldMap.get(name); + } + @Override public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java index a126d1092..1a0194b6a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software 2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software 2020-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. @@ -66,10 +66,10 @@ public class DataFormProvider extends ExtensionElementProvider { @Override public DataForm parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { DataForm.Type dataFormType = DataForm.Type.fromString(parser.getAttributeValue("", "type")); - DataForm.Builder dataForm = DataForm.builder(); - dataForm.setType(dataFormType); + DataForm.Builder dataForm = DataForm.builder(dataFormType); String formType = null; + DataForm.ReportedData reportedData = null; outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); @@ -86,6 +86,8 @@ public class DataFormProvider extends ExtensionElementProvider { dataForm.setTitle(parser.nextText()); break; case "field": + // Note that we parse this form field without any potential reportedData. We only use reportedData + // to lookup form field types of fields under . FormField formField = parseField(parser, elementXmlEnvironment, formType); TextSingleFormField hiddenFormTypeField = formField.asHiddenFormTypeFieldIfPossible(); @@ -99,12 +101,15 @@ public class DataFormProvider extends ExtensionElementProvider { dataForm.addField(formField); break; case "item": - DataForm.Item item = parseItem(parser, elementXmlEnvironment, formType); + DataForm.Item item = parseItem(parser, elementXmlEnvironment, formType, reportedData); dataForm.addItem(item); break; case "reported": - DataForm.ReportedData reported = parseReported(parser, elementXmlEnvironment, formType); - dataForm.setReportedData(reported); + if (reportedData != null) { + throw new SmackParsingException("Data form with multiple elements"); + } + reportedData = parseReported(parser, elementXmlEnvironment, formType); + dataForm.setReportedData(reportedData); break; // See XEP-133 Example 32 for a corner case where the data form contains this extension. case RosterPacket.ELEMENT: @@ -135,6 +140,11 @@ public class DataFormProvider extends ExtensionElementProvider { private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType) throws XmlPullParserException, IOException, SmackParsingException { + return parseField(parser, xmlEnvironment, formType, null); + } + + private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType, DataForm.ReportedData reportedData) + throws XmlPullParserException, IOException, SmackParsingException { final int initialDepth = parser.getDepth(); final String fieldName = parser.getAttributeValue("var"); @@ -186,6 +196,16 @@ public class DataFormProvider extends ExtensionElementProvider { } } + // Data forms of type 'result' may contain and elements. If this is the case, then the type + // of the s within the elements is determined by the information found in . See + // XEP-0004 ยง 3.4 and SMACK-902 + if (type == null && reportedData != null) { + FormField reportedFormField = reportedData.getField(fieldName); + if (reportedFormField != null) { + type = reportedFormField.getType(); + } + } + if (type == null) { // If no field type was explicitly provided, then we need to lookup the // field's type in the registry. @@ -301,7 +321,8 @@ public class DataFormProvider extends ExtensionElementProvider { return builder; } - private static DataForm.Item parseItem(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType) + private static DataForm.Item parseItem(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType, + DataForm.ReportedData reportedData) throws XmlPullParserException, IOException, SmackParsingException { final int initialDepth = parser.getDepth(); List fields = new ArrayList<>(); @@ -312,7 +333,8 @@ public class DataFormProvider extends ExtensionElementProvider { String name = parser.getName(); switch (name) { case "field": - FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType); + FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType, + reportedData); fields.add(field); break; } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java new file mode 100644 index 000000000..f25eb67d3 --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/provider/DataFormProviderTest.java @@ -0,0 +1,117 @@ +/** + * + * 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.xdata.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.util.List; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +import org.junit.jupiter.api.Test; + +public class DataFormProviderTest { + + @Test + public void testRetrieveFieldTypeFromReported() throws XmlPullParserException, IOException, SmackParsingException { + + String firstForm = + "" + + " Advanced User Search" + + " The following fields are available for searching. Wildcard (*) characters are allowed as part of the query." + + " " + + " jabber:iq:search" + + " " + + " " + + " " + + " " + + " " + + " true" + + " " + + " " + + " true" + + " " + + " " + + " true" + + " " + + ""; + XmlPullParser parser = PacketParserUtils.getParserFor(firstForm); + DataForm firstDataForm = DataFormProvider.INSTANCE.parse(parser); + FormField usernameFormField = firstDataForm.getField("Username"); + assertEquals(FormField.Type.bool, usernameFormField.getType()); + + String secondForm = + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " 0" + + " " + + " " + + " " + + " frank@orphu" + + " " + + " " + + " " + + " frank" + + " " + + " " + + " " + + " " + + " 0" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " frank2@orphu" + + " " + + " " + + " " + + " frank2" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""; + parser = PacketParserUtils.getParserFor(secondForm); + DataForm secondDataForm = DataFormProvider.INSTANCE.parse(parser); + List items = secondDataForm.getItems(); + assertEquals(2, items.size()); + } + +}