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); | 
