extdisco.rs

  1// Copyright (c) 2021 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::data_forms::DataForm;
  8use crate::date::DateTime;
  9use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 10
 11generate_attribute!(
 12    /// When sending a push update, the action value indicates if the service is being added or
 13    /// deleted from the set of known services (or simply being modified).
 14    Action, "action", {
 15        /// The service is being added from the set of known services.
 16        Add => "add",
 17
 18        /// The service is being removed from the set of known services.
 19        Remove => "remove",
 20
 21        /// The service is being modified.
 22        Modify => "modify",
 23    }, Default = Add
 24);
 25
 26generate_attribute!(
 27    /// The underlying transport protocol to be used when communicating with the service.
 28    Transport, "transport", {
 29        /// Use TCP as a transport protocol.
 30        Tcp => "tcp",
 31
 32        /// Use UDP as a transport protocol.
 33        Udp => "udp",
 34    }
 35);
 36
 37generate_attribute!(
 38    /// The service type as registered with the XMPP Registrar.
 39    Type, "type", {
 40        /// A server that provides Session Traversal Utilities for NAT (STUN).
 41        Stun => "stun",
 42
 43        /// A server that provides Traversal Using Relays around NAT (TURN).
 44        Turn => "turn",
 45    }
 46);
 47
 48generate_attribute!(
 49    /// Username and password credentials are required and will need to be requested if not already
 50    /// provided.
 51    Restricted,
 52    "restricted",
 53    bool
 54);
 55
 56generate_element!(
 57    /// Structure representing a `<service xmlns='urn:xmpp:extdisco:2'/>` element.
 58    Service, "service", EXT_DISCO,
 59    attributes: [
 60        /// When sending a push update, the action value indicates if the service is being added or
 61        /// deleted from the set of known services (or simply being modified).
 62        action: Default<Action> = "action",
 63
 64        /// A timestamp indicating when the provided username and password credentials will expire.
 65        expires: Option<DateTime> = "expires",
 66
 67        /// Either a fully qualified domain name (FQDN) or an IP address (IPv4 or IPv6).
 68        host: Required<String> = "host",
 69
 70        /// A friendly (human-readable) name or label for the service.
 71        name: Option<String> = "name",
 72
 73        /// A service- or server-generated password for use at the service.
 74        password: Option<String> = "password",
 75
 76        /// The communications port to be used at the host.
 77        port: Option<u16> = "port",
 78
 79        /// A boolean value indicating that username and password credentials are required and will
 80        /// need to be requested if not already provided.
 81        restricted: Default<Restricted> = "restricted",
 82
 83        /// The underlying transport protocol to be used when communicating with the service (typically
 84        /// either TCP or UDP).
 85        transport: Option<Transport> = "transport",
 86
 87        /// The service type as registered with the XMPP Registrar.
 88        type_: Required<Type> = "type",
 89
 90        /// A service- or server-generated username for use at the service.
 91        username: Option<String> = "username",
 92    ], children: [
 93        /// Extended information
 94        ext_info: Vec<DataForm> = ("x", DATA_FORMS) => DataForm
 95    ]
 96);
 97
 98impl IqGetPayload for Service {}
 99
100generate_element!(
101    /// Structure representing a `<services xmlns='urn:xmpp:extdisco:2'/>` element.
102    ServicesQuery, "services", EXT_DISCO,
103    attributes: [
104        /// TODO
105        type_: Option<Type> = "type",
106    ]
107);
108
109impl IqGetPayload for ServicesQuery {}
110
111generate_element!(
112    /// Structure representing a `<services xmlns='urn:xmpp:extdisco:2'/>` element.
113    ServicesResult, "services", EXT_DISCO,
114    attributes: [
115        /// TODO
116        type_: Option<Type> = "type",
117    ],
118    children: [
119        /// List of services.
120        services: Vec<Service> = ("service", EXT_DISCO) => Service
121    ]
122);
123
124impl IqResultPayload for ServicesResult {}
125impl IqSetPayload for ServicesResult {}
126
127generate_element!(
128    /// Structure representing a `<credentials xmlns='urn:xmpp:extdisco:2'/>` element.
129    Credentials, "credentials", EXT_DISCO,
130    children: [
131        /// List of services.
132        services: Vec<Service> = ("service", EXT_DISCO) => Service
133    ]
134);
135
136impl IqGetPayload for Credentials {}
137impl IqResultPayload for Credentials {}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::ns;
143    use crate::Element;
144    use std::convert::TryFrom;
145
146    #[cfg(target_pointer_width = "32")]
147    #[test]
148    fn test_size() {
149        assert_size!(Action, 1);
150        assert_size!(Transport, 1);
151        assert_size!(Restricted, 1);
152        assert_size!(Type, 1);
153        assert_size!(Service, 88);
154        assert_size!(ServicesQuery, 1);
155        assert_size!(ServicesResult, 16);
156        assert_size!(Credentials, 12);
157    }
158
159    #[cfg(target_pointer_width = "64")]
160    #[test]
161    fn test_size() {
162        assert_size!(Action, 1);
163        assert_size!(Transport, 1);
164        assert_size!(Restricted, 1);
165        assert_size!(Type, 1);
166        assert_size!(Service, 152);
167        assert_size!(ServicesQuery, 1);
168        assert_size!(ServicesResult, 32);
169        assert_size!(Credentials, 24);
170    }
171
172    #[test]
173    fn test_simple() {
174        let elem: Element = "<service xmlns='urn:xmpp:extdisco:2' host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/>".parse().unwrap();
175        let service = Service::try_from(elem).unwrap();
176        assert_eq!(service.action, Action::Add);
177        assert!(service.expires.is_none());
178        assert_eq!(service.host, "stun.shakespeare.lit");
179        assert!(service.name.is_none());
180        assert!(service.password.is_none());
181        assert_eq!(service.port.unwrap(), 9998);
182        assert_eq!(service.restricted, Restricted::False);
183        assert_eq!(service.transport.unwrap(), Transport::Udp);
184        assert_eq!(service.type_, Type::Stun);
185        assert!(service.username.is_none());
186        assert!(service.ext_info.is_empty());
187    }
188
189    #[test]
190    fn test_service_query() {
191        let query = ServicesQuery { type_: None };
192        let elem = Element::from(query);
193        assert!(elem.is("services", ns::EXT_DISCO));
194        assert_eq!(elem.attrs().next(), None);
195        assert_eq!(elem.nodes().next(), None);
196    }
197
198    #[test]
199    fn test_service_result() {
200        let elem: Element = "<services xmlns='urn:xmpp:extdisco:2' type='stun'><service host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/></services>".parse().unwrap();
201        let services = ServicesResult::try_from(elem).unwrap();
202        assert_eq!(services.type_, Some(Type::Stun));
203        assert_eq!(services.services.len(), 1);
204    }
205}