1// Copyright (c) 2025 saarko <saarko@tutanota.com>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use crate::data_forms::DataForm;
8use crate::iq::IqSetPayload;
9use crate::jid::BareJid;
10use crate::ns;
11use crate::pubsub::PubSubPayload;
12use minidom::Element;
13use xso::{AsXml, FromXml};
14
15/// An enable element for push notifications
16#[derive(Debug, Clone, FromXml, AsXml)]
17#[xml(namespace = ns::PUSH, name = "enable")]
18pub struct Enable {
19 /// The 'jid' attribute of the XMPP Push Service being enabled.
20 #[xml(attribute)]
21 pub jid: BareJid,
22
23 /// The 'node' attribute which is set to the provisioned node specified by the App Server.
24 #[xml(attribute(default))]
25 pub node: Option<String>,
26
27 /// Optional additional information to be provided with each published notification, such as authentication credentials.
28 #[xml(child(default))]
29 pub form: Option<DataForm>,
30}
31
32impl IqSetPayload for Enable {}
33
34/// A disable element for push notifications
35#[derive(Debug, Clone, FromXml, AsXml)]
36#[xml(namespace = ns::PUSH, name = "disable")]
37pub struct Disable {
38 /// The 'jid' attribute of the XMPP Push Service being disabled.
39 #[xml(attribute)]
40 pub jid: BareJid,
41
42 /// The 'node' attribute which was set to the provisioned node specified by the App Server.
43 #[xml(attribute(default))]
44 pub node: Option<String>,
45}
46
47impl IqSetPayload for Disable {}
48
49/// A notification element containing push notification data
50#[derive(Debug, Clone, FromXml, AsXml)]
51#[xml(namespace = ns::PUSH, name = "notification")]
52pub struct Notification {
53 /// The 'form' to provide summarized information such as the number of unread messages or number of pending subscription requests.
54 #[xml(child(default))]
55 pub form: Option<DataForm>,
56
57 /// Child elements for the notification that are not part of the summary data form.
58 #[xml(element(n = ..))]
59 pub payloads: Vec<Element>,
60}
61
62impl PubSubPayload for Notification {}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use std::str::FromStr;
68
69 #[test]
70 fn test_enable() {
71 let test_enable = r#"<enable xmlns='urn:xmpp:push:0'
72 jid='push-5.client.example'
73 node='yxs32uqsflafdk3iuqo'>
74 <x xmlns='jabber:x:data' type='submit'>
75 <field var='FORM_TYPE'><value>http://jabber.org/protocol/pubsub#publish-options</value></field>
76 <field var='secret'><value>eruio234vzxc2kla-91</value></field>
77 </x>
78 </enable>"#;
79
80 let elem = Element::from_str(test_enable).expect("Failed to parse XML");
81 let enable = Enable::try_from(elem).expect("Failed to parse enable");
82
83 assert_eq!(
84 enable.jid,
85 BareJid::from_str("push-5.client.example").unwrap()
86 );
87 assert_eq!(enable.node.unwrap(), "yxs32uqsflafdk3iuqo");
88 assert!(enable.form.is_some());
89 }
90
91 #[test]
92 fn test_enable_only_with_required_fields() {
93 let test_enable = r#"<enable xmlns='urn:xmpp:push:0'
94 jid='push-5.client.example' />"#;
95
96 let elem = Element::from_str(test_enable).expect("Failed to parse XML");
97 let enable = Enable::try_from(elem).expect("Failed to parse enable");
98
99 assert_eq!(
100 enable.jid,
101 BareJid::from_str("push-5.client.example").unwrap()
102 );
103 assert!(enable.node.is_none());
104 assert!(enable.form.is_none());
105 }
106
107 #[test]
108 fn test_disable() {
109 let test_disable = r#"<disable xmlns='urn:xmpp:push:0'
110 jid='push-5.client.example'
111 node='yxs32uqsflafdk3iuqo' />"#;
112
113 let elem = Element::from_str(test_disable).expect("Failed to parse XML");
114 let disable = Disable::try_from(elem).expect("Failed to parse disable");
115
116 assert_eq!(
117 disable.jid,
118 BareJid::from_str("push-5.client.example").unwrap()
119 );
120 assert_eq!(disable.node.unwrap(), "yxs32uqsflafdk3iuqo");
121 }
122
123 #[test]
124 fn test_disable_only_with_required_fields() {
125 let test_disable = r#"<disable xmlns='urn:xmpp:push:0'
126 jid='push-5.client.example' />"#;
127
128 let elem = Element::from_str(test_disable).expect("Failed to parse XML");
129 let disable = Disable::try_from(elem).expect("Failed to parse disable");
130
131 assert_eq!(
132 disable.jid,
133 BareJid::from_str("push-5.client.example").unwrap()
134 );
135 assert!(disable.node.is_none());
136 }
137
138 #[test]
139 fn test_notification() {
140 let test_notification = r#"<notification xmlns='urn:xmpp:push:0'>
141 <x xmlns='jabber:x:data' type='result'>
142 <field var='FORM_TYPE'><value>urn:xmpp:push:summary</value></field>
143 <field var='message-count'><value>1</value></field>
144 <field var='last-message-sender'><value>juliet@capulet.example/balcony</value></field>
145 <field var='last-message-body'><value>Wherefore art thou, Romeo?</value></field>
146 </x>
147 <additional xmlns='http://example.com/custom'>Additional custom elements</additional>
148 </notification>"#;
149
150 let elem = Element::from_str(test_notification).expect("Failed to parse XML");
151 let notification = Notification::try_from(elem).expect("Failed to parse notification");
152
153 assert!(notification.form.is_some());
154 assert_eq!(notification.payloads.len(), 1);
155 }
156
157 #[test]
158 fn test_notification_only_with_required_fields() {
159 let test_notification = r#"<notification xmlns='urn:xmpp:push:0'>
160 <additional xmlns='http://example.com/custom'>Additional custom elements</additional>
161 </notification>"#;
162
163 let elem = Element::from_str(test_notification).expect("Failed to parse XML");
164 let notification = Notification::try_from(elem).expect("Failed to parse notification");
165
166 assert!(notification.form.is_none());
167 assert_eq!(notification.payloads.len(), 1);
168 }
169}