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