aboutsummaryrefslogtreecommitdiffstats
path: root/stanza
diff options
context:
space:
mode:
Diffstat (limited to 'stanza')
-rw-r--r--stanza/Cargo.toml7
-rw-r--r--stanza/src/bind.rs8
-rw-r--r--stanza/src/client/error.rs4
-rw-r--r--stanza/src/client/iq.rs22
-rw-r--r--stanza/src/client/message.rs90
-rw-r--r--stanza/src/client/mod.rs4
-rw-r--r--stanza/src/client/presence.rs4
-rw-r--r--stanza/src/lib.rs10
-rw-r--r--stanza/src/rfc_7395.rs14
-rw-r--r--stanza/src/roster.rs8
-rw-r--r--stanza/src/sasl.rs4
-rw-r--r--stanza/src/stanza_error.rs4
-rw-r--r--stanza/src/starttls.rs4
-rw-r--r--stanza/src/stream.rs21
-rw-r--r--stanza/src/stream_error.rs4
-rw-r--r--stanza/src/xep_0004.rs4
-rw-r--r--stanza/src/xep_0030/info.rs4
-rw-r--r--stanza/src/xep_0030/items.rs4
-rw-r--r--stanza/src/xep_0030/mod.rs4
-rw-r--r--stanza/src/xep_0059.rs4
-rw-r--r--stanza/src/xep_0060/errors.rs4
-rw-r--r--stanza/src/xep_0060/event.rs4
-rw-r--r--stanza/src/xep_0060/mod.rs4
-rw-r--r--stanza/src/xep_0060/owner.rs8
-rw-r--r--stanza/src/xep_0060/pubsub.rs4
-rw-r--r--stanza/src/xep_0084/data.rs4
-rw-r--r--stanza/src/xep_0084/metadata.rs4
-rw-r--r--stanza/src/xep_0084/mod.rs4
-rw-r--r--stanza/src/xep_0115.rs4
-rw-r--r--stanza/src/xep_0131.rs4
-rw-r--r--stanza/src/xep_0156.rs4
-rw-r--r--stanza/src/xep_0172.rs4
-rw-r--r--stanza/src/xep_0199.rs4
-rw-r--r--stanza/src/xep_0203.rs4
-rw-r--r--stanza/src/xep_0280.rs113
-rw-r--r--stanza/src/xep_0297.rs79
-rw-r--r--stanza/src/xep_0300.rs4
-rw-r--r--stanza/src/xep_0334.rs87
-rw-r--r--stanza/src/xep_0390.rs4
39 files changed, 558 insertions, 17 deletions
diff --git a/stanza/Cargo.toml b/stanza/Cargo.toml
index 884584a..9bbe3f3 100644
--- a/stanza/Cargo.toml
+++ b/stanza/Cargo.toml
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+#
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
[package]
name = "stanza"
version = "0.1.0"
@@ -24,5 +28,8 @@ xep_0156 = ["dep:chrono"]
xep_0172 = []
xep_0199 = []
xep_0203 = ["dep:chrono"]
+xep_0280 = ["xep_0297"]
+xep_0297 = ["xep_0203"]
xep_0300 = []
+xep_0334 = []
xep_0390 = ["xep_0300"]
diff --git a/stanza/src/bind.rs b/stanza/src/bind.rs
index 3ce2246..c04651c 100644
--- a/stanza/src/bind.rs
+++ b/stanza/src/bind.rs
@@ -1,4 +1,8 @@
-use jid::JID;
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use jid::FullJID;
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind";
@@ -54,7 +58,7 @@ impl IntoElement for BindType {
// minLength 8 maxLength 3071
#[derive(Clone, Debug)]
-pub struct FullJidType(pub JID);
+pub struct FullJidType(pub FullJID);
impl FromElement for FullJidType {
fn from_element(mut element: peanuts::Element) -> peanuts::DeserializeResult<Self> {
diff --git a/stanza/src/client/error.rs b/stanza/src/client/error.rs
index 9cc85a9..7dac178 100644
--- a/stanza/src/client/error.rs
+++ b/stanza/src/client/error.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::fmt::Display;
use std::str::FromStr;
diff --git a/stanza/src/client/iq.rs b/stanza/src/client/iq.rs
index 478530a..be2a3f1 100644
--- a/stanza/src/client/iq.rs
+++ b/stanza/src/client/iq.rs
@@ -1,8 +1,14 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
use jid::JID;
use peanuts::{DeserializeError, Element, FromElement, IntoElement, XML_NS};
+#[cfg(feature = "xep_0280")]
+use crate::xep_0280::{self, Disable, Enable};
use crate::{
bind::{self, Bind},
client::error::Error,
@@ -53,6 +59,10 @@ pub enum Query {
Ping(Ping),
#[cfg(feature = "rfc_6121")]
Roster(roster::Query),
+ #[cfg(feature = "xep_0280")]
+ CarbonsEnable(Enable),
+ #[cfg(feature = "xep_0280")]
+ CarbonsDisable(Disable),
Unsupported,
}
@@ -80,6 +90,14 @@ impl FromElement for Query {
(Some(xep_0060::owner::XMLNS), "pubsub") => Ok(Query::PubsubOwner(
xep_0060::owner::Pubsub::from_element(element)?,
)),
+ #[cfg(feature = "xep_0280")]
+ (Some(xep_0280::XMLNS), "enable") => {
+ Ok(Query::CarbonsEnable(Enable::from_element(element)?))
+ }
+ #[cfg(feature = "xep_0280")]
+ (Some(xep_0280::XMLNS), "disable") => {
+ Ok(Query::CarbonsDisable(Disable::from_element(element)?))
+ }
_ => Ok(Query::Unsupported),
}
}
@@ -103,6 +121,10 @@ impl IntoElement for Query {
Query::Pubsub(pubsub) => pubsub.builder(),
#[cfg(feature = "xep_0060")]
Query::PubsubOwner(pubsub) => pubsub.builder(),
+ #[cfg(feature = "xep_0280")]
+ Query::CarbonsEnable(enable) => enable.builder(),
+ #[cfg(feature = "xep_0280")]
+ Query::CarbonsDisable(disable) => disable.builder(),
}
}
}
diff --git a/stanza/src/client/message.rs b/stanza/src/client/message.rs
index 41761d2..a489201 100644
--- a/stanza/src/client/message.rs
+++ b/stanza/src/client/message.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
use jid::JID;
@@ -11,6 +15,12 @@ use crate::xep_0131::Headers;
use crate::xep_0172::Nick;
#[cfg(feature = "xep_0203")]
use crate::xep_0203::Delay;
+#[cfg(feature = "xep_0280")]
+use crate::xep_0280::{Private, Received, Sent};
+#[cfg(feature = "xep_0297")]
+use crate::xep_0297::Forwarded;
+#[cfg(feature = "xep_0334")]
+use crate::xep_0334::{NoCopy, NoPermanentStore, NoStore, Store};
use super::XMLNS;
@@ -34,6 +44,22 @@ pub struct Message {
pub nick: Option<Nick>,
#[cfg(feature = "xep_0060")]
pub event: Option<Event>,
+ #[cfg(feature = "xep_0297")]
+ pub forwarded: Option<Forwarded>,
+ #[cfg(feature = "xep_0280")]
+ pub sent: Option<Sent>,
+ #[cfg(feature = "xep_0280")]
+ pub received: Option<Received>,
+ #[cfg(feature = "xep_0280")]
+ pub private: Option<Private>,
+ #[cfg(feature = "xep_0334")]
+ pub no_permanent_store: Option<NoPermanentStore>,
+ #[cfg(feature = "xep_0334")]
+ pub no_store: Option<NoStore>,
+ #[cfg(feature = "xep_0334")]
+ pub no_copy: Option<NoCopy>,
+ #[cfg(feature = "xep_0334")]
+ pub store: Option<Store>,
}
impl FromElement for Message {
@@ -63,6 +89,30 @@ impl FromElement for Message {
#[cfg(feature = "xep_0060")]
let event = element.child_opt()?;
+ #[cfg(feature = "xep_0297")]
+ let forwarded = element.child_opt()?;
+
+ #[cfg(feature = "xep_0280")]
+ let sent = element.child_opt()?;
+
+ #[cfg(feature = "xep_0280")]
+ let received = element.child_opt()?;
+
+ #[cfg(feature = "xep_0280")]
+ let private = element.child_opt()?;
+
+ #[cfg(feature = "xep_0334")]
+ let no_permanent_store = element.child_opt()?;
+
+ #[cfg(feature = "xep_0334")]
+ let no_store = element.child_opt()?;
+
+ #[cfg(feature = "xep_0334")]
+ let no_copy = element.child_opt()?;
+
+ #[cfg(feature = "xep_0334")]
+ let store = element.child_opt()?;
+
Ok(Message {
from,
id,
@@ -80,6 +130,22 @@ impl FromElement for Message {
nick,
#[cfg(feature = "xep_0060")]
event,
+ #[cfg(feature = "xep_0297")]
+ forwarded,
+ #[cfg(feature = "xep_0280")]
+ sent,
+ #[cfg(feature = "xep_0280")]
+ received,
+ #[cfg(feature = "xep_0280")]
+ private,
+ #[cfg(feature = "xep_0334")]
+ no_permanent_store,
+ #[cfg(feature = "xep_0334")]
+ no_store,
+ #[cfg(feature = "xep_0334")]
+ no_copy,
+ #[cfg(feature = "xep_0334")]
+ store,
})
}
}
@@ -114,6 +180,30 @@ impl IntoElement for Message {
#[cfg(feature = "xep_0060")]
let builder = builder.push_child_opt(self.event.clone());
+ #[cfg(feature = "xep_0297")]
+ let builder = builder.push_child_opt(self.forwarded.clone());
+
+ #[cfg(feature = "xep_0280")]
+ let builder = builder.push_child_opt(self.sent.clone());
+
+ #[cfg(feature = "xep_0280")]
+ let builder = builder.push_child_opt(self.received.clone());
+
+ #[cfg(feature = "xep_0280")]
+ let builder = builder.push_child_opt(self.private.clone());
+
+ #[cfg(feature = "xep_0334")]
+ let builder = builder.push_child_opt(self.no_permanent_store);
+
+ #[cfg(feature = "xep_0334")]
+ let builder = builder.push_child_opt(self.no_store);
+
+ #[cfg(feature = "xep_0334")]
+ let builder = builder.push_child_opt(self.no_copy);
+
+ #[cfg(feature = "xep_0334")]
+ let builder = builder.push_child_opt(self.store);
+
builder
}
}
diff --git a/stanza/src/client/mod.rs b/stanza/src/client/mod.rs
index aca4fad..00ad3ed 100644
--- a/stanza/src/client/mod.rs
+++ b/stanza/src/client/mod.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Content, ContentBuilder, DeserializeError, FromContent, FromElement, IntoContent};
use iq::Iq;
diff --git a/stanza/src/client/presence.rs b/stanza/src/client/presence.rs
index b0a0bc0..02de229 100644
--- a/stanza/src/client/presence.rs
+++ b/stanza/src/client/presence.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
use jid::JID;
diff --git a/stanza/src/lib.rs b/stanza/src/lib.rs
index 8f8d430..8cc3755 100644
--- a/stanza/src/lib.rs
+++ b/stanza/src/lib.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::declaration::VersionInfo;
pub mod bind;
@@ -33,8 +37,14 @@ pub mod xep_0172;
pub mod xep_0199;
#[cfg(feature = "xep_0203")]
pub mod xep_0203;
+#[cfg(feature = "xep_0280")]
+pub mod xep_0280;
+#[cfg(feature = "xep_0297")]
+pub mod xep_0297;
#[cfg(feature = "xep_0300")]
pub mod xep_0300;
+#[cfg(feature = "xep_0334")]
+pub mod xep_0334;
#[cfg(feature = "xep_0390")]
pub mod xep_0390;
diff --git a/stanza/src/rfc_7395.rs b/stanza/src/rfc_7395.rs
index 64d9f70..c632116 100644
--- a/stanza/src/rfc_7395.rs
+++ b/stanza/src/rfc_7395.rs
@@ -1,12 +1,16 @@
-use jid::JID;
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use jid::BareJID;
use peanuts::{Element, ElementBuilder, FromElement, IntoElement};
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-framing";
#[derive(Debug)]
pub struct Open {
- pub from: Option<JID>,
- pub to: Option<JID>,
+ pub from: Option<BareJID>,
+ pub to: Option<BareJID>,
pub id: Option<String>,
pub version: Option<String>,
pub lang: Option<String>,
@@ -46,8 +50,8 @@ impl IntoElement for Open {
#[derive(Debug, Default)]
pub struct Close {
- pub from: Option<JID>,
- pub to: Option<JID>,
+ pub from: Option<BareJID>,
+ pub to: Option<BareJID>,
pub id: Option<String>,
pub version: Option<String>,
pub lang: Option<String>,
diff --git a/stanza/src/roster.rs b/stanza/src/roster.rs
index 14f65ef..fe67ad1 100644
--- a/stanza/src/roster.rs
+++ b/stanza/src/roster.rs
@@ -1,6 +1,10 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
-use jid::JID;
+use jid::BareJID;
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
pub const XMLNS: &str = "jabber:iq:roster";
@@ -38,7 +42,7 @@ pub struct Item {
/// signals subscription sub-states (server only)
pub ask: bool,
/// uniquely identifies item
- pub jid: JID,
+ pub jid: BareJID,
/// handle that is determined by user, not contact
pub name: Option<String>,
/// state of the presence subscription
diff --git a/stanza/src/sasl.rs b/stanza/src/sasl.rs
index 58aab84..2935923 100644
--- a/stanza/src/sasl.rs
+++ b/stanza/src/sasl.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::{fmt::Display, ops::Deref};
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
diff --git a/stanza/src/stanza_error.rs b/stanza/src/stanza_error.rs
index 8fb862f..bb0c24f 100644
--- a/stanza/src/stanza_error.rs
+++ b/stanza/src/stanza_error.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
// https://datatracker.ietf.org/doc/html/rfc6120#appendix-A.8
use peanuts::{Element, FromElement, IntoElement, XML_NS};
diff --git a/stanza/src/starttls.rs b/stanza/src/starttls.rs
index 730c044..92ae315 100644
--- a/stanza/src/starttls.rs
+++ b/stanza/src/starttls.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-tls";
diff --git a/stanza/src/stream.rs b/stanza/src/stream.rs
index 5be235a..f8aef33 100644
--- a/stanza/src/stream.rs
+++ b/stanza/src/stream.rs
@@ -1,6 +1,10 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::fmt::Display;
-use jid::JID;
+use jid::BareJID;
use peanuts::{Element, ElementBuilder, FromElement, IntoElement};
use thiserror::Error;
@@ -18,8 +22,8 @@ pub const XMLNS: &str = "http://etherx.jabber.org/streams";
// #[peanuts(xmlns = XMLNS)]
#[derive(Debug)]
pub struct Stream {
- pub from: Option<JID>,
- to: Option<JID>,
+ pub from: Option<BareJID>,
+ to: Option<BareJID>,
id: Option<String>,
version: Option<String>,
// TODO: lang enum
@@ -64,8 +68,8 @@ impl IntoElement for Stream {
impl<'s> Stream {
pub fn new(
- from: Option<JID>,
- to: Option<JID>,
+ from: Option<BareJID>,
+ to: Option<BareJID>,
id: Option<String>,
version: Option<String>,
lang: Option<String>,
@@ -81,7 +85,12 @@ impl<'s> Stream {
/// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute.
/// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection.
- pub fn new_client(from: Option<JID>, to: JID, id: Option<String>, lang: String) -> Self {
+ pub fn new_client(
+ from: Option<BareJID>,
+ to: BareJID,
+ id: Option<String>,
+ lang: String,
+ ) -> Self {
Self {
from,
to: Some(to),
diff --git a/stanza/src/stream_error.rs b/stanza/src/stream_error.rs
index 19ad1ae..e068cbf 100644
--- a/stanza/src/stream_error.rs
+++ b/stanza/src/stream_error.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{DeserializeError, Element, FromElement, IntoElement, XML_NS};
use thiserror::Error;
diff --git a/stanza/src/xep_0004.rs b/stanza/src/xep_0004.rs
index f6ff7a0..b7b4878 100644
--- a/stanza/src/xep_0004.rs
+++ b/stanza/src/xep_0004.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
diff --git a/stanza/src/xep_0030/info.rs b/stanza/src/xep_0030/info.rs
index 0344ccb..69c795d 100644
--- a/stanza/src/xep_0030/info.rs
+++ b/stanza/src/xep_0030/info.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{DeserializeError, Element, FromElement, IntoElement, XML_NS};
#[cfg(feature = "xep_0128")]
diff --git a/stanza/src/xep_0030/items.rs b/stanza/src/xep_0030/items.rs
index 7707eac..4096707 100644
--- a/stanza/src/xep_0030/items.rs
+++ b/stanza/src/xep_0030/items.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use jid::JID;
use peanuts::{Element, FromElement, IntoElement};
diff --git a/stanza/src/xep_0030/mod.rs b/stanza/src/xep_0030/mod.rs
index 914c17b..0faaf04 100644
--- a/stanza/src/xep_0030/mod.rs
+++ b/stanza/src/xep_0030/mod.rs
@@ -1,2 +1,6 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
pub mod info;
pub mod items;
diff --git a/stanza/src/xep_0059.rs b/stanza/src/xep_0059.rs
index 6490ad1..84f84ff 100644
--- a/stanza/src/xep_0059.rs
+++ b/stanza/src/xep_0059.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "http://jabber.org/protocol/rsm";
diff --git a/stanza/src/xep_0060/errors.rs b/stanza/src/xep_0060/errors.rs
index 6c6c530..a52ea85 100644
--- a/stanza/src/xep_0060/errors.rs
+++ b/stanza/src/xep_0060/errors.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::{fmt::Display, str::FromStr};
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
diff --git a/stanza/src/xep_0060/event.rs b/stanza/src/xep_0060/event.rs
index 3cb124b..f3cae8b 100644
--- a/stanza/src/xep_0060/event.rs
+++ b/stanza/src/xep_0060/event.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
use chrono::{DateTime, Utc};
diff --git a/stanza/src/xep_0060/mod.rs b/stanza/src/xep_0060/mod.rs
index 566310f..ac8a849 100644
--- a/stanza/src/xep_0060/mod.rs
+++ b/stanza/src/xep_0060/mod.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
pub mod errors;
pub mod event;
pub mod owner;
diff --git a/stanza/src/xep_0060/owner.rs b/stanza/src/xep_0060/owner.rs
index 0617712..3b6c6f6 100644
--- a/stanza/src/xep_0060/owner.rs
+++ b/stanza/src/xep_0060/owner.rs
@@ -1,6 +1,10 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
-use jid::JID;
+use jid::{BareJID, JID};
use peanuts::{DeserializeError, Element, FromElement, IntoElement};
use crate::xep_0004::X;
@@ -85,7 +89,7 @@ impl IntoElement for Affiliations {
#[derive(Clone, Debug)]
pub struct Affiliation {
affiliation: AffiliationType,
- jid: JID,
+ jid: BareJID,
}
impl FromElement for Affiliation {
diff --git a/stanza/src/xep_0060/pubsub.rs b/stanza/src/xep_0060/pubsub.rs
index 0416b78..94277b9 100644
--- a/stanza/src/xep_0060/pubsub.rs
+++ b/stanza/src/xep_0060/pubsub.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::str::FromStr;
use jid::JID;
diff --git a/stanza/src/xep_0084/data.rs b/stanza/src/xep_0084/data.rs
index 4b3223f..d147550 100644
--- a/stanza/src/xep_0084/data.rs
+++ b/stanza/src/xep_0084/data.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "urn:xmpp:avatar:data";
diff --git a/stanza/src/xep_0084/metadata.rs b/stanza/src/xep_0084/metadata.rs
index e4edb2f..b9d3e60 100644
--- a/stanza/src/xep_0084/metadata.rs
+++ b/stanza/src/xep_0084/metadata.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "urn:xmpp:avatar:metadata";
diff --git a/stanza/src/xep_0084/mod.rs b/stanza/src/xep_0084/mod.rs
index be7d5d7..1e80c9a 100644
--- a/stanza/src/xep_0084/mod.rs
+++ b/stanza/src/xep_0084/mod.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
pub use data::Data;
pub use metadata::Info;
pub use metadata::Metadata;
diff --git a/stanza/src/xep_0115.rs b/stanza/src/xep_0115.rs
index 1c9cd6f..c983f2b 100644
--- a/stanza/src/xep_0115.rs
+++ b/stanza/src/xep_0115.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "http://jabber.org/protocol/caps";
diff --git a/stanza/src/xep_0131.rs b/stanza/src/xep_0131.rs
index 68e6e96..8d296b3 100644
--- a/stanza/src/xep_0131.rs
+++ b/stanza/src/xep_0131.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "http://jabber.org/protocol/disco#info";
diff --git a/stanza/src/xep_0156.rs b/stanza/src/xep_0156.rs
index bf6eac5..8b3d07c 100644
--- a/stanza/src/xep_0156.rs
+++ b/stanza/src/xep_0156.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use chrono::{DateTime, Utc};
use peanuts::{Element, FromElement, IntoElement, XML_NS};
diff --git a/stanza/src/xep_0172.rs b/stanza/src/xep_0172.rs
index 54846b8..8374bab 100644
--- a/stanza/src/xep_0172.rs
+++ b/stanza/src/xep_0172.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "http://jabber.org/protocol/nick";
diff --git a/stanza/src/xep_0199.rs b/stanza/src/xep_0199.rs
index 2ab3a86..17e9fd4 100644
--- a/stanza/src/xep_0199.rs
+++ b/stanza/src/xep_0199.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use peanuts::{Element, FromElement, IntoElement};
pub const XMLNS: &str = "urn:xmpp:ping";
diff --git a/stanza/src/xep_0203.rs b/stanza/src/xep_0203.rs
index 41ff196..0f4b298 100644
--- a/stanza/src/xep_0203.rs
+++ b/stanza/src/xep_0203.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use chrono::{DateTime, Utc};
use jid::JID;
use peanuts::{Element, FromElement, IntoElement};
diff --git a/stanza/src/xep_0280.rs b/stanza/src/xep_0280.rs
new file mode 100644
index 0000000..74d2897
--- /dev/null
+++ b/stanza/src/xep_0280.rs
@@ -0,0 +1,113 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use peanuts::{Element, FromElement, IntoElement};
+
+use crate::xep_0297::Forwarded;
+
+pub const XMLNS: &str = "urn:xmpp:carbons:2";
+
+#[derive(Clone, Debug)]
+pub struct Disable;
+
+impl FromElement for Disable {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("disable")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for Disable {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("disable", Some(XMLNS))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Enable;
+
+impl FromElement for Enable {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("enable")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for Enable {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("enable", Some(XMLNS))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Private;
+
+impl FromElement for Private {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("private")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for Private {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("private", Some(XMLNS))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Received {
+ forwarded: Forwarded,
+}
+
+impl FromElement for Received {
+ fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("received")?;
+ element.check_namespace(XMLNS)?;
+
+ let forwarded = element.pop_child_one()?;
+
+ Ok(Self { forwarded })
+ }
+}
+
+impl IntoElement for Received {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("received", Some(XMLNS)).push_child(self.forwarded.clone())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Sent {
+ forwarded: Forwarded,
+}
+
+impl FromElement for Sent {
+ fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("sent")?;
+ element.check_namespace(XMLNS)?;
+
+ let forwarded = element.pop_child_one()?;
+
+ Ok(Self { forwarded })
+ }
+}
+
+impl IntoElement for Sent {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("sent", Some(XMLNS)).push_child(self.forwarded.clone())
+ }
+}
diff --git a/stanza/src/xep_0297.rs b/stanza/src/xep_0297.rs
new file mode 100644
index 0000000..d686e49
--- /dev/null
+++ b/stanza/src/xep_0297.rs
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use peanuts::{Element, FromElement, IntoElement};
+
+use crate::{
+ client::{self, iq::Iq, message::Message, presence::Presence},
+ xep_0203::Delay,
+};
+
+pub const XMLNS: &str = "urn:xmpp:forward:0";
+
+#[derive(Clone, Debug)]
+pub struct Forwarded {
+ delay: Option<Delay>,
+ stanza: Option<Box<Stanza>>,
+}
+
+// TODO: raw stanza source option
+pub enum ForwardedStanza {
+ Parsed(Box<Stanza>),
+ Raw(String),
+}
+
+impl FromElement for Forwarded {
+ fn from_element(mut element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("forwarded")?;
+ element.check_namespace(XMLNS)?;
+
+ let delay = element.pop_child_opt()?;
+ let stanza = element.pop_child_opt()?;
+ let stanza = stanza.map(|stanza| Box::new(stanza));
+
+ Ok(Self { delay, stanza })
+ }
+}
+
+impl IntoElement for Forwarded {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("forwarded", Some(XMLNS))
+ .push_child_opt(self.delay.clone())
+ .push_child_opt(self.stanza.clone().map(|stanza| *stanza))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum Stanza {
+ Message(Message),
+ Presence(Presence),
+ Iq(Iq),
+ // TODO: raw elements are received with reads.
+ // Raw(Element),
+}
+
+impl FromElement for Stanza {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ match element.identify() {
+ (Some(client::XMLNS), "message") => {
+ Ok(Stanza::Message(Message::from_element(element)?))
+ }
+ (Some(client::XMLNS), "presence") => {
+ Ok(Stanza::Presence(Presence::from_element(element)?))
+ }
+ (Some(client::XMLNS), "iq") => Ok(Stanza::Iq(Iq::from_element(element)?)),
+ _ => Err(peanuts::DeserializeError::UnexpectedElement(element)),
+ }
+ }
+}
+
+impl IntoElement for Stanza {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ match self {
+ Stanza::Message(message) => message.builder(),
+ Stanza::Presence(presence) => presence.builder(),
+ Stanza::Iq(iq) => iq.builder(),
+ }
+ }
+}
diff --git a/stanza/src/xep_0300.rs b/stanza/src/xep_0300.rs
index f522f1c..5a2a88b 100644
--- a/stanza/src/xep_0300.rs
+++ b/stanza/src/xep_0300.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use std::{convert::Infallible, str::FromStr};
use peanuts::{Element, FromElement, IntoElement};
diff --git a/stanza/src/xep_0334.rs b/stanza/src/xep_0334.rs
new file mode 100644
index 0000000..a13e125
--- /dev/null
+++ b/stanza/src/xep_0334.rs
@@ -0,0 +1,87 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use peanuts::{Element, FromElement, IntoElement};
+
+pub const XMLNS: &str = "urn:xmpp:hints";
+
+#[derive(Clone, Copy, Debug)]
+pub struct NoPermanentStore;
+
+impl FromElement for NoPermanentStore {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("no-permanent-store")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for NoPermanentStore {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("no-permanent-store", Some(XMLNS))
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct NoStore;
+
+impl FromElement for NoStore {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("no-store")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for NoStore {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("no-store", Some(XMLNS))
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct NoCopy;
+
+impl FromElement for NoCopy {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("no-copy")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for NoCopy {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("no-copy", Some(XMLNS))
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct Store;
+
+impl FromElement for Store {
+ fn from_element(element: Element) -> peanuts::DeserializeResult<Self> {
+ element.check_name("store")?;
+ element.check_namespace(XMLNS)?;
+
+ element.no_more_content()?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for Store {
+ fn builder(&self) -> peanuts::ElementBuilder {
+ Element::builder("store", Some(XMLNS))
+ }
+}
diff --git a/stanza/src/xep_0390.rs b/stanza/src/xep_0390.rs
index 1a079c2..248ff9a 100644
--- a/stanza/src/xep_0390.rs
+++ b/stanza/src/xep_0390.rs
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
use crate::xep_0300::Hash;
use peanuts::{Element, FromElement, IntoElement};