From 932575c8d674c267bd3e474e70de348eb69839be Mon Sep 17 00:00:00 2001 From: Saarko Date: Sat, 22 Feb 2025 09:31:02 +0200 Subject: [PATCH] xmpp_parsers: Add Push Notifications (XEP-0357) Add support for Push Notification parsing according to https://xmpp.org/extensions/xep-0357.html version 0.4.1. --- parsers/ChangeLog | 1 + parsers/doap.xml | 8 +++ parsers/src/lib.rs | 3 + parsers/src/ns.rs | 3 + parsers/src/push.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 184 insertions(+) create mode 100644 parsers/src/push.rs diff --git a/parsers/ChangeLog b/parsers/ChangeLog index c589ebd4d2aa08cdf28d131331b72a0ff2286723..00086acff45806ba7094a73184c30b22b04d0243 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -73,6 +73,7 @@ XXXX-YY-ZZ RELEASER - Message Displayed Synchronization (XEP-0490) - RFC 6120 stream errors - XEP-0045 mediated invites + - Push Notifications (XEP-0357) (!543) * Improvements: - Add support for ` in XEP-0198 feature advertisment - Add support application-specific error conditions in XEP-0198 diff --git a/parsers/doap.xml b/parsers/doap.xml index ad48dbcd89d42b79112d852be1455d0938f84304..bc600aace3ed3290ef122f686f191b964015c915 100644 --- a/parsers/doap.xml +++ b/parsers/doap.xml @@ -546,6 +546,14 @@ 0.7.0 + + + + complete + 0.4.1 + NEXT + + diff --git a/parsers/src/lib.rs b/parsers/src/lib.rs index 76d3b54c49b252cef85b152e5daf67bd71ef20a8..42446bfdd6cfacd47124383b241f00bf9cae65e1 100644 --- a/parsers/src/lib.rs +++ b/parsers/src/lib.rs @@ -245,6 +245,9 @@ pub mod csi; /// XEP-0353: Jingle Message Initiation pub mod jingle_message; +/// XEP-0357: Push Notifications +pub mod push; + /// XEP-0359: Unique and Stable Stanza IDs pub mod stanza_id; diff --git a/parsers/src/ns.rs b/parsers/src/ns.rs index 64ba8b7992292818bd9fbc6e6b5c4c983619aad3..8e85751b79db062aae377102173d8cd4a4789e1a 100644 --- a/parsers/src/ns.rs +++ b/parsers/src/ns.rs @@ -237,6 +237,9 @@ pub const CSI: &str = "urn:xmpp:csi:0"; /// XEP-0353: Jingle Message Initiation pub const JINGLE_MESSAGE: &str = "urn:xmpp:jingle-message:0"; +/// XEP-0357: Push Notifications +pub const PUSH: &str = "urn:xmpp:push:0"; + /// XEP-0359: Unique and Stable Stanza IDs pub const SID: &str = "urn:xmpp:sid:0"; diff --git a/parsers/src/push.rs b/parsers/src/push.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd59bf8100a2780993b657644bb0c13bae41c88f --- /dev/null +++ b/parsers/src/push.rs @@ -0,0 +1,169 @@ +// Copyright (c) 2025 saarko +// +// 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/. + +use crate::data_forms::DataForm; +use crate::iq::IqSetPayload; +use crate::jid::BareJid; +use crate::ns; +use crate::pubsub::PubSubPayload; +use minidom::Element; +use xso::{AsXml, FromXml}; + +/// An enable element for push notifications +#[derive(Debug, Clone, FromXml, AsXml)] +#[xml(namespace = ns::PUSH, name = "enable")] +pub struct Enable { + /// The 'jid' attribute of the XMPP Push Service being enabled. + #[xml(attribute)] + pub jid: BareJid, + + /// The 'node' attribute which is set to the provisioned node specified by the App Server. + #[xml(attribute(default))] + pub node: Option, + + /// Optional additional information to be provided with each published notification, such as authentication credentials. + #[xml(child(default))] + pub form: Option, +} + +impl IqSetPayload for Enable {} + +/// A disable element for push notifications +#[derive(Debug, Clone, FromXml, AsXml)] +#[xml(namespace = ns::PUSH, name = "disable")] +pub struct Disable { + /// The 'jid' attribute of the XMPP Push Service being disabled. + #[xml(attribute)] + pub jid: BareJid, + + /// The 'node' attribute which was set to the provisioned node specified by the App Server. + #[xml(attribute(default))] + pub node: Option, +} + +impl IqSetPayload for Disable {} + +/// A notification element containing push notification data +#[derive(Debug, Clone, FromXml, AsXml)] +#[xml(namespace = ns::PUSH, name = "notification")] +pub struct Notification { + /// The 'form' to provide summarized information such as the number of unread messages or number of pending subscription requests. + #[xml(child(default))] + pub form: Option, + + /// Child elements for the notification that are not part of the summary data form. + #[xml(element(n = ..))] + pub payloads: Vec, +} + +impl PubSubPayload for Notification {} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn test_enable() { + let test_enable = r#" + + http://jabber.org/protocol/pubsub#publish-options + eruio234vzxc2kla-91 + + "#; + + let elem = Element::from_str(test_enable).expect("Failed to parse XML"); + let enable = Enable::try_from(elem).expect("Failed to parse enable"); + + assert_eq!( + enable.jid, + BareJid::from_str("push-5.client.example").unwrap() + ); + assert_eq!(enable.node.unwrap(), "yxs32uqsflafdk3iuqo"); + assert!(enable.form.is_some()); + } + + #[test] + fn test_enable_only_with_required_fields() { + let test_enable = r#""#; + + let elem = Element::from_str(test_enable).expect("Failed to parse XML"); + let enable = Enable::try_from(elem).expect("Failed to parse enable"); + + assert_eq!( + enable.jid, + BareJid::from_str("push-5.client.example").unwrap() + ); + assert!(enable.node.is_none()); + assert!(enable.form.is_none()); + } + + #[test] + fn test_disable() { + let test_disable = r#""#; + + let elem = Element::from_str(test_disable).expect("Failed to parse XML"); + let disable = Disable::try_from(elem).expect("Failed to parse disable"); + + assert_eq!( + disable.jid, + BareJid::from_str("push-5.client.example").unwrap() + ); + assert_eq!(disable.node.unwrap(), "yxs32uqsflafdk3iuqo"); + } + + #[test] + fn test_disable_only_with_required_fields() { + let test_disable = r#""#; + + let elem = Element::from_str(test_disable).expect("Failed to parse XML"); + let disable = Disable::try_from(elem).expect("Failed to parse disable"); + + assert_eq!( + disable.jid, + BareJid::from_str("push-5.client.example").unwrap() + ); + assert!(disable.node.is_none()); + } + + #[test] + fn test_notification() { + let test_notification = r#" + + urn:xmpp:push:summary + 1 + juliet@capulet.example/balcony + Wherefore art thou, Romeo? + + Additional custom elements + "#; + + let elem = Element::from_str(test_notification).expect("Failed to parse XML"); + let notification = Notification::try_from(elem).expect("Failed to parse notification"); + + assert!(notification.form.is_some()); + assert_eq!(notification.payloads.len(), 1); + } + + #[test] + fn test_notification_only_with_required_fields() { + let test_notification = r#" + Additional custom elements + "#; + + let elem = Element::from_str(test_notification).expect("Failed to parse XML"); + let notification = Notification::try_from(elem).expect("Failed to parse notification"); + + assert!(notification.form.is_none()); + assert_eq!(notification.payloads.len(), 1); + } +}