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}