// Copyright (c) 2017 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::data_forms::DataForm; use crate::util::error::Error; use crate::forwarding::Forwarded; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::message::MessagePayload; use crate::ns; use crate::pubsub::NodeName; use crate::rsm::{SetQuery, SetResult}; use jid::Jid; use minidom::Element; use std::convert::TryFrom; generate_id!( /// An identifier matching a result message to the query requesting it. QueryId ); generate_element!( /// Starts a query to the archive. Query, "query", MAM, attributes: [ /// An optional identifier for matching forwarded messages to this /// query. queryid: Option = "queryid", /// Must be set to Some when querying a PubSub node’s archive. node: Option = "node" ], children: [ /// Used for filtering the results. form: Option = ("x", DATA_FORMS) => DataForm, /// Used for paging through results. set: Option = ("set", RSM) => SetQuery ] ); impl IqGetPayload for Query {} impl IqSetPayload for Query {} impl IqResultPayload for Query {} generate_element!( /// The wrapper around forwarded stanzas. Result_, "result", MAM, attributes: [ /// The stanza-id under which the archive stored this stanza. id: Required = "id", /// The same queryid as the one requested in the /// [query](struct.Query.html). queryid: Option = "queryid", ], children: [ /// The actual stanza being forwarded. forwarded: Required = ("forwarded", FORWARD) => Forwarded ] ); impl MessagePayload for Result_ {} generate_attribute!( /// True when the end of a MAM query has been reached. Complete, "complete", bool ); generate_element!( /// Notes the end of a page in a query. Fin, "fin", MAM, attributes: [ /// True when the end of a MAM query has been reached. complete: Default = "complete", ], children: [ /// Describes the current page, it should contain at least [first] /// (with an [index]) and [last], and generally [count]. /// /// [first]: ../rsm/struct.SetResult.html#structfield.first /// [index]: ../rsm/struct.SetResult.html#structfield.first_index /// [last]: ../rsm/struct.SetResult.html#structfield.last /// [count]: ../rsm/struct.SetResult.html#structfield.count set: Required = ("set", RSM) => SetResult ] ); impl IqResultPayload for Fin {} generate_attribute!( /// Notes the default archiving preference for the user. DefaultPrefs, "default", { /// The default is to always log messages in the archive. Always => "always", /// The default is to never log messages in the archive. Never => "never", /// The default is to log messages in the archive only for contacts /// present in the user’s [roster](../roster/index.html). Roster => "roster", } ); /// Controls the archiving preferences of the user. #[derive(Debug, Clone)] pub struct Prefs { /// The default preference for JIDs in neither /// [always](#structfield.always) or [never](#structfield.never) lists. pub default_: DefaultPrefs, /// The set of JIDs for which to always store messages in the archive. pub always: Vec, /// The set of JIDs for which to never store messages in the archive. pub never: Vec, } impl IqGetPayload for Prefs {} impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} impl TryFrom for Prefs { type Error = Error; fn try_from(elem: Element) -> Result { check_self!(elem, "prefs", MAM); check_no_unknown_attributes!(elem, "prefs", ["default"]); let mut always = vec![]; let mut never = vec![]; for child in elem.children() { if child.is("always", ns::MAM) { for jid_elem in child.children() { if !jid_elem.is("jid", ns::MAM) { return Err(Error::ParseError("Invalid jid element in always.")); } always.push(jid_elem.text().parse()?); } } else if child.is("never", ns::MAM) { for jid_elem in child.children() { if !jid_elem.is("jid", ns::MAM) { return Err(Error::ParseError("Invalid jid element in never.")); } never.push(jid_elem.text().parse()?); } } else { return Err(Error::ParseError("Unknown child in prefs element.")); } } let default_ = get_attr!(elem, "default", Required); Ok(Prefs { default_, always, never, }) } } fn serialise_jid_list(name: &str, jids: Vec) -> Option { if jids.is_empty() { None } else { Some( Element::builder(name) .ns(ns::MAM) .append( jids.into_iter() .map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build()) .collect::>(), ) .build(), ) } } impl From for Element { fn from(prefs: Prefs) -> Element { Element::builder("prefs") .ns(ns::MAM) .attr("default", prefs.default_) .append(serialise_jid_list("always", prefs.always)) .append(serialise_jid_list("never", prefs.never)) .build() } } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; #[cfg(target_pointer_width = "32")] #[test] fn test_size() { assert_size!(QueryId, 12); assert_size!(Query, 116); assert_size!(Result_, 228); assert_size!(Complete, 1); assert_size!(Fin, 44); assert_size!(DefaultPrefs, 1); assert_size!(Prefs, 28); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(QueryId, 24); assert_size!(Query, 232); assert_size!(Result_, 456); assert_size!(Complete, 1); assert_size!(Fin, 88); assert_size!(DefaultPrefs, 1); assert_size!(Prefs, 56); } #[test] fn test_query() { let elem: Element = "".parse().unwrap(); Query::try_from(elem).unwrap(); } #[test] fn test_result() { #[cfg(not(feature = "component"))] let elem: Element = r#" Hail to thee "# .parse() .unwrap(); #[cfg(feature = "component")] let elem: Element = r#" Hail to thee "#.parse().unwrap(); Result_::try_from(elem).unwrap(); } #[test] fn test_fin() { let elem: Element = r#" 28482-98726-73623 09af3-cc343-b409f "# .parse() .unwrap(); Fin::try_from(elem).unwrap(); } #[test] fn test_query_x() { let elem: Element = r#" urn:xmpp:mam:2 juliet@capulet.lit "# .parse() .unwrap(); Query::try_from(elem).unwrap(); } #[test] fn test_query_x_set() { let elem: Element = r#" urn:xmpp:mam:2 2010-08-07T00:00:00Z 10 "# .parse() .unwrap(); Query::try_from(elem).unwrap(); } #[test] fn test_prefs_get() { let elem: Element = "" .parse() .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!(prefs.always, vec!()); assert_eq!(prefs.never, vec!()); let elem: Element = r#" "# .parse() .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!(prefs.always, vec!()); assert_eq!(prefs.never, vec!()); } #[test] fn test_prefs_result() { let elem: Element = r#" romeo@montague.lit montague@montague.lit "# .parse() .unwrap(); let prefs = Prefs::try_from(elem).unwrap(); assert_eq!( prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap()) ); assert_eq!( prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap()) ); let elem2 = Element::from(prefs.clone()); println!("{:?}", elem2); let prefs2 = Prefs::try_from(elem2).unwrap(); assert_eq!(prefs.default_, prefs2.default_); assert_eq!(prefs.always, prefs2.always); assert_eq!(prefs.never, prefs2.never); } #[test] fn test_invalid_child() { let elem: Element = "" .parse() .unwrap(); let error = Query::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in query element."); } #[test] fn test_serialise() { let elem: Element = "".parse().unwrap(); let replace = Query { queryid: None, node: None, form: None, set: None, }; let elem2 = replace.into(); assert_eq!(elem, elem2); } }