mam.rs

  1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use try_from::TryFrom;
  8
  9use minidom::Element;
 10use jid::Jid;
 11
 12use error::Error;
 13
 14use message::MessagePayload;
 15use iq::{IqGetPayload, IqSetPayload, IqResultPayload};
 16use data_forms::DataForm;
 17use rsm::{SetQuery, SetResult};
 18use forwarding::Forwarded;
 19use pubsub::NodeName;
 20
 21use ns;
 22
 23generate_id!(
 24    /// An identifier matching a result message to the query requesting it.
 25    QueryId
 26);
 27
 28generate_element!(
 29    /// Starts a query to the archive.
 30    Query, "query", MAM,
 31    attributes: [
 32        /// An optional identifier for matching forwarded messages to this
 33        /// query.
 34        queryid: Option<QueryId> = "queryid" => optional,
 35
 36        /// Must be set to Some when querying a PubSub node’s archive.
 37        node: Option<NodeName> = "node" => optional
 38    ],
 39    children: [
 40        /// Used for filtering the results.
 41        form: Option<DataForm> = ("x", DATA_FORMS) => DataForm,
 42
 43        /// Used for paging through results.
 44        set: Option<SetQuery> = ("set", RSM) => SetQuery
 45    ]
 46);
 47
 48impl IqGetPayload for Query {}
 49impl IqSetPayload for Query {}
 50impl IqResultPayload for Query {}
 51
 52generate_element!(
 53    /// The wrapper around forwarded stanzas.
 54    Result_, "result", MAM,
 55    attributes: [
 56        /// The stanza-id under which the archive stored this stanza.
 57        id: String = "id" => required,
 58
 59        /// The same queryid as the one requested in the
 60        /// [query](struct.Query.html).
 61        queryid: Option<QueryId> = "queryid" => optional,
 62    ],
 63    children: [
 64        /// The actual stanza being forwarded.
 65        forwarded: Required<Forwarded> = ("forwarded", FORWARD) => Forwarded
 66    ]
 67);
 68
 69impl MessagePayload for Result_ {}
 70
 71generate_attribute!(
 72    /// True when the end of a MAM query has been reached.
 73    Complete, "complete", bool
 74);
 75
 76generate_element!(
 77    /// Notes the end of a page in a query.
 78    Fin, "fin", MAM,
 79    attributes: [
 80        /// True when the end of a MAM query has been reached.
 81        complete: Complete = "complete" => default
 82    ],
 83    children: [
 84        /// Describes the current page, it should contain at least [first]
 85        /// (with an [index]) and [last], and generally [count].
 86        ///
 87        /// [first]: ../rsm/struct.SetResult.html#structfield.first
 88        /// [index]: ../rsm/struct.SetResult.html#structfield.first_index
 89        /// [last]: ../rsm/struct.SetResult.html#structfield.last
 90        /// [count]: ../rsm/struct.SetResult.html#structfield.count
 91        set: Required<SetResult> = ("set", RSM) => SetResult
 92    ]
 93);
 94
 95impl IqResultPayload for Fin {}
 96
 97generate_attribute!(
 98    /// Notes the default archiving preference for the user.
 99    DefaultPrefs, "default", {
100        /// The default is to always log messages in the archive.
101        Always => "always",
102
103        /// The default is to never log messages in the archive.
104        Never => "never",
105
106        /// The default is to log messages in the archive only for contacts
107        /// present in the user’s [roster](../roster/index.html).
108        Roster => "roster",
109    }
110);
111
112/// Controls the archiving preferences of the user.
113#[derive(Debug, Clone)]
114pub struct Prefs {
115    /// The default preference for JIDs in neither
116    /// [always](#structfield.always) or [never](#structfield.never) lists.
117    pub default_: DefaultPrefs,
118
119    /// The set of JIDs for which to always store messages in the archive.
120    pub always: Vec<Jid>,
121
122    /// The set of JIDs for which to never store messages in the archive.
123    pub never: Vec<Jid>,
124}
125
126impl IqGetPayload for Prefs {}
127impl IqSetPayload for Prefs {}
128impl IqResultPayload for Prefs {}
129
130impl TryFrom<Element> for Prefs {
131    type Err = Error;
132
133    fn try_from(elem: Element) -> Result<Prefs, Error> {
134        check_self!(elem, "prefs", MAM);
135        check_no_unknown_attributes!(elem, "prefs", ["default"]);
136        let mut always = vec!();
137        let mut never = vec!();
138        for child in elem.children() {
139            if child.is("always", ns::MAM) {
140                for jid_elem in child.children() {
141                    if !jid_elem.is("jid", ns::MAM) {
142                        return Err(Error::ParseError("Invalid jid element in always."));
143                    }
144                    always.push(jid_elem.text().parse()?);
145                }
146            } else if child.is("never", ns::MAM) {
147                for jid_elem in child.children() {
148                    if !jid_elem.is("jid", ns::MAM) {
149                        return Err(Error::ParseError("Invalid jid element in never."));
150                    }
151                    never.push(jid_elem.text().parse()?);
152                }
153            } else {
154                return Err(Error::ParseError("Unknown child in prefs element."));
155            }
156        }
157        let default_ = get_attr!(elem, "default", required);
158        Ok(Prefs { default_, always, never })
159    }
160}
161
162fn serialise_jid_list(name: &str, jids: Vec<Jid>) -> Option<Element> {
163    if jids.is_empty() {
164        None
165    } else {
166        Some(Element::builder(name)
167                     .ns(ns::MAM)
168                     .append(jids.into_iter()
169                                 .map(|jid| Element::builder("jid")
170                                                    .ns(ns::MAM)
171                                                    .append(jid)
172                                                    .build())
173                                 .collect::<Vec<_>>())
174                     .build())
175    }
176}
177
178impl From<Prefs> for Element {
179    fn from(prefs: Prefs) -> Element {
180        Element::builder("prefs")
181                .ns(ns::MAM)
182                .attr("default", prefs.default_)
183                .append(serialise_jid_list("always", prefs.always))
184                .append(serialise_jid_list("never", prefs.never))
185                .build()
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    use std::str::FromStr;
193
194    #[test]
195    fn test_size() {
196        assert_size!(QueryId, 24);
197        assert_size!(Query, 232);
198        assert_size!(Result_, 440);
199        assert_size!(Complete, 1);
200        assert_size!(Fin, 88);
201        assert_size!(DefaultPrefs, 1);
202        assert_size!(Prefs, 56);
203    }
204
205    #[test]
206    fn test_query() {
207        let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
208        Query::try_from(elem).unwrap();
209    }
210
211    #[test]
212    fn test_result() {
213        #[cfg(not(feature = "component"))]
214        let elem: Element = r#"
215<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
216  <forwarded xmlns='urn:xmpp:forward:0'>
217    <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
218    <message xmlns='jabber:client' from="witch@shakespeare.lit" to="macbeth@shakespeare.lit">
219      <body>Hail to thee</body>
220    </message>
221  </forwarded>
222</result>
223"#.parse().unwrap();
224        #[cfg(feature = "component")]
225        let elem: Element = r#"
226<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
227  <forwarded xmlns='urn:xmpp:forward:0'>
228    <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
229    <message xmlns='jabber:component:accept' from="witch@shakespeare.lit" to="macbeth@shakespeare.lit">
230      <body>Hail to thee</body>
231    </message>
232  </forwarded>
233</result>
234"#.parse().unwrap();
235        Result_::try_from(elem).unwrap();
236    }
237
238    #[test]
239    fn test_fin() {
240        let elem: Element = r#"
241<fin xmlns='urn:xmpp:mam:2'>
242  <set xmlns='http://jabber.org/protocol/rsm'>
243    <first index='0'>28482-98726-73623</first>
244    <last>09af3-cc343-b409f</last>
245  </set>
246</fin>
247"#.parse().unwrap();
248        Fin::try_from(elem).unwrap();
249    }
250
251    #[test]
252    fn test_query_x() {
253        let elem: Element = r#"
254<query xmlns='urn:xmpp:mam:2'>
255  <x xmlns='jabber:x:data' type='submit'>
256    <field var='FORM_TYPE' type='hidden'>
257      <value>urn:xmpp:mam:2</value>
258    </field>
259    <field var='with'>
260      <value>juliet@capulet.lit</value>
261    </field>
262  </x>
263</query>
264"#.parse().unwrap();
265        Query::try_from(elem).unwrap();
266    }
267
268    #[test]
269    fn test_query_x_set() {
270        let elem: Element = r#"
271<query xmlns='urn:xmpp:mam:2'>
272  <x xmlns='jabber:x:data' type='submit'>
273    <field var='FORM_TYPE' type='hidden'>
274      <value>urn:xmpp:mam:2</value>
275    </field>
276    <field var='start'>
277      <value>2010-08-07T00:00:00Z</value>
278    </field>
279  </x>
280  <set xmlns='http://jabber.org/protocol/rsm'>
281    <max>10</max>
282  </set>
283</query>
284"#.parse().unwrap();
285        Query::try_from(elem).unwrap();
286    }
287
288    #[test]
289    fn test_prefs_get() {
290        let elem: Element = "<prefs xmlns='urn:xmpp:mam:2' default='always'/>".parse().unwrap();
291        let prefs = Prefs::try_from(elem).unwrap();
292        assert_eq!(prefs.always, vec!());
293        assert_eq!(prefs.never, vec!());
294
295        let elem: Element = r#"
296<prefs xmlns='urn:xmpp:mam:2' default='roster'>
297  <always/>
298  <never/>
299</prefs>
300"#.parse().unwrap();
301        let prefs = Prefs::try_from(elem).unwrap();
302        assert_eq!(prefs.always, vec!());
303        assert_eq!(prefs.never, vec!());
304    }
305
306    #[test]
307    fn test_prefs_result() {
308        let elem: Element = r#"
309<prefs xmlns='urn:xmpp:mam:2' default='roster'>
310  <always>
311    <jid>romeo@montague.lit</jid>
312  </always>
313  <never>
314    <jid>montague@montague.lit</jid>
315  </never>
316</prefs>
317"#.parse().unwrap();
318        let prefs = Prefs::try_from(elem).unwrap();
319        assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap()));
320        assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap()));
321
322        let elem2 = Element::from(prefs.clone());
323        println!("{:?}", elem2);
324        let prefs2 = Prefs::try_from(elem2).unwrap();
325        assert_eq!(prefs.default_, prefs2.default_);
326        assert_eq!(prefs.always, prefs2.always);
327        assert_eq!(prefs.never, prefs2.never);
328    }
329
330    #[test]
331    fn test_invalid_child() {
332        let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>".parse().unwrap();
333        let error = Query::try_from(elem).unwrap_err();
334        let message = match error {
335            Error::ParseError(string) => string,
336            _ => panic!(),
337        };
338        assert_eq!(message, "Unknown child in query element.");
339    }
340
341    #[test]
342    fn test_serialise() {
343        let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
344        let replace = Query { queryid: None, node: None, form: None, set: None };
345        let elem2 = replace.into();
346        assert_eq!(elem, elem2);
347    }
348}