diff options
-rw-r--r-- | protocols/jabber/Makefile | 2 | ||||
-rw-r--r-- | protocols/jabber/hipchat.c | 93 | ||||
-rw-r--r-- | protocols/jabber/iq.c | 11 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 10 | ||||
-rw-r--r-- | protocols/jabber/sasl.c | 43 |
5 files changed, 148 insertions, 11 deletions
diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index b5c3b107..49a9b3f4 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -12,7 +12,7 @@ _SRCDIR_ := $(_SRCDIR_)protocols/jabber/ endif # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o hipchat.o LFLAGS += -r diff --git a/protocols/jabber/hipchat.c b/protocols/jabber/hipchat.c new file mode 100644 index 00000000..66675885 --- /dev/null +++ b/protocols/jabber/hipchat.c @@ -0,0 +1,93 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - HipChat specific functions * +* * +* Copyright 2015 Xamarin Inc * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" + +xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node) +{ + struct jabber_data *jd = ic->proto_data; + char *sep, *jid; + + jid = xt_find_attr(node, "jid"); + + sep = strchr(jid, '/'); + if (sep) { + *sep = '\0'; + } + + jabber_set_me(ic, jid); + imcb_log(ic, "Setting Hipchat JID to %s", jid); + + if (sep) { + *sep = '/'; + } + + /* Hipchat's auth doesn't expect a restart here */ + jd->flags &= ~JFLAG_STREAM_RESTART; + + if (!jabber_get_roster(ic) || + !jabber_iq_disco_server(ic) || + !jabber_get_hipchat_profile(ic)) { + return XT_ABORT; + } + + return XT_HANDLED; +} + +int jabber_get_hipchat_profile(struct im_connection *ic) +{ + struct jabber_data *jd = ic->proto_data; + struct xt_node *node; + int st; + + imcb_log(ic, "Fetching hipchat profile for %s", jd->me); + + node = xt_new_node("query", NULL, NULL); + xt_add_attr(node, "xmlns", XMLNS_HIPCHAT_PROFILE); + node = jabber_make_packet("iq", "get", jd->me, node); + + jabber_cache_add(ic, node, jabber_parse_hipchat_profile); + st = jabber_write_packet(ic, node); + + return st; +} + +xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) +{ + struct xt_node *query, *name_node; + + if (!(query = xt_find_node(node->children, "query"))) { + imcb_log(ic, "Warning: Received NULL profile packet"); + return XT_ABORT; + } + + name_node = xt_find_node(query->children, "name"); + if (!name_node) { + imcb_log(ic, "Warning: Can't find real name in profile. Joining groupchats will not be possible."); + return XT_ABORT; + } + + set_setstr(&ic->acc->set, "display_name", name_node->text); + return XT_HANDLED; + +} diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index d7ba6f55..33889d32 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -26,7 +26,6 @@ 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 int jabber_iq_disco_server(struct im_connection *ic); xt_status jabber_pkt_iq(struct xt_node *node, gpointer data) { @@ -373,6 +372,7 @@ int jabber_get_roster(struct im_connection *ic) static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) { + struct jabber_data *jd = ic->proto_data; struct xt_node *query, *c; int initial = (orig != NULL); @@ -387,6 +387,7 @@ static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *n char *jid = xt_find_attr(c, "jid"); char *name = xt_find_attr(c, "name"); char *sub = xt_find_attr(c, "subscription"); + char *mention_name = xt_find_attr(c, "mention_name"); if (jid && sub) { if ((strcmp(sub, "both") == 0 || strcmp(sub, "to") == 0)) { @@ -396,6 +397,12 @@ static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *n if (name) { imcb_rename_buddy(ic, jid, name); } + + /* This could also be used to set the full name as nick for fb/gtalk, + * but i'm keeping the old (ugly?) default behavior just to be safe */ + if (mention_name && (jd->flags & JFLAG_HIPCHAT)) { + imcb_buddy_nick_hint(ic, jid, mention_name); + } } else if (strcmp(sub, "remove") == 0) { jabber_buddy_remove_bare(ic, jid); imcb_remove_buddy(ic, jid, NULL); @@ -854,7 +861,7 @@ static xt_status jabber_iq_version_response(struct im_connection *ic, static xt_status jabber_iq_disco_server_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); -static int jabber_iq_disco_server(struct im_connection *ic) +int jabber_iq_disco_server(struct im_connection *ic) { struct xt_node *node, *iq; struct jabber_data *jd = ic->proto_data; diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 6a329a06..8416e1a7 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -235,6 +235,10 @@ struct jabber_transfer { #define XMLNS_BYTESTREAMS "http://jabber.org/protocol/bytestreams" /* XEP-0065 */ #define XMLNS_IBB "http://jabber.org/protocol/ibb" /* XEP-0047 */ +/* Hipchat protocol extensions*/ +#define XMLNS_HIPCHAT "http://hipchat.com" +#define XMLNS_HIPCHAT_PROFILE "http://hipchat.com/protocol/profile" + /* jabber.c */ void jabber_connect(struct im_connection *ic); @@ -249,6 +253,7 @@ int jabber_remove_from_roster(struct im_connection *ic, char *handle); xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid); xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns); void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data); +int jabber_iq_disco_server(struct im_connection *ic); /* si.c */ int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode); @@ -341,4 +346,9 @@ void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud void jabber_chat_pkt_message(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node); void jabber_chat_invite(struct groupchat *c, char *who, char *message); +/* hipchat.c */ +int jabber_get_hipchat_profile(struct im_connection *ic); +xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); +xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node); + #endif diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c index adcc0f1a..45d52593 100644 --- a/protocols/jabber/sasl.c +++ b/protocols/jabber/sasl.c @@ -54,7 +54,7 @@ xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data) struct xt_node *c, *reply; char *s; int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0; - int want_oauth = FALSE; + int want_oauth = FALSE, want_hipchat = FALSE; GString *mechs; if (!sasl_supported(ic)) { @@ -74,6 +74,7 @@ xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data) } want_oauth = set_getbool(&ic->acc->set, "oauth"); + want_hipchat = (jd->flags & JFLAG_HIPCHAT); mechs = g_string_new(""); c = node->children; @@ -110,7 +111,11 @@ xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data) g_string_free(mechs, TRUE); reply = xt_new_node("auth", NULL, NULL); - xt_add_attr(reply, "xmlns", XMLNS_SASL); + if (!want_hipchat) { + xt_add_attr(reply, "xmlns", XMLNS_SASL); + } else { + xt_add_attr(reply, "xmlns", XMLNS_HIPCHAT); + } if (sup_gtalk && want_oauth) { int len; @@ -142,15 +147,33 @@ xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data) /* The rest will be done later, when we receive a <challenge/>. */ } else if (sup_plain) { int len; + GString *gs; + char *username; - xt_add_attr(reply, "mechanism", "PLAIN"); + if (!want_hipchat) { + xt_add_attr(reply, "mechanism", "PLAIN"); + username = jd->username; + } else { + username = jd->me; + } + + /* set an arbitrary initial size to avoid reallocations */ + gs = g_string_sized_new(128); /* With SASL PLAIN in XMPP, the text should be b64(\0user\0pass) */ - len = strlen(jd->username) + strlen(ic->acc->pass) + 2; - s = g_malloc(len + 1); - s[0] = 0; - strcpy(s + 1, jd->username); - strcpy(s + 2 + strlen(jd->username), ic->acc->pass); + g_string_append_c(gs, '\0'); + g_string_append(gs, username); + g_string_append_c(gs, '\0'); + g_string_append(gs, ic->acc->pass); + if (want_hipchat) { + /* Hipchat's variation adds \0resource at the end */ + g_string_append_c(gs, '\0'); + g_string_append(gs, set_getstr(&ic->acc->set, "resource")); + } + + len = gs->len; + s = g_string_free(gs, FALSE); + reply->text = base64_encode((unsigned char *) s, len); reply->text_len = strlen(reply->text); g_free(s); @@ -396,6 +419,10 @@ xt_status sasl_pkt_result(struct xt_node *node, gpointer data) if (strcmp(node->name, "success") == 0) { imcb_log(ic, "Authentication finished"); jd->flags |= JFLAG_AUTHENTICATED | JFLAG_STREAM_RESTART; + + if (jd->flags & JFLAG_HIPCHAT) { + return hipchat_handle_success(ic, node); + } } else if (strcmp(node->name, "failure") == 0) { imcb_error(ic, "Authentication failure"); imc_logout(ic, FALSE); |