2019-09-28 23:47:21 +00:00
// Copyright (c) 2019 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/.
2023-12-16 12:03:22 +00:00
//!
//! Chatroom bookmarks from [XEP-0402](https://xmpp.org/extensions/xep-0402.html) for newer servers
//! which advertise `urn:xmpp:bookmarks:1#compat` on the user's BareJID in a disco info request.
2023-12-16 14:23:20 +00:00
//! On legacy non-compliant servers, use the [`private`][crate::private] module instead.
2023-12-16 12:03:22 +00:00
//!
//! See [ModernXMPP docs](https://docs.modernxmpp.org/client/groupchat/#bookmarks) on how to handle all historic
//! and newer specifications for your clients.
//!
//! This module exposes the [`Autojoin`][crate::bookmarks2::Autojoin] boolean flag, the [`Conference`][crate::bookmarks2::Conference] chatroom element, and the [BOOKMARKS2][crate::ns::BOOKMARKS2] XML namespace.
2020-10-31 12:27:58 +00:00
use crate ::ns ;
use crate ::util ::error ::Error ;
use crate ::Element ;
2019-09-28 23:47:21 +00:00
generate_attribute! (
/// Whether a conference bookmark should be joined automatically.
Autojoin ,
" autojoin " ,
bool
) ;
2020-10-31 12:27:58 +00:00
/// A conference bookmark.
2021-10-11 13:22:19 +00:00
#[ derive(Debug, Clone, Default) ]
2020-10-31 12:27:58 +00:00
pub struct Conference {
/// Whether a conference bookmark should be joined automatically.
pub autojoin : Autojoin ,
/// A user-defined name for this conference.
pub name : Option < String > ,
/// The nick the user will use to join this conference.
pub nick : Option < String > ,
/// The password required to join this conference.
pub password : Option < String > ,
/// Extensions elements.
2022-01-03 11:34:24 +00:00
pub extensions : Vec < Element > ,
2020-10-31 12:27:58 +00:00
}
2019-09-28 23:47:21 +00:00
impl Conference {
/// Create a new conference.
pub fn new ( ) -> Conference {
2021-10-11 13:22:19 +00:00
Conference ::default ( )
2020-10-31 12:27:58 +00:00
}
}
impl TryFrom < Element > for Conference {
type Error = Error ;
fn try_from ( root : Element ) -> Result < Conference , Error > {
check_self! ( root , " conference " , BOOKMARKS2 , " Conference " ) ;
check_no_unknown_attributes! ( root , " Conference " , [ " autojoin " , " name " ] ) ;
let mut conference = Conference {
autojoin : get_attr ! ( root , " autojoin " , Default ) ,
name : get_attr ! ( root , " name " , Option ) ,
nick : None ,
password : None ,
2022-01-03 11:34:24 +00:00
extensions : Vec ::new ( ) ,
2020-10-31 12:27:58 +00:00
} ;
2023-12-15 19:15:35 +00:00
for child in root . children ( ) {
2022-01-03 11:34:24 +00:00
if child . is ( " nick " , ns ::BOOKMARKS2 ) {
2020-10-31 12:27:58 +00:00
if conference . nick . is_some ( ) {
return Err ( Error ::ParseError (
" Conference must not have more than one nick. " ,
) ) ;
}
check_no_children! ( child , " nick " ) ;
2021-01-12 18:35:14 +00:00
check_no_attributes! ( child , " nick " ) ;
2020-10-31 12:27:58 +00:00
conference . nick = Some ( child . text ( ) ) ;
} else if child . is ( " password " , ns ::BOOKMARKS2 ) {
if conference . password . is_some ( ) {
return Err ( Error ::ParseError (
" Conference must not have more than one password. " ,
) ) ;
}
check_no_children! ( child , " password " ) ;
2021-01-12 18:35:14 +00:00
check_no_attributes! ( child , " password " ) ;
2020-10-31 12:27:58 +00:00
conference . password = Some ( child . text ( ) ) ;
2022-01-03 11:34:24 +00:00
} else if child . is ( " extensions " , ns ::BOOKMARKS2 ) {
if ! conference . extensions . is_empty ( ) {
return Err ( Error ::ParseError (
" Conference must not have more than one extensions element. " ,
) ) ;
}
conference . extensions . extend ( child . children ( ) . cloned ( ) ) ;
} else {
return Err ( Error ::ParseError (
" Unknown element in bookmarks2 conference " ,
) ) ;
2020-10-31 12:27:58 +00:00
}
2019-09-28 23:47:21 +00:00
}
2020-10-31 12:27:58 +00:00
Ok ( conference )
}
}
impl From < Conference > for Element {
fn from ( conference : Conference ) -> Element {
Element ::builder ( " conference " , ns ::BOOKMARKS2 )
. attr ( " autojoin " , conference . autojoin )
. attr ( " name " , conference . name )
. append_all (
conference
. nick
. map ( | nick | Element ::builder ( " nick " , ns ::BOOKMARKS2 ) . append ( nick ) ) ,
)
. append_all (
conference
. password
. map ( | password | Element ::builder ( " password " , ns ::BOOKMARKS2 ) . append ( password ) ) ,
)
2020-12-06 13:15:55 +00:00
. append_all ( match conference . extensions {
2022-01-03 11:34:24 +00:00
empty if empty . is_empty ( ) = > None ,
extensions = > {
Some ( Element ::builder ( " extensions " , ns ::BOOKMARKS2 ) . append_all ( extensions ) )
}
2020-12-06 13:15:55 +00:00
} )
2020-10-31 12:27:58 +00:00
. build ( )
2019-09-28 23:47:21 +00:00
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2022-03-21 18:44:07 +00:00
use crate ::pubsub ::{ pubsub ::Item as PubSubItem , PubSubEvent } ;
2019-09-28 23:47:21 +00:00
#[ cfg(target_pointer_width = " 32 " ) ]
#[ test ]
fn test_size ( ) {
2020-10-31 12:27:58 +00:00
assert_size! ( Conference , 52 ) ;
2019-09-28 23:47:21 +00:00
}
#[ cfg(target_pointer_width = " 64 " ) ]
#[ test ]
fn test_size ( ) {
2020-10-31 12:27:58 +00:00
assert_size! ( Conference , 104 ) ;
2019-09-28 23:47:21 +00:00
}
#[ test ]
fn simple ( ) {
2020-09-29 16:12:18 +00:00
let elem : Element = " <conference xmlns='urn:xmpp:bookmarks:1'/> "
2019-10-22 23:32:41 +00:00
. parse ( )
. unwrap ( ) ;
2019-09-28 23:47:21 +00:00
let elem1 = elem . clone ( ) ;
let conference = Conference ::try_from ( elem ) . unwrap ( ) ;
assert_eq! ( conference . autojoin , Autojoin ::False ) ;
assert_eq! ( conference . name , None ) ;
assert_eq! ( conference . nick , None ) ;
assert_eq! ( conference . password , None ) ;
let elem2 = Element ::from ( Conference ::new ( ) ) ;
2019-11-29 13:33:17 +00:00
assert_eq! ( elem1 , elem2 ) ;
2019-09-28 23:47:21 +00:00
}
#[ test ]
fn complete ( ) {
2020-10-31 12:27:58 +00:00
let elem : Element = " <conference xmlns='urn:xmpp:bookmarks:1' autojoin='true' name='Test MUC'><nick>Coucou</nick><password>secret</password><extensions><test xmlns='urn:xmpp:unknown' /></extensions></conference> " . parse ( ) . unwrap ( ) ;
2019-09-28 23:47:21 +00:00
let conference = Conference ::try_from ( elem ) . unwrap ( ) ;
assert_eq! ( conference . autojoin , Autojoin ::True ) ;
assert_eq! ( conference . name , Some ( String ::from ( " Test MUC " ) ) ) ;
assert_eq! ( conference . clone ( ) . nick . unwrap ( ) , " Coucou " ) ;
assert_eq! ( conference . clone ( ) . password . unwrap ( ) , " secret " ) ;
2022-01-03 11:34:24 +00:00
assert_eq! ( conference . clone ( ) . extensions . len ( ) , 1 ) ;
assert! ( conference . clone ( ) . extensions [ 0 ] . is ( " test " , " urn:xmpp:unknown " ) ) ;
2019-09-28 23:47:21 +00:00
}
#[ test ]
fn wrapped ( ) {
2020-09-29 16:12:18 +00:00
let elem : Element = " <item xmlns='http://jabber.org/protocol/pubsub' id='test-muc@muc.localhost'><conference xmlns='urn:xmpp:bookmarks:1' autojoin='true' name='Test MUC'><nick>Coucou</nick><password>secret</password></conference></item> " . parse ( ) . unwrap ( ) ;
2019-09-28 23:47:21 +00:00
let item = PubSubItem ::try_from ( elem ) . unwrap ( ) ;
let payload = item . payload . clone ( ) . unwrap ( ) ;
2020-03-28 12:07:26 +00:00
println! ( " FOO: payload: {:?} " , payload ) ;
// let conference = Conference::try_from(payload).unwrap();
2022-03-21 18:44:07 +00:00
let conference = Conference ::try_from ( payload ) . unwrap ( ) ;
2020-03-28 12:07:26 +00:00
println! ( " FOO: conference: {:?} " , conference ) ;
2019-09-28 23:47:21 +00:00
assert_eq! ( conference . autojoin , Autojoin ::True ) ;
assert_eq! ( conference . name , Some ( String ::from ( " Test MUC " ) ) ) ;
assert_eq! ( conference . clone ( ) . nick . unwrap ( ) , " Coucou " ) ;
assert_eq! ( conference . clone ( ) . password . unwrap ( ) , " secret " ) ;
2020-09-29 16:12:18 +00:00
let elem : Element = " <event xmlns='http://jabber.org/protocol/pubsub#event'><items node='urn:xmpp:bookmarks:1'><item xmlns='http://jabber.org/protocol/pubsub#event' id='test-muc@muc.localhost'><conference xmlns='urn:xmpp:bookmarks:1' autojoin='true' name='Test MUC'><nick>Coucou</nick><password>secret</password></conference></item></items></event> " . parse ( ) . unwrap ( ) ;
2019-09-28 23:47:21 +00:00
let mut items = match PubSubEvent ::try_from ( elem ) {
Ok ( PubSubEvent ::PublishedItems { node , items } ) = > {
assert_eq! ( & node . 0 , ns ::BOOKMARKS2 ) ;
items
2019-10-22 23:32:41 +00:00
}
2019-09-28 23:47:21 +00:00
_ = > panic! ( ) ,
} ;
assert_eq! ( items . len ( ) , 1 ) ;
let item = items . pop ( ) . unwrap ( ) ;
let payload = item . payload . clone ( ) . unwrap ( ) ;
let conference = Conference ::try_from ( payload ) . unwrap ( ) ;
assert_eq! ( conference . autojoin , Autojoin ::True ) ;
assert_eq! ( conference . name , Some ( String ::from ( " Test MUC " ) ) ) ;
assert_eq! ( conference . clone ( ) . nick . unwrap ( ) , " Coucou " ) ;
assert_eq! ( conference . clone ( ) . password . unwrap ( ) , " secret " ) ;
}
}