aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/user-guide/commands.xml16
-rw-r--r--protocols/jabber/iq.c39
-rw-r--r--protocols/jabber/jabber.c3
-rw-r--r--protocols/jabber/jabber.h2
-rw-r--r--protocols/jabber/message.c54
5 files changed, 106 insertions, 8 deletions
diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml
index 3a1ad082..b14744d8 100644
--- a/doc/user-guide/commands.xml
+++ b/doc/user-guide/commands.xml
@@ -788,6 +788,22 @@
</description>
</bitlbee-setting>
+ <bitlbee-setting name="carbons" type="boolean" scope="account">
+ <default>true</default>
+
+ <description>
+ <para>
+ Jabber specific. "Message carbons" (XEP-0280) is a server feature to get copies of outgoing messages sent from other clients connected to the same account. It's not widely supported by most public XMPP servers (easier if you host your own), but this will probably change in the next few years.
+ </para>
+ <para>
+ This defaults to true, which will enable it if the server supports it, or fail silently if it's not. This setting only exists to allow disabling the feature if anyone considers it undesirable.
+ </para>
+ <para>
+ See also the <emphasis>self_messages</emphasis> setting.
+ </para>
+ </description>
+ </bitlbee-setting>
+
<bitlbee-setting name="charset" type="string" scope="global">
<default>utf-8</default>
<possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values>
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index b3fe4206..d5ed8f21 100644
--- a/protocols/jabber/iq.c
+++ b/protocols/jabber/iq.c
@@ -27,6 +27,7 @@
static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
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)
{
@@ -117,6 +118,7 @@ xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
XMLNS_SI,
XMLNS_BYTESTREAMS,
XMLNS_FILETRANSFER,
+ XMLNS_CARBONS,
NULL };
const char **f;
@@ -1003,9 +1005,26 @@ static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
struct xt_node *node, struct xt_node *orig)
{
struct jabber_data *jd = ic->proto_data;
- struct xt_node *id;
+ struct xt_node *query, *id;
- if ((id = xt_find_path(node, "query/identity"))) {
+ 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;
if (!(cat = xt_find_attr(id, "category")) ||
@@ -1022,3 +1041,19 @@ static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
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;
+}
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
index 0dbcfa4e..35cf0c90 100644
--- a/protocols/jabber/jabber.c
+++ b/protocols/jabber/jabber.c
@@ -113,6 +113,9 @@ static void jabber_init(account_t *acc)
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 |
ACC_FLAG_HANDLE_DOMAINS;
}
diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h
index 37c99ff0..75bd123f 100644
--- a/protocols/jabber/jabber.h
+++ b/protocols/jabber/jabber.h
@@ -227,6 +227,8 @@ struct jabber_transfer {
#define XMLNS_DELAY "urn:xmpp:delay" /* XEP-0203 */
#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 */
#define XMLNS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" /* XEP-0030 */
diff --git a/protocols/jabber/message.c b/protocols/jabber/message.c
index 94ef8014..c57e6337 100644
--- a/protocols/jabber/message.c
+++ b/protocols/jabber/message.c
@@ -23,10 +23,10 @@
#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");
struct xt_node *body = xt_find_node(node->children, "body"), *c;
@@ -38,7 +38,7 @@ xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
return XT_HANDLED; /* Consider this packet corrupted. */
}
- 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='...'>
* <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/>
@@ -127,7 +127,7 @@ xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
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) {
imcb_chat_invite(ic, room, from, reason);
@@ -136,8 +136,9 @@ xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
g_string_free(fullmsg, TRUE);
/* 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;
imcb_buddy_typing(ic, from, OPT_TYPING);
@@ -161,3 +162,44 @@ xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
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);
+}