aboutsummaryrefslogtreecommitdiffstats
path: root/protocols
diff options
context:
space:
mode:
authordequis <dx@dxzone.com.ar>2015-02-21 03:18:21 -0300
committerdequis <dx@dxzone.com.ar>2015-04-28 10:47:48 -0300
commit40cfbc54088702d4887ccfb761eafe65b4376d59 (patch)
tree58f9e7290c934eee6ea3506033923279643cd455 /protocols
parent1493c4b7eff51e231c65f7728c0cf84ca45cf837 (diff)
hipchat: Basic implementation: Auth, profile and mention names
This is enough to log in with their usernames, make 'chat add' based groupchat joins slightly more smooth, and see mention names as nicks. All the MUC list stuff is left out intentionally since that's not as stable as I wish.
Diffstat (limited to 'protocols')
-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);