Ejemplo n.º 1
0
/* Garbage collection: Cleans up all nodes that are handled. Useful for
   streams because there's no reason to keep a complete packet history
   in memory. */
void xt_cleanup(struct xt_parser *xt, struct xt_node *node, int depth)
{
	struct xt_node *c, *prev;

	if (!xt || !xt->root) {
		return;
	}

	if (node == NULL) {
		xt_cleanup(xt, xt->root, depth);
		return;
	}

	if (node->flags & XT_SEEN && node == xt->root) {
		xt_free_node(xt->root);
		xt->root = xt->cur = NULL;
		/* xt->cur should be NULL already, BTW... */

		return;
	}

	/* c contains the current node, prev the previous node (or NULL).
	   I admit, this one's pretty horrible. */
	for (c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children) {
		if (c->flags & XT_SEEN) {
			/* Remove the node from the linked list. */
			if (prev) {
				prev->next = c->next;
			} else {
				node->children = c->next;
			}

			xt_free_node(c);

			/* Since the for loop wants to get c->next, make sure
			   c points at something that exists (and that c->next
			   will actually be the next item we should check). c
			   can be NULL now, if we just removed the first item.
			   That explains the ? thing in for(). */
			c = prev;
		} else {
			/* This node can't be cleaned up yet, but maybe a
			   subnode can. */
			if (depth != 0) {
				xt_cleanup(xt, c, depth > 0 ? depth - 1 : depth);
			}
		}
	}
}
Ejemplo n.º 2
0
/* Frees a node. This doesn't clean up references to itself from parents! */
void xt_free_node(struct xt_node *node)
{
	int i;

	if (!node) {
		return;
	}

	g_free(node->name);
	g_free(node->text);

	for (i = 0; node->attr[i].key; i++) {
		g_free(node->attr[i].key);
		g_free(node->attr[i].value);
	}
	g_free(node->attr);

	while (node->children) {
		struct xt_node *next = node->children->next;

		xt_free_node(node->children);
		node->children = next;
	}

	g_free(node);
}
Ejemplo n.º 3
0
Archivo: iq.c Proyecto: shiplu/bitlbee
xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid )
{
	struct xt_node *node, *query;
	struct jabber_buddy *bud;
	
	if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL )
	{
		/* Who cares about the unknown... */
		imcb_log( ic, "Couldn't find buddy: %s", bare_jid);
		return XT_HANDLED;
	}
	
	if( bud->features ) /* been here already */
		return XT_HANDLED;
	
	node = xt_new_node( "query", NULL, NULL );
	xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO );
	
	if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) )
	{
		imcb_log( ic, "WARNING: Couldn't generate feature query" );
		xt_free_node( node );
		return XT_HANDLED;
	}

	jabber_cache_add( ic, query, jabber_iq_parse_features );

	return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
}
Ejemplo n.º 4
0
Archivo: jabber.c Proyecto: AlD/bitlbee
static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
{
	struct jabber_data *jd = ic->proto_data;
	struct jabber_buddy *bud;
	struct xt_node *node;
	char *s;
	int st;
	
	if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
		return jabber_write( ic, message, strlen( message ) );
	
	if( g_strcasecmp( who, JABBER_OAUTH_HANDLE ) == 0 &&
	    !( jd->flags & OPT_LOGGED_IN ) && jd->fd == -1 )
	{
		if( sasl_oauth2_get_refresh_token( ic, message ) )
		{
			return 1;
		}
		else
		{
			imcb_error( ic, "OAuth failure" );
			imc_logout( ic, TRUE );
			return 0;
		}
	}
	
	if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) )
		bud = jabber_buddy_by_ext_jid( ic, who, 0 );
	else
		bud = jabber_buddy_by_jid( ic, who, GET_BUDDY_BARE_OK );
	
	node = xt_new_node( "body", message, NULL );
	node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node );
	
	if( bud && ( jd->flags & JFLAG_WANT_TYPING ) &&
	    ( ( bud->flags & JBFLAG_DOES_XEP85 ) ||
	     !( bud->flags & JBFLAG_PROBED_XEP85 ) ) )
	{
		struct xt_node *act;
		
		/* If the user likes typing notification and if we don't know
		   (and didn't probe before) if this resource supports XEP85,
		   include a probe in this packet now. Also, if we know this
		   buddy does support XEP85, we have to send this <active/>
		   tag to tell that the user stopped typing (well, that's what
		   we guess when s/he pressed Enter...). */
		act = xt_new_node( "active", NULL, NULL );
		xt_add_attr( act, "xmlns", XMLNS_CHATSTATES );
		xt_add_child( node, act );
		
		/* Just make sure we do this only once. */
		bud->flags |= JBFLAG_PROBED_XEP85;
	}
	
	st = jabber_write_packet( ic, node );
	xt_free_node( node );
	
	return st;
}
Ejemplo n.º 5
0
Archivo: iq.c Proyecto: shiplu/bitlbee
static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
{
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *reply, *query;
	xt_status st;
	char *s;
	
	if( !( query = xt_find_node( node->children, "query" ) ) )
	{
		imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" );
		imc_logout( ic, FALSE );
		return XT_HANDLED;
	}
	
	/* Time to authenticate ourselves! */
	reply = xt_new_node( "query", NULL, NULL );
	xt_add_attr( reply, "xmlns", XMLNS_AUTH );
	xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
	xt_add_child( reply, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
	
	if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
	{
		/* We can do digest authentication, it seems, and of
		   course we prefer that. */
		sha1_state_t sha;
		char hash_hex[41];
		unsigned char hash[20];
		int i;
		
		sha1_init( &sha );
		sha1_append( &sha, (unsigned char*) s, strlen( s ) );
		sha1_append( &sha, (unsigned char*) ic->acc->pass, strlen( ic->acc->pass ) );
		sha1_finish( &sha, hash );
		
		for( i = 0; i < 20; i ++ )
			sprintf( hash_hex + i * 2, "%02x", hash[i] );
		
		xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
	}
	else if( xt_find_node( query->children, "password" ) )
	{
		/* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
		xt_add_child( reply, xt_new_node( "password", ic->acc->pass, NULL ) );
	}
	else
	{
		xt_free_node( reply );
		
		imcb_error( ic, "Can't find suitable authentication method" );
		imc_logout( ic, FALSE );
		return XT_ABORT;
	}
	
	reply = jabber_make_packet( "iq", "set", NULL, reply );
	jabber_cache_add( ic, reply, jabber_finish_iq_auth );
	st = jabber_write_packet( ic, reply );
	
	return st ? XT_HANDLED : XT_ABORT;
}
Ejemplo n.º 6
0
int jabber_chat_topic(struct groupchat *c, char *topic)
{
	struct im_connection *ic = c->ic;
	struct jabber_chat *jc = c->data;
	struct xt_node *node;

	node = xt_new_node("subject", topic, NULL);
	node = jabber_make_packet("message", "groupchat", jc->name, node);

	if (!jabber_write_packet(ic, node)) {
		xt_free_node(node);
		return 0;
	}
	xt_free_node(node);

	return 1;
}
Ejemplo n.º 7
0
int jabber_chat_leave(struct groupchat *c, const char *reason)
{
	struct im_connection *ic = c->ic;
	struct jabber_chat *jc = c->data;
	struct xt_node *node;

	node = xt_new_node("x", NULL, NULL);
	xt_add_attr(node, "xmlns", XMLNS_MUC);
	node = jabber_make_packet("presence", "unavailable", jc->my_full_jid, node);

	if (!jabber_write_packet(ic, node)) {
		xt_free_node(node);
		return 0;
	}
	xt_free_node(node);

	return 1;
}
Ejemplo n.º 8
0
int jabber_chat_msg(struct groupchat *c, char *message, int flags)
{
	struct im_connection *ic = c->ic;
	struct jabber_chat *jc = c->data;
	struct xt_node *node;

	jc->flags |= JCFLAG_MESSAGE_SENT;

	node = xt_new_node("body", message, NULL);
	node = jabber_make_packet("message", "groupchat", jc->name, node);

	if (!jabber_write_packet(ic, node)) {
		xt_free_node(node);
		return 0;
	}
	xt_free_node(node);

	return 1;
}
Ejemplo n.º 9
0
/* file_transfer canceled() callback */
void jabber_si_canceled(file_transfer_t *ft, char *reason)
{
	struct jabber_transfer *tf = ft->data;
	struct xt_node *reply, *iqnode;

	if (tf->accepted) {
		return;
	}

	iqnode = jabber_make_packet("iq", "error", tf->ini_jid, NULL);
	xt_add_attr(iqnode, "id", tf->iq_id);
	reply = jabber_make_error_packet(iqnode, "forbidden", "cancel", "403");
	xt_free_node(iqnode);

	if (!jabber_write_packet(tf->ic, reply)) {
		imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
	}
	xt_free_node(reply);

}
Ejemplo n.º 10
0
static storage_status_t xml_save( irc_t *irc, int overwrite )
{
	storage_status_t ret = STORAGE_OK;
	char path[512], *path2 = NULL, *xml = NULL;
	struct xt_node *tree = NULL;
	size_t len;
	int fd;
	
	path2 = g_strdup( irc->user->nick );
	nick_lc( NULL, path2 );
	g_snprintf( path, sizeof( path ) - 20, "%s%s%s", global.conf->configdir, path2, ".xml" );
	g_free( path2 );
	
	if( !overwrite && g_access( path, F_OK ) == 0 )
		return STORAGE_ALREADY_EXISTS;
	
	strcat( path, ".XXXXXX" );
	if( ( fd = mkstemp( path ) ) < 0 )
	{
		irc_rootmsg( irc, "Error while opening configuration file." );
		return STORAGE_OTHER_ERROR;
	}
	
	tree = xml_generate( irc );
	xml = xt_to_string_i( tree );
	len = strlen( xml );
	if( write( fd, xml, len ) != len ||
	    fsync( fd ) != 0 || /* #559 */
	    close( fd ) != 0 )
		goto error;
	
	path2 = g_strndup( path, strlen( path ) - 7 );
	if( rename( path, path2 ) != 0 )
	{
		g_free( path2 );
		goto error;
	}
	g_free( path2 );
	
	goto finish;

error:
	irc_rootmsg( irc, "Write error. Disk full?" );
	ret = STORAGE_OTHER_ERROR;

finish:	
	close( fd );
	unlink( path );
	g_free( xml );
	xt_free_node( tree );
	
	return ret;
}
Ejemplo n.º 11
0
void xt_free(struct xt_parser *xt)
{
	if (!xt) {
		return;
	}

	if (xt->root) {
		xt_free_node(xt->root);
	}

	g_markup_parse_context_free(xt->parser);

	g_free(xt);
}
Ejemplo n.º 12
0
/* Reset the parser, flush everything we have so far. For example, we need
   this for XMPP when doing TLS/SASL to restart the stream. */
void xt_reset(struct xt_parser *xt)
{
	if (xt->parser) {
		g_markup_parse_context_free(xt->parser);
	}

	xt->parser = g_markup_parse_context_new(&xt_parser_funcs, 0, xt, NULL);

	if (xt->root) {
		xt_free_node(xt->root);
		xt->root = NULL;
		xt->cur = NULL;
	}
}
Ejemplo n.º 13
0
/* Send a subscribe/unsubscribe request to a buddy. */
int presence_send_request( struct im_connection *ic, char *handle, char *request )
{
	struct xt_node *node;
	int st;
	
	node = jabber_make_packet( "presence", NULL, NULL, NULL );
	xt_add_attr( node, "to", handle );
	xt_add_attr( node, "type", request );
	
	st = jabber_write_packet( ic, node );
	
	xt_free_node( node );
	return st;
}
Ejemplo n.º 14
0
static int jabber_send_typing(struct im_connection *ic, char *who, int typing)
{
	struct jabber_data *jd = ic->proto_data;
	struct jabber_buddy *bud, *bare;

	/* Enable typing notification related code from now. */
	jd->flags |= JFLAG_WANT_TYPING;

	if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL ||
	    (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) {
		/* Sending typing notifications to unknown buddies is
		   unsupported for now. Shouldn't be a problem, I think. */
		return 0;
	}


	if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) {
		/* We're only allowed to send this stuff if we know the other
		   side supports it. If the bare JID has the flag, all other
		   resources get it, too (That is the case in gtalk) */

		struct xt_node *node;
		char *type;
		int st;

		if (typing & OPT_TYPING) {
			type = "composing";
		} else if (typing & OPT_THINKING) {
			type = "paused";
		} else {
			type = "active";
		}

		node = xt_new_node(type, NULL, NULL);
		xt_add_attr(node, "xmlns", XMLNS_CHATSTATES);
		node = jabber_make_packet("message", "chat", bud->full_jid, node);

		st = jabber_write_packet(ic, node);
		xt_free_node(node);

		return st;
	}

	return 1;
}
Ejemplo n.º 15
0
Archivo: iq.c Proyecto: shiplu/bitlbee
xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns )
{
	struct xt_node *node, *query;
	struct jabber_data *jd = ic->proto_data;
	
	node = xt_new_node( "query", NULL, NULL );
	xt_add_attr( node, "xmlns", xmlns );
	
	if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) )
	{
		imcb_log( ic, "WARNING: Couldn't generate server query" );
		xt_free_node( node );
	}

	jd->have_streamhosts--;
	jabber_cache_add( ic, query, jabber_iq_parse_server_features );

	return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
}
Ejemplo n.º 16
0
void jabber_chat_invite(struct groupchat *c, char *who, char *message)
{
	struct xt_node *node;
	struct im_connection *ic = c->ic;
	struct jabber_chat *jc = c->data;

	node = xt_new_node("reason", message, NULL);

	node = xt_new_node("invite", NULL, node);
	xt_add_attr(node, "to", who);

	node = xt_new_node("x", NULL, node);
	xt_add_attr(node, "xmlns", XMLNS_MUC_USER);

	node = jabber_make_packet("message", NULL, jc->name, node);

	jabber_write_packet(ic, node);

	xt_free_node(node);
}
Ejemplo n.º 17
0
Archivo: iq.c Proyecto: shiplu/bitlbee
int jabber_remove_from_roster( struct im_connection *ic, char *handle )
{
	struct xt_node *node;
	int st;
	
	/* Build the item entry */
	node = xt_new_node( "item", NULL, NULL );
	xt_add_attr( node, "jid", handle );
	xt_add_attr( node, "subscription", "remove" );
	
	/* And pack it into a roster-add packet */
	node = xt_new_node( "query", NULL, node );
	xt_add_attr( node, "xmlns", XMLNS_ROSTER );
	node = jabber_make_packet( "iq", "set", NULL, node );
	
	st = jabber_write_packet( ic, node );
	
	xt_free_node( node );
	return st;
}
Ejemplo n.º 18
0
/* Whenever presence information is updated, call this function to inform the
   server. */
int presence_send_update( struct im_connection *ic )
{
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *node, *cap;
	char *show = jd->away_state->code;
	char *status = jd->away_message;
	struct groupchat *c;
	int st;
	
	node = jabber_make_packet( "presence", NULL, NULL, NULL );
	xt_add_child( node, xt_new_node( "priority", set_getstr( &ic->acc->set, "priority" ), NULL ) );
	if( show && *show )
		xt_add_child( node, xt_new_node( "show", show, NULL ) );
	if( status )
		xt_add_child( node, xt_new_node( "status", status, NULL ) );
	
	/* This makes the packet slightly bigger, but clients interested in
	   capabilities can now cache the discovery info. This reduces the
	   usual post-login iq-flood. See XEP-0115. At least libpurple and
	   Trillian seem to do this right. */
	cap = xt_new_node( "c", NULL, NULL );
	xt_add_attr( cap, "xmlns", XMLNS_CAPS );
	xt_add_attr( cap, "node", "http://bitlbee.org/xmpp/caps" );
	xt_add_attr( cap, "ver", BITLBEE_VERSION ); /* The XEP wants this hashed, but nobody's doing that. */
	xt_add_child( node, cap );
	
	st = jabber_write_packet( ic, node );
	
	/* Have to send this update to all groupchats too, the server won't
	   do this automatically. */
	for( c = ic->groupchats; c && st; c = c->next )
	{
		struct jabber_chat *jc = c->data;
		
		xt_add_attr( node, "to", jc->my_full_jid );
		st = jabber_write_packet( ic, node );
	}
	
	xt_free_node( node );
	return st;
}
Ejemplo n.º 19
0
void jabber_end_stream( struct im_connection *ic )
{
	struct jabber_data *jd = ic->proto_data;
	
	/* Let's only do this if the queue is currently empty, otherwise it'd
	   take too long anyway. */
	if( jd->tx_len == 0 )
	{
		char eos[] = "</stream:stream>";
		struct xt_node *node;
		int st = 1;
		
		if( ic->flags & OPT_LOGGED_IN )
		{
			node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
			st = jabber_write_packet( ic, node );
			xt_free_node( node );
		}
		
		if( st )
			jabber_write( ic, eos, strlen( eos ) );
	}
}
Ejemplo n.º 20
0
static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *c, *reply;
	int trytls;
	
	trytls = g_strcasecmp( set_getstr( &ic->acc->set, "tls" ), "try" ) == 0;
	c = xt_find_node( node->children, "starttls" );
	if( c && !jd->ssl )
	{
		/* If the server advertises the STARTTLS feature and if we're
		   not in a secure connection already: */
		
		c = xt_find_node( c->children, "required" );
		
		if( c && ( !trytls && !set_getbool( &ic->acc->set, "tls" ) ) )
		{
			imcb_error( ic, "Server requires TLS connections, but TLS is turned off for this account" );
			imc_logout( ic, FALSE );
			
			return XT_ABORT;
		}
		
		/* Only run this if the tls setting is set to true or try: */
		if( ( trytls || set_getbool( &ic->acc->set, "tls" ) ) )
		{
			reply = xt_new_node( "starttls", NULL, NULL );
			xt_add_attr( reply, "xmlns", XMLNS_TLS );
			if( !jabber_write_packet( ic, reply ) )
			{
				xt_free_node( reply );
				return XT_ABORT;
			}
			xt_free_node( reply );
			
			return XT_HANDLED;
		}
	}
	else if( !c && !jd->ssl )
	{
		/* If the server does not advertise the STARTTLS feature and
		   we're not in a secure connection already: (Servers have a
		   habit of not advertising <starttls/> anymore when already
		   using SSL/TLS. */
		
		if( !trytls && set_getbool( &ic->acc->set, "tls" ) )
		{
			imcb_error( ic, "TLS is turned on for this account, but is not supported by this server" );
			imc_logout( ic, FALSE );
			
			return XT_ABORT;
		}
	}
	
	/* This one used to be in jabber_handlers[], but it has to be done
	   from here to make sure the TLS session will be initialized
	   properly before we attempt SASL authentication. */
	if( ( c = xt_find_node( node->children, "mechanisms" ) ) )
	{
		if( sasl_pkt_mechanisms( c, data ) == XT_ABORT )
			return XT_ABORT;
	}
	/* If the server *SEEMS* to support SASL authentication but doesn't
	   support it after all, we should try to do authentication the
	   other way. jabber.com doesn't seem to do SASL while it pretends
	   to be XMPP 1.0 compliant! */
	else if( !( jd->flags & JFLAG_AUTHENTICATED ) && sasl_supported( ic ) )
	{
		if( !jabber_init_iq_auth( ic ) )
			return XT_ABORT;
	}
	
	if( ( c = xt_find_node( node->children, "bind" ) ) )
	{
		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
		xt_add_attr( reply, "xmlns", XMLNS_BIND );
		reply = jabber_make_packet( "iq", "set", NULL, reply );
		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
		
		if( !jabber_write_packet( ic, reply ) )
			return XT_ABORT;
		
		jd->flags |= JFLAG_WAIT_BIND;
	}
	
	if( ( c = xt_find_node( node->children, "session" ) ) )
	{
		reply = xt_new_node( "session", NULL, NULL );
		xt_add_attr( reply, "xmlns", XMLNS_SESSION );
		reply = jabber_make_packet( "iq", "set", NULL, reply );
		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
		
		if( !jabber_write_packet( ic, reply ) )
			return XT_ABORT;
		
		jd->flags |= JFLAG_WAIT_SESSION;
	}
	
	/* This flag is already set if we authenticated via SASL, so now
	   we can resume the session in the new stream, if we don't have
	   to bind/initialize the session. */
	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
	{
		if( !jabber_get_roster( ic ) )
			return XT_ABORT;
	}
	
	return XT_HANDLED;
}
Ejemplo n.º 21
0
Archivo: sasl.c Proyecto: mrdon/bitlbee
xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_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;
	GString *mechs;
	
	if( !sasl_supported( ic ) )
	{
		/* Should abort this now, since we should already be doing
		   IQ authentication. Strange things happen when you try
		   to do both... */
		imcb_log( ic, "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!" );
		return XT_HANDLED;
	}
	
	s = xt_find_attr( node, "xmlns" );
	if( !s || strcmp( s, XMLNS_SASL ) != 0 )
	{
		imcb_log( ic, "Stream error while authenticating" );
		imc_logout( ic, FALSE );
		return XT_ABORT;
	}
	
	want_oauth = set_getbool( &ic->acc->set, "oauth" );
	
	mechs = g_string_new( "" );
	c = node->children;
	while( ( c = xt_find_node( c, "mechanism" ) ) )
	{
		if( c->text && g_strcasecmp( c->text, "PLAIN" ) == 0 )
			sup_plain = 1;
		else if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 )
			sup_digest = 1;
		else if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 )
			sup_gtalk = 1;
		else if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 )
			sup_fb = 1;
		
		if( c->text )
			g_string_append_printf( mechs, " %s", c->text );
		
		c = c->next;
	}
	
	if( !want_oauth && !sup_plain && !sup_digest )
	{
		if( !sup_gtalk && !sup_fb )
			imcb_error( ic, "This server requires OAuth "
			                "(supported schemes:%s)", mechs->str );
		else
			imcb_error( ic, "BitlBee does not support any of the offered SASL "
			                "authentication schemes:%s", mechs->str );
		imc_logout( ic, FALSE );
		g_string_free( mechs, TRUE );
		return XT_ABORT;
	}
	g_string_free( mechs, TRUE );
	
	reply = xt_new_node( "auth", NULL, NULL );
	xt_add_attr( reply, "xmlns", XMLNS_SASL );
	
	if( sup_gtalk && want_oauth )
	{
		int len;
		
		/* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
		   It's currently used by GTalk and vaguely documented on
		   http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
		xt_add_attr( reply, "mechanism", "X-OAUTH2" );
		
		len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2;
		s = g_malloc( len + 1 );
		s[0] = 0;
		strcpy( s + 1, jd->username );
		strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token );
		reply->text = base64_encode( (unsigned char *)s, len );
		reply->text_len = strlen( reply->text );
		g_free( s );
	}
	else if( sup_fb && want_oauth )
	{
		xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" );
		jd->flags |= JFLAG_SASL_FB;
	}
	else if( want_oauth )
	{
		imcb_error( ic, "OAuth requested, but not supported by server" );
		imc_logout( ic, FALSE );
		xt_free_node( reply );
		return XT_ABORT;
	}
	else if( sup_digest )
	{
		xt_add_attr( reply, "mechanism", "DIGEST-MD5" );
		
		/* The rest will be done later, when we receive a <challenge/>. */
	}
	else if( sup_plain )
	{
		int len;
		
		xt_add_attr( reply, "mechanism", "PLAIN" );
		
		/* 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 );
		reply->text = base64_encode( (unsigned char *)s, len );
		reply->text_len = strlen( reply->text );
		g_free( s );
	}
	
	if( reply && !jabber_write_packet( ic, reply ) )
	{
		xt_free_node( reply );
		return XT_ABORT;
	}
	xt_free_node( reply );
	
	/* To prevent classic authentication from happening. */
	jd->flags |= JFLAG_STREAM_STARTED;
	
	return XT_HANDLED;
}
Ejemplo n.º 22
0
Archivo: sasl.c Proyecto: mrdon/bitlbee
xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *reply_pkt = NULL;
	char *nonce = NULL, *realm = NULL, *cnonce = NULL;
	unsigned char cnonce_bin[30];
	char *digest_uri = NULL;
	char *dec = NULL;
	char *s = NULL, *reply = NULL;
	xt_status ret = XT_ABORT;
	
	if( node->text_len == 0 )
		goto error;
	
	dec = frombase64( node->text );
	
	if( jd->flags & JFLAG_SASL_FB )
	{
		/* New-style Facebook OAauth2 support. Instead of sending a refresh
		   token, they just send an access token that should never expire. */
		GSList *p_in = NULL, *p_out = NULL;
		char time[33];
		
		oauth_params_parse( &p_in, dec );
		oauth_params_add( &p_out, "nonce", oauth_params_get( &p_in, "nonce" ) );
		oauth_params_add( &p_out, "method", oauth_params_get( &p_in, "method" ) );
		oauth_params_free( &p_in );
		
		g_snprintf( time, sizeof( time ), "%lld", (long long) ( gettime() * 1000 ) );
		oauth_params_add( &p_out, "call_id", time );
		oauth_params_add( &p_out, "api_key", oauth2_service_facebook.consumer_key );
		oauth_params_add( &p_out, "v", "1.0" );
		oauth_params_add( &p_out, "format", "XML" );
		oauth_params_add( &p_out, "access_token", jd->oauth2_access_token );
		
		reply = oauth_params_string( p_out );
		oauth_params_free( &p_out );
	}
	else if( !( s = sasl_get_part( dec, "rspauth" ) ) )
	{
		/* See RFC 2831 for for information. */
		md5_state_t A1, A2, H;
		md5_byte_t A1r[16], A2r[16], Hr[16];
		char A1h[33], A2h[33], Hh[33];
		int i;
		
		nonce = sasl_get_part( dec, "nonce" );
		realm = sasl_get_part( dec, "realm" );
		
		if( !nonce )
			goto error;
		
		/* Jabber.Org considers the realm part optional and doesn't
		   specify one. Oh well, actually they're right, but still,
		   don't know if this is right... */
		if( !realm )
			realm = g_strdup( jd->server );
		
		random_bytes( cnonce_bin, sizeof( cnonce_bin ) );
		cnonce = base64_encode( cnonce_bin, sizeof( cnonce_bin ) );
		digest_uri = g_strdup_printf( "%s/%s", "xmpp", jd->server );
		
		/* Generate the MD5 hash of username:realm:password,
		   I decided to call it H. */
		md5_init( &H );
		s = g_strdup_printf( "%s:%s:%s", jd->username, realm, ic->acc->pass );
		md5_append( &H, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &H, Hr );
		
		/* Now generate the hex. MD5 hash of H:nonce:cnonce, called A1. */
		md5_init( &A1 );
		s = g_strdup_printf( ":%s:%s", nonce, cnonce );
		md5_append( &A1, Hr, 16 );
		md5_append( &A1, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &A1, A1r );
		for( i = 0; i < 16; i ++ )
			sprintf( A1h + i * 2, "%02x", A1r[i] );
		
		/* A2... */
		md5_init( &A2 );
		s = g_strdup_printf( "%s:%s", "AUTHENTICATE", digest_uri );
		md5_append( &A2, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &A2, A2r );
		for( i = 0; i < 16; i ++ )
			sprintf( A2h + i * 2, "%02x", A2r[i] );
		
		/* Final result: A1:nonce:00000001:cnonce:auth:A2. Let's reuse H for it. */
		md5_init( &H );
		s = g_strdup_printf( "%s:%s:%s:%s:%s:%s", A1h, nonce, "00000001", cnonce, "auth", A2h );
		md5_append( &H, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &H, Hr );
		for( i = 0; i < 16; i ++ )
			sprintf( Hh + i * 2, "%02x", Hr[i] );
		
		/* Now build the SASL response string: */
		reply = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\","
		                         "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s",
		                         jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" );
	}
	else
	{
		/* We found rspauth, but don't really care... */
		g_free( s );
	}
	
	s = reply ? tobase64( reply ) : NULL;
	reply_pkt = xt_new_node( "response", s, NULL );
	xt_add_attr( reply_pkt, "xmlns", XMLNS_SASL );
	
	if( !jabber_write_packet( ic, reply_pkt ) )
		goto silent_error;
	
	ret = XT_HANDLED;
	goto silent_error;

error:
	imcb_error( ic, "Incorrect SASL challenge received" );
	imc_logout( ic, FALSE );

silent_error:
	g_free( digest_uri );
	g_free( cnonce );
	g_free( nonce );
	g_free( reply );
	g_free( realm );
	g_free( dec );
	g_free( s );
	xt_free_node( reply_pkt );
	
	return ret;
}
Ejemplo n.º 23
0
xt_status jabber_pkt_message( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	char *from = xt_find_attr( node, "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;
	struct xt_node *request = xt_find_node( node->children, "request" );
	struct jabber_buddy *bud = NULL;
	char *s, *room = NULL, *reason = NULL;
	
	if( !from )
		return XT_HANDLED; /* Consider this packet corrupted. */

	if( request && id )
	{
		/* Send a message receipt (XEP-0184), looking like this:
		 * <message
		 *  from='[email protected]/throne'
		 *  id='bi29sg183b4v'
		 *  to='[email protected]/westminster'>
		 *  <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/>
		 * </message> */
		struct xt_node *received, *receipt;
		
		received = xt_new_node( "received", NULL, NULL );
		xt_add_attr( received, "xmlns", XMLNS_RECEIPTS );
		xt_add_attr( received, "id", id );
		receipt = jabber_make_packet( "message", NULL, from, received );

		jabber_write_packet( ic, receipt );
		xt_free_node( receipt );
	}
	
	bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT );
	
	if( type && strcmp( type, "error" ) == 0 )
	{
		/* Handle type=error packet. */
	}
	else if( type && from && strcmp( type, "groupchat" ) == 0 )
	{
		jabber_chat_pkt_message( ic, bud, node );
	}
	else /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
	{
		GString *fullmsg = g_string_new( "" );

		for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
		{
			char *ns = xt_find_attr( c, "xmlns" );
			struct xt_node *inv;
			
			if( ns && strcmp( ns, XMLNS_MUC_USER ) == 0 &&
			    ( inv = xt_find_node( c->children, "invite" ) ) )
			{
				/* This is an invitation. Set some vars which
				   will be passed to imcb_chat_invite() below. */
				room = from;
				if( ( from = xt_find_attr( inv, "from" ) ) == NULL )
					from = room;
				if( ( inv = xt_find_node( inv->children, "reason" ) ) && inv->text_len > 0 )
					reason = inv->text;
			}
		}
		
		if( ( s = strchr( from, '/' ) ) )
		{
			if( bud )
			{
				bud->last_msg = time( NULL );
				from = bud->ext_jid ? bud->ext_jid : bud->bare_jid;
			}
			else
				*s = 0; /* We need to generate a bare JID now. */
		}
		
		if( type && strcmp( type, "headline" ) == 0 )
		{
			if( ( c = xt_find_node( node->children, "subject" ) ) && c->text_len > 0 )
				g_string_append_printf( fullmsg, "Headline: %s\n", c->text );
			
			/* <x xmlns="jabber:x:oob"><url>http://....</url></x> can contain a URL, it seems. */
			for( c = node->children; c; c = c->next )
			{
				struct xt_node *url;
				
				if( ( url = xt_find_node( c->children, "url" ) ) && url->text_len > 0 )
					g_string_append_printf( fullmsg, "URL: %s\n", url->text );
			}
		}
		else if( ( c = xt_find_node( node->children, "subject" ) ) && c->text_len > 0 &&
		         ( !bud || !( bud->flags & JBFLAG_HIDE_SUBJECT ) ) )
		{
			g_string_append_printf( fullmsg, "<< \002BitlBee\002 - Message with subject: %s >>\n", c->text );
			if( bud )
				bud->flags |= JBFLAG_HIDE_SUBJECT;
		}
		else if( bud && !c )
		{
			/* Yeah, possibly we're hiding changes to this field now. But nobody uses
			   this for anything useful anyway, except GMail when people reply to an
			   e-mail via chat, repeating the same subject all the time. I don't want
			   to have to remember full subject strings for everyone. */
			bud->flags &= ~JBFLAG_HIDE_SUBJECT;
		}
		
		if( body && body->text_len > 0 ) /* Could be just a typing notification. */
			fullmsg = g_string_append( fullmsg, body->text );
		
		if( fullmsg->len > 0 )
			imcb_buddy_msg( ic, from, fullmsg->str,
			                0, jabber_get_timestamp( node ) );
		if( room )
			imcb_chat_invite( ic, room, from, reason );
		
		g_string_free( fullmsg, TRUE );
		
		/* Handling of incoming typing notifications. */
		if( bud == NULL )
		{
			/* Can't handle these for unknown buddies. */
		}
		else if( xt_find_node( node->children, "composing" ) )
		{
			bud->flags |= JBFLAG_DOES_XEP85;
			imcb_buddy_typing( ic, from, OPT_TYPING );
		}
		/* No need to send a "stopped typing" signal when there's a message. */
		else if( xt_find_node( node->children, "active" ) && ( body == NULL ) )
		{
			bud->flags |= JBFLAG_DOES_XEP85;
			imcb_buddy_typing( ic, from, 0 );
		}
		else if( xt_find_node( node->children, "paused" ) )
		{
			bud->flags |= JBFLAG_DOES_XEP85;
			imcb_buddy_typing( ic, from, OPT_THINKING );
		}
		
		if( s )
			*s = '/'; /* And convert it back to a full JID. */
	}
	
	return XT_HANDLED;
}
Ejemplo n.º 24
0
Archivo: iq.c Proyecto: shiplu/bitlbee
xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *c, *reply = NULL;
	char *type, *s;
	int st, pack = 1;
	
	type = xt_find_attr( node, "type" );
	
	if( !type )
	{
		imcb_error( ic, "Received IQ packet without type." );
		imc_logout( ic, TRUE );
		return XT_ABORT;
	}
	
	if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 )
	{
		return jabber_cache_handle_packet( ic, node );
	}
	else if( strcmp( type, "get" ) == 0 )
	{
		if( !( ( c = xt_find_node( node->children, "query" ) ) ||
		       ( c = xt_find_node( node->children, "ping" ) ) ||
		       ( c = xt_find_node( node->children, "time" ) ) ) ||
		    !( s = xt_find_attr( c, "xmlns" ) ) )
		{
			/* Sigh. Who decided to suddenly invent new elements
			   instead of just sticking with <query/>? */
			return XT_HANDLED;
		}
		
		reply = xt_new_node( "query", NULL, NULL );
		xt_add_attr( reply, "xmlns", s );
		
		/* Of course this is a very essential query to support. ;-) */
		if( strcmp( s, XMLNS_VERSION ) == 0 )
		{
			xt_add_child( reply, xt_new_node( "name", set_getstr( &ic->acc->set, "user_agent" ), NULL ) );
			xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
			xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
		}
		else if( strcmp( s, XMLNS_TIME_OLD ) == 0 )
		{
			time_t time_ep;
			char buf[1024];
			
			buf[sizeof(buf)-1] = 0;
			time_ep = time( NULL );
			
			strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
			xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
			
			strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
			xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
		}
		else if( strcmp( s, XMLNS_TIME ) == 0 )
		{
			time_t time_ep;
			char buf[1024];
			
			buf[sizeof(buf)-1] = 0;
			time_ep = time( NULL );
			
			xt_free_node( reply );
			reply = xt_new_node( "time", NULL, NULL );
			xt_add_attr( reply, "xmlns", XMLNS_TIME );
			
			strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%SZ", gmtime( &time_ep ) );
			xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
			
			strftime( buf, sizeof( buf ) - 1, "%z", localtime( &time_ep ) );
			if( strlen( buf ) >= 5 )
			{
				buf[6] = '\0';
				buf[5] = buf[4];
				buf[4] = buf[3];
				buf[3] = ':';
			}
			xt_add_child( reply, xt_new_node( "tzo", buf, NULL ) );
		}
		else if( strcmp( s, XMLNS_PING ) == 0 )
		{
			xt_free_node( reply );
			reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), NULL );
			if( ( s = xt_find_attr( node, "id" ) ) )
				xt_add_attr( reply, "id", s );
			pack = 0;
		}
		else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 )
		{
			const char *features[] = { XMLNS_DISCO_INFO,
			                           XMLNS_VERSION,
			                           XMLNS_TIME_OLD,
			                           XMLNS_TIME,
			                           XMLNS_CHATSTATES,
			                           XMLNS_MUC,
			                           XMLNS_PING,
			                           XMLNS_SI,
			                           XMLNS_BYTESTREAMS,
			                           XMLNS_FILETRANSFER,
			                           NULL };
			const char **f;
			
			c = xt_new_node( "identity", NULL, NULL );
			xt_add_attr( c, "category", "client" );
			xt_add_attr( c, "type", "pc" );
			xt_add_attr( c, "name", set_getstr( &ic->acc->set, "user_agent" ) );
			xt_add_child( reply, c );
			
			for( f = features; *f; f ++ )
			{
				c = xt_new_node( "feature", NULL, NULL );
				xt_add_attr( c, "var", *f );
				xt_add_child( reply, c );
			}
		}
		else
		{
			xt_free_node( reply );
			reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
			pack = 0;
		}
	}
	else if( strcmp( type, "set" ) == 0 )
	{
		if( ( c = xt_find_node( node->children, "si" ) ) &&
		    ( s = xt_find_attr( c, "xmlns" ) ) &&
		    ( strcmp( s, XMLNS_SI ) == 0 ) )
		{
			return jabber_si_handle_request( ic, node, c );
		}
		else if( !( c = xt_find_node( node->children, "query" ) ) ||
		         !( s = xt_find_attr( c, "xmlns" ) ) )
		{
			return XT_HANDLED;
		}
		else if( strcmp( s, XMLNS_ROSTER ) == 0 )
		{
		/* This is a roster push. XMPP servers send this when someone
		   was added to (or removed from) the buddy list. AFAIK they're
		   sent even if we added this buddy in our own session. */
			int bare_len = strlen( jd->me );
			
			if( ( s = xt_find_attr( node, "from" ) ) == NULL ||
			    ( strncmp( s, jd->me, bare_len ) == 0 &&
			      ( s[bare_len] == 0 || s[bare_len] == '/' ) ) )
			{
				jabber_parse_roster( ic, node, NULL );
				
				/* Should we generate a reply here? Don't think it's
				   very important... */
			}
			else
			{
				imcb_log( ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)" );
				
				xt_free_node( reply );
				reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL );
				pack = 0;
			}
		}
		else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 )
		{
			/* Bytestream Request (stage 2 of file transfer) */
			return jabber_bs_recv_request( ic, node, c );
		}
		else
		{
			xt_free_node( reply );
			reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
			pack = 0;
		}
	}
	
	/* If we recognized the xmlns and managed to generate a reply,
	   finish and send it. */
	if( reply )
	{
		/* Normally we still have to pack it into an iq-result
		   packet, but for errors, for example, we don't. */
		if( pack )
		{
			reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
			if( ( s = xt_find_attr( node, "id" ) ) )
				xt_add_attr( reply, "id", s );
		}
		
		st = jabber_write_packet( ic, reply );
		xt_free_node( reply );
		if( !st )
			return XT_ABORT;
	}
	
	return XT_HANDLED;
}
Ejemplo n.º 25
0
static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent)
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	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;
	struct xt_node *request = xt_find_node(node->children, "request");
	struct jabber_buddy *bud = NULL;
	char *s, *room = NULL, *reason = NULL;

	if (!from) {
		return XT_HANDLED; /* Consider this packet corrupted. */
	}

	/* try to detect hipchat's own version of self-messages */
	if (jd->flags & JFLAG_HIPCHAT) {
		struct xt_node *c;

		if ((c = xt_find_node_by_attr(node->children, "delay", "xmlns", XMLNS_DELAY)) &&
		    (s = xt_find_attr(c, "from_jid")) &&
		    jabber_compare_jid(s, jd->me)) {
			carbons_sent = TRUE;
		}
	}

	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'/>
		 * </message>
		 *
		 * MUC messages are excluded, since receipts aren't supposed to be sent over MUCs
		 * (XEP-0184 section 5.3) and replying to those may result in 'forbidden' errors.
		 */
		struct xt_node *received, *receipt;

		received = xt_new_node("received", NULL, NULL);
		xt_add_attr(received, "xmlns", XMLNS_RECEIPTS);
		xt_add_attr(received, "id", id);
		receipt = jabber_make_packet("message", NULL, from, received);

		jabber_write_packet(ic, receipt);
		xt_free_node(receipt);
	}

	bud = jabber_buddy_by_jid(ic, from, GET_BUDDY_EXACT);

	if (type && strcmp(type, "error") == 0) {
		/* Handle type=error packet. */
	} else if (type && from && strcmp(type, "groupchat") == 0) {
		jabber_chat_pkt_message(ic, bud, node);
	} else { /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
		GString *fullmsg = g_string_new("");

		for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) {
			char *ns = xt_find_attr(c, "xmlns");
			struct xt_node *inv;

			if (ns && strcmp(ns, XMLNS_MUC_USER) == 0 &&
			    (inv = xt_find_node(c->children, "invite"))) {
				/* This is an invitation. Set some vars which
				   will be passed to imcb_chat_invite() below. */
				room = from;
				if ((from = xt_find_attr(inv, "from")) == NULL) {
					from = room;
				}
				if ((inv = xt_find_node(inv->children, "reason")) && inv->text_len > 0) {
					reason = inv->text;
				}
			}
		}

		if ((s = strchr(from, '/'))) {
			if (bud) {
				bud->last_msg = time(NULL);
				from = bud->ext_jid ? bud->ext_jid : bud->bare_jid;
			} else {
				*s = 0; /* We need to generate a bare JID now. */
			}
		}

		if (type && strcmp(type, "headline") == 0) {
			if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0) {
				g_string_append_printf(fullmsg, "Headline: %s\n", c->text);
			}

			/* <x xmlns="jabber:x:oob"><url>http://....</url></x> can contain a URL, it seems. */
			for (c = node->children; c; c = c->next) {
				struct xt_node *url;

				if ((url = xt_find_node(c->children, "url")) && url->text_len > 0) {
					g_string_append_printf(fullmsg, "URL: %s\n", url->text);
				}
			}
		} else if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0 &&
		           (!bud || !(bud->flags & JBFLAG_HIDE_SUBJECT))) {
			g_string_append_printf(fullmsg, "<< \002BitlBee\002 - Message with subject: %s >>\n", c->text);
			if (bud) {
				bud->flags |= JBFLAG_HIDE_SUBJECT;
			}
		} else if (bud && !c) {
			/* Yeah, possibly we're hiding changes to this field now. But nobody uses
			   this for anything useful anyway, except GMail when people reply to an
			   e-mail via chat, repeating the same subject all the time. I don't want
			   to have to remember full subject strings for everyone. */
			bud->flags &= ~JBFLAG_HIDE_SUBJECT;
		}

		if (body && body->text_len > 0) { /* Could be just a typing notification. */
			fullmsg = g_string_append(fullmsg, body->text);
		}

		if (fullmsg->len > 0) {
			imcb_buddy_msg(ic, from, fullmsg->str,
			               carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node));
		}
		if (room) {
			imcb_chat_invite(ic, room, from, reason);
		}

		g_string_free(fullmsg, TRUE);

		/* Handling of incoming typing notifications. */
		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);
		}
		else if (xt_find_node(node->children, "active")) {
			bud->flags |= JBFLAG_DOES_XEP85;

			/* No need to send a "stopped typing" signal when there's a message. */
			if (body == NULL) {
				imcb_buddy_typing(ic, from, 0);
			}
		} else if (xt_find_node(node->children, "paused")) {
			bud->flags |= JBFLAG_DOES_XEP85;
			imcb_buddy_typing(ic, from, OPT_THINKING);
		}

		if (s) {
			*s = '/'; /* And convert it back to a full JID. */
		}
	}

	return XT_HANDLED;
}