From 3a6cd231663134aa6bc76dc53d369ba8975decc2 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 11 Oct 2021 12:36:53 +0200 Subject: [PATCH] parsers: Add support for XEP-0215: External Service Discovery --- parsers/doap.xml | 8 ++ parsers/src/extdisco.rs | 204 ++++++++++++++++++++++++++++++++++++++++ parsers/src/lib.rs | 3 + parsers/src/ns.rs | 3 + 4 files changed, 218 insertions(+) create mode 100644 parsers/src/extdisco.rs diff --git a/parsers/doap.xml b/parsers/doap.xml index 31d6e130..1671423f 100644 --- a/parsers/doap.xml +++ b/parsers/doap.xml @@ -296,6 +296,14 @@ 0.1.0 + + + + complete + 0.7.1 + NEXT + + diff --git a/parsers/src/extdisco.rs b/parsers/src/extdisco.rs new file mode 100644 index 00000000..e7e4f0bd --- /dev/null +++ b/parsers/src/extdisco.rs @@ -0,0 +1,204 @@ +// Copyright (c) 2021 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::data_forms::DataForm; +use crate::date::DateTime; +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; + +generate_attribute!( + /// When sending a push update, the action value indicates if the service is being added or + /// deleted from the set of known services (or simply being modified). + Action, "action", { + /// The service is being added from the set of known services. + Add => "add", + + /// The service is being removed from the set of known services. + Remove => "remove", + + /// The service is being modified. + Modify => "modify", + }, Default = Add +); + +generate_attribute!( + /// TODO + Transport, "transport", { + /// TODO + Tcp => "tcp", + + /// TODO + Udp => "udp", + } +); + +generate_attribute!( + /// TODO + Type, "type", { + /// A server that provides Session Traversal Utilities for NAT (STUN). + Stun => "stun", + + /// A server that provides Traversal Using Relays around NAT (TURN). + Turn => "turn", + } +); + +generate_attribute!( + /// TODO + Restricted, + "restricted", + bool +); + +generate_element!( + /// Structure representing a `` element. + Service, "service", EXT_DISCO, + attributes: [ + /// When sending a push update, the action value indicates if the service is being added or + /// deleted from the set of known services (or simply being modified). + action: Default = "action", + + /// A timestamp indicating when the provided username and password credentials will expire. + expires: Option = "expires", + + /// Either a fully qualified domain name (FQDN) or an IP address (IPv4 or IPv6). + host: Required = "host", + + /// A friendly (human-readable) name or label for the service. + name: Option = "name", + + /// A service- or server-generated password for use at the service. + password: Option = "password", + + /// The communications port to be used at the host. + port: Option = "port", + + /// A boolean value indicating that username and password credentials are required and will + /// need to be requested if not already provided. + restricted: Default = "restricted", + + /// The underlying transport protocol to be used when communicating with the service (typically + /// either TCP or UDP). + transport: Option = "transport", + + /// The service type as registered with the XMPP Registrar. + type_: Required = "type", + + /// A service- or server-generated username for use at the service. + username: Option = "username", + ], children: [ + /// Extended information + ext_info: Vec = ("x", DATA_FORMS) => DataForm + ] +); + +impl IqGetPayload for Service {} + +generate_element!( + /// Structure representing a `` element. + ServicesQuery, "services", EXT_DISCO, + attributes: [ + /// TODO + type_: Option = "type", + ] +); + +impl IqGetPayload for ServicesQuery {} + +generate_element!( + /// Structure representing a `` element. + ServicesResult, "services", EXT_DISCO, + attributes: [ + /// TODO + type_: Option = "type", + ], + children: [ + /// List of services. + services: Vec = ("service", EXT_DISCO) => Service + ] +); + +impl IqResultPayload for ServicesResult {} +impl IqSetPayload for ServicesResult {} + +generate_element!( + /// Structure representing a `` element. + Credentials, "credentials", EXT_DISCO, + children: [ + /// List of services. + services: Vec = ("service", EXT_DISCO) => Service + ] +); + +impl IqGetPayload for Credentials {} +impl IqResultPayload for Credentials {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ns; + use crate::Element; + use std::convert::TryFrom; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Action, 1); + assert_size!(Transport, 1); + assert_size!(Restricted, 1); + assert_size!(Type, 1); + assert_size!(Service, 100); // XXX + assert_size!(ServicesQuery, 1); + assert_size!(ServicesResult, 16); + assert_size!(Credentials, 12); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Action, 1); + assert_size!(Transport, 1); + assert_size!(Restricted, 1); + assert_size!(Type, 1); + assert_size!(Service, 152); + assert_size!(ServicesQuery, 1); + assert_size!(ServicesResult, 32); + assert_size!(Credentials, 24); + } + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let service = Service::try_from(elem).unwrap(); + assert_eq!(service.action, Action::Add); + assert!(service.expires.is_none()); + assert_eq!(service.host, "stun.shakespeare.lit"); + assert!(service.name.is_none()); + assert!(service.password.is_none()); + assert_eq!(service.port.unwrap(), 9998); + assert_eq!(service.restricted, Restricted::False); + assert_eq!(service.transport.unwrap(), Transport::Udp); + assert_eq!(service.type_, Type::Stun); + assert!(service.username.is_none()); + assert!(service.ext_info.is_empty()); + } + + #[test] + fn test_service_query() { + let query = ServicesQuery { type_: None }; + let elem = Element::from(query); + assert!(elem.is("services", ns::EXT_DISCO)); + assert_eq!(elem.attrs().next(), None); + assert_eq!(elem.nodes().next(), None); + } + + #[test] + fn test_service_result() { + let elem: Element = "".parse().unwrap(); + let services = ServicesResult::try_from(elem).unwrap(); + assert_eq!(services.type_, Some(Type::Stun)); + assert_eq!(services.services.len(), 1); + } +} diff --git a/parsers/src/lib.rs b/parsers/src/lib.rs index d18f3996..c0ad7eeb 100644 --- a/parsers/src/lib.rs +++ b/parsers/src/lib.rs @@ -141,6 +141,9 @@ pub mod time; /// XEP-0203: Delayed Delivery pub mod delay; +/// XEP-0215: External Service Discovery +pub mod extdisco; + /// XEP-0221: Data Forms Media Element pub mod media_element; diff --git a/parsers/src/ns.rs b/parsers/src/ns.rs index 9acb7fbf..ad11298b 100644 --- a/parsers/src/ns.rs +++ b/parsers/src/ns.rs @@ -133,6 +133,9 @@ pub const TIME: &str = "urn:xmpp:time"; /// XEP-0203: Delayed Delivery pub const DELAY: &str = "urn:xmpp:delay"; +/// XEP-0215: External Service Discovery +pub const EXT_DISCO: &str = "urn:xmpp:extdisco:2"; + /// XEP-0221: Data Forms Media Element pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element";