aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--protocols/jabber/Makefile2
-rw-r--r--protocols/jabber/hipchat.c93
-rw-r--r--protocols/jabber/iq.c11
-rw-r--r--protocols/jabber/jabber.h10
-rw-r--r--protocols/jabber/sasl.c43
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);