Detailed changes
@@ -4,11 +4,12 @@
// 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/.
+use xso::{error::Error, text::Base64, FromXml, FromXmlText, IntoXml, IntoXmlText};
+
use crate::hashes::{Algo, Hash};
-use crate::util::text_node_codecs::{Base64, Codec};
+use crate::ns;
use minidom::IntoAttributeValue;
use std::str::FromStr;
-use xso::error::Error;
/// A Content-ID, as defined in RFC2111.
///
@@ -49,6 +50,23 @@ impl FromStr for ContentId {
}
}
+impl FromXmlText for ContentId {
+ fn from_xml_text(value: String) -> Result<Self, Error> {
+ value.parse().map_err(Error::text_parse_error)
+ }
+}
+
+impl IntoXmlText for ContentId {
+ fn into_xml_text(self) -> Result<String, Error> {
+ let algo = match self.hash.algo {
+ Algo::Sha_1 => "sha1",
+ Algo::Sha_256 => "sha256",
+ _ => unimplemented!(),
+ };
+ Ok(format!("{}+{}@bob.xmpp.org", algo, self.hash.to_hex()))
+ }
+}
+
impl IntoAttributeValue for ContentId {
fn into_attribute_value(self) -> Option<String> {
let algo = match self.hash.algo {
@@ -60,30 +78,32 @@ impl IntoAttributeValue for ContentId {
}
}
-generate_element!(
- /// Request for an uncached cid file.
- Data, "data", BOB,
- attributes: [
- /// The cid in question.
- cid: Required<ContentId> = "cid",
-
- /// How long to cache it (in seconds).
- max_age: Option<usize> = "max-age",
-
- /// The MIME type of the data being transmitted.
- ///
- /// See the [IANA MIME Media Types Registry][1] for a list of
- /// registered types, but unregistered or yet-to-be-registered are
- /// accepted too.
- ///
- /// [1]: <https://www.iana.org/assignments/media-types/media-types.xhtml>
- type_: Option<String> = "type"
- ],
- text: (
- /// The actual data.
- data: Base64
- )
-);
+/// Request for an uncached cid file.
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::BOB, name = "data")]
+pub struct Data {
+ /// The cid in question.
+ #[xml(attribute)]
+ pub cid: ContentId,
+
+ /// How long to cache it (in seconds).
+ #[xml(attribute(default, name = "max-age"))]
+ pub max_age: Option<usize>,
+
+ /// The MIME type of the data being transmitted.
+ ///
+ /// See the [IANA MIME Media Types Registry][1] for a list of
+ /// registered types, but unregistered or yet-to-be-registered are
+ /// accepted too.
+ ///
+ /// [1]: <https://www.iana.org/assignments/media-types/media-types.xhtml>
+ #[xml(attribute(default, name = "type"))]
+ pub type_: Option<String>,
+
+ /// The actual data.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
#[cfg(test)]
mod tests {
@@ -169,7 +189,7 @@ mod tests {
#[test]
fn unknown_child() {
- let elem: Element = "<data xmlns='urn:xmpp:bob'><coucou/></data>"
+ let elem: Element = "<data xmlns='urn:xmpp:bob' cid='sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org'><coucou/></data>"
.parse()
.unwrap();
let error = Data::try_from(elem).unwrap_err();
@@ -177,6 +197,6 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(),
};
- assert_eq!(message, "Unknown child in data element.");
+ assert_eq!(message, "Unknown child in Data element.");
}
}
@@ -4,11 +4,10 @@
// 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/.
-use xso::{FromXml, IntoXml};
+use xso::{text::Base64, FromXml, IntoXml};
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
-use crate::util::text_node_codecs::{Base64, Codec};
generate_elem_id!(
/// The name of a certificate.
@@ -17,14 +16,14 @@ generate_elem_id!(
SASL_CERT
);
-generate_element!(
- /// An X.509 certificate.
- Cert, "x509cert", SASL_CERT,
- text: (
- /// The BER X.509 data.
- data: Base64
- )
-);
+/// An X.509 certificate.
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::SASL_CERT, name = "x509cert")]
+pub struct Cert {
+ /// The BER X.509 data.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_element!(
/// For the client to upload an X.509 certificate.
@@ -4,15 +4,15 @@
// 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/.
-use xso::{FromXmlText, IntoXmlText};
+use xso::{error::Error, text::Base64, FromXml, FromXmlText, IntoXml, IntoXmlText};
-use crate::util::text_node_codecs::{Base64, Codec};
use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine};
use minidom::IntoAttributeValue;
use std::num::ParseIntError;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
-use xso::error::Error;
+
+use crate::ns;
/// List of the algorithms we support, or Unknown.
#[allow(non_camel_case_types)]
@@ -91,25 +91,46 @@ impl From<Algo> for String {
}
}
+impl FromXmlText for Algo {
+ fn from_xml_text(value: String) -> Result<Self, Error> {
+ value.parse().map_err(Error::text_parse_error)
+ }
+}
+
+impl IntoXmlText for Algo {
+ fn into_xml_text(self) -> Result<String, Error> {
+ Ok(String::from(match self {
+ Algo::Sha_1 => "sha-1",
+ Algo::Sha_256 => "sha-256",
+ Algo::Sha_512 => "sha-512",
+ Algo::Sha3_256 => "sha3-256",
+ Algo::Sha3_512 => "sha3-512",
+ Algo::Blake2b_256 => "blake2b-256",
+ Algo::Blake2b_512 => "blake2b-512",
+ Algo::Unknown(text) => return Ok(text),
+ }))
+ }
+}
+
impl IntoAttributeValue for Algo {
fn into_attribute_value(self) -> Option<String> {
Some(String::from(self))
}
}
-generate_element!(
- /// This element represents a hash of some data, defined by the hash
- /// algorithm used and the computed value.
- Hash, "hash", HASHES,
- attributes: [
- /// The algorithm used to create this hash.
- algo: Required<Algo> = "algo"
- ],
- text: (
- /// The hash value, as a vector of bytes.
- hash: Base64
- )
-);
+/// This element represents a hash of some data, defined by the hash
+/// algorithm used and the computed value.
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::HASHES, name = "hash")]
+pub struct Hash {
+ /// The algorithm used to create this hash.
+ #[xml(attribute)]
+ pub algo: Algo,
+
+ /// The hash value, as a vector of bytes.
+ #[xml(text = Base64)]
+ pub hash: Vec<u8>,
+}
impl Hash {
/// Creates a [struct@Hash] element with the given algo and data.
@@ -281,7 +302,7 @@ mod tests {
#[test]
fn test_invalid_child() {
- let elem: Element = "<hash xmlns='urn:xmpp:hashes:2'><coucou/></hash>"
+ let elem: Element = "<hash xmlns='urn:xmpp:hashes:2' algo='sha-1'><coucou/></hash>"
.parse()
.unwrap();
let error = Hash::try_from(elem).unwrap_err();
@@ -289,6 +310,6 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(),
};
- assert_eq!(message, "Unknown child in hash element.");
+ assert_eq!(message, "Unknown child in Hash element.");
}
}
@@ -4,11 +4,10 @@
// 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/.
-use xso::{FromXml, IntoXml};
+use xso::{text::Base64, FromXml, IntoXml};
use crate::iq::IqSetPayload;
use crate::ns;
-use crate::util::text_node_codecs::{Base64, Codec};
generate_id!(
/// An identifier matching a stream.
@@ -44,21 +43,22 @@ attributes: [
impl IqSetPayload for Open {}
-generate_element!(
/// Exchange a chunk of data in an open stream.
-Data, "data", IBB,
- attributes: [
- /// Sequence number of this chunk, must wraparound after 65535.
- seq: Required<u16> = "seq",
-
- /// The identifier of the stream on which data is being exchanged.
- sid: Required<StreamId> = "sid"
- ],
- text: (
- /// Vector of bytes to be exchanged.
- data: Base64
- )
-);
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::IBB, name = "data")]
+pub struct Data {
+ /// Sequence number of this chunk, must wraparound after 65535.
+ #[xml(attribute)]
+ pub seq: u16,
+
+ /// The identifier of the stream on which data is being exchanged.
+ #[xml(attribute)]
+ pub sid: StreamId,
+
+ /// Vector of bytes to be exchanged.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
impl IqSetPayload for Data {}
@@ -4,12 +4,11 @@
// 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/.
-use xso::{FromXml, IntoXml};
+use xso::{text::Base64, FromXml, IntoXml};
use crate::message::MessagePayload;
use crate::ns;
use crate::pubsub::PubSubPayload;
-use crate::util::text_node_codecs::{Base64, Codec};
/// Element of the device list
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
@@ -32,38 +31,38 @@ generate_element!(
impl PubSubPayload for DeviceList {}
-generate_element!(
- /// SignedPreKey public key
- /// Part of a device's bundle
- SignedPreKeyPublic, "signedPreKeyPublic", LEGACY_OMEMO,
- attributes: [
- /// SignedPreKey id
- signed_pre_key_id: Option<u32> = "signedPreKeyId"
- ],
- text: (
- /// Serialized PublicKey
- data: Base64
- )
-);
+/// SignedPreKey public key
+/// Part of a device's bundle
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeyPublic")]
+pub struct SignedPreKeyPublic {
+ /// SignedPreKey id
+ #[xml(attribute(default, name = "signedPreKeyId"))]
+ pub signed_pre_key_id: Option<u32>,
-generate_element!(
- /// SignedPreKey signature
- /// Part of a device's bundle
- SignedPreKeySignature, "signedPreKeySignature", LEGACY_OMEMO,
- text: (
- /// Signature bytes
- data: Base64
- )
-);
+ /// Serialized PublicKey
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
-generate_element!(
- /// Part of a device's bundle
- IdentityKey, "identityKey", LEGACY_OMEMO,
- text: (
- /// Serialized PublicKey
- data: Base64
- )
-);
+/// SignedPreKey signature
+/// Part of a device's bundle
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeySignature")]
+pub struct SignedPreKeySignature {
+ /// Signature bytes
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
+
+/// Part of a device's bundle
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "identityKey")]
+pub struct IdentityKey {
+ /// Serialized PublicKey
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_element!(
/// List of (single use) PreKeys
@@ -75,19 +74,19 @@ generate_element!(
]
);
-generate_element!(
- /// PreKey public key
- /// Part of a device's bundle
- PreKeyPublic, "preKeyPublic", LEGACY_OMEMO,
- attributes: [
- /// PreKey id
- pre_key_id: Required<u32> = "preKeyId",
- ],
- text: (
- /// Serialized PublicKey
- data: Base64
- )
-);
+/// PreKey public key
+/// Part of a device's bundle
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "preKeyPublic")]
+pub struct PreKeyPublic {
+ /// PreKey id
+ #[xml(attribute = "preKeyId")]
+ pub pre_key_id: u32,
+
+ /// Serialized PublicKey
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_element!(
/// A collection of publicly accessible data that can be used to build a session with a device, namely its public IdentityKey, a signed PreKey with corresponding signature, and a list of (single use) PreKeys.
@@ -123,14 +122,14 @@ generate_element!(
]
);
-generate_element!(
- /// IV used for payload encryption
- IV, "iv", LEGACY_OMEMO,
- text: (
- /// IV bytes
- data: Base64
- )
-);
+/// IV used for payload encryption
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "iv")]
+pub struct IV {
+ /// IV bytes
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_attribute!(
/// prekey attribute for the key element.
@@ -139,33 +138,34 @@ generate_attribute!(
bool
);
-generate_element!(
- /// Part of the OMEMO element header
- Key, "key", LEGACY_OMEMO,
- attributes: [
- /// The device id this key is encrypted for.
- rid: Required<u32> = "rid",
+/// Part of the OMEMO element header
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "key")]
+pub struct Key {
+ /// The device id this key is encrypted for.
+ #[xml(attribute)]
+ pub rid: u32,
- /// The key element MUST be tagged with a prekey attribute set to true
- /// if a PreKeySignalMessage is being used.
- prekey: Default<IsPreKey> = "prekey",
- ],
- text: (
- /// The 16 bytes key and the GCM authentication tag concatenated together
- /// and encrypted using the corresponding long-standing SignalProtocol
- /// session
- data: Base64
- )
-);
+ /// The key element MUST be tagged with a prekey attribute set to true
+ /// if a PreKeySignalMessage is being used.
+ #[xml(attribute(default))]
+ pub prekey: IsPreKey,
-generate_element!(
- /// The encrypted message body
- Payload, "payload", LEGACY_OMEMO,
- text: (
- /// Encrypted with AES-128 in Galois/Counter Mode (GCM)
- data: Base64
- )
-);
+ /// The 16 bytes key and the GCM authentication tag concatenated together
+ /// and encrypted using the corresponding long-standing SignalProtocol
+ /// session
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
+
+/// The encrypted message body
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "payload")]
+pub struct Payload {
+ /// Encrypted with AES-128 in Galois/Counter Mode (GCM)
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_element!(
/// An OMEMO element, which can be either a MessageElement (with payload),
@@ -4,22 +4,21 @@
// 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/.
-use xso::{FromXml, IntoXml};
+use xso::{text::Base64, FromXml, IntoXml};
use crate::date::DateTime;
use crate::ns;
use crate::pubsub::PubSubPayload;
-use crate::util::text_node_codecs::{Base64, Codec};
+/// Data contained in the PubKey element
// TODO: Merge this container with the PubKey struct
-generate_element!(
- /// Data contained in the PubKey element
- PubKeyData, "data", OX,
- text: (
- /// Base64 data
- data: Base64
- )
-);
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::OX, name = "data")]
+pub struct PubKeyData {
+ /// Base64 data
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_element!(
/// Pubkey element to be used in PubSub publish payloads.
@@ -4,13 +4,15 @@
// 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/.
-use xso::{FromXml, IntoXml};
+use xso::{
+ error::{Error, FromElementError},
+ text::Base64,
+ FromXml, IntoXml,
+};
use crate::ns;
-use crate::util::text_node_codecs::{Base64, Codec};
use crate::Element;
use std::collections::BTreeMap;
-use xso::error::{Error, FromElementError};
generate_attribute!(
/// The list of available SASL mechanisms.
@@ -44,41 +46,41 @@ generate_attribute!(
}
);
-generate_element!(
- /// The first step of the SASL process, selecting the mechanism and sending
- /// the first part of the handshake.
- Auth, "auth", SASL,
- attributes: [
- /// The mechanism used.
- mechanism: Required<Mechanism> = "mechanism"
- ],
- text: (
- /// The content of the handshake.
- data: Base64
- )
-);
+/// The first step of the SASL process, selecting the mechanism and sending
+/// the first part of the handshake.
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::SASL, name = "auth")]
+pub struct Auth {
+ /// The mechanism used.
+ #[xml(attribute)]
+ pub mechanism: Mechanism,
+
+ /// The content of the handshake.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
-generate_element!(
- /// In case the mechanism selected at the [auth](struct.Auth.html) step
- /// requires a second step, the server sends this element with additional
- /// data.
- Challenge, "challenge", SASL,
- text: (
- /// The challenge data.
- data: Base64
- )
-);
+/// In case the mechanism selected at the [auth](struct.Auth.html) step
+/// requires a second step, the server sends this element with additional
+/// data.
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::SASL, name = "challenge")]
+pub struct Challenge {
+ /// The challenge data.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
-generate_element!(
- /// In case the mechanism selected at the [auth](struct.Auth.html) step
- /// requires a second step, this contains the client’s response to the
- /// server’s [challenge](struct.Challenge.html).
- Response, "response", SASL,
- text: (
- /// The response data.
- data: Base64
- )
-);
+/// In case the mechanism selected at the [auth](struct.Auth.html) step
+/// requires a second step, this contains the client’s response to the
+/// server’s [challenge](struct.Challenge.html).
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::SASL, name = "response")]
+pub struct Response {
+ /// The response data.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
/// Sent by the client at any point after [auth](struct.Auth.html) if it
/// wants to cancel the current authentication process.
@@ -86,14 +88,14 @@ generate_element!(
#[xml(namespace = ns::SASL, name = "abort")]
pub struct Abort;
-generate_element!(
- /// Sent by the server on SASL success.
- Success, "success", SASL,
- text: (
- /// Possible data sent on success.
- data: Base64
- )
-);
+/// Sent by the server on SASL success.
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::SASL, name = "success")]
+pub struct Success {
+ /// Possible data sent on success.
+ #[xml(text = Base64)]
+ pub data: Vec<u8>,
+}
generate_element_enum!(
/// List of possible failure conditions for SASL.
@@ -211,6 +211,29 @@ macro_rules! generate_attribute {
})
}
}
+ impl ::xso::FromXmlText for $elem {
+ fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> {
+ match s.parse::<bool>().map_err(xso::error::Error::text_parse_error)? {
+ true => Ok(Self::True),
+ false => Ok(Self::False),
+ }
+ }
+ }
+ impl ::xso::IntoXmlText for $elem {
+ fn into_xml_text(self) -> Result<String, xso::error::Error> {
+ match self {
+ Self::True => Ok("true".to_owned()),
+ Self::False => Ok("false".to_owned()),
+ }
+ }
+
+ fn into_optional_xml_text(self) -> Result<Option<String>, xso::error::Error> {
+ match self {
+ Self::True => Ok(Some("true".to_owned())),
+ Self::False => Ok(None),
+ }
+ }
+ }
impl ::minidom::IntoAttributeValue for $elem {
fn into_attribute_value(self) -> Option<String> {
match self {