xmpp_parsers/
bind.rs

1// Copyright (c) 2018 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 xso::{AsXml, FromXml};
8
9use crate::iq::{IqResultPayload, IqSetPayload};
10use crate::ns;
11use jid::{FullJid, Jid};
12
13/// The bind feature exposed in stream:features.
14#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
15#[xml(namespace = ns::BIND, name = "bind")]
16pub struct BindFeature {
17    /// Present if bind is required.
18    #[xml(flag)]
19    required: bool,
20}
21
22/// The request for resource binding, which is the process by which a client
23/// can obtain a full JID and start exchanging on the XMPP network.
24///
25/// See <https://xmpp.org/rfcs/rfc6120.html#bind>
26#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
27#[xml(namespace = ns::BIND, name = "bind")]
28pub struct BindQuery {
29    /// Requests this resource, the server may associate another one though.
30    ///
31    /// If this is None, we request no particular resource, and a random one
32    /// will be affected by the server.
33    #[xml(extract(default, fields(text(type_ = String))))]
34    resource: Option<String>,
35}
36
37impl BindQuery {
38    /// Creates a resource binding request.
39    pub fn new(resource: Option<String>) -> BindQuery {
40        BindQuery { resource }
41    }
42}
43
44impl IqSetPayload for BindQuery {}
45
46/// The response for resource binding, containing the client’s full JID.
47///
48/// See <https://xmpp.org/rfcs/rfc6120.html#bind>
49#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
50#[xml(namespace = ns::BIND, name = "bind")]
51pub struct BindResponse {
52    /// The full JID returned by the server for this client.
53    #[xml(extract(fields(text(type_ = FullJid))))]
54    jid: FullJid,
55}
56
57impl IqResultPayload for BindResponse {}
58
59impl From<BindResponse> for FullJid {
60    fn from(bind: BindResponse) -> FullJid {
61        bind.jid
62    }
63}
64
65impl From<BindResponse> for Jid {
66    fn from(bind: BindResponse) -> Jid {
67        Jid::from(bind.jid)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use minidom::Element;
75    #[cfg(not(feature = "disable-validation"))]
76    use xso::error::{Error, FromElementError};
77
78    #[cfg(target_pointer_width = "32")]
79    #[test]
80    fn test_size() {
81        assert_size!(BindFeature, 1);
82        assert_size!(BindQuery, 12);
83        assert_size!(BindResponse, 16);
84    }
85
86    #[cfg(target_pointer_width = "64")]
87    #[test]
88    fn test_size() {
89        assert_size!(BindFeature, 1);
90        assert_size!(BindQuery, 24);
91        assert_size!(BindResponse, 32);
92    }
93
94    #[test]
95    fn test_simple() {
96        let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
97            .parse()
98            .unwrap();
99        let bind = BindQuery::try_from(elem).unwrap();
100        assert_eq!(bind.resource, None);
101
102        let elem: Element =
103            "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>Hello™</resource></bind>"
104                .parse()
105                .unwrap();
106        let bind = BindQuery::try_from(elem).unwrap();
107        // FIXME: “™” should be resourceprep’d into “TM” here…
108        //assert_eq!(bind.resource.unwrap(), "HelloTM");
109        assert_eq!(bind.resource.unwrap(), "Hello™");
110
111        let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>coucou@linkmauve.fr/Hello™</jid></bind>"
112            .parse()
113            .unwrap();
114        let bind = BindResponse::try_from(elem).unwrap();
115        assert_eq!(
116            bind.jid,
117            FullJid::new("coucou@linkmauve.fr/HelloTM").unwrap()
118        );
119    }
120
121    #[cfg(not(feature = "disable-validation"))]
122    #[test]
123    fn test_invalid_resource() {
124        let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource attr='coucou'>resource</resource></bind>"
125            .parse()
126            .unwrap();
127        let error = BindQuery::try_from(elem).unwrap_err();
128        let message = match error {
129            FromElementError::Invalid(Error::Other(string)) => string,
130            _ => panic!(),
131        };
132        assert_eq!(
133            message,
134            "Unknown attribute in extraction for field 'resource' in BindQuery element."
135        );
136
137        let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource><hello-world/>resource</resource></bind>"
138            .parse()
139            .unwrap();
140        let error = BindQuery::try_from(elem).unwrap_err();
141        let message = match error {
142            FromElementError::Invalid(Error::Other(string)) => string,
143            _ => panic!(),
144        };
145        assert_eq!(
146            message,
147            "Unknown child in extraction for field 'resource' in BindQuery element."
148        );
149    }
150}