diff --git a/parsers/ChangeLog b/parsers/ChangeLog index b8372dbdcf10512d1d352d693f19eb68bddcafe0..e099f34582e81a968494c55959883b021fb2180d 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -34,6 +34,8 @@ XXXX-YY-ZZ RELEASER use them just like normal booleans. The following structs are deprecated: pubsub::pubsub::Notify, bookmarks2::Autojoin, extdisco::Restricted, fast::Tls0Rtt, legacy_omemo::IsPreKey, mam::Complete, sm::ResumeAttr (!476) + - bookmarks::Conference and bookmarks2::Conference use ResourcePart to store + the optional nickname instead of a String (!485) * Improvements: - Keep unsupported vCard elements as `minidom::Element`, so that they get serialized back instead of being dropped. We now also test for diff --git a/parsers/src/bookmarks.rs b/parsers/src/bookmarks.rs index 9968327ec24b4a97c0ceaf56b6b67953d3879c7e..614ddb6499b557c8b20b99e1ab7ae73b26c2bee3 100644 --- a/parsers/src/bookmarks.rs +++ b/parsers/src/bookmarks.rs @@ -19,6 +19,7 @@ use xso::{AsXml, FromXml}; use jid::BareJid; pub use crate::bookmarks2; +use crate::jid::ResourcePart; use crate::ns; /// A conference bookmark. @@ -38,8 +39,8 @@ pub struct Conference { pub name: Option, /// The nick the user will use to join this conference. - #[xml(extract(default, fields(text(type_ = String))))] - pub nick: Option, + #[xml(extract(default, fields(text(type_ = ResourcePart))))] + pub nick: Option, /// The password required to join this conference. #[xml(extract(default, fields(text(type_ = String))))] @@ -129,6 +130,18 @@ mod tests { assert_eq!(elem1, elem2); } + #[test] + fn wrong_resource() { + // This emoji is not valid according to Resource prep + let elem: Element = "Whatever\u{1F469}\u{1F3FE}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F469}\u{1F3FC}".parse().unwrap(); + let res = Storage::try_from(elem); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err().to_string().as_str(), + "text parse error: resource doesn’t pass resourceprep validation" + ); + } + #[test] fn complete() { let elem: Element = "Coucousecret".parse().unwrap(); @@ -143,7 +156,10 @@ mod tests { BareJid::new("test-muc@muc.localhost").unwrap() ); assert_eq!(storage.conferences[0].clone().name.unwrap(), "Test MUC"); - assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou"); + assert_eq!( + storage.conferences[0].clone().nick.unwrap().as_str(), + "Coucou" + ); assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret"); } } diff --git a/parsers/src/bookmarks2.rs b/parsers/src/bookmarks2.rs index 4e11394554fa0e70d2650dfa8ce28a2f76e96e4e..2848757cbae04559d6a2fe036514f04203e2235f 100644 --- a/parsers/src/bookmarks2.rs +++ b/parsers/src/bookmarks2.rs @@ -14,6 +14,7 @@ use xso::{AsXml, FromXml}; +use crate::jid::ResourcePart; use crate::ns; use minidom::Element; @@ -39,8 +40,8 @@ pub struct Conference { pub name: Option, /// The nick the user will use to join this conference. - #[xml(extract(default, fields(text(type_ = String))))] - pub nick: Option, + #[xml(extract(default, fields(text(type_ = ResourcePart))))] + pub nick: Option, /// The password required to join this conference. #[xml(extract(default, fields(text(type_ = String))))] @@ -91,13 +92,25 @@ mod tests { assert_eq!(elem1, elem2); } + #[test] + fn wrong_resource() { + // This emoji is not valid according to Resource prep + let elem: Element = "Whatever\u{1F469}\u{1F3FE}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F469}\u{1F3FC}".parse().unwrap(); + let res = Conference::try_from(elem); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err().to_string().as_str(), + "text parse error: resource doesn’t pass resourceprep validation" + ); + } + #[test] fn complete() { let elem: Element = "Coucousecret".parse().unwrap(); let conference = Conference::try_from(elem).unwrap(); assert_eq!(conference.autojoin, true); assert_eq!(conference.name, Some(String::from("Test MUC"))); - assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().nick.unwrap().as_str(), "Coucou"); assert_eq!(conference.clone().password.unwrap(), "secret"); let payloads = conference.clone().extensions.unwrap().payloads; assert_eq!(payloads.len(), 1); @@ -115,7 +128,7 @@ mod tests { println!("FOO: conference: {:?}", conference); assert_eq!(conference.autojoin, true); assert_eq!(conference.name, Some(String::from("Test MUC"))); - assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().nick.unwrap().as_str(), "Coucou"); assert_eq!(conference.clone().password.unwrap(), "secret"); let elem: Element = "Coucousecret".parse().unwrap(); @@ -132,7 +145,7 @@ mod tests { let conference = Conference::try_from(payload).unwrap(); assert_eq!(conference.autojoin, true); assert_eq!(conference.name, Some(String::from("Test MUC"))); - assert_eq!(conference.clone().nick.unwrap(), "Coucou"); + assert_eq!(conference.clone().nick.unwrap().as_str(), "Coucou"); assert_eq!(conference.clone().password.unwrap(), "secret"); } } diff --git a/xmpp/ChangeLog b/xmpp/ChangeLog index 7e842f26d9b68de596b24cfbc202cf9f3de1ef9e..41247abbc958ecac3c1c5a30db518ebc54eafe3c 100644 --- a/xmpp/ChangeLog +++ b/xmpp/ChangeLog @@ -5,6 +5,9 @@ XXXX-YY-ZZ [ RELEASER ] Agent now handles MUC connection states internally. (!481) - Agent::leave_room now takes LeaveRoomSettings argument (!483) - Agent::join_room now takes JoinRoomSettings argument (!483) + - builder::ClientBuilder::set_default_nick no longer takes a &str, but + any type that implements AsRef, such as produced + by ResourcePart::new (!485) * Added: - Agent::send_room_message takes RoomMessageSettings argument (!483) * Fixes: diff --git a/xmpp/examples/hello_bot.rs b/xmpp/examples/hello_bot.rs index d23c2fc2330b49d77476cb829e3cd306ed0b664c..a31d6249df79d0b31760805573e12d151d399b9e 100644 --- a/xmpp/examples/hello_bot.rs +++ b/xmpp/examples/hello_bot.rs @@ -6,7 +6,7 @@ use std::env::args; use std::str::FromStr; -use tokio_xmpp::jid::BareJid; +use tokio_xmpp::jid::{BareJid, ResourcePart}; use xmpp::muc::room::{JoinRoomSettings, RoomMessageSettings}; use xmpp::{ClientBuilder, ClientFeature, ClientType, Event}; @@ -39,11 +39,13 @@ async fn main() -> Result<(), Option<()>> { } } + let nick = ResourcePart::new("bot").unwrap(); + // Client instance let mut client = ClientBuilder::new(jid, password) .set_client(ClientType::Bot, "xmpp-rs") .set_website("https://gitlab.com/xmpp-rs/xmpp-rs") - .set_default_nick("bot") + .set_default_nick(nick) .enable_feature(ClientFeature::ContactList) .enable_feature(ClientFeature::JoinRooms) .build(); diff --git a/xmpp/src/agent.rs b/xmpp/src/agent.rs index 3a32e6f75b7dc93f5d780fc297115e99d7c5b3b0..3036facfa72179d226674f66357fd41451cabc59 100644 --- a/xmpp/src/agent.rs +++ b/xmpp/src/agent.rs @@ -11,7 +11,7 @@ use tokio::sync::RwLock; pub use tokio_xmpp::parsers; use tokio_xmpp::parsers::{disco::DiscoInfoResult, message::MessageType}; pub use tokio_xmpp::{ - jid::{BareJid, FullJid, Jid}, + jid::{BareJid, FullJid, Jid, ResourcePart}, minidom::Element, Client as TokioXmppClient, }; @@ -20,16 +20,16 @@ use crate::{event_loop, message, muc, upload, Error, Event, RoomNick}; pub struct Agent { pub(crate) client: TokioXmppClient, - pub(crate) default_nick: Arc>, + pub(crate) default_nick: Arc>, pub(crate) lang: Arc>, pub(crate) disco: DiscoInfoResult, pub(crate) node: String, pub(crate) uploads: Vec<(String, Jid, PathBuf)>, pub(crate) awaiting_disco_bookmarks_type: bool, // Mapping of room->nick - pub(crate) rooms_joined: HashMap, - pub(crate) rooms_joining: HashMap, - pub(crate) rooms_leaving: HashMap, + pub(crate) rooms_joined: HashMap, + pub(crate) rooms_joining: HashMap, + pub(crate) rooms_leaving: HashMap, } impl Agent { diff --git a/xmpp/src/builder.rs b/xmpp/src/builder.rs index df9526b5016ece797ec0b79e85f900ca2ecc8896..f015242ee3337d53aa7fd92802f79bf8d7cfd9e8 100644 --- a/xmpp/src/builder.rs +++ b/xmpp/src/builder.rs @@ -11,7 +11,7 @@ use tokio::sync::RwLock; use tokio_xmpp::connect::{DnsConfig, StartTlsServerConnector}; use tokio_xmpp::{ connect::ServerConnector, - jid::{BareJid, Jid}, + jid::{BareJid, Jid, ResourcePart, ResourceRef}, parsers::{ disco::{DiscoInfoResult, Feature, Identity}, ns, @@ -48,7 +48,7 @@ pub struct ClientBuilder<'a, C: ServerConnector> { password: &'a str, server_connector: C, website: String, - default_nick: String, + default_nick: ResourcePart, lang: Vec, disco: (ClientType, String), features: Vec, @@ -78,7 +78,7 @@ impl ClientBuilder<'_, C> { password, server_connector, website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"), - default_nick: String::from("xmpp-rs"), + default_nick: ResourcePart::new("xmpp-rs").unwrap().into(), lang: vec![String::from("en")], disco: (ClientType::default(), String::from("tokio-xmpp")), features: vec![], @@ -103,8 +103,8 @@ impl ClientBuilder<'_, C> { self } - pub fn set_default_nick(mut self, nick: &str) -> Self { - self.default_nick = String::from(nick); + pub fn set_default_nick(mut self, nick: impl AsRef) -> Self { + self.default_nick = nick.as_ref().to_owned(); self } diff --git a/xmpp/src/lib.rs b/xmpp/src/lib.rs index 617785e7e56e6d9cf998bb8af9bad018c4247f37..aa70ee80a50a841b2c10fff9bd607aac00012e7e 100644 --- a/xmpp/src/lib.rs +++ b/xmpp/src/lib.rs @@ -44,7 +44,7 @@ pub type RoomNick = String; /* #[cfg(all(test, any(feature = "starttls-rust", feature = "starttls-native")))] mod tests { - use super::jid::BareJid; + use super::jid::{BareJid, ResourcePart}; use super::{ClientBuilder, ClientFeature, ClientType, Event}; use std::str::FromStr; use tokio_xmpp::Client as TokioXmppClient; @@ -52,6 +52,7 @@ mod tests { #[tokio::test] async fn test_simple() { let jid = BareJid::from_str("foo@bar").unwrap(); + let nick = ResourcePart::new("bot").unwrap(); let client = TokioXmppClient::new(jid.clone(), "meh"); @@ -59,7 +60,7 @@ mod tests { let client_builder = ClientBuilder::new(jid, "meh") .set_client(ClientType::Bot, "xmpp-rs") .set_website("https://gitlab.com/xmpp-rs/xmpp-rs") - .set_default_nick("bot") + .set_default_nick(nick) .enable_feature(ClientFeature::ContactList); #[cfg(feature = "avatars")] diff --git a/xmpp/src/muc/room.rs b/xmpp/src/muc/room.rs index 7791b79da1c793bc22893a98a660de1ecc149ba5..23d6f72e55a5eb4f8aca0b76c8cef4a3437bf766 100644 --- a/xmpp/src/muc/room.rs +++ b/xmpp/src/muc/room.rs @@ -6,7 +6,7 @@ use crate::parsers::message::MessageType; use tokio_xmpp::{ - jid::BareJid, + jid::{BareJid, ResourcePart, ResourceRef}, parsers::{ muc::Muc, presence::{Presence, Type as PresenceType}, @@ -18,7 +18,7 @@ use crate::Agent; #[derive(Clone, Debug)] pub struct JoinRoomSettings<'a> { pub room: BareJid, - pub nick: Option, + pub nick: Option, pub password: Option, pub status: Option<(&'a str, &'a str)>, } @@ -33,7 +33,7 @@ impl<'a> JoinRoomSettings<'a> { } } - pub fn with_nick(mut self, nick: impl AsRef) -> Self { + pub fn with_nick(mut self, nick: impl AsRef) -> Self { self.nick = Some(nick.as_ref().into()); self } @@ -81,7 +81,7 @@ pub async fn join_room<'a>(agent: &mut Agent, settings: JoinRoomSettings<'a>) { agent.default_nick.read().await.clone() }; - let room_jid = room.with_resource_str(&nick).unwrap(); + let room_jid = room.with_resource(&nick); let mut presence = Presence::new(PresenceType::None).with_to(room_jid); presence.add_payload(muc); @@ -156,7 +156,7 @@ pub async fn leave_room<'a>(agent: &mut Agent, settings: LeaveRoomSettings<'a>) error!("Failed to send leave room presence: {}", e); } - agent.rooms_leaving.insert(room, nickname.to_string()); + agent.rooms_leaving.insert(room, nickname.clone()); } #[derive(Clone, Debug)] diff --git a/xmpp/src/presence/receive.rs b/xmpp/src/presence/receive.rs index 916ae9bf21c7771ca63026ef0997e71e38a2c795..3fcf071a4cd251567e64a7834ac1196731b4e745 100644 --- a/xmpp/src/presence/receive.rs +++ b/xmpp/src/presence/receive.rs @@ -35,7 +35,7 @@ pub async fn handle_presence(agent: &mut Agent, presence: Presence) -> Vec { // According to https://xmpp.org/extensions/xep-0045.html#enter-pres, no type should be seen as "available". if let Some(nick) = agent.rooms_joining.get(&from) { - agent.rooms_joined.insert(from.clone(), nick.to_string()); + agent.rooms_joined.insert(from.clone(), nick.clone()); agent.rooms_joining.remove(&from); } else { warn!("Received self-presence from {} while the room was not marked as joining.", presence.from.unwrap());