From 88758e2b2a24372386b8d3d5fa5390414cc8ec0f Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Fri, 20 Feb 2015 10:06:33 +0100
Subject: Add elements/parsers/serializers/tests for Message Carbons (XEP-0280)

In addition this patch adds an element, a parser and a serializer for
the <thread/> element from XMPP IM.

Test-Information:

Implemented unit tests pass as expected.

Change-Id: I0a14c778c2c0bf65f4b405c9878c741449bfe142

diff --git a/Swiften/Elements/CarbonsDisable.cpp b/Swiften/Elements/CarbonsDisable.cpp
new file mode 100644
index 0000000..b5d80ca
--- /dev/null
+++ b/Swiften/Elements/CarbonsDisable.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Elements/CarbonsDisable.h>
+
+namespace Swift {
+	CarbonsDisable::~CarbonsDisable() { 
+	}
+}
diff --git a/Swiften/Elements/CarbonsDisable.h b/Swiften/Elements/CarbonsDisable.h
new file mode 100644
index 0000000..e31526d
--- /dev/null
+++ b/Swiften/Elements/CarbonsDisable.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsDisable : public Payload {
+		public:
+			typedef boost::shared_ptr<CarbonsDisable> ref;
+
+		public:
+			virtual ~CarbonsDisable();
+	};
+}
diff --git a/Swiften/Elements/CarbonsEnable.cpp b/Swiften/Elements/CarbonsEnable.cpp
new file mode 100644
index 0000000..7d38be1
--- /dev/null
+++ b/Swiften/Elements/CarbonsEnable.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Elements/CarbonsEnable.h>
+
+namespace Swift {
+	CarbonsEnable::~CarbonsEnable() { 
+	}
+}
diff --git a/Swiften/Elements/CarbonsEnable.h b/Swiften/Elements/CarbonsEnable.h
new file mode 100644
index 0000000..b100e8d
--- /dev/null
+++ b/Swiften/Elements/CarbonsEnable.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsEnable : public Payload {
+		public:
+			typedef boost::shared_ptr<CarbonsEnable> ref;
+
+		public:
+			virtual ~CarbonsEnable();
+	};
+}
diff --git a/Swiften/Elements/CarbonsPrivate.cpp b/Swiften/Elements/CarbonsPrivate.cpp
new file mode 100644
index 0000000..a0c6369
--- /dev/null
+++ b/Swiften/Elements/CarbonsPrivate.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Elements/CarbonsPrivate.h>
+
+namespace Swift {
+	CarbonsPrivate::~CarbonsPrivate() { 
+	}
+}
diff --git a/Swiften/Elements/CarbonsPrivate.h b/Swiften/Elements/CarbonsPrivate.h
new file mode 100644
index 0000000..e363b5c
--- /dev/null
+++ b/Swiften/Elements/CarbonsPrivate.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsPrivate : public Payload {
+		public:
+			typedef boost::shared_ptr<CarbonsPrivate> ref;
+
+		public:
+			virtual ~CarbonsPrivate();
+	};
+}
diff --git a/Swiften/Elements/CarbonsReceived.cpp b/Swiften/Elements/CarbonsReceived.cpp
new file mode 100644
index 0000000..d906f8e
--- /dev/null
+++ b/Swiften/Elements/CarbonsReceived.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Elements/CarbonsReceived.h>
+
+namespace Swift {
+	CarbonsReceived::~CarbonsReceived() {
+
+	}
+
+	void CarbonsReceived::setForwarded(boost::shared_ptr<Forwarded> forwarded) {
+		forwarded_ = forwarded;
+	}
+
+	boost::shared_ptr<Forwarded> CarbonsReceived::getForwarded() const {
+		return forwarded_;
+	}
+}
diff --git a/Swiften/Elements/CarbonsReceived.h b/Swiften/Elements/CarbonsReceived.h
new file mode 100644
index 0000000..b057893
--- /dev/null
+++ b/Swiften/Elements/CarbonsReceived.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Forwarded.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsReceived : public Payload {
+		public:
+			typedef boost::shared_ptr<CarbonsReceived> ref;
+
+		public:
+			virtual ~CarbonsReceived();
+			void setForwarded(boost::shared_ptr<Forwarded> forwarded);
+			boost::shared_ptr<Forwarded> getForwarded() const;
+
+		private:
+			boost::shared_ptr<Forwarded> forwarded_;
+	};
+}
diff --git a/Swiften/Elements/CarbonsSent.cpp b/Swiften/Elements/CarbonsSent.cpp
new file mode 100644
index 0000000..08b2e13
--- /dev/null
+++ b/Swiften/Elements/CarbonsSent.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Elements/CarbonsSent.h>
+
+namespace Swift {
+	CarbonsSent::~CarbonsSent() {
+		
+	}
+
+	void CarbonsSent::setForwarded(boost::shared_ptr<Forwarded> forwarded) {
+		forwarded_ = forwarded;
+	}
+
+	boost::shared_ptr<Forwarded> CarbonsSent::getForwarded() const {
+		return forwarded_;
+	}
+}
diff --git a/Swiften/Elements/CarbonsSent.h b/Swiften/Elements/CarbonsSent.h
new file mode 100644
index 0000000..f6b2b53
--- /dev/null
+++ b/Swiften/Elements/CarbonsSent.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Forwarded.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsSent : public Payload {
+		public:
+			typedef boost::shared_ptr<CarbonsSent> ref;
+
+		public:
+			virtual ~CarbonsSent();
+			void setForwarded(boost::shared_ptr<Forwarded> forwarded);
+			boost::shared_ptr<Forwarded> getForwarded() const;
+
+		private:
+			boost::shared_ptr<Forwarded> forwarded_;
+	};
+}
diff --git a/Swiften/Elements/Thread.cpp b/Swiften/Elements/Thread.cpp
new file mode 100644
index 0000000..dfd9029
--- /dev/null
+++ b/Swiften/Elements/Thread.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Elements/Thread.h>
+
+namespace Swift {
+
+	Thread::Thread(const std::string& text, const std::string& parent) : text_(text), parent_(parent) {
+	
+	}
+
+	Thread::~Thread() {
+
+	}
+
+	void Thread::setText(const std::string& text) {
+		text_ = text;
+	}
+
+	const std::string& Thread::getText() const {
+		return text_;
+	}
+
+	void Thread::setParent(const std::string& parent) {
+		parent_ = parent;
+	}
+
+	const std::string& Thread::getParent() const {
+		return parent_;
+	}
+}
diff --git a/Swiften/Elements/Thread.h b/Swiften/Elements/Thread.h
new file mode 100644
index 0000000..8207851
--- /dev/null
+++ b/Swiften/Elements/Thread.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+	class SWIFTEN_API Thread : public Payload {
+		public:
+			Thread(const std::string& text = "", const std::string& parent = "");
+			virtual ~Thread();
+			void setText(const std::string& text);
+			const std::string& getText() const;
+			void setParent(const std::string& parent);
+			const std::string& getParent() const;
+
+		private:
+			std::string text_;
+			std::string parent_;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsDisableParser.cpp b/Swiften/Parser/PayloadParsers/CarbonsDisableParser.cpp
new file mode 100644
index 0000000..7e26f3b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsDisableParser.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/CarbonsDisableParser.h>
+
+namespace Swift {
+
+	CarbonsDisableParser::CarbonsDisableParser() : GenericPayloadParser<CarbonsDisable>() {
+	}
+
+	CarbonsDisableParser::~CarbonsDisableParser() {
+	}
+
+	void CarbonsDisableParser::handleStartElement(const std::string&, const std::string&, const AttributeMap&) {
+	}
+
+	void CarbonsDisableParser::handleEndElement(const std::string&, const std::string&) {
+	}
+
+	void CarbonsDisableParser::handleCharacterData(const std::string&) {
+	}
+	
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsDisableParser.h b/Swiften/Parser/PayloadParsers/CarbonsDisableParser.h
new file mode 100644
index 0000000..56f6787
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsDisableParser.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsDisable.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsDisableParser : public GenericPayloadParser<CarbonsDisable> {
+		public:
+			CarbonsDisableParser();
+			virtual ~CarbonsDisableParser();
+
+			virtual void handleStartElement(const std::string&, const std::string&, const AttributeMap&);
+			virtual void handleEndElement(const std::string&, const std::string&);
+			virtual void handleCharacterData(const std::string&);
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsEnableParser.cpp b/Swiften/Parser/PayloadParsers/CarbonsEnableParser.cpp
new file mode 100644
index 0000000..52bf6d7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsEnableParser.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/CarbonsEnableParser.h>
+
+namespace Swift {
+
+	CarbonsEnableParser::CarbonsEnableParser() : GenericPayloadParser<CarbonsEnable>() {
+	}
+
+	CarbonsEnableParser::~CarbonsEnableParser() {
+	}
+
+	void CarbonsEnableParser::handleStartElement(const std::string&, const std::string&, const AttributeMap&) {
+	}
+
+	void CarbonsEnableParser::handleEndElement(const std::string&, const std::string&) {
+	}
+
+	void CarbonsEnableParser::handleCharacterData(const std::string&) {
+	}
+	
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsEnableParser.h b/Swiften/Parser/PayloadParsers/CarbonsEnableParser.h
new file mode 100644
index 0000000..d0e5771
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsEnableParser.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsEnable.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsEnableParser : public GenericPayloadParser<CarbonsEnable> {
+		public:
+			CarbonsEnableParser();
+			virtual ~CarbonsEnableParser();
+			virtual void handleStartElement(const std::string&, const std::string&, const AttributeMap&);
+			virtual void handleEndElement(const std::string&, const std::string&);
+			virtual void handleCharacterData(const std::string&);
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsPrivateParser.cpp b/Swiften/Parser/PayloadParsers/CarbonsPrivateParser.cpp
new file mode 100644
index 0000000..888edf1
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsPrivateParser.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/CarbonsPrivateParser.h>
+
+namespace Swift {
+
+	CarbonsPrivateParser::CarbonsPrivateParser() : GenericPayloadParser<CarbonsPrivate>() {
+	}
+
+	CarbonsPrivateParser::~CarbonsPrivateParser() {
+	}
+
+	void CarbonsPrivateParser::handleStartElement(const std::string&, const std::string&, const AttributeMap&) {
+	}
+
+	void CarbonsPrivateParser::handleEndElement(const std::string&, const std::string&) {
+	}
+
+	void CarbonsPrivateParser::handleCharacterData(const std::string&) {
+	}
+	
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsPrivateParser.h b/Swiften/Parser/PayloadParsers/CarbonsPrivateParser.h
new file mode 100644
index 0000000..2797808
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsPrivateParser.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsPrivate.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsPrivateParser : public GenericPayloadParser<CarbonsPrivate> {
+		public:
+			CarbonsPrivateParser();
+			virtual ~CarbonsPrivateParser();
+			virtual void handleStartElement(const std::string&, const std::string&, const AttributeMap&);
+			virtual void handleEndElement(const std::string&, const std::string&);
+			virtual void handleCharacterData(const std::string&);
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsReceivedParser.cpp b/Swiften/Parser/PayloadParsers/CarbonsReceivedParser.cpp
new file mode 100644
index 0000000..267c541
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsReceivedParser.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/CarbonsReceivedParser.h>
+
+namespace Swift {
+	CarbonsReceivedParser::CarbonsReceivedParser(PayloadParserFactoryCollection* factories) : GenericPayloadParser<CarbonsReceived>(), factories_(factories), level_(TopLevel) {
+	}
+
+	CarbonsReceivedParser::~CarbonsReceivedParser() {
+	}
+
+	void CarbonsReceivedParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+		if (level_ == PayloadLevel) {
+			if (element == "forwarded") {
+				forwardedParser_ = boost::dynamic_pointer_cast<ForwardedParser>(boost::make_shared<ForwardedParser>(factories_));
+			}
+		}
+		if (forwardedParser_) {
+			forwardedParser_->handleStartElement(element, ns, attributes);
+		}
+		++level_;
+	}
+
+	void CarbonsReceivedParser::handleEndElement(const std::string& element, const std::string& ns) {
+		--level_;
+		if (forwardedParser_ && level_ >= PayloadLevel) {
+			forwardedParser_->handleEndElement(element, ns);
+		}
+		if (forwardedParser_ && level_ == PayloadLevel) {
+			/* done parsing nested stanza */
+			getPayloadInternal()->setForwarded(forwardedParser_->getPayloadInternal());
+			forwardedParser_.reset();
+		}
+	}
+
+	void CarbonsReceivedParser::handleCharacterData(const std::string& data) {
+		if (forwardedParser_) {
+			forwardedParser_->handleCharacterData(data);
+		}
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsReceivedParser.h b/Swiften/Parser/PayloadParsers/CarbonsReceivedParser.h
new file mode 100644
index 0000000..6aa6326
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsReceivedParser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsReceived.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/ForwardedParser.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsReceivedParser : public GenericPayloadParser<CarbonsReceived> {
+		public:
+			CarbonsReceivedParser(PayloadParserFactoryCollection* factories);
+			virtual ~CarbonsReceivedParser();
+			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap&);
+			virtual void handleEndElement(const std::string& element, const std::string&);
+			virtual void handleCharacterData(const std::string&);
+
+		private:
+			enum Level {
+				TopLevel = 0,
+				PayloadLevel = 1
+			};
+
+
+		private:
+			PayloadParserFactoryCollection* factories_;
+			boost::shared_ptr<ForwardedParser> forwardedParser_;
+			int level_;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsSentParser.cpp b/Swiften/Parser/PayloadParsers/CarbonsSentParser.cpp
new file mode 100644
index 0000000..b430249
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsSentParser.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/CarbonsSentParser.h>
+
+namespace Swift {
+	CarbonsSentParser::CarbonsSentParser(PayloadParserFactoryCollection* factories) : GenericPayloadParser<CarbonsSent>(), factories_(factories), level_(TopLevel) {
+	}
+
+	CarbonsSentParser::~CarbonsSentParser() {
+	}
+
+	void CarbonsSentParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+		if (level_ == PayloadLevel) {
+			if (element == "forwarded") {
+				forwardedParser_ = boost::dynamic_pointer_cast<ForwardedParser>(boost::make_shared<ForwardedParser>(factories_));
+			}
+		}
+		if (forwardedParser_) {
+			forwardedParser_->handleStartElement(element, ns, attributes);
+		}
+		++level_;
+	}
+
+	void CarbonsSentParser::handleEndElement(const std::string& element, const std::string& ns) {
+		--level_;
+		if (forwardedParser_ && level_ >= PayloadLevel) {
+			forwardedParser_->handleEndElement(element, ns);
+		}
+		if (forwardedParser_ && level_ == PayloadLevel) {
+			/* done parsing nested stanza */
+			getPayloadInternal()->setForwarded(forwardedParser_->getPayloadInternal());
+			forwardedParser_.reset();
+		}
+	}
+
+	void CarbonsSentParser::handleCharacterData(const std::string& data) {
+		if (forwardedParser_) {
+			forwardedParser_->handleCharacterData(data);
+		}
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/CarbonsSentParser.h b/Swiften/Parser/PayloadParsers/CarbonsSentParser.h
new file mode 100644
index 0000000..91c3292
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/CarbonsSentParser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsSent.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/ForwardedParser.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsSentParser : public GenericPayloadParser<CarbonsSent> {
+		public:
+			CarbonsSentParser(PayloadParserFactoryCollection* factories);
+			virtual ~CarbonsSentParser();
+			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap&);
+			virtual void handleEndElement(const std::string& element, const std::string&);
+			virtual void handleCharacterData(const std::string&);
+
+		private:
+			enum Level {
+				TopLevel = 0,
+				PayloadLevel = 1
+			};
+
+
+		private:
+			PayloadParserFactoryCollection* factories_;
+			boost::shared_ptr<ForwardedParser> forwardedParser_;
+			int level_;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index da2f596..d791819 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -20,6 +20,11 @@
 #include <Swiften/Parser/PayloadParsers/BodyParser.h>
 #include <Swiften/Parser/PayloadParsers/BytestreamsParser.h>
 #include <Swiften/Parser/PayloadParsers/CapsInfoParser.h>
+#include <Swiften/Parser/PayloadParsers/CarbonsDisableParser.h>
+#include <Swiften/Parser/PayloadParsers/CarbonsEnableParser.h>
+#include <Swiften/Parser/PayloadParsers/CarbonsPrivateParser.h>
+#include <Swiften/Parser/PayloadParsers/CarbonsReceivedParser.h>
+#include <Swiften/Parser/PayloadParsers/CarbonsSentParser.h>
 #include <Swiften/Parser/PayloadParsers/ChatStateParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/CommandParser.h>
 #include <Swiften/Parser/PayloadParsers/DelayParser.h>
@@ -38,6 +43,7 @@
 #include <Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h>
 #include <Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferFileInfoParser.h>
 #include <Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h>
 #include <Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h>
 #include <Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h>
@@ -77,9 +83,9 @@
 #include <Swiften/Parser/PayloadParsers/StatusShowParser.h>
 #include <Swiften/Parser/PayloadParsers/StorageParser.h>
 #include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
-#include <Swiften/Parser/PayloadParsers/JingleFileTransferFileInfoParser.h>
 #include <Swiften/Parser/PayloadParsers/StreamInitiationParser.h>
 #include <Swiften/Parser/PayloadParsers/SubjectParser.h>
+#include <Swiften/Parser/PayloadParsers/ThreadParser.h>
 #include <Swiften/Parser/PayloadParsers/UserLocationParser.h>
 #include <Swiften/Parser/PayloadParsers/UserTuneParser.h>
 #include <Swiften/Parser/PayloadParsers/VCardParser.h>
@@ -99,6 +105,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<LastParser> >("query", "jabber:iq:last"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<BodyParser> >("body"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<SubjectParser> >("subject"));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<ThreadParser> >("thread"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<PriorityParser> >("priority"));
 	factories_.push_back(boost::make_shared<ErrorParserFactory>(this));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<DelayParser> >("delay", "urn:xmpp:delay"));
@@ -159,6 +166,11 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<MAMQueryParser> >("query", "urn:xmpp:mam:0"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<MAMFinParser> >("fin", "urn:xmpp:mam:0"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory2<IsodeIQDelegationParser> >("delegate", "http://isode.com/iq_delegation", this));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<CarbonsEnableParser> >("enable", "urn:xmpp:carbons:2"));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<CarbonsDisableParser> >("disable", "urn:xmpp:carbons:2"));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory2<CarbonsReceivedParser> >("received", "urn:xmpp:carbons:2", this));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory2<CarbonsSentParser> >("sent", "urn:xmpp:carbons:2", this));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<CarbonsPrivateParser> >("private", "urn:xmpp:carbons:2"));
 
 	foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
 		addFactory(factory.get());
diff --git a/Swiften/Parser/PayloadParsers/SubjectParser.cpp b/Swiften/Parser/PayloadParsers/SubjectParser.cpp
index b756953..7b2d885 100644
--- a/Swiften/Parser/PayloadParsers/SubjectParser.cpp
+++ b/Swiften/Parser/PayloadParsers/SubjectParser.cpp
@@ -23,7 +23,9 @@ void SubjectParser::handleEndElement(const std::string&, const std::string&) {
 }
 
 void SubjectParser::handleCharacterData(const std::string& data) {
-	text_ += data;
+	if (level_ == 1) { 
+		text_ += data;
+	}
 }
 
 }
diff --git a/Swiften/Parser/PayloadParsers/ThreadParser.cpp b/Swiften/Parser/PayloadParsers/ThreadParser.cpp
new file mode 100644
index 0000000..0d0aca8
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ThreadParser.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/ThreadParser.h>
+
+#include <boost/optional.hpp>
+
+namespace Swift {
+
+ThreadParser::ThreadParser() : level_(0) {
+}
+
+ThreadParser::~ThreadParser() {
+}
+
+void ThreadParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+	++level_;
+	if (element == "thread") {
+		getPayloadInternal()->setParent(attributes.getAttributeValue("parent").get_value_or(""));
+	}
+}
+
+void ThreadParser::handleEndElement(const std::string&, const std::string&) {
+	--level_;
+	if (level_ == 0) {
+		getPayloadInternal()->setText(text_);
+	}
+}
+
+void ThreadParser::handleCharacterData(const std::string& data) {
+	if (level_ == 1) {
+		text_ += data;
+	}
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/ThreadParser.h b/Swiften/Parser/PayloadParsers/ThreadParser.h
new file mode 100644
index 0000000..270ea99
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ThreadParser.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/Thread.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+	class SWIFTEN_API ThreadParser : public GenericPayloadParser<Thread> {
+		public:
+			ThreadParser();
+			virtual ~ThreadParser();
+
+			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+			virtual void handleEndElement(const std::string& element, const std::string&);
+			virtual void handleCharacterData(const std::string& data);
+
+		private:
+			int level_;
+			std::string text_;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/CarbonsParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/CarbonsParserTest.cpp
new file mode 100644
index 0000000..6fd38bc
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/CarbonsParserTest.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/Message.h>
+#include <Swiften/Elements/Thread.h>
+#include <Swiften/Elements/CarbonsEnable.h>
+#include <Swiften/Elements/CarbonsDisable.h>
+#include <Swiften/Elements/CarbonsReceived.h>
+#include <Swiften/Elements/CarbonsSent.h>
+#include <Swiften/Elements/CarbonsPrivate.h>
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
+
+using namespace Swift;
+
+class CarbonsParserTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(CarbonsParserTest);
+		CPPUNIT_TEST(testParseExample3);
+		CPPUNIT_TEST(testParseExample6);
+		CPPUNIT_TEST(testParseExample12);
+		CPPUNIT_TEST(testParseExample14);
+		CPPUNIT_TEST(testParseExample15);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		CarbonsParserTest() {}
+
+		/*
+		 * Test parsing of example 3 in XEP-0280.
+		 */
+		void testParseExample3() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse("<enable xmlns='urn:xmpp:carbons:2' />"));
+
+			CarbonsEnable::ref enable = parser.getPayload<CarbonsEnable>();
+			CPPUNIT_ASSERT(enable);
+		}
+
+		/*
+		 * Test parsing of example 6 in XEP-0280.
+		 */
+		void testParseExample6() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse("<disable xmlns='urn:xmpp:carbons:2' />"));
+
+			CarbonsDisable::ref disable = parser.getPayload<CarbonsDisable>();
+			CPPUNIT_ASSERT(disable);
+		}
+
+		/*
+		 * Test parsing of example 12 in XEP-0280.
+		 */
+		void testParseExample12() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse("<received xmlns='urn:xmpp:carbons:2'>"
+											"<forwarded xmlns='urn:xmpp:forward:0'>"
+												"<message xmlns='jabber:client'"
+													" from='juliet@capulet.example/balcony'"
+													" to='romeo@montague.example/garden'"
+													" type='chat'>"
+													"<body>What man art thou that, thus bescreen'd in night, so stumblest on my counsel?</body>"
+													"<thread>0e3141cd80894871a68e6fe6b1ec56fa</thread>"
+												"</message>"
+											"</forwarded>"
+										"</received>"));
+
+			CarbonsReceived::ref received = parser.getPayload<CarbonsReceived>();
+			CPPUNIT_ASSERT(received);
+
+			boost::shared_ptr<Forwarded> forwarded = received->getForwarded();
+			CPPUNIT_ASSERT(forwarded);
+
+			boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(forwarded->getStanza());
+			CPPUNIT_ASSERT(message);
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.example/balcony"), message->getFrom());
+
+			boost::shared_ptr<Thread> thread = message->getPayload<Thread>();
+			CPPUNIT_ASSERT(thread);
+			CPPUNIT_ASSERT_EQUAL(std::string("0e3141cd80894871a68e6fe6b1ec56fa"), thread->getText());
+		}
+
+		/*
+		 * Test parsing of example 14 in XEP-0280.
+		 */
+		void testParseExample14() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse("<sent xmlns='urn:xmpp:carbons:2'>"
+											"<forwarded xmlns='urn:xmpp:forward:0'>"
+												"<message xmlns='jabber:client'"
+													" to='juliet@capulet.example/balcony'"
+													" from='romeo@montague.example/home'"
+													" type='chat'>"
+													"<body>Neither, fair saint, if either thee dislike.</body>"
+													"<thread>0e3141cd80894871a68e6fe6b1ec56fa</thread>"
+												"</message>"
+											"</forwarded>"
+										"</sent>"));
+
+			CarbonsSent::ref sent = parser.getPayload<CarbonsSent>();
+			CPPUNIT_ASSERT(sent);
+
+			boost::shared_ptr<Forwarded> forwarded = sent->getForwarded();
+			CPPUNIT_ASSERT(forwarded);
+
+			boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(forwarded->getStanza());
+			CPPUNIT_ASSERT(message);
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.example/balcony"), message->getTo());
+		}
+
+		/*
+		 * Test parsing of example 15 in XEP-0280.
+		 */
+		void testParseExample15() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse("<private xmlns='urn:xmpp:carbons:2'/>"));
+
+			CPPUNIT_ASSERT(parser.getPayload<CarbonsPrivate>());
+		}
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CarbonsParserTest);
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 050d293..03870e5 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -23,6 +23,12 @@ sources = [
 		"PayloadParserFactoryCollection.cpp",
 		"PayloadParsers/BodyParser.cpp",
 		"PayloadParsers/SubjectParser.cpp",
+		"PayloadParsers/ThreadParser.cpp",
+		"PayloadParsers/CarbonsEnableParser.cpp",
+		"PayloadParsers/CarbonsDisableParser.cpp",
+		"PayloadParsers/CarbonsPrivateParser.cpp",
+		"PayloadParsers/CarbonsReceivedParser.cpp",
+		"PayloadParsers/CarbonsSentParser.cpp",
 		"PayloadParsers/ChatStateParser.cpp",
 		"PayloadParsers/CapsInfoParser.cpp",
 		"PayloadParsers/DiscoInfoParser.cpp",
diff --git a/Swiften/QA/ConcurrentFileTransferTest/ConcurrentFileTransferTest.cpp b/Swiften/QA/ConcurrentFileTransferTest/ConcurrentFileTransferTest.cpp
new file mode 100644
index 0000000..4da2672
--- /dev/null
+++ b/Swiften/QA/ConcurrentFileTransferTest/ConcurrentFileTransferTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <fstream>
+#include <string>
+#include <map>
+
+#include <boost/numeric/conversion/cast.hpp>
+#include <boost/filesystem.hpp>
+
+#include <Swiften/Base/sleep.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/ClientXMLTracer.h>
+#include <Swiften/Client/Client.h>
+#include <Swiften/EventLoop/SimpleEventLoop.h>
+#include <Swiften/Network/BoostNetworkFactories.h>
+#include <Swiften/Network/Timer.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/Base/BoostRandomGenerator.h>
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Disco/ClientDiscoManager.h>
+#include <Swiften/FileTransfer/FileWriteBytestream.h>
+#include <Swiften/Base/Debug.h>
+
+using namespace Swift;
+
+static const std::string CLIENT_NAME = "Swiften FT Test";
+static const std::string CLIENT_NODE = "http://swift.im";
+
+static boost::shared_ptr<SimpleEventLoop> eventLoop;
+static boost::shared_ptr<BoostNetworkFactories> networkFactories;
+
+BoostRandomGenerator randGen;
+
+enum Candidate {
+	InBandBytestream = 1,
+	S5B_Direct = 2,
+	S5B_Proxied = 4,
+	S5B_Assisted = 8,
+};
+
+class ConcurrentFileTransferTest {
+	public:
+		ConcurrentFileTransferTest(int clientACandidates, int clientBCandidates) : clientACandidates_(clientACandidates), clientBCandidates_(clientBCandidates) {
+
+		}
+
+	private:
+		int clientACandidates_;
+		boost::shared_ptr<Client> clientA_;
+		std::map<std::string, ByteArray> clientASendFiles_;
+
+
+		int clientBCandidates_;
+		boost::shared_ptr<Client> clientB_;
+};
+
+/**
+ *	This program tests the concurrent transfer of multiple file-transfers.
+ *  
+ */
+int main(int argc, char** argv) {
+	int failedTests = 0;
+
+	if (!env("SWIFT_FILETRANSFERTEST_JID") && !env("SWIFT_FILETRANSFERTEST_PASS") && !env("SWIFT_FILETRANSFERTEST2_JID") && !env("SWIFT_FILETRANSFERTEST2_PASS")) {
+
+		return -1;
+	}
+
+
+	return failedTests;
+}
diff --git a/Swiften/QA/ConcurrentFileTransferTest/SConscript b/Swiften/QA/ConcurrentFileTransferTest/SConscript
new file mode 100644
index 0000000..1b4cdbc
--- /dev/null
+++ b/Swiften/QA/ConcurrentFileTransferTest/SConscript
@@ -0,0 +1,17 @@
+import os
+
+Import("env")
+
+if env["TEST"] :
+	myenv = env.Clone()
+	myenv.UseFlags(myenv["SWIFTEN_FLAGS"])
+	myenv.UseFlags(myenv["SWIFTEN_DEP_FLAGS"])
+
+	for i in ["SWIFT_FILETRANSFERTEST_JID", "SWIFT_FILETRANSFERTEST_PASS", "SWIFT_FILETRANSFERTEST2_JID", "SWIFT_FILETRANSFERTEST2_PASS"]:
+		if ARGUMENTS.get(i.lower(), False) :
+			myenv["ENV"][i] = ARGUMENTS[i.lower()]
+		elif os.environ.get(i, "") :
+			myenv["ENV"][i] = os.environ[i]
+
+	tester = myenv.Program("ConcurrentFileTransferTest", ["ConcurrentFileTransferTest.cpp"])
+	myenv.Test(tester, "system")
diff --git a/Swiften/SConscript b/Swiften/SConscript
index d1709dc..7934030 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -121,6 +121,11 @@ if env["SCONS_STAGE"] == "build" :
 			"Compress/ZLibCodecompressor.cpp",
 			"Compress/ZLibDecompressor.cpp",
 			"Compress/ZLibCompressor.cpp",
+			"Elements/CarbonsEnable.cpp",
+			"Elements/CarbonsDisable.cpp",
+			"Elements/CarbonsPrivate.cpp",
+			"Elements/CarbonsReceived.cpp",
+			"Elements/CarbonsSent.cpp",
 			"Elements/DiscoInfo.cpp",
 			"Elements/Presence.cpp",
 			"Elements/Form.cpp",
@@ -154,6 +159,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Elements/MAMResult.cpp",
 			"Elements/MAMQuery.cpp",
 			"Elements/MAMFin.cpp",
+			"Elements/Thread.cpp",
 			"Elements/IsodeIQDelegation.cpp",
 			"Entity/Entity.cpp",
 			"Entity/PayloadPersister.cpp",
@@ -191,6 +197,11 @@ if env["SCONS_STAGE"] == "build" :
 			"Serializer/PayloadSerializerCollection.cpp",
 			"Serializer/PayloadSerializers/IBBSerializer.cpp",
 			"Serializer/PayloadSerializers/CapsInfoSerializer.cpp",
+			"Serializer/PayloadSerializers/CarbonsDisableSerializer.cpp",
+			"Serializer/PayloadSerializers/CarbonsEnableSerializer.cpp",
+			"Serializer/PayloadSerializers/CarbonsPrivateSerializer.cpp",
+			"Serializer/PayloadSerializers/CarbonsSentSerializer.cpp",
+			"Serializer/PayloadSerializers/CarbonsReceivedSerializer.cpp",
 			"Serializer/PayloadSerializers/ChatStateSerializer.cpp",
 			"Serializer/PayloadSerializers/DiscoInfoSerializer.cpp",
 			"Serializer/PayloadSerializers/DiscoItemsSerializer.cpp",
@@ -239,6 +250,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Serializer/PayloadSerializers/MAMQuerySerializer.cpp",
 			"Serializer/PayloadSerializers/IsodeIQDelegationSerializer.cpp",
 			"Serializer/PayloadSerializers/JingleFileTransferFileInfoSerializer.cpp",
+			"Serializer/PayloadSerializers/ThreadSerializer.cpp",
 			"Serializer/PresenceSerializer.cpp",
 			"Serializer/StanzaSerializer.cpp",
 			"Serializer/StreamErrorSerializer.cpp",
@@ -433,6 +445,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Parser/PayloadParsers/UnitTest/MAMFinParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/MAMResultParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/MAMQueryParserTest.cpp"),
+			File("Parser/PayloadParsers/UnitTest/CarbonsParserTest.cpp"),
 			File("Parser/UnitTest/BOSHBodyExtractorTest.cpp"),
 			File("Parser/UnitTest/AttributeMapTest.cpp"),
 			File("Parser/UnitTest/EnumParserTest.cpp"),
@@ -460,6 +473,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Roster/UnitTest/XMPPRosterSignalHandler.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/BlockSerializerTest.cpp"),
+			File("Serializer/PayloadSerializers/UnitTest/CarbonsSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.cpp
new file mode 100644
index 0000000..e39d005
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.h>
+
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	CarbonsDisableSerializer::CarbonsDisableSerializer() : GenericPayloadSerializer<CarbonsDisable>() {
+
+	}
+
+	CarbonsDisableSerializer::~CarbonsDisableSerializer() {
+
+	}
+
+ 	std::string CarbonsDisableSerializer::serializePayload(boost::shared_ptr<CarbonsDisable>)	const {
+		XMLElement element("disable", "urn:xmpp:carbons:2");
+		return element.serialize();
+	}
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.h b/Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.h
new file mode 100644
index 0000000..6eeb5d5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsDisable.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsDisableSerializer : public GenericPayloadSerializer<CarbonsDisable> {
+		public:
+			CarbonsDisableSerializer();
+			virtual ~CarbonsDisableSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<CarbonsDisable>)	const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.cpp
new file mode 100644
index 0000000..0bc3822
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.h>
+
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	CarbonsEnableSerializer::CarbonsEnableSerializer() : GenericPayloadSerializer<CarbonsEnable>() {
+	}
+
+	CarbonsEnableSerializer::~CarbonsEnableSerializer() {
+
+	}
+
+	std::string CarbonsEnableSerializer::serializePayload(boost::shared_ptr<CarbonsEnable>)	const {
+		XMLElement element("enable", "urn:xmpp:carbons:2");
+		return element.serialize();
+	}
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.h b/Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.h
new file mode 100644
index 0000000..aa76d5f
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsEnable.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsEnableSerializer : public GenericPayloadSerializer<CarbonsEnable> {
+		public:
+			CarbonsEnableSerializer();
+			virtual ~CarbonsEnableSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<CarbonsEnable>)	const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.cpp
new file mode 100644
index 0000000..1e77470
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.h>
+
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	CarbonsPrivateSerializer::CarbonsPrivateSerializer() : GenericPayloadSerializer<CarbonsPrivate>() {
+	}
+
+	CarbonsPrivateSerializer::~CarbonsPrivateSerializer() {
+
+	}
+
+	std::string CarbonsPrivateSerializer::serializePayload(boost::shared_ptr<CarbonsPrivate>)	const {
+		XMLElement element("private", "urn:xmpp:carbons:2");
+		return element.serialize();
+	}
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.h b/Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.h
new file mode 100644
index 0000000..37a6dd1
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsPrivate.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsPrivateSerializer : public GenericPayloadSerializer<CarbonsPrivate> {
+		public:
+			CarbonsPrivateSerializer();
+			virtual ~CarbonsPrivateSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<CarbonsPrivate>)	const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.cpp
new file mode 100644
index 0000000..bf63a69
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.h>
+
+#include <boost/make_shared.hpp>
+
+namespace Swift {
+	CarbonsReceivedSerializer::CarbonsReceivedSerializer(PayloadSerializerCollection* serializers) : GenericPayloadSerializer<CarbonsReceived>(), serializers_(serializers) {
+	}
+
+	CarbonsReceivedSerializer::~CarbonsReceivedSerializer() {	
+	}
+	
+	std::string CarbonsReceivedSerializer::serializePayload(boost::shared_ptr<CarbonsReceived> received) const {
+		XMLElement element("received", "urn:xmpp:carbons:2");
+		if (received->getForwarded()) {
+			element.addNode(boost::make_shared<XMLRawTextNode>(ForwardedSerializer(serializers_).serialize(received->getForwarded())));
+		}
+		return element.serialize();
+	}
+}
+
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.h b/Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.h
new file mode 100644
index 0000000..9ce928d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsReceived.h>
+#include <Swiften/Elements/Forwarded.h>
+#include <Swiften/Serializer/PayloadSerializers/ForwardedSerializer.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsReceivedSerializer : public GenericPayloadSerializer<CarbonsReceived> {
+		public:
+			CarbonsReceivedSerializer(PayloadSerializerCollection* serializers);
+			virtual ~CarbonsReceivedSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<CarbonsReceived> received) const;
+
+		private:
+			PayloadSerializerCollection* serializers_;
+	};
+}
+
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.cpp
new file mode 100644
index 0000000..30a283e
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.h>
+
+namespace Swift {
+	CarbonsSentSerializer::CarbonsSentSerializer(PayloadSerializerCollection* serializers) : GenericPayloadSerializer<CarbonsSent>(), serializers_(serializers) {
+	}
+
+	CarbonsSentSerializer::~CarbonsSentSerializer() {
+	}
+	
+	std::string CarbonsSentSerializer::serializePayload(boost::shared_ptr<CarbonsSent> sent) const {
+		XMLElement element("sent", "urn:xmpp:carbons:2");
+		if (sent->getForwarded()) {
+			element.addNode(boost::make_shared<XMLRawTextNode>(ForwardedSerializer(serializers_).serialize(sent->getForwarded())));
+		}
+		return element.serialize();
+	}
+}
+
diff --git a/Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.h b/Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.h
new file mode 100644
index 0000000..03d10ca
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Elements/CarbonsSent.h>
+#include <Swiften/Elements/Forwarded.h>
+#include <Swiften/Serializer/PayloadSerializers/ForwardedSerializer.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+namespace Swift {
+	class SWIFTEN_API CarbonsSentSerializer : public GenericPayloadSerializer<CarbonsSent> {
+		public:
+			CarbonsSentSerializer(PayloadSerializerCollection* serializers);
+			virtual ~CarbonsSentSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<CarbonsSent> sent) const;
+
+		private:
+			PayloadSerializerCollection* serializers_;
+	};
+}
+
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 5da0829..2f02704 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -70,6 +70,7 @@
 #include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/SubjectSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/ThreadSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/UserLocationSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/UserTuneSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/VCardSerializer.h>
@@ -82,6 +83,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
 	serializers_.push_back(new IBBSerializer());
 	serializers_.push_back(new BodySerializer());
 	serializers_.push_back(new SubjectSerializer());
+	serializers_.push_back(new ThreadSerializer());
 	serializers_.push_back(new ChatStateSerializer());
 	serializers_.push_back(new PrioritySerializer());
 	serializers_.push_back(new ErrorSerializer(this));
diff --git a/Swiften/Serializer/PayloadSerializers/ThreadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/ThreadSerializer.cpp
new file mode 100644
index 0000000..cc4d573
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ThreadSerializer.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/ThreadSerializer.h>
+
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	ThreadSerializer::ThreadSerializer() : GenericPayloadSerializer<Thread>() {	
+	}
+
+	ThreadSerializer::~ThreadSerializer() {
+	}
+
+	std::string ThreadSerializer::serializePayload(boost::shared_ptr<Thread> thread)  const {
+		XMLElement threadNode("thread", "", thread->getText());
+		if (!thread->getParent().empty()) {
+			threadNode.setAttribute("parent", thread->getParent());
+		}
+		return threadNode.serialize();
+	}
+}
diff --git a/Swiften/Serializer/PayloadSerializers/ThreadSerializer.h b/Swiften/Serializer/PayloadSerializers/ThreadSerializer.h
new file mode 100644
index 0000000..9aae887
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ThreadSerializer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLTextNode.h>
+#include <Swiften/Elements/Thread.h>
+
+namespace Swift {
+	class SWIFTEN_API ThreadSerializer : public GenericPayloadSerializer<Thread> {
+		public:
+			ThreadSerializer();
+			virtual ~ThreadSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<Thread> thread) const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/CarbonsSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/CarbonsSerializerTest.cpp
new file mode 100644
index 0000000..1f0394d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/CarbonsSerializerTest.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Serializer/PayloadSerializers/CarbonsDisableSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/CarbonsEnableSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/CarbonsPrivateSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/CarbonsReceivedSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/CarbonsSentSerializer.h> 
+#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h>
+#include <Swiften/Elements/CarbonsDisable.h>
+#include <Swiften/Elements/CarbonsEnable.h>
+#include <Swiften/Elements/CarbonsPrivate.h>
+#include <Swiften/Elements/CarbonsReceived.h>
+#include <Swiften/Elements/CarbonsSent.h>
+#include <Swiften/Elements/Message.h>
+#include <Swiften/Elements/Forwarded.h>
+#include <Swiften/Elements/Thread.h>
+#include <Swiften/JID/JID.h>
+
+using namespace Swift;
+
+class CarbonsSerializerTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(CarbonsSerializerTest);
+		CPPUNIT_TEST(testSerializeExample3);
+		CPPUNIT_TEST(testSerializeExample6);
+		CPPUNIT_TEST(testSerializeExample12);
+		CPPUNIT_TEST(testSerializeExample14);
+		CPPUNIT_TEST(testSerializeExample15);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		CarbonsSerializerTest() {}
+
+		/*
+		 * Test serializing of example 3 in XEP-0280.
+		 */
+		void testSerializeExample3() {
+			CarbonsEnableSerializer testling;
+
+			CPPUNIT_ASSERT_EQUAL(std::string("<enable xmlns=\"urn:xmpp:carbons:2\"/>"), testling.serialize(boost::make_shared<CarbonsEnable>()));
+		}
+
+		/*
+		 * Test serializing of example 6 in XEP-0280.
+		 */
+		void testSerializeExample6() {
+			CarbonsDisableSerializer testling;
+
+			CPPUNIT_ASSERT_EQUAL(std::string("<disable xmlns=\"urn:xmpp:carbons:2\"/>"), testling.serialize(boost::make_shared<CarbonsDisable>()));
+		}
+
+		/*
+		 * Test serializing of example 12 in XEP-0280.
+		 */
+		void testSerializeExample12() {
+			CarbonsReceivedSerializer testling(&serializers);
+
+			CarbonsReceived::ref received = boost::make_shared<CarbonsReceived>();
+
+			boost::shared_ptr<Forwarded> forwarded = boost::make_shared<Forwarded>();
+
+			Message::ref message = boost::make_shared<Message>();
+			message->setFrom(JID("juliet@capulet.example/balcony"));
+			message->setTo(JID("romeo@montague.example/garden"));
+			message->setBody("What man art thou that, thus bescreen'd in night, so stumblest on my counsel?");
+			message->addPayload(boost::make_shared<Thread>("0e3141cd80894871a68e6fe6b1ec56fa"));
+
+			forwarded->setStanza(message);
+			received->setForwarded(forwarded);
+
+			CPPUNIT_ASSERT_EQUAL(std::string(
+				"<received xmlns=\"urn:xmpp:carbons:2\">"
+					"<forwarded xmlns=\"urn:xmpp:forward:0\">"
+						"<message from=\"juliet@capulet.example/balcony\""
+							" to=\"romeo@montague.example/garden\""
+							" type=\"chat\""
+							" xmlns=\"jabber:client\">"
+							"<body>What man art thou that, thus bescreen'd in night, so stumblest on my counsel?</body>"
+							"<thread>0e3141cd80894871a68e6fe6b1ec56fa</thread>"
+						"</message>"
+					"</forwarded>"
+				"</received>"), testling.serialize(received));
+		}
+
+		/*
+		 * Test serializing of example 14 in XEP-0280.
+		 */
+		void testSerializeExample14() {
+			CarbonsSentSerializer testling(&serializers);
+
+			CarbonsSent::ref sent = boost::make_shared<CarbonsSent>();
+
+			boost::shared_ptr<Forwarded> forwarded = boost::make_shared<Forwarded>();
+
+			Message::ref message = boost::make_shared<Message>();
+			message->setTo(JID("juliet@capulet.example/balcony"));
+			message->setFrom(JID("romeo@montague.example/home"));
+			message->setBody("Neither, fair saint, if either thee dislike.");
+			message->addPayload(boost::make_shared<Thread>("0e3141cd80894871a68e6fe6b1ec56fa"));
+
+			forwarded->setStanza(message);
+			sent->setForwarded(forwarded);
+
+			CPPUNIT_ASSERT_EQUAL(std::string(
+				"<sent xmlns=\"urn:xmpp:carbons:2\">"
+					"<forwarded xmlns=\"urn:xmpp:forward:0\">"
+						"<message from=\"romeo@montague.example/home\""
+							" to=\"juliet@capulet.example/balcony\""
+							" type=\"chat\""
+							" xmlns=\"jabber:client\">"
+							"<body>Neither, fair saint, if either thee dislike.</body>"
+							"<thread>0e3141cd80894871a68e6fe6b1ec56fa</thread>"
+						"</message>"
+					"</forwarded>"
+				"</sent>"), testling.serialize(sent));
+		}
+
+		/*
+		 * Test serializing of example 15 in XEP-0280.
+		 */
+		void testSerializeExample15() {
+			CarbonsPrivateSerializer testling;
+
+			CPPUNIT_ASSERT_EQUAL(std::string("<private xmlns=\"urn:xmpp:carbons:2\"/>"), testling.serialize(boost::make_shared<CarbonsPrivate>()));
+		}
+	private:
+		FullPayloadSerializerCollection serializers;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CarbonsSerializerTest);
-- 
cgit v0.10.2-6-g49f6