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}