// Copyright (c) 2020 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/. // TODO: validate nicks by applying the “nickname” profile of the PRECIS OpaqueString class, as // defined in RFC 7700. use crate::iq::{IqResultPayload, IqSetPayload}; use crate::message::MessagePayload; use crate::pubsub::{NodeName, PubSubPayload}; use jid::BareJid; generate_id!( /// The identifier a participant receives when joining a channel. ParticipantId ); generate_id!( /// A MIX channel identifier. ChannelId ); generate_element!( /// Represents a participant in a MIX channel, usually returned on the /// urn:xmpp:mix:nodes:participants PubSub node. Participant, "participant", MIX_CORE, children: [ /// The nick of this participant. nick: Required = ("nick", MIX_CORE) => String, /// The bare JID of this participant. // TODO: should be a BareJid! jid: Required = ("jid", MIX_CORE) => String ] ); impl PubSubPayload for Participant {} generate_element!( /// A node to subscribe to. Subscribe, "subscribe", MIX_CORE, attributes: [ /// The PubSub node to subscribe to. node: Required = "node", ] ); generate_element!( /// A request from a user’s server to join a MIX channel. Join, "join", MIX_CORE, attributes: [ /// The participant identifier returned by the MIX service on successful join. id: Option = "id", ], children: [ /// The nick requested by the user or set by the service. nick: Required = ("nick", MIX_CORE) => String, /// Which MIX nodes to subscribe to. subscribes: Vec = ("subscribe", MIX_CORE) => Subscribe ] ); impl IqSetPayload for Join {} impl IqResultPayload for Join {} generate_element!( /// Update a given subscription. UpdateSubscription, "update-subscription", MIX_CORE, attributes: [ /// The JID of the user to be affected. // TODO: why is it not a participant id instead? jid: Option = "jid", ], children: [ /// The list of additional nodes to subscribe to. // TODO: what happens when we are already subscribed? Also, how do we unsubscribe from // just one? subscribes: Vec = ("subscribe", MIX_CORE) => Subscribe ] ); impl IqSetPayload for UpdateSubscription {} impl IqResultPayload for UpdateSubscription {} generate_empty_element!( /// Request to leave a given MIX channel. It will automatically unsubscribe the user from all /// nodes on this channel. Leave, "leave", MIX_CORE ); impl IqSetPayload for Leave {} impl IqResultPayload for Leave {} generate_element!( /// A request to change the user’s nick. SetNick, "setnick", MIX_CORE, children: [ /// The new requested nick. nick: Required = ("nick", MIX_CORE) => String ] ); impl IqSetPayload for SetNick {} impl IqResultPayload for SetNick {} generate_element!( /// Message payload describing who actually sent the message, since unlike in MUC, all messages /// are sent from the channel’s JID. Mix, "mix", MIX_CORE, children: [ /// The nick of the user who said something. nick: Required = ("nick", MIX_CORE) => String, /// The JID of the user who said something. // TODO: should be a BareJid! jid: Required = ("jid", MIX_CORE) => String ] ); impl MessagePayload for Mix {} generate_element!( /// Create a new MIX channel. Create, "create", MIX_CORE, attributes: [ /// The requested channel identifier. channel: Option = "channel", ] ); impl IqSetPayload for Create {} impl IqResultPayload for Create {} generate_element!( /// Destroy a given MIX channel. Destroy, "destroy", MIX_CORE, attributes: [ /// The channel identifier to be destroyed. channel: Required = "channel", ] ); // TODO: section 7.3.4, example 33, doesn’t mirror the in the iq result unlike every // other section so far. impl IqSetPayload for Destroy {} #[cfg(test)] mod tests { use super::*; use crate::Element; use std::convert::TryFrom; #[test] fn participant() { let elem: Element = "foo@barcoucou" .parse() .unwrap(); let participant = Participant::try_from(elem).unwrap(); assert_eq!(participant.nick, "coucou"); assert_eq!(participant.jid, "foo@bar"); } #[test] fn join() { let elem: Element = "coucou" .parse() .unwrap(); let join = Join::try_from(elem).unwrap(); assert_eq!(join.nick, "coucou"); assert_eq!(join.id, None); assert_eq!(join.subscribes.len(), 2); assert_eq!(join.subscribes[0].node.0, "urn:xmpp:mix:nodes:messages"); assert_eq!(join.subscribes[1].node.0, "urn:xmpp:mix:nodes:info"); } #[test] fn update_subscription() { let elem: Element = "" .parse() .unwrap(); let update_subscription = UpdateSubscription::try_from(elem).unwrap(); assert_eq!(update_subscription.jid, None); assert_eq!(update_subscription.subscribes.len(), 1); assert_eq!( update_subscription.subscribes[0].node.0, "urn:xmpp:mix:nodes:participants" ); } #[test] fn leave() { let elem: Element = "".parse().unwrap(); Leave::try_from(elem).unwrap(); } #[test] fn setnick() { let elem: Element = "coucou" .parse() .unwrap(); let setnick = SetNick::try_from(elem).unwrap(); assert_eq!(setnick.nick, "coucou"); } #[test] fn message_mix() { let elem: Element = "foo@barcoucou" .parse() .unwrap(); let mix = Mix::try_from(elem).unwrap(); assert_eq!(mix.nick, "coucou"); assert_eq!(mix.jid, "foo@bar"); } #[test] fn create() { let elem: Element = "" .parse() .unwrap(); let create = Create::try_from(elem).unwrap(); assert_eq!(create.channel.unwrap().0, "coucou"); let elem: Element = "".parse().unwrap(); let create = Create::try_from(elem).unwrap(); assert_eq!(create.channel, None); } #[test] fn destroy() { let elem: Element = "" .parse() .unwrap(); let destroy = Destroy::try_from(elem).unwrap(); assert_eq!(destroy.channel.0, "coucou"); } }