owner.rs

  1// Copyright (c) 2020 Paul Fariello <paul@fariello.eu>
  2// Copyright (c) 2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  3//
  4// This Source Code Form is subject to the terms of the Mozilla Public
  5// License, v. 2.0. If a copy of the MPL was not distributed with this
  6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  7
  8use xso::{AsXml, FromXml};
  9
 10use crate::data_forms::DataForm;
 11use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 12use crate::ns;
 13use crate::pubsub::{AffiliationAttribute, NodeName, Subscription};
 14use jid::Jid;
 15
 16/// An affiliation element.
 17#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
 18#[xml(namespace = ns::PUBSUB_OWNER, name = "affiliation")]
 19pub struct Affiliation {
 20    /// The node this affiliation pertains to.
 21    #[xml(attribute)]
 22    jid: Jid,
 23
 24    /// The affiliation you currently have on this node.
 25    #[xml(attribute)]
 26    affiliation: AffiliationAttribute,
 27}
 28
 29/// A subscription element, describing the state of a subscription.
 30#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
 31#[xml(namespace = ns::PUBSUB_OWNER, name = "subscription")]
 32pub struct SubscriptionElem {
 33    /// The JID affected by this subscription.
 34    #[xml(attribute)]
 35    pub jid: Jid,
 36
 37    /// The state of the subscription.
 38    #[xml(attribute)]
 39    pub subscription: Subscription,
 40
 41    /// Subscription unique id.
 42    #[xml(attribute(default))]
 43    pub subid: Option<String>,
 44}
 45
 46/// Represents an owner request to a PubSub service.
 47#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
 48#[xml(namespace = ns::PUBSUB_OWNER, name = "pubsub")]
 49pub struct Owner {
 50    /// The inner child of this request.
 51    #[xml(child)]
 52    pub payload: Payload,
 53}
 54
 55// TODO: Differentiate each payload per type someday, to simplify our types.
 56impl IqGetPayload for Owner {}
 57impl IqSetPayload for Owner {}
 58impl IqResultPayload for Owner {}
 59
 60/// Main payload used to communicate with a PubSub service.
 61///
 62/// `<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"/>`
 63#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
 64#[xml(namespace = ns::PUBSUB_OWNER, exhaustive)]
 65pub enum Payload {
 66    /// Manage the affiliations of a node.
 67    #[xml(name = "affiliations")]
 68    Affiliations {
 69        /// The node name this request pertains to.
 70        #[xml(attribute)]
 71        node: NodeName,
 72
 73        /// The actual list of affiliation elements.
 74        #[xml(child(n = ..))]
 75        affiliations: Vec<Affiliation>,
 76    },
 77
 78    /// Request to configure a node.
 79    #[xml(name = "configure")]
 80    Configure {
 81        /// The node to be configured.
 82        #[xml(attribute(default))]
 83        node: Option<NodeName>,
 84
 85        /// The form to configure it.
 86        #[xml(child(default))]
 87        form: Option<DataForm>,
 88    },
 89
 90    /// Request the default node configuration.
 91    #[xml(name = "default")]
 92    Default {
 93        /// The form to configure it.
 94        #[xml(child(default))]
 95        form: Option<DataForm>,
 96    },
 97
 98    /// Delete a node.
 99    #[xml(name = "delete")]
100    Delete {
101        /// The node to be deleted.
102        #[xml(attribute)]
103        node: NodeName,
104
105        /// Redirection to replace the deleted node.
106        #[xml(extract(default, name = "redirect", fields(attribute(name = "uri", type_ = String))))]
107        redirect_uri: Option<String>,
108    },
109
110    /// Purge all items from node.
111    #[xml(name = "purge")]
112    Purge {
113        /// The node to be cleared.
114        #[xml(attribute)]
115        node: NodeName,
116    },
117
118    /// Request the current subscriptions to a node.
119    #[xml(name = "subscriptions")]
120    Subscriptions {
121        /// The node to query.
122        #[xml(attribute)]
123        node: NodeName,
124
125        /// The list of subscription elements returned.
126        #[xml(child(n = ..))]
127        subscriptions: Vec<SubscriptionElem>,
128    },
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use crate::data_forms::{DataFormType, Field, FieldType};
135    use core::str::FromStr;
136    use jid::BareJid;
137    use minidom::Element;
138
139    #[test]
140    fn affiliations() {
141        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><affiliations node='foo'><affiliation jid='hamlet@denmark.lit' affiliation='owner'/><affiliation jid='polonius@denmark.lit' affiliation='outcast'/></affiliations></pubsub>"
142        .parse()
143        .unwrap();
144        let elem1 = elem.clone();
145
146        let payload = Payload::Affiliations {
147            node: NodeName(String::from("foo")),
148            affiliations: vec![
149                Affiliation {
150                    jid: Jid::from(BareJid::from_str("hamlet@denmark.lit").unwrap()),
151                    affiliation: AffiliationAttribute::Owner,
152                },
153                Affiliation {
154                    jid: Jid::from(BareJid::from_str("polonius@denmark.lit").unwrap()),
155                    affiliation: AffiliationAttribute::Outcast,
156                },
157            ],
158        };
159        let pubsub = Owner { payload };
160
161        let elem2 = Element::from(pubsub);
162        assert_eq!(elem1, elem2);
163    }
164
165    #[test]
166    fn configure() {
167        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><configure node='foo'><x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var='pubsub#access_model' type='list-single'><value>whitelist</value></field></x></configure></pubsub>"
168        .parse()
169        .unwrap();
170        let elem1 = elem.clone();
171
172        let payload = Payload::Configure {
173            node: Some(NodeName(String::from("foo"))),
174            form: Some(DataForm::new(
175                DataFormType::Submit,
176                ns::PUBSUB_CONFIGURE,
177                vec![Field::new("pubsub#access_model", FieldType::ListSingle)
178                    .with_value("whitelist")],
179            )),
180        };
181        let pubsub = Owner { payload };
182
183        let elem2 = Element::from(pubsub);
184        assert_eq!(elem1, elem2);
185    }
186
187    #[test]
188    fn test_serialize_configure() {
189        let reference: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><configure node='foo'><x xmlns='jabber:x:data' type='submit'/></configure></pubsub>"
190        .parse()
191        .unwrap();
192
193        let elem: Element = "<x xmlns='jabber:x:data' type='submit'/>".parse().unwrap();
194
195        let form = DataForm::try_from(elem).unwrap();
196
197        let payload = Payload::Configure {
198            node: Some(NodeName(String::from("foo"))),
199            form: Some(form),
200        };
201        let configure = Owner { payload };
202        let serialized: Element = configure.into();
203        assert_eq!(serialized, reference);
204    }
205
206    #[test]
207    fn default() {
208        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><default><x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var='pubsub#access_model' type='list-single'><value>whitelist</value></field></x></default></pubsub>"
209        .parse()
210        .unwrap();
211        let elem1 = elem.clone();
212
213        let payload = Payload::Default {
214            form: Some(DataForm::new(
215                DataFormType::Submit,
216                ns::PUBSUB_CONFIGURE,
217                vec![Field::new("pubsub#access_model", FieldType::ListSingle)
218                    .with_value("whitelist")],
219            )),
220        };
221        let pubsub = Owner { payload };
222
223        let elem2 = Element::from(pubsub);
224        assert_eq!(elem1, elem2);
225    }
226
227    #[test]
228    fn delete() {
229        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><delete node='foo'><redirect uri='xmpp:hamlet@denmark.lit?;node=blog'/></delete></pubsub>"
230        .parse()
231        .unwrap();
232        let elem1 = elem.clone();
233
234        let payload = Payload::Delete {
235            node: NodeName(String::from("foo")),
236            redirect_uri: Some(String::from("xmpp:hamlet@denmark.lit?;node=blog")),
237        };
238        let pubsub = Owner { payload };
239
240        let elem2 = Element::from(pubsub);
241        assert_eq!(elem1, elem2);
242    }
243
244    #[test]
245    fn purge() {
246        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><purge node='foo'></purge></pubsub>"
247        .parse()
248        .unwrap();
249        let elem1 = elem.clone();
250
251        let payload = Payload::Purge {
252            node: NodeName(String::from("foo")),
253        };
254        let pubsub = Owner { payload };
255
256        let elem2 = Element::from(pubsub);
257        assert_eq!(elem1, elem2);
258    }
259
260    #[test]
261    fn subscriptions() {
262        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><subscriptions node='foo'><subscription jid='hamlet@denmark.lit' subscription='subscribed'/><subscription jid='polonius@denmark.lit' subscription='unconfigured'/><subscription jid='bernardo@denmark.lit' subscription='subscribed' subid='123-abc'/><subscription jid='bernardo@denmark.lit' subscription='subscribed' subid='004-yyy'/></subscriptions></pubsub>"
263        .parse()
264        .unwrap();
265        let elem1 = elem.clone();
266
267        let payload = Payload::Subscriptions {
268            node: NodeName(String::from("foo")),
269            subscriptions: vec![
270                SubscriptionElem {
271                    jid: Jid::from(BareJid::from_str("hamlet@denmark.lit").unwrap()),
272                    subscription: Subscription::Subscribed,
273                    subid: None,
274                },
275                SubscriptionElem {
276                    jid: Jid::from(BareJid::from_str("polonius@denmark.lit").unwrap()),
277                    subscription: Subscription::Unconfigured,
278                    subid: None,
279                },
280                SubscriptionElem {
281                    jid: Jid::from(BareJid::from_str("bernardo@denmark.lit").unwrap()),
282                    subscription: Subscription::Subscribed,
283                    subid: Some(String::from("123-abc")),
284                },
285                SubscriptionElem {
286                    jid: Jid::from(BareJid::from_str("bernardo@denmark.lit").unwrap()),
287                    subscription: Subscription::Subscribed,
288                    subid: Some(String::from("004-yyy")),
289                },
290            ],
291        };
292        let pubsub = Owner { payload };
293
294        let elem2 = Element::from(pubsub);
295        assert_eq!(elem1, elem2);
296    }
297}