示例#1
0
文件: iq.c 项目: 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;
}
示例#2
0
文件: jabber.c 项目: 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;
}
示例#3
0
static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data )
{
	struct xt_node *node = xt_new_node( "buddy", NULL, NULL );
	xt_add_attr( node, "handle", key );
	xt_add_attr( node, "nick", value );
	xt_add_child( (struct xt_node *) data, node );
	
	return FALSE;
}
示例#4
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;
}
示例#5
0
static void xml_generate_settings( struct xt_node *cur, set_t **head )
{
	set_t *set;
	
	for( set = *head; set; set = set->next )
		if( set->value && !( set->flags & SET_NOSAVE ) )
		{
			struct xt_node *xset;
			xt_add_child( cur, xset = xt_new_node( "setting", set->value, NULL ) );
			xt_add_attr( xset, "name", set->key );
		}
}
示例#6
0
struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password)
{
	struct jabber_chat *jc;
	struct xt_node *node;
	struct groupchat *c;
	char *roomjid;

	roomjid = g_strdup_printf("%s/%s", room, nick);
	node = xt_new_node("x", NULL, NULL);
	xt_add_attr(node, "xmlns", XMLNS_MUC);
	if (password) {
		xt_add_child(node, xt_new_node("password", password, NULL));
	}
	node = jabber_make_packet("presence", NULL, roomjid, node);
	jabber_cache_add(ic, node, jabber_chat_join_failed);

	if (!jabber_write_packet(ic, node)) {
		g_free(roomjid);
		return NULL;
	}

	jc = g_new0(struct jabber_chat, 1);
	jc->name = jabber_normalize(room);

	if ((jc->me = jabber_buddy_add(ic, roomjid)) == NULL) {
		g_free(roomjid);
		g_free(jc->name);
		g_free(jc);
		return NULL;
	}

	/* roomjid isn't normalized yet, and we need an original version
	   of the nick to send a proper presence update. */
	jc->my_full_jid = roomjid;

	c = imcb_chat_new(ic, room);
	c->data = jc;

	return c;
}
示例#7
0
文件: iq.c 项目: shiplu/bitlbee
int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group )
{
	struct xt_node *node;
	int st;
	
	/* Build the item entry */
	node = xt_new_node( "item", NULL, NULL );
	xt_add_attr( node, "jid", handle );
	if( name )
		xt_add_attr( node, "name", name );
	if( group )
		xt_add_child( node, xt_new_node( "group", group, NULL ) );
	
	/* 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 );
	jabber_cache_add( ic, node, jabber_add_to_roster_callback );
	
	st = jabber_write_packet( ic, node );
	
	return st;
}
示例#8
0
struct xt_node *xml_generate( irc_t *irc )
{
	char *pass_buf = NULL;
	account_t *acc;
	md5_byte_t pass_md5[21];
	md5_state_t md5_state;
	GSList *l;
	struct xt_node *root, *cur;
	
	/* Generate a salted md5sum of the password. Use 5 bytes for the salt
	   (to prevent dictionary lookups of passwords) to end up with a 21-
	   byte password hash, more convenient for base64 encoding. */
	random_bytes( pass_md5 + 16, 5 );
	md5_init( &md5_state );
	md5_append( &md5_state, (md5_byte_t*) irc->password, strlen( irc->password ) );
	md5_append( &md5_state, pass_md5 + 16, 5 ); /* Add the salt. */
	md5_finish( &md5_state, pass_md5 );
	/* Save the hash in base64-encoded form. */
	pass_buf = base64_encode( pass_md5, 21 );
	
	root = cur = xt_new_node( "user", NULL, NULL );
	xt_add_attr( cur, "nick", irc->user->nick );
	xt_add_attr( cur, "password", pass_buf );
	xt_add_attr( cur, "version", XML_FORMAT_VERSION );
	
	g_free( pass_buf );
	
	xml_generate_settings( cur, &irc->b->set );
	
	for( acc = irc->b->accounts; acc; acc = acc->next )
	{
		unsigned char *pass_cr;
		char *pass_b64;
		int pass_len;
		
		pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password, 12 );
		pass_b64 = base64_encode( pass_cr, pass_len );
		g_free( pass_cr );
		
		cur = xt_new_node( "account", NULL, NULL );
		xt_add_attr( cur, "protocol", acc->prpl->name );
		xt_add_attr( cur, "handle", acc->user );
		xt_add_attr( cur, "password", pass_b64 );
		xt_add_attr( cur, "autoconnect", acc->auto_connect ? "true" : "false" );
		xt_add_attr( cur, "tag", acc->tag );
		if( acc->server && acc->server[0] )
			xt_add_attr( cur, "server", acc->server );
		
		g_free( pass_b64 );
		
		/* This probably looks pretty strange. g_hash_table_foreach
		   is quite a PITA already (but it can't get much better in
		   C without using #define, I'm afraid), and it
		   doesn't seem to be possible to abort the foreach on write
		   errors, so instead let's use the _find function and
		   return TRUE on write errors. Which means, if we found
		   something, there was an error. :-) */
		g_hash_table_find( acc->nicks, xml_generate_nick, cur );
		
		xml_generate_settings( cur, &acc->set );
		
		xt_add_child( root, cur );
	}
	
	for( l = irc->channels; l; l = l->next )
	{
		irc_channel_t *ic = l->data;
		
		if( ic->flags & IRC_CHANNEL_TEMP )
			continue;
		
		cur = xt_new_node( "channel", NULL, NULL );
		xt_add_attr( cur, "name", ic->name );
		xt_add_attr( cur, "type", set_getstr( &ic->set, "type" ) );
		
		xml_generate_settings( cur, &ic->set );
		
		xt_add_child( root, cur );
	}
	
	return root;
}
示例#9
0
文件: iq.c 项目: 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;
}
示例#10
0
struct xt_node *xml_generate(irc_t *irc)
{
	char *pass_buf = NULL;
	account_t *acc;
	md5_byte_t pass_md5[21];
	md5_state_t md5_state;
	GSList *l;
	struct xt_node *root, *cur;

	root = cur = xt_new_node("user", NULL, NULL);
	if (irc->auth_backend) {
		xt_add_attr(cur, "auth_backend", irc->auth_backend);
	} else {
		/* Generate a salted md5sum of the password. Use 5 bytes for the salt
		   (to prevent dictionary lookups of passwords) to end up with a 21-
		   byte password hash, more convenient for base64 encoding. */
		random_bytes(pass_md5 + 16, 5);
		md5_init(&md5_state);
		md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
		md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
		md5_finish(&md5_state, pass_md5);
		/* Save the hash in base64-encoded form. */
		pass_buf = base64_encode(pass_md5, 21);
		xt_add_attr(cur, "password", pass_buf);
		g_free(pass_buf);
	}

	xt_add_attr(cur, "nick", irc->user->nick);
	xt_add_attr(cur, "version", XML_FORMAT_VERSION);

	xml_generate_settings(cur, &irc->b->set);

	for (acc = irc->b->accounts; acc; acc = acc->next) {
		GHashTableIter iter;
		gpointer key, value;
		unsigned char *pass_cr;
		char *pass_b64;
		int pass_len;

		if(irc->auth_backend) {
			/* If we don't "own" the password, it may change without us
			 * knowing, so we cannot encrypt the data, as we then may not be
			 * able to decrypt it */
			pass_b64 = base64_encode((unsigned char *)acc->pass, strlen(acc->pass));
		} else {
			pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
			pass_b64 = base64_encode(pass_cr, pass_len);
			g_free(pass_cr);
		}

		cur = xt_new_node("account", NULL, NULL);
		xt_add_attr(cur, "protocol", acc->prpl->name);
		xt_add_attr(cur, "handle", acc->user);
		xt_add_attr(cur, "password", pass_b64);
		xt_add_attr(cur, "autoconnect", acc->auto_connect ? "true" : "false");
		xt_add_attr(cur, "tag", acc->tag);
		if (acc->server && acc->server[0]) {
			xt_add_attr(cur, "server", acc->server);
		}
		if (acc->flags & ACC_FLAG_LOCKED) {
			xt_add_attr(cur, "locked", "true");
		}

		g_free(pass_b64);

		g_hash_table_iter_init(&iter, acc->nicks);
		while (g_hash_table_iter_next(&iter, &key, &value)) {
			struct xt_node *node = xt_new_node("buddy", NULL, NULL);
			xt_add_attr(node, "handle", key);
			xt_add_attr(node, "nick", value);
			xt_add_child(cur, node);
		}

		xml_generate_settings(cur, &acc->set);

		xt_add_child(root, cur);
	}

	for (l = irc->channels; l; l = l->next) {
		irc_channel_t *ic = l->data;

		if (ic->flags & IRC_CHANNEL_TEMP) {
			continue;
		}

		cur = xt_new_node("channel", NULL, NULL);
		xt_add_attr(cur, "name", ic->name);
		xt_add_attr(cur, "type", set_getstr(&ic->set, "type"));

		xml_generate_settings(cur, &ic->set);

		xt_add_child(root, cur);
	}

	return root;
}