2024-01-10 20:50:34 +00:00
|
|
|
// 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).
|
|
|
|
//!
|
2024-01-27 17:18:58 +00:00
|
|
|
//! Only the `<PHOTO>` element is supported as a member of this legacy vCard. For more modern and complete
|
2024-01-10 20:50:34 +00:00
|
|
|
//! user profile management, see [XEP-0292](https://xmpp.org/extensions/xep-0292.html): vCard4 Over XMPP.
|
2024-01-10 22:22:16 +00:00
|
|
|
//!
|
|
|
|
//! For vCard updates defined in [XEP-0153](https://xmpp.org/extensions/xep-0153.html),
|
|
|
|
//! see [`vcard_update`][crate::vcard_update] module.
|
2024-01-10 20:50:34 +00:00
|
|
|
|
2024-07-24 15:24:19 +00:00
|
|
|
use xso::{
|
|
|
|
error::Error,
|
2024-08-03 08:51:23 +00:00
|
|
|
text::{Base64, StripWhitespace, TextCodec},
|
2024-07-24 15:24:19 +00:00
|
|
|
AsXml, FromXml,
|
|
|
|
};
|
2024-06-26 16:05:13 +00:00
|
|
|
|
2024-01-10 20:50:34 +00:00
|
|
|
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
2024-07-24 18:28:22 +00:00
|
|
|
use crate::ns;
|
2024-01-10 20:50:34 +00:00
|
|
|
use minidom::Element;
|
|
|
|
|
2024-06-29 14:14:12 +00:00
|
|
|
/// A photo element.
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
#[xml(namespace = ns::VCARD, name = "PHOTO")]
|
|
|
|
pub struct Photo {
|
|
|
|
/// The type of the photo.
|
|
|
|
#[xml(child)]
|
|
|
|
pub type_: Type,
|
|
|
|
|
|
|
|
/// The binary data of the photo.
|
|
|
|
#[xml(child)]
|
|
|
|
pub binval: Binval,
|
|
|
|
}
|
2024-01-10 20:50:34 +00:00
|
|
|
|
2024-06-26 16:05:13 +00:00
|
|
|
/// The type of the photo.
|
2024-07-09 15:01:42 +00:00
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-26 16:05:13 +00:00
|
|
|
#[xml(namespace = ns::VCARD, name = "TYPE")]
|
|
|
|
pub struct Type {
|
|
|
|
/// The type as a plain text string; at least "image/jpeg", "image/gif" and "image/png" SHOULD be supported.
|
|
|
|
#[xml(text)]
|
|
|
|
pub data: String,
|
|
|
|
}
|
2024-01-10 20:50:34 +00:00
|
|
|
|
2024-07-24 15:24:19 +00:00
|
|
|
/// The binary data of the photo.
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
#[xml(namespace = ns::VCARD, name = "BINVAL")]
|
|
|
|
pub struct Binval {
|
|
|
|
/// The actual data.
|
2024-08-03 08:51:23 +00:00
|
|
|
#[xml(text(codec = Base64.filtered(StripWhitespace)))]
|
2024-07-24 15:24:19 +00:00
|
|
|
pub data: Vec<u8>,
|
|
|
|
}
|
2024-01-10 20:50:34 +00:00
|
|
|
|
2024-01-27 17:18:58 +00:00
|
|
|
/// A `<vCard>` element; only the `<PHOTO>` element is supported for this legacy vCard ; the rest is ignored.
|
2024-01-10 20:50:34 +00:00
|
|
|
pub struct VCard {
|
|
|
|
/// A photo element.
|
|
|
|
pub photo: Option<Photo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<Element> for VCard {
|
2024-06-21 14:27:43 +00:00
|
|
|
type Error = xso::error::Error;
|
2024-01-10 20:50:34 +00:00
|
|
|
|
|
|
|
fn try_from(value: Element) -> Result<Self, Self::Error> {
|
|
|
|
// Check that the root element is <vCard>
|
|
|
|
if !value.is("vCard", ns::VCARD) {
|
2024-07-03 09:06:33 +00:00
|
|
|
return Err(Error::Other(
|
|
|
|
"Root element is not <vCard xmlns='vcard-temp'>",
|
|
|
|
));
|
2024-01-10 20:50:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the <PHOTO> 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 })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-03 09:06:33 +00:00
|
|
|
impl From<VCard> for Element {
|
|
|
|
fn from(vcard: VCard) -> Element {
|
|
|
|
Element::builder("vCard", ns::VCARD)
|
|
|
|
.append_all(vcard.photo)
|
|
|
|
.build()
|
2024-01-10 20:50:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IqGetPayload for VCard {}
|
|
|
|
impl IqSetPayload for VCard {}
|
|
|
|
impl IqResultPayload for VCard {}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
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"<vCard xmlns='vcard-temp'>
|
|
|
|
<BDAY>1476-06-09</BDAY>
|
|
|
|
<ADR>
|
|
|
|
<CTRY>Italy</CTRY>
|
|
|
|
<LOCALITY>Verona</LOCALITY>
|
|
|
|
<HOME/>
|
|
|
|
</ADR>
|
|
|
|
<NICKNAME/>
|
|
|
|
<N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
|
|
|
|
<EMAIL>jcapulet@shakespeare.lit</EMAIL>
|
|
|
|
<PHOTO>
|
|
|
|
<TYPE>image/jpeg</TYPE>
|
|
|
|
<BINVAL>{}</BINVAL>
|
|
|
|
</PHOTO>
|
|
|
|
</vCard>",
|
|
|
|
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);
|
|
|
|
}
|
2024-04-30 16:16:27 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_vcard_with_linebreaks() {
|
|
|
|
// Test xml stolen from https://xmpp.org/extensions/xep-0153.html#example-5
|
|
|
|
// extended to use a multi-line base64 string as is allowed as per RFC 2426
|
|
|
|
let test_vcard = r"<vCard xmlns='vcard-temp'>
|
|
|
|
<BDAY>1476-06-09</BDAY>
|
|
|
|
<ADR>
|
|
|
|
<CTRY>Italy</CTRY>
|
|
|
|
<LOCALITY>Verona</LOCALITY>
|
|
|
|
<HOME/>
|
|
|
|
</ADR>
|
|
|
|
<NICKNAME/>
|
|
|
|
<N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
|
|
|
|
<EMAIL>jcapulet@shakespeare.lit</EMAIL>
|
|
|
|
<PHOTO>
|
|
|
|
<TYPE>image/jpeg</TYPE>
|
|
|
|
<BINVAL>Zm9v
|
|
|
|
Cg==</BINVAL>
|
|
|
|
</PHOTO>
|
|
|
|
</vCard>";
|
|
|
|
|
|
|
|
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, b"foo\n");
|
|
|
|
}
|
2024-01-10 20:50:34 +00:00
|
|
|
}
|