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, Node};
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>) -> ::std::option::IntoIter<Node> {
165 if jids.is_empty() {
166 None.into_iter()
167 } else {
168 Some(
169 Element::builder(name)
170 .ns(ns::MAM)
171 .append_all(
172 jids.into_iter()
173 .map(|jid|
174 Element::builder("jid")
175 .ns(ns::MAM)
176 .append(String::from(jid))))
177 .into(),
178 ).into_iter()
179 }
180}
181
182impl From<Prefs> for Element {
183 fn from(prefs: Prefs) -> Element {
184 Element::builder("prefs")
185 .ns(ns::MAM)
186 .attr("default", prefs.default_)
187 .append_all(serialise_jid_list("always", prefs.always))
188 .append_all(serialise_jid_list("never", prefs.never))
189 .build()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use std::str::FromStr;
197
198 #[cfg(target_pointer_width = "32")]
199 #[test]
200 fn test_size() {
201 assert_size!(QueryId, 12);
202 assert_size!(Query, 116);
203 assert_size!(Result_, 236);
204 assert_size!(Complete, 1);
205 assert_size!(Fin, 44);
206 assert_size!(DefaultPrefs, 1);
207 assert_size!(Prefs, 28);
208 }
209
210 #[cfg(target_pointer_width = "64")]
211 #[test]
212 fn test_size() {
213 assert_size!(QueryId, 24);
214 assert_size!(Query, 232);
215 assert_size!(Result_, 456);
216 assert_size!(Complete, 1);
217 assert_size!(Fin, 88);
218 assert_size!(DefaultPrefs, 1);
219 assert_size!(Prefs, 56);
220 }
221
222 #[test]
223 fn test_query() {
224 let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
225 Query::try_from(elem).unwrap();
226 }
227
228 #[test]
229 fn test_result() {
230 #[cfg(not(feature = "component"))]
231 let elem: Element = r#"
232<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
233 <forwarded xmlns='urn:xmpp:forward:0'>
234 <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
235 <message xmlns='jabber:client' from="witch@shakespeare.lit" to="macbeth@shakespeare.lit">
236 <body>Hail to thee</body>
237 </message>
238 </forwarded>
239</result>
240"#
241 .parse()
242 .unwrap();
243 #[cfg(feature = "component")]
244 let elem: Element = r#"
245<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
246 <forwarded xmlns='urn:xmpp:forward:0'>
247 <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
248 <message xmlns='jabber:component:accept' from="witch@shakespeare.lit" to="macbeth@shakespeare.lit">
249 <body>Hail to thee</body>
250 </message>
251 </forwarded>
252</result>
253"#.parse().unwrap();
254 Result_::try_from(elem).unwrap();
255 }
256
257 #[test]
258 fn test_fin() {
259 let elem: Element = r#"
260<fin xmlns='urn:xmpp:mam:2'>
261 <set xmlns='http://jabber.org/protocol/rsm'>
262 <first index='0'>28482-98726-73623</first>
263 <last>09af3-cc343-b409f</last>
264 </set>
265</fin>
266"#
267 .parse()
268 .unwrap();
269 Fin::try_from(elem).unwrap();
270 }
271
272 #[test]
273 fn test_query_x() {
274 let elem: Element = r#"
275<query xmlns='urn:xmpp:mam:2'>
276 <x xmlns='jabber:x:data' type='submit'>
277 <field var='FORM_TYPE' type='hidden'>
278 <value>urn:xmpp:mam:2</value>
279 </field>
280 <field var='with'>
281 <value>juliet@capulet.lit</value>
282 </field>
283 </x>
284</query>
285"#
286 .parse()
287 .unwrap();
288 Query::try_from(elem).unwrap();
289 }
290
291 #[test]
292 fn test_query_x_set() {
293 let elem: Element = r#"
294<query xmlns='urn:xmpp:mam:2'>
295 <x xmlns='jabber:x:data' type='submit'>
296 <field var='FORM_TYPE' type='hidden'>
297 <value>urn:xmpp:mam:2</value>
298 </field>
299 <field var='start'>
300 <value>2010-08-07T00:00:00Z</value>
301 </field>
302 </x>
303 <set xmlns='http://jabber.org/protocol/rsm'>
304 <max>10</max>
305 </set>
306</query>
307"#
308 .parse()
309 .unwrap();
310 Query::try_from(elem).unwrap();
311 }
312
313 #[test]
314 fn test_prefs_get() {
315 let elem: Element = "<prefs xmlns='urn:xmpp:mam:2' default='always'/>"
316 .parse()
317 .unwrap();
318 let prefs = Prefs::try_from(elem).unwrap();
319 assert_eq!(prefs.always, vec!());
320 assert_eq!(prefs.never, vec!());
321
322 let elem: Element = r#"
323<prefs xmlns='urn:xmpp:mam:2' default='roster'>
324 <always/>
325 <never/>
326</prefs>
327"#
328 .parse()
329 .unwrap();
330 let prefs = Prefs::try_from(elem).unwrap();
331 assert_eq!(prefs.always, vec!());
332 assert_eq!(prefs.never, vec!());
333 }
334
335 #[test]
336 fn test_prefs_result() {
337 let elem: Element = r#"
338<prefs xmlns='urn:xmpp:mam:2' default='roster'>
339 <always>
340 <jid>romeo@montague.lit</jid>
341 </always>
342 <never>
343 <jid>montague@montague.lit</jid>
344 </never>
345</prefs>
346"#
347 .parse()
348 .unwrap();
349 let prefs = Prefs::try_from(elem).unwrap();
350 assert_eq!(
351 prefs.always,
352 vec!(Jid::from_str("romeo@montague.lit").unwrap())
353 );
354 assert_eq!(
355 prefs.never,
356 vec!(Jid::from_str("montague@montague.lit").unwrap())
357 );
358
359 let elem2 = Element::from(prefs.clone());
360 println!("{:?}", elem2);
361 let prefs2 = Prefs::try_from(elem2).unwrap();
362 assert_eq!(prefs.default_, prefs2.default_);
363 assert_eq!(prefs.always, prefs2.always);
364 assert_eq!(prefs.never, prefs2.never);
365 }
366
367 #[test]
368 fn test_invalid_child() {
369 let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>"
370 .parse()
371 .unwrap();
372 let error = Query::try_from(elem).unwrap_err();
373 let message = match error {
374 Error::ParseError(string) => string,
375 _ => panic!(),
376 };
377 assert_eq!(message, "Unknown child in query element.");
378 }
379
380 #[test]
381 fn test_serialise() {
382 let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
383 let replace = Query {
384 queryid: None,
385 node: None,
386 form: None,
387 set: None,
388 };
389 let elem2 = replace.into();
390 assert_eq!(elem, elem2);
391 }
392}