diff --git a/parsers/src/lib.rs b/parsers/src/lib.rs index 81c199d..ddad57f 100644 --- a/parsers/src/lib.rs +++ b/parsers/src/lib.rs @@ -73,6 +73,9 @@ pub mod bookmarks; /// XEP-0049: Private XML storage pub mod private; +/// XEP-0054: vcard-temp +pub mod vcard; + /// XEP-0059: Result Set Management pub mod rsm; diff --git a/parsers/src/ns.rs b/parsers/src/ns.rs index f163b0c..1534f3e 100644 --- a/parsers/src/ns.rs +++ b/parsers/src/ns.rs @@ -46,6 +46,9 @@ pub const BOOKMARKS: &str = "storage:bookmarks"; /// XEP-0049: Private XML Storage pub const PRIVATE: &str = "jabber:iq:private"; +/// XEP-0054: vcard-temp +pub const VCARD: &str = "vcard-temp"; + /// XEP-0059: Result Set Management pub const RSM: &str = "http://jabber.org/protocol/rsm"; diff --git a/parsers/src/vcard.rs b/parsers/src/vcard.rs new file mode 100644 index 0000000..0a40030 --- /dev/null +++ b/parsers/src/vcard.rs @@ -0,0 +1,132 @@ +// Copyright (c) 2024 xmpp-rs contributors. +// +// 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/. + +//! This module implements vCard, for the purpose of vCard-based avatars as defined in +//! [XEP-0054](https://xmpp.org/extensions/xep-0054.html). +//! +//! Only the element is supported as a member of this legacy vCard. For more modern and complete +//! user profile management, see [XEP-0292](https://xmpp.org/extensions/xep-0292.html): vCard4 Over XMPP. + +use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; +use crate::util::text_node_codecs::{Base64, Codec, Text}; +use crate::{ns, Error}; +use minidom::Element; + +generate_element!( + /// A photo element. + Photo, "PHOTO", VCARD, + attributes: [], + children: [ + /// The type of the photo. + type_: Required = ("TYPE", VCARD) => Type, + /// The binary data of the photo. + binval: Required = ("BINVAL", VCARD) => Binval, + ] +); + +generate_element!( + /// The type of the photo. + Type, "TYPE", VCARD, + text: ( + /// The type as a plain text string; at least "image/jpeg", "image/gif" and "image/png" SHOULD be supported. + data: Text + ) +); + +generate_element!( + /// The binary data of the photo. + Binval, "BINVAL", VCARD, + text: ( + /// The actual data. + data: Base64 + ) +); + +/// A element; only the element is supported for this legacy vCard ; the rest is ignored. +pub struct VCard { + /// A photo element. + pub photo: Option, +} + +impl TryFrom for VCard { + type Error = crate::util::error::Error; + + fn try_from(value: Element) -> Result { + // Check that the root element is + if !value.is("vCard", ns::VCARD) { + return Err(Error::ParseError( + "Root element is not ", + )); + } + + // Parse the element, if any. + let photo = value + .get_child("PHOTO", ns::VCARD) + .map(|photo| Photo::try_from(photo.clone())) + .transpose()?; + + // Return the result. + Ok(VCard { photo }) + } +} + +impl Into for VCard { + fn into(self) -> Element { + let mut builder = Element::builder("vCard", ns::VCARD); + + if let Some(photo) = self.photo { + builder = builder.append(photo); + } + + builder.build() + } +} + +impl IqGetPayload for VCard {} +impl IqSetPayload for VCard {} +impl IqResultPayload for VCard {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use base64::Engine; + use std::str::FromStr; + + #[test] + fn test_vcard() { + // Create some bytes: + let bytes = [0u8, 1, 2, 129]; + + // Test xml stolen from https://xmpp.org/extensions/xep-0153.html#example-5 + let test_vcard = format!( + r" + 1476-06-09 + + Italy + Verona + + + + JulietCapulet + jcapulet@shakespeare.lit + + image/jpeg + {} + + ", + base64::prelude::BASE64_STANDARD.encode(&bytes) + ); + + let test_vcard = Element::from_str(&test_vcard).expect("Failed to parse XML"); + let test_vcard = VCard::try_from(test_vcard).expect("Failed to parse vCard"); + + let photo = test_vcard.photo.expect("No photo found"); + + assert_eq!(photo.type_.data, "image/jpeg".to_string()); + assert_eq!(photo.binval.data, bytes); + } +}