diff --git a/doap.xml b/doap.xml
index d4d449fcb218859c5adb093911c03af097caf4da..241965cdf9e007ff897d57c4c5af1ef0676ec88f 100644
--- a/doap.xml
+++ b/doap.xml
@@ -339,6 +339,14 @@
only the namespace is included for now
+
+
+
+ complete
+ 0.13.0
+ NEXT
+
+
diff --git a/src/carbons.rs b/src/carbons.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b40c1e2e7563c8b1f02e4c3311079c7567cb619e
--- /dev/null
+++ b/src/carbons.rs
@@ -0,0 +1,127 @@
+// Copyright (c) 2019 Emmanuel Gil Peyrot
+//
+// 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::forwarding::Forwarded;
+use crate::iq::IqSetPayload;
+use crate::message::MessagePayload;
+
+generate_empty_element!(
+ /// Enable carbons for this session.
+ Enable,
+ "enable",
+ CARBONS
+);
+
+impl IqSetPayload for Enable {}
+
+generate_empty_element!(
+ /// Disable a previously-enabled carbons.
+ Disable,
+ "disable",
+ CARBONS
+);
+
+impl IqSetPayload for Disable {}
+
+generate_empty_element!(
+ /// Request the enclosing message to not be copied to other carbons-enabled
+ /// resources of the user.
+ Private,
+ "private",
+ CARBONS
+);
+
+impl MessagePayload for Private {}
+
+generate_element!(
+ /// Wrapper for a message received on another resource.
+ Received, "received", CARBONS,
+
+ children: [
+ /// Wrapper for the enclosed message.
+ forwarded: Required = ("forwarded", FORWARD) => Forwarded
+ ]
+);
+
+impl MessagePayload for Received {}
+
+generate_element!(
+ /// Wrapper for a message sent from another resource.
+ Sent, "sent", CARBONS,
+
+ children: [
+ /// Wrapper for the enclosed message.
+ forwarded: Required = ("forwarded", FORWARD) => Forwarded
+ ]
+);
+
+impl MessagePayload for Sent {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use minidom::Element;
+ use std::convert::TryFrom;
+
+ #[cfg(target_pointer_width = "32")]
+ #[test]
+ fn test_size() {
+ assert_size!(Enable, 0);
+ assert_size!(Disable, 0);
+ assert_size!(Private, 0);
+ assert_size!(Received, 212);
+ assert_size!(Sent, 212);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_size() {
+ assert_size!(Enable, 0);
+ assert_size!(Disable, 0);
+ assert_size!(Private, 0);
+ assert_size!(Received, 408);
+ assert_size!(Sent, 408);
+ }
+
+ #[test]
+ fn empty_elements() {
+ let elem: Element = "".parse().unwrap();
+ Enable::try_from(elem).unwrap();
+
+ let elem: Element = "".parse().unwrap();
+ Disable::try_from(elem).unwrap();
+
+ let elem: Element = "".parse().unwrap();
+ Private::try_from(elem).unwrap();
+ }
+
+ #[test]
+ fn forwarded_elements() {
+ let elem: Element = "
+
+
+
+"
+ .parse()
+ .unwrap();
+ let received = Received::try_from(elem).unwrap();
+ assert!(received.forwarded.stanza.is_some());
+
+ let elem: Element = "
+
+
+
+"
+ .parse()
+ .unwrap();
+ let sent = Sent::try_from(elem).unwrap();
+ assert!(sent.forwarded.stanza.is_some());
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 6863afec8de05478a5f207251cf55c3cb75b37ac..607ea1bea6872e03cb2569d33148e2132378e856 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -149,6 +149,9 @@ pub mod jingle_s5b;
/// XEP-0261: Jingle In-Band Bytestreams Transport Method
pub mod jingle_ibb;
+/// XEP-0280: Message Carbons
+pub mod carbons;
+
/// XEP-0297: Stanza Forwarding
pub mod forwarding;
diff --git a/src/ns.rs b/src/ns.rs
index e89ad67a0ad5379bcc2c223f2fb030201c57c3cc..c1a5e96c21d60a999918ff5fa8f15de2c816c64f 100644
--- a/src/ns.rs
+++ b/src/ns.rs
@@ -138,6 +138,9 @@ pub const JINGLE_IBB: &str = "urn:xmpp:jingle:transports:ibb:1";
/// XEP-0277: Microblogging over XMPP
pub const MICROBLOG: &str = "urn:xmpp:microblog:0";
+/// XEP-0280: Message Carbons
+pub const CARBONS: &str = "urn:xmpp:carbons:2";
+
/// XEP-0297: Stanza Forwarding
pub const FORWARD: &str = "urn:xmpp:forward:0";