powered by Jive Software

DataFormProvider parseField does not use reported element

When receiving form results, the fields and field types of the results can differ from the request form. The result fields and their types are described in a element that precedes the fields and their values.

The DataFormProvider does not use the reported data when parsing fields, and can throw IllegalArgumentExecptions if the result field type differs from the request field type.

When the request is send, the request form (type form) fields are registered in the FormFieldRegistry. When parsing the result, field types for fields without an explicit type are looked up again in the FormFieldRegistry. If the field type of the results are different than of the request, the field values cannot be set to the FormField subclass instance, and an exception is thrown.

This bug makes using the UserSearch impossible. The code below fails with an exception:

UserSearchManager manager = new UserSearchManager(connection);
DomainBareJid searchServiceJid = JidCreate.domainBareFrom("search.orphu");
DataForm searchForm = manager.getSearchForm(searchServiceJid);
FillableForm requestForm = new FillableForm(searchForm);
requestForm.setAnswer("search", "frank");
requestForm.setAnswer("Username", true);
requestForm.setAnswer("Name", true);
requestForm.setAnswer("Email", true);
ReportedData searchResults = manager.getSearchResults(requestForm.getDataFormToSubmit(), searchServiceJid);

This fails when parsing the Username field in the result:

java.lang.IllegalArgumentException: frank is not a valid boolean string
	at org.jivesoftware.smack.util.ParserUtils.parseXmlBoolean(ParserUtils.java:165)
	at org.jivesoftware.smackx.xdata.BooleanFormField$Builder.setValue(BooleanFormField.java:77)
	at org.jivesoftware.smackx.xdata.provider.DataFormProvider.parseBooleanFormField(DataFormProvider.java:280)
	at org.jivesoftware.smackx.xdata.provider.DataFormProvider.parseField(DataFormProvider.java:204)
	at org.jivesoftware.smackx.xdata.provider.DataFormProvider.parseItem(DataFormProvider.java:315)
	at org.jivesoftware.smackx.xdata.provider.DataFormProvider.parse(DataFormProvider.java:102)
	at org.jivesoftware.smackx.xdata.provider.DataFormProvider.parse(DataFormProvider.java:60)
	at org.jivesoftware.smack.provider.Provider.parse(Provider.java:53)
	at org.jivesoftware.smack.util.PacketParserUtils.parseExtensionElement(PacketParserUtils.java:834)
	at org.jivesoftware.smack.util.PacketParserUtils.addExtensionElement(PacketParserUtils.java:922)
	at org.jivesoftware.smack.util.PacketParserUtils.addExtensionElement(PacketParserUtils.java:917)
	at org.jivesoftware.smackx.search.UserSearch$Provider.parse(UserSearch.java:145)
	at org.jivesoftware.smack.provider.IQProvider.parse(IQProvider.java:64)
	at org.jivesoftware.smack.provider.IqProvider.parse(IqProvider.java:40)
	at org.jivesoftware.smack.util.PacketParserUtils.parseIQ(PacketParserUtils.java:549)
	at org.jivesoftware.smack.util.PacketParserUtils.parseStanza(PacketParserUtils.java:113)
	at org.jivesoftware.smack.AbstractXMPPConnection.parseAndProcessStanza(AbstractXMPPConnection.java:1448)
	at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$1000(XMPPTCPConnection.java:130)
	at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:969)
	at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$700(XMPPTCPConnection.java:913)
	at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:936)
	at java.base/java.lang.Thread.run(Thread.java:834)

I would propose that we pass the DataForm to the parseField method, and check any ReportedData for field types before checking the FormFieldRegistry.

Using Smack 4.4.0, server is Openfire 4.6.1.

Cheers,
Frank

Could you show the involved stanzas, especially the incoming data form?

Search form returned by the server:

<iq xmlns="jabber:client" to="unittest@orphu/9fayvreg1" from="search.orphu" id="IDGXX-10" type="result">
  <query xmlns="jabber:iq:search">
    <x xmlns="jabber:x:data" type="form">
      <title>Advanced User Search</title>
      <instructions>The following fields are available for searching. Wildcard (*) characters are allowed as part of the query.</instructions>
      <field var="FORM_TYPE" type="hidden">
        <value>jabber:iq:search</value>
      </field>
      <field label="Search" var="search">
        <required/>
      </field>
      <field label="Username" var="Username" type="boolean">
        <value>true</value>
      </field>
      <field label="Name" var="Name" type="boolean">
        <value>true</value>
      </field>
      <field label="Email" var="Email" type="boolean">
        <value>true</value>
      </field>
    </x>
  </query>
</iq>

Submitted Search Result request:

<iq xmlns="jabber:client" to="search.orphu" id="7VHP7-13" type="set">
  <query xmlns="jabber:iq:search">
    <x xmlns="jabber:x:data" type="submit">
      <field var="FORM_TYPE">
        <value>jabber:iq:search</value>
      </field>
      <field var="search">
        <value>frank</value>
      </field>
      <field var="Email">
        <value>true</value>
      </field>
      <field var="Username">
        <value>true</value>
      </field>
      <field var="Name">
        <value>true</value>
      </field>
    </x>
  </query>
</iq>

And the response:

<iq type="result" id="7VHP7-13" from="search.orphu" to="unittest@orphu/ad7s5fgrd8">
  <query xmlns="jabber:iq:search">
    <x xmlns="jabber:x:data" type="result">
      <field var="FORM_TYPE" type="hidden"/>
      <reported>
        <field var="jid" type="jid-single" label="JID"/>
        <field var="Username" type="text-single" label="Username"/>
        <field var="Name" type="text-single" label="Name"/>
        <field var="Email" type="text-single" label="Email"/>
      </reported>
      <item>
        <field var="Email">
          <value>
            0
          </value>
        </field>
        <field var="jid">
          <value>
            frank@orphu
          </value>
        </field>
        <field var="Username">
          <value>
            frank
          </value>
        </field>
        <field var="Name">
          <value>
            0
          </value>
        </field>
      </item>
      <item>
        <field var="Email">
          <value>
          </value>
        </field>
        <field var="jid">
          <value>
            frank2@orphu
          </value>
        </field>
        <field var="Username">
          <value>
            frank2
          </value>
        </field>
        <field var="Name">
          <value>
          </value>
        </field>
      </item>
    </x>
  </query>
</iq>

Perhaps interesting to note that in this case the parsing fails on the Username. This is because I set the Email and Name for the user to 0, which is parsed successfully as a boolean xD.

Your issue appears to be that something registers the, non-standard, “search” field as boolean type. Probably even another form type.

What further contributes is that the the ‘search’ field has not type annotation. If it where annotated with type=text-single, then this shouldn’t happen, because Smack would register the field ‘search’ for the form type ‘jabber:iq:search’ as type boolean.

You edited your post an now the returned form shows

      <field label="Username" var="Username" type="boolean">
        <value>true</value>
      </field>

previously there was no type annotation for the ‘Username’ field. And type ‘boolean’ sounds wrong here. Same is true for the other fields.

Which server implementation is used here?

You may also want to have a look at https://xmpp.org/extensions/xep-0055.html#registrar-formtypes

The server used in Openfire 4.6.1 with the latest search plugin. The boolean fields are intended to modify the scope of the text search (enabling ‘Username’ would apply the search string to the username field).

I am under the impression that reported results do not need to use the same fields as the submit form. The DataForms reported element is intended to specify the types of the returned fields.

The registrar form types section suggests that the server incorrectly returns the ‘jabber:iq:search’ FORM_TYPE right? The fields registered for this form type are not actually in the form.

A Field must always be of the same type. And a field is identified by it’s name (‘var’ attribute) and the form field type (‘FROM_TYPE’).

You could try to determine why Smack considers the ‘search’ field to be of type ‘boolean’. The form returned by the server denotes it as text-single, as the ‘type’ attribute is omitted, and text-single is the default.

But yes, the other fields, like ‘Username’ change type from boolean in the ‘form’ and ‘submit’ type data forms, but come text-single in the ‘result’ type form. And that is IMHO a bug in the implementation. Potentially the fields in the form/submit data forms should not be named ‘Username’ but ‘search-in-username’ (or so).

Smack does not treat the search field as boolean though. It treats the response Username field as boolean. This is because Smack registered it as boolean when it it encountered the jabber:iq:search form.

What happens is:

  1. We request the search form
  2. We receive the search form. Upon the creation of a DataForm instance Smack registers the fields in the form in the FormFieldRegistry. This registers the type boolean to the fieldname ‘Username’ with formType ‘jabber:iq:search’. It also fills a reverse fieldName to formType map, for reverse lookup of the formType from a fieldName.
  3. We fill in the search form, and submit it to the server.
  4. We receive the response to the search. It is a data form without a FORM_TYPE, and it instead includes a <reported> element as described by XEP-0004 to specify the field types of the fields.
  5. When parsing the data form, smack parses the <reported> element, but does nothing with it besides adding it to the DataForm object.
  6. When parsing the items in the data form, Smack tries to find the field type of the item fields by a lookup in the FormFieldRegistry. As no FORM_TYPE is set in the result data form, the FormFieldRegistry does a reverse lookup to find the formType for the fieldName. It comes back with the jabber:iq:search formType and decides the field type for the Username field must be boolean.
  7. When setting the value of the Username field to a string (‘frank’), an IllegalArgumentException is thrown, as its not a boolean.

So the search form is received, and submitted without any problems. But the results cannot be parsed because the FormFieldRegistry, if no form type is known, resolves the form type based on the field name.

Should an attempt be made to resolve a FORM_TYPE for forms without one? Also, the reverse lookup assumes that a fieldName can only occur for one FORM_TYPE. Looking in the form types registry, https://xmpp.org/registrar/formtypes.html, I can see this is not the case for all forms.

From what I understand of XEP-0068, custom/non-registered form field names should be in clark notation, with a organisation specific prefix. But it is for me unclear if this is only for unregistered fields, or also for registered fields. In any case duplicate field names are currently registered for some form types. They do not cause a real problem I suppose, because they are all of the same type.

Regardless, for result forms, that include a <reported> element, this element defines the types of the returned field, as defined by XEP-0004. The type should not be resolved from a registered FORM_TYPE, which is in turn found by a reverse lookup on the fieldname.

I think this summary is pretty close to what happens.

That suggestion appears sensible.

Can I be of assistance? Should I contribute a patch? Currently we are migrating our client to 4.4.0, and this is one of our blocking issues.

Created SMACK-902 and PR #459

Please report back if this helps or not. Thank you.

Using this PR allows us to use the user search once again. Cheers!