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