diff --git a/parsers/src/data_forms.rs b/parsers/src/data_forms.rs index 6e7572e..8fa597e 100644 --- a/parsers/src/data_forms.rs +++ b/parsers/src/data_forms.rs @@ -120,6 +120,47 @@ impl Field { fn is_list(&self) -> bool { self.type_ == FieldType::ListSingle || self.type_ == FieldType::ListMulti } + + /// Return true if this field is a valid form type specifier as per + /// [XEP-0068](https://xmpp.org/extensions/xep-0068.html). + /// + /// This function requires knowledge of the form's type attribute as the + /// criteria differ slighly among form types. + pub fn is_form_type(&self, ty: &DataFormType) -> bool { + // 1. A field must have the var FORM_TYPE + if self.var != "FORM_TYPE" { + return false; + } + + match ty { + // https://xmpp.org/extensions/xep-0068.html#usecases-incorrect + // > If the FORM_TYPE field is not hidden in a form with + // > type="form" or type="result", it MUST be ignored as a context + // > indicator. + DataFormType::Form | DataFormType::Result_ => self.type_ == FieldType::Hidden, + + // https://xmpp.org/extensions/xep-0068.html#impl + // > Data forms with the type "submit" are free to omit any + // > explicit field type declaration (as per Data Forms (XEP-0004) + // > § 3.2), as the type is implied by the corresponding + // > "form"-type data form. As consequence, implementations MUST + // > treat a FORM_TYPE field without an explicit type attribute, + // > in data forms of type "submit", as the FORM_TYPE field with + // > the special meaning defined herein. + DataFormType::Submit => match self.type_ { + FieldType::Hidden => true, + FieldType::TextSingle => true, + _ => false, + }, + + // XEP-0068 does not explicitly mention cancel type forms. + // However, XEP-0004 states: + // > a data form of type "cancel" SHOULD NOT contain any + // > elements. + // thus we ignore those. + DataFormType::Cancel => false, + } + } } impl TryFrom for Field { @@ -276,14 +317,11 @@ impl TryFrom for DataForm { form.instructions = Some(child.text()); } else if child.is("field", ns::DATA_FORMS) { let field = Field::try_from(child.clone())?; - if field.var == "FORM_TYPE" { + if field.is_form_type(&form.type_) { let mut field = field; if form.form_type.is_some() { return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); } - if field.type_ != FieldType::Hidden { - return Err(Error::ParseError("Invalid field type for FORM_TYPE.")); - } if field.values.len() != 1 { return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); } @@ -418,4 +456,92 @@ mod tests { "Element option must not have more than one value child." ); } + + #[test] + fn test_ignore_form_type_field_if_field_type_mismatches_in_form_typed_forms() { + // https://xmpp.org/extensions/xep-0068.html#usecases-incorrect + // […] it MUST be ignored as a context indicator + let elem: Element = "foo".parse().unwrap(); + match DataForm::try_from(elem) { + Ok(form) => { + match form.form_type { + None => (), + other => panic!("unexpected extracted form type: {:?}", other), + }; + } + other => panic!("unexpected result: {:?}", other), + } + } + + #[test] + fn test_ignore_form_type_field_if_field_type_mismatches_in_result_typed_forms() { + // https://xmpp.org/extensions/xep-0068.html#usecases-incorrect + // […] it MUST be ignored as a context indicator + let elem: Element = "foo".parse().unwrap(); + match DataForm::try_from(elem) { + Ok(form) => { + match form.form_type { + None => (), + other => panic!("unexpected extracted form type: {:?}", other), + }; + } + other => panic!("unexpected result: {:?}", other), + } + } + + #[test] + fn test_accept_form_type_field_without_type_attribute_in_submit_typed_forms() { + let elem: Element = "foo".parse().unwrap(); + match DataForm::try_from(elem) { + Ok(form) => { + match form.form_type { + Some(ty) => assert_eq!(ty, "foo"), + other => panic!("unexpected extracted form type: {:?}", other), + }; + } + other => panic!("unexpected result: {:?}", other), + } + } + + #[test] + fn test_accept_form_type_field_with_type_hidden_in_submit_typed_forms() { + let elem: Element = "foo".parse().unwrap(); + match DataForm::try_from(elem) { + Ok(form) => { + match form.form_type { + Some(ty) => assert_eq!(ty, "foo"), + other => panic!("unexpected extracted form type: {:?}", other), + }; + } + other => panic!("unexpected result: {:?}", other), + } + } + + #[test] + fn test_accept_form_type_field_with_type_hidden_in_result_typed_forms() { + let elem: Element = "foo".parse().unwrap(); + match DataForm::try_from(elem) { + Ok(form) => { + match form.form_type { + Some(ty) => assert_eq!(ty, "foo"), + other => panic!("unexpected extracted form type: {:?}", other), + }; + } + other => panic!("unexpected result: {:?}", other), + } + } + + #[test] + fn test_accept_form_type_field_with_type_hidden_in_form_typed_forms() { + let elem: Element = "foo".parse().unwrap(); + match DataForm::try_from(elem) { + Ok(form) => { + match form.form_type { + Some(ty) => assert_eq!(ty, "foo"), + other => panic!("unexpected extracted form type: {:?}", other), + }; + } + other => panic!("unexpected result: {:?}", other), + } + } }