blocking.rs

  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::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
  8use crate::ns;
  9use crate::util::error::Error;
 10use crate::Element;
 11use jid::Jid;
 12use std::convert::TryFrom;
 13
 14generate_empty_element!(
 15    /// The element requesting the blocklist, the result iq will contain a
 16    /// [BlocklistResult].
 17    BlocklistRequest,
 18    "blocklist",
 19    BLOCKING
 20);
 21
 22impl IqGetPayload for BlocklistRequest {}
 23
 24macro_rules! generate_blocking_element {
 25    ($(#[$meta:meta])* $elem:ident, $name:tt) => (
 26        $(#[$meta])*
 27        #[derive(Debug, Clone)]
 28        pub struct $elem {
 29            /// List of JIDs affected by this command.
 30            pub items: Vec<Jid>,
 31        }
 32
 33        impl TryFrom<Element> for $elem {
 34            type Error = Error;
 35
 36            fn try_from(elem: Element) -> Result<$elem, Error> {
 37                check_self!(elem, $name, BLOCKING);
 38                check_no_attributes!(elem, $name);
 39                let mut items = vec!();
 40                for child in elem.children() {
 41                    check_self!(child, "item", BLOCKING);
 42                    check_no_unknown_attributes!(child, "item", ["jid"]);
 43                    check_no_children!(child, "item");
 44                    items.push(get_attr!(child, "jid", Required));
 45                }
 46                Ok($elem { items })
 47            }
 48        }
 49
 50        impl From<$elem> for Element {
 51            fn from(elem: $elem) -> Element {
 52                Element::builder($name, ns::BLOCKING)
 53                        .append_all(elem.items.into_iter().map(|jid| {
 54                             Element::builder("item", ns::BLOCKING)
 55                                     .attr("jid", jid)
 56                         }))
 57                        .build()
 58            }
 59        }
 60    );
 61}
 62
 63generate_blocking_element!(
 64    /// The element containing the current blocklist, as a reply from
 65    /// [BlocklistRequest].
 66    BlocklistResult,
 67    "blocklist"
 68);
 69
 70impl IqResultPayload for BlocklistResult {}
 71
 72// TODO: Prevent zero elements from being allowed.
 73generate_blocking_element!(
 74    /// A query to block one or more JIDs.
 75    Block,
 76    "block"
 77);
 78
 79impl IqSetPayload for Block {}
 80
 81generate_blocking_element!(
 82    /// A query to unblock one or more JIDs, or all of them.
 83    ///
 84    /// Warning: not putting any JID there means clearing out the blocklist.
 85    Unblock,
 86    "unblock"
 87);
 88
 89impl IqSetPayload for Unblock {}
 90
 91generate_empty_element!(
 92    /// The application-specific error condition when a message is blocked.
 93    Blocked,
 94    "blocked",
 95    BLOCKING_ERRORS
 96);
 97
 98#[cfg(test)]
 99mod tests {
100    use super::*;
101
102    #[cfg(target_pointer_width = "32")]
103    #[test]
104    fn test_size() {
105        assert_size!(BlocklistRequest, 0);
106        assert_size!(BlocklistResult, 12);
107        assert_size!(Block, 12);
108        assert_size!(Unblock, 12);
109    }
110
111    #[cfg(target_pointer_width = "64")]
112    #[test]
113    fn test_size() {
114        assert_size!(BlocklistRequest, 0);
115        assert_size!(BlocklistResult, 24);
116        assert_size!(Block, 24);
117        assert_size!(Unblock, 24);
118    }
119
120    #[test]
121    fn test_simple() {
122        let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'/>".parse().unwrap();
123        let request_elem = elem.clone();
124        BlocklistRequest::try_from(request_elem).unwrap();
125
126        let result_elem = elem.clone();
127        let result = BlocklistResult::try_from(result_elem).unwrap();
128        assert!(result.items.is_empty());
129
130        let elem: Element = "<block xmlns='urn:xmpp:blocking'/>".parse().unwrap();
131        let block = Block::try_from(elem).unwrap();
132        assert!(block.items.is_empty());
133
134        let elem: Element = "<unblock xmlns='urn:xmpp:blocking'/>".parse().unwrap();
135        let unblock = Unblock::try_from(elem).unwrap();
136        assert!(unblock.items.is_empty());
137    }
138
139    #[test]
140    fn test_items() {
141        let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();
142        let two_items = vec![
143            Jid::new("coucou@coucou").unwrap(),
144            Jid::new("domain").unwrap(),
145        ];
146
147        let result_elem = elem.clone();
148        let result = BlocklistResult::try_from(result_elem).unwrap();
149        assert_eq!(result.items, two_items);
150
151        let elem: Element = "<block xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></block>".parse().unwrap();
152        let block = Block::try_from(elem).unwrap();
153        assert_eq!(block.items, two_items);
154
155        let elem: Element = "<unblock xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></unblock>".parse().unwrap();
156        let unblock = Unblock::try_from(elem).unwrap();
157        assert_eq!(unblock.items, two_items);
158    }
159
160    #[cfg(not(feature = "disable-validation"))]
161    #[test]
162    fn test_invalid() {
163        let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>"
164            .parse()
165            .unwrap();
166        let request_elem = elem.clone();
167        let error = BlocklistRequest::try_from(request_elem).unwrap_err();
168        let message = match error {
169            Error::ParseError(string) => string,
170            _ => panic!(),
171        };
172        assert_eq!(message, "Unknown attribute in blocklist element.");
173
174        let result_elem = elem.clone();
175        let error = BlocklistResult::try_from(result_elem).unwrap_err();
176        let message = match error {
177            Error::ParseError(string) => string,
178            _ => panic!(),
179        };
180        assert_eq!(message, "Unknown attribute in blocklist element.");
181
182        let elem: Element = "<block xmlns='urn:xmpp:blocking' coucou=''/>"
183            .parse()
184            .unwrap();
185        let error = Block::try_from(elem).unwrap_err();
186        let message = match error {
187            Error::ParseError(string) => string,
188            _ => panic!(),
189        };
190        assert_eq!(message, "Unknown attribute in block element.");
191
192        let elem: Element = "<unblock xmlns='urn:xmpp:blocking' coucou=''/>"
193            .parse()
194            .unwrap();
195        let error = Unblock::try_from(elem).unwrap_err();
196        let message = match error {
197            Error::ParseError(string) => string,
198            _ => panic!(),
199        };
200        assert_eq!(message, "Unknown attribute in unblock element.");
201    }
202
203    #[cfg(not(feature = "disable-validation"))]
204    #[test]
205    fn test_non_empty_blocklist_request() {
206        let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();
207        let error = BlocklistRequest::try_from(elem).unwrap_err();
208        let message = match error {
209            Error::ParseError(string) => string,
210            _ => panic!(),
211        };
212        assert_eq!(message, "Unknown child in blocklist element.");
213    }
214}