parsers: Add support for XEP-0215: External Service Discovery

This commit is contained in:
Emmanuel Gil Peyrot 2021-10-11 12:36:53 +02:00 committed by Link Mauve
parent 9891cc48ac
commit 3a6cd23166
4 changed files with 218 additions and 0 deletions

View file

@ -296,6 +296,14 @@
<xmpp:since>0.1.0</xmpp:since> <xmpp:since>0.1.0</xmpp:since>
</xmpp:SupportedXep> </xmpp:SupportedXep>
</implements> </implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0215.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.7.1</xmpp:version>
<xmpp:since>NEXT</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements> <implements>
<xmpp:SupportedXep> <xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0221.html"/> <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0221.html"/>

204
parsers/src/extdisco.rs Normal file
View file

@ -0,0 +1,204 @@
// Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// 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 `<service xmlns='urn:xmpp:extdisco:2'/>` 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> = "action",
/// A timestamp indicating when the provided username and password credentials will expire.
expires: Option<DateTime> = "expires",
/// Either a fully qualified domain name (FQDN) or an IP address (IPv4 or IPv6).
host: Required<String> = "host",
/// A friendly (human-readable) name or label for the service.
name: Option<String> = "name",
/// A service- or server-generated password for use at the service.
password: Option<String> = "password",
/// The communications port to be used at the host.
port: Option<u16> = "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> = "restricted",
/// The underlying transport protocol to be used when communicating with the service (typically
/// either TCP or UDP).
transport: Option<Transport> = "transport",
/// The service type as registered with the XMPP Registrar.
type_: Required<Type> = "type",
/// A service- or server-generated username for use at the service.
username: Option<String> = "username",
], children: [
/// Extended information
ext_info: Vec<DataForm> = ("x", DATA_FORMS) => DataForm
]
);
impl IqGetPayload for Service {}
generate_element!(
/// Structure representing a `<services xmlns='urn:xmpp:extdisco:2'/>` element.
ServicesQuery, "services", EXT_DISCO,
attributes: [
/// TODO
type_: Option<Type> = "type",
]
);
impl IqGetPayload for ServicesQuery {}
generate_element!(
/// Structure representing a `<services xmlns='urn:xmpp:extdisco:2'/>` element.
ServicesResult, "services", EXT_DISCO,
attributes: [
/// TODO
type_: Option<Type> = "type",
],
children: [
/// List of services.
services: Vec<Service> = ("service", EXT_DISCO) => Service
]
);
impl IqResultPayload for ServicesResult {}
impl IqSetPayload for ServicesResult {}
generate_element!(
/// Structure representing a `<credentials xmlns='urn:xmpp:extdisco:2'/>` element.
Credentials, "credentials", EXT_DISCO,
children: [
/// List of services.
services: Vec<Service> = ("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 = "<service xmlns='urn:xmpp:extdisco:2' host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/>".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 = "<services xmlns='urn:xmpp:extdisco:2' type='stun'><service host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/></services>".parse().unwrap();
let services = ServicesResult::try_from(elem).unwrap();
assert_eq!(services.type_, Some(Type::Stun));
assert_eq!(services.services.len(), 1);
}
}

View file

@ -141,6 +141,9 @@ pub mod time;
/// XEP-0203: Delayed Delivery /// XEP-0203: Delayed Delivery
pub mod delay; pub mod delay;
/// XEP-0215: External Service Discovery
pub mod extdisco;
/// XEP-0221: Data Forms Media Element /// XEP-0221: Data Forms Media Element
pub mod media_element; pub mod media_element;

View file

@ -133,6 +133,9 @@ pub const TIME: &str = "urn:xmpp:time";
/// XEP-0203: Delayed Delivery /// XEP-0203: Delayed Delivery
pub const DELAY: &str = "urn:xmpp:delay"; 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 /// XEP-0221: Data Forms Media Element
pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element"; pub const MEDIA_ELEMENT: &str = "urn:xmpp:media-element";