Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision d6e2aa8220ae87c58c10ff54b2de80e32b51ac08)
+++ protocols/jabber/iq.c	(revision fa8f57b7f51a44ee0870d1bf29646007e087f24e)
@@ -28,4 +28,5 @@
 static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
 static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
+static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
 
 xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
@@ -118,4 +119,5 @@
 				                   XMLNS_BYTESTREAMS,
 				                   XMLNS_FILETRANSFER,
+				                   XMLNS_CARBONS,
 				                   NULL };
 			const char **f;
@@ -1004,7 +1006,24 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *id;
-
-	if ((id = xt_find_path(node, "query/identity"))) {
+	struct xt_node *query, *id;
+
+	if (!(query = xt_find_node(node->children, "query"))) {
+		return XT_HANDLED;
+	}
+
+	if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) &&
+	    set_getbool(&ic->acc->set, "carbons")) {
+
+		struct xt_node *enable, *iq;
+
+		enable = xt_new_node("enable", NULL, NULL);
+		xt_add_attr(enable, "xmlns", XMLNS_CARBONS);
+		iq = jabber_make_packet("iq", "set", NULL, enable);
+
+		jabber_cache_add(ic, iq, jabber_iq_carbons_response);
+		jabber_write_packet(ic, iq);
+	}
+
+	if ((id = xt_find_node(query->children, "identity"))) {
 		char *cat, *type, *name;
 
@@ -1023,2 +1042,18 @@
 	return XT_HANDLED;
 }
+
+static xt_status jabber_iq_carbons_response(struct im_connection *ic,
+                                            struct xt_node *node, struct xt_node *orig)
+{
+	struct jabber_error *err;
+
+	if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
+		imcb_error(ic, "Error enabling carbons: %s%s%s",
+		           err->code, err->text ? ": " : "", err->text ? err->text : "");
+		jabber_error_free(err);
+	} else {
+		imcb_log(ic, "Carbons enabled");
+	}
+
+	return XT_HANDLED;
+}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision d6e2aa8220ae87c58c10ff54b2de80e32b51ac08)
+++ protocols/jabber/jabber.c	(revision fa8f57b7f51a44ee0870d1bf29646007e087f24e)
@@ -113,4 +113,7 @@
 	s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc);
 	s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
+
+	s = set_add(&acc->set, "carbons", "true", set_eval_bool, acc);
+	s->flags |= ACC_SET_OFFLINE_ONLY;
 
 	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision d6e2aa8220ae87c58c10ff54b2de80e32b51ac08)
+++ protocols/jabber/jabber.h	(revision fa8f57b7f51a44ee0870d1bf29646007e087f24e)
@@ -228,4 +228,6 @@
 #define XMLNS_XDATA        "jabber:x:data"                                       /* XEP-0004 */
 #define XMLNS_GMAILNOTIFY  "google:mail:notify"                                  /* Not a XEP */
+#define XMLNS_CARBONS      "urn:xmpp:carbons:2"                                  /* XEP-0280 */
+#define XMLNS_FORWARDING   "urn:xmpp:forward:0"                                  /* XEP-0297 */
 #define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"               /* XEP-0085 */
 #define XMLNS_DISCO_INFO   "http://jabber.org/protocol/disco#info"               /* XEP-0030 */
Index: protocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision d6e2aa8220ae87c58c10ff54b2de80e32b51ac08)
+++ protocols/jabber/message.c	(revision fa8f57b7f51a44ee0870d1bf29646007e087f24e)
@@ -24,8 +24,8 @@
 #include "jabber.h"
 
-xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
+static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent)
 {
 	struct im_connection *ic = data;
-	char *from = xt_find_attr(node, "from");
+	char *from = xt_find_attr(node, carbons_sent ? "to" : "from");
 	char *type = xt_find_attr(node, "type");
 	char *id = xt_find_attr(node, "id");
@@ -39,5 +39,5 @@
 	}
 
-	if (request && id && g_strcmp0(type, "groupchat") != 0) {
+	if (request && id && g_strcmp0(type, "groupchat") != 0 && !carbons_sent) {
 		/* Send a message receipt (XEP-0184), looking like this:
 		 * <message from='...' id='...' to='...'>
@@ -128,5 +128,5 @@
 		if (fullmsg->len > 0) {
 			imcb_buddy_msg(ic, from, fullmsg->str,
-			               0, jabber_get_timestamp(node));
+			               carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node));
 		}
 		if (room) {
@@ -137,6 +137,7 @@
 
 		/* Handling of incoming typing notifications. */
-		if (bud == NULL) {
-			/* Can't handle these for unknown buddies. */
+		if (bud == NULL || carbons_sent) {
+			/* Can't handle these for unknown buddies.
+			   And ignore them if it's just carbons */
 		} else if (xt_find_node(node->children, "composing")) {
 			bud->flags |= JBFLAG_DOES_XEP85;
@@ -162,2 +163,43 @@
 	return XT_HANDLED;
 }
+
+static xt_status jabber_carbons_message(struct xt_node *node, gpointer data)
+{
+	struct im_connection *ic = data;
+	struct xt_node *wrap, *fwd, *msg;
+	gboolean carbons_sent;
+
+	if ((wrap = xt_find_node(node->children, "received"))) {
+		carbons_sent = FALSE;
+	} else if ((wrap = xt_find_node(node->children, "sent"))) {
+		carbons_sent = TRUE;
+	}
+
+	if (wrap == NULL || g_strcmp0(xt_find_attr(wrap, "xmlns"), XMLNS_CARBONS) != 0) {
+		return XT_NEXT;
+	}
+
+	if (!(fwd = xt_find_node(wrap->children, "forwarded")) ||
+	     (g_strcmp0(xt_find_attr(fwd, "xmlns"), XMLNS_FORWARDING) != 0) ||
+	    !(msg = xt_find_node(fwd->children, "message"))) {
+		imcb_log(ic, "Error: Invalid carbons message received");
+		return XT_ABORT;
+	}
+
+	return jabber_pkt_message_normal(msg, data, carbons_sent);
+}
+
+xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
+{
+	struct im_connection *ic = data;
+	struct jabber_data *jd = ic->proto_data;
+	char *from = xt_find_attr(node, "from");
+
+	if (jabber_compare_jid(jd->me, from)) {    /* Probably a Carbons message */
+		xt_status st = jabber_carbons_message(node, data);
+		if (st == XT_HANDLED || st == XT_ABORT) {
+			return st;
+		}
+	}
+	return jabber_pkt_message_normal(node, data, FALSE);
+}
