diff options
Diffstat (limited to 'protocols')
52 files changed, 9511 insertions, 3747 deletions
| diff --git a/protocols/Makefile b/protocols/Makefile index 18d79e8d..d4aa6e14 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -7,9 +7,13 @@  ### DEFINITIONS  -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/ +endif  # [SH] Program variables -objects = nogaim.o +objects = account.o bee.o bee_chat.o bee_ft.o bee_user.o nogaim.o +  # [SH] The next two lines should contain the directory name (in $(subdirs))  #      and the name of the object file, which should be linked into @@ -48,6 +52,6 @@ protocols.o: $(objects) $(subdirs)  $(objects): ../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/account.c b/protocols/account.c new file mode 100644 index 00000000..7fceae91 --- /dev/null +++ b/protocols/account.c @@ -0,0 +1,429 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2010 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Account management functions                                         */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "account.h" + +static char *set_eval_nick_source( set_t *set, char *value ); + +account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) +{ +	account_t *a; +	set_t *s; +	char tag[strlen(prpl->name)+10]; +	 +	if( bee->accounts ) +	{ +		for( a = bee->accounts; a->next; a = a->next ); +		a = a->next = g_new0( account_t, 1 ); +	} +	else +	{ +		bee->accounts = a = g_new0 ( account_t, 1 ); +	} +	 +	a->prpl = prpl; +	a->user = g_strdup( user ); +	a->pass = g_strdup( pass ); +	a->auto_connect = 1; +	a->bee = bee; +	 +	s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); +	s->flags |= ACC_SET_NOSAVE; +	 +	s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); +	 +	s = set_add( &a->set, "nick_format", NULL, NULL, a ); +	s->flags |= SET_NULL_OK; +	 +	s = set_add( &a->set, "nick_source", "handle", set_eval_nick_source, a ); +	s->flags |= ACC_SET_NOSAVE; /* Just for bw compatibility! */ +	 +	s = set_add( &a->set, "password", NULL, set_eval_account, a ); +	s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; +	 +	s = set_add( &a->set, "tag", NULL, set_eval_account, a ); +	s->flags |= ACC_SET_NOSAVE; +	 +	s = set_add( &a->set, "username", NULL, set_eval_account, a ); +	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; +	set_setstr( &a->set, "username", user ); +	 +	if( account_by_tag( bee, prpl->name ) ) +	{ +		int i; + +		for( i = 2; i < 10000; i ++ ) +		{ +			sprintf( tag, "%s%d", prpl->name, i ); +			if( !account_by_tag( bee, tag ) ) +				break; +		} +	} +	else +	{ +		strcpy( tag, prpl->name ); +	} +	set_setstr( &a->set, "tag", tag ); +	 +	a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); +	 +	/* This function adds some more settings (and might want to do more +	   things that have to be done now, although I can't think of anything. */ +	if( prpl->init ) +		prpl->init( a ); +	 +	s = set_add( &a->set, "away", NULL, set_eval_account, a ); +	s->flags |= SET_NULL_OK; +	 +	if( a->flags & ACC_FLAG_STATUS_MESSAGE ) +	{ +		s = set_add( &a->set, "status", NULL, set_eval_account, a ); +		s->flags |= SET_NULL_OK; +	} +	 +	return a; +} + +char *set_eval_account( set_t *set, char *value ) +{ +	account_t *acc = set->data; +	 +	/* Double-check: We refuse to edit on-line accounts. */ +	if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic ) +		return SET_INVALID; +	 +	if( strcmp( set->key, "server" ) == 0 ) +	{ +		g_free( acc->server ); +		if( value && *value ) +		{ +			acc->server = g_strdup( value ); +			return value; +		} +		else +		{ +			acc->server = g_strdup( set->def ); +			return g_strdup( set->def ); +		} +	} +	else if( strcmp( set->key, "username" ) == 0 ) +	{ +		g_free( acc->user ); +		acc->user = g_strdup( value ); +		return value; +	} +	else if( strcmp( set->key, "password" ) == 0 ) +	{ +		if( value ) +		{ +			g_free( acc->pass ); +			acc->pass = g_strdup( value ); +			return NULL;	/* password shouldn't be visible in plaintext! */ +		} +		else +		{ +			/* NULL can (should) be stored in the set_t +			   variable, but is otherwise not correct. */ +			return SET_INVALID; +		} +	} +	else if( strcmp( set->key, "tag" ) == 0 ) +	{ +		account_t *oa; +		 +		/* Enforce uniqueness. */ +		if( ( oa = account_by_tag( acc->bee, value ) ) && oa != acc ) +			return SET_INVALID; +		 +		g_free( acc->tag ); +		acc->tag = g_strdup( value ); +		return value; +	} +	else if( strcmp( set->key, "auto_connect" ) == 0 ) +	{ +		if( !is_bool( value ) ) +			return SET_INVALID; +		 +		acc->auto_connect = bool2int( value ); +		return value; +	} +	else if( strcmp( set->key, "away" ) == 0 || +	         strcmp( set->key, "status" ) == 0 ) +	{ +		if( acc->ic && acc->ic->flags & OPT_LOGGED_IN ) +		{ +			/* If we're currently on-line, set the var now already +			   (bit of a hack) and send an update. */ +			g_free( set->value ); +			set->value = g_strdup( value ); +			 +			imc_away_send_update( acc->ic ); +		} +		 +		return value; +	} +	 +	return SET_INVALID; +} + +/* For bw compatibility, have this write-only setting. */ +static char *set_eval_nick_source( set_t *set, char *value ) +{ +	account_t *a = set->data; +	 +	if( strcmp( value, "full_name" ) == 0 ) +		set_setstr( &a->set, "nick_format", "%full_name" ); +	else if( strcmp( value, "first_name" ) == 0 ) +		set_setstr( &a->set, "nick_format", "%first_name" ); +	else +		set_setstr( &a->set, "nick_format", "%-@nick" ); +	 +	return value; +} + +account_t *account_get( bee_t *bee, const char *id ) +{ +	account_t *a, *ret = NULL; +	char *handle, *s; +	int nr; +	 +	/* Tags get priority above anything else. */ +	if( ( a = account_by_tag( bee, id ) ) ) +		return a; +	 +	/* This checks if the id string ends with (...) */ +	if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 ) +	{ +		struct prpl *proto; +		 +		*s = *handle = 0; +		handle ++; +		 +		if( ( proto = find_protocol( id ) ) ) +		{ +			for( a = bee->accounts; a; a = a->next ) +				if( a->prpl == proto && +				    a->prpl->handle_cmp( handle, a->user ) == 0 ) +					ret = a; +		} +		 +		/* Restore the string. */ +		handle --; +		*handle = '('; +		*s = ')'; +		 +		if( ret ) +			return ret; +	} +	 +	if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) +	{ +		for( a = bee->accounts; a; a = a->next ) +			if( ( nr-- ) == 0 ) +				return( a ); +		 +		return( NULL ); +	} +	 +	for( a = bee->accounts; a; a = a->next ) +	{ +		if( g_strcasecmp( id, a->prpl->name ) == 0 ) +		{ +			if( !ret ) +				ret = a; +			else +				return( NULL ); /* We don't want to match more than one... */ +		} +		else if( strstr( a->user, id ) ) +		{ +			if( !ret ) +				ret = a; +			else +				return( NULL ); +		} +	} +	 +	return( ret ); +} + +account_t *account_by_tag( bee_t *bee, const char *tag ) +{ +	account_t *a; +	 +	for( a = bee->accounts; a; a = a->next ) +		if( a->tag && g_strcasecmp( tag, a->tag ) == 0 ) +			return a; +	 +	return NULL; +} + +void account_del( bee_t *bee, account_t *acc ) +{ +	account_t *a, *l = NULL; +	 +	if( acc->ic ) +		/* Caller should have checked, accounts still in use can't be deleted. */ +		return; +	 +	for( a = bee->accounts; a; a = (l=a)->next ) +		if( a == acc ) +		{ +			if( l ) +				l->next = a->next; +			else +				bee->accounts = a->next; +			 +			/** FIXME +			for( c = bee->chatrooms; c; c = nc ) +			{ +				nc = c->next; +				if( acc == c->acc ) +					chat_del( bee, c ); +			} +			*/ +			 +			while( a->set ) +				set_del( &a->set, a->set->key ); +			 +			g_hash_table_destroy( a->nicks ); +			 +			g_free( a->tag ); +			g_free( a->user ); +			g_free( a->pass ); +			g_free( a->server ); +			if( a->reconnect )	/* This prevents any reconnect still queued to happen */ +				cancel_auto_reconnect( a ); +			g_free( a ); +			 +			break; +		} +} + +void account_on( bee_t *bee, account_t *a ) +{ +	if( a->ic ) +	{ +		/* Trying to enable an already-enabled account */ +		return; +	} +	 +	cancel_auto_reconnect( a ); +	 +	a->reconnect = 0; +	a->prpl->login( a ); +} + +void account_off( bee_t *bee, account_t *a ) +{ +	imc_logout( a->ic, FALSE ); +	a->ic = NULL; +	if( a->reconnect ) +	{ +		/* Shouldn't happen */ +		cancel_auto_reconnect( a ); +	} +} + +struct account_reconnect_delay +{ +	int start; +	char op; +	int step; +	int max; +}; + +int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p ) +{ +	memset( p, 0, sizeof( *p ) ); +	/* A whole day seems like a sane "maximum maximum". */ +	p->max = 86400; +	 +	/* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */ +	while( *value && isdigit( *value ) ) +		p->start = p->start * 10 + *value++ - '0'; +	 +	/* Sure, call me evil for implementing my own fscanf here, but it's +	   dead simple and I immediately know where to continue parsing. */ +	 +	if( *value == 0 ) +		/* If the string ends now, the delay is constant. */ +		return 1; +	else if( *value != '+' && *value != '*' ) +		/* Otherwise allow either a + or a * */ +		return 0; +	 +	p->op = *value++; +	 +	/* + or * the delay by this number every time. */ +	while( *value && isdigit( *value ) ) +		p->step = p->step * 10 + *value++ - '0'; +	 +	if( *value == 0 ) +		/* Use the default maximum (one day). */ +		return 1; +	else if( *value != '<' ) +		return 0; +	 +	p->max = 0; +	value ++; +	while( *value && isdigit( *value ) ) +		p->max = p->max * 10 + *value++ - '0'; +	 +	return p->max > 0; +} + +char *set_eval_account_reconnect_delay( set_t *set, char *value ) +{ +	struct account_reconnect_delay p; +	 +	return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID; +} + +int account_reconnect_delay( account_t *a ) +{ +	char *setting = set_getstr( &a->bee->set, "auto_reconnect_delay" ); +	struct account_reconnect_delay p; +	 +	if( account_reconnect_delay_parse( setting, &p ) ) +	{ +		if( a->auto_reconnect_delay == 0 ) +			a->auto_reconnect_delay = p.start; +		else if( p.op == '+' ) +			a->auto_reconnect_delay += p.step; +		else if( p.op == '*' ) +			a->auto_reconnect_delay *= p.step; +		 +		if( a->auto_reconnect_delay > p.max ) +			a->auto_reconnect_delay = p.max; +	} +	else +	{ +		a->auto_reconnect_delay = 0; +	} +	 +	return a->auto_reconnect_delay; +} diff --git a/protocols/account.h b/protocols/account.h new file mode 100644 index 00000000..a39be2e2 --- /dev/null +++ b/protocols/account.h @@ -0,0 +1,74 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Account management functions                                         */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#ifndef _ACCOUNT_H +#define _ACCOUNT_H + +typedef struct account +{ +	struct prpl *prpl; +	char *user; +	char *pass; +	char *server; +	char *tag; +	 +	int auto_connect; +	int auto_reconnect_delay; +	int reconnect; +	int flags; +	 +	set_t *set; +	GHashTable *nicks; +	 +	struct bee *bee; +	struct im_connection *ic; +	struct account *next; +} account_t; + +account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ); +account_t *account_get( bee_t *bee, const char *id ); +account_t *account_by_tag( bee_t *bee, const char *tag ); +void account_del( bee_t *bee, account_t *acc ); +void account_on( bee_t *bee, account_t *a ); +void account_off( bee_t *bee, account_t *a ); + +char *set_eval_account( set_t *set, char *value ); +char *set_eval_account_reconnect_delay( set_t *set, char *value ); +int account_reconnect_delay( account_t *a ); + +typedef enum +{ +	ACC_SET_NOSAVE = 0x01,          /* Don't save this setting (i.e. stored elsewhere). */ +	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */ +	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */ +} account_set_flag_t; + +typedef enum +{ +	ACC_FLAG_AWAY_MESSAGE = 0x01,   /* Supports away messages instead of just states. */ +	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */ +} account_flag_t; + +#endif diff --git a/protocols/bee.c b/protocols/bee.c new file mode 100644 index 00000000..c5eeee17 --- /dev/null +++ b/protocols/bee.c @@ -0,0 +1,95 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2010 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Some IM-core stuff                                                   */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +static char *set_eval_away_status( set_t *set, char *value ); + +bee_t *bee_new() +{ +	bee_t *b = g_new0( bee_t, 1 ); +	set_t *s; +	 +	s = set_add( &b->set, "away", NULL, set_eval_away_status, b ); +	s->flags |= SET_NULL_OK; +	s = set_add( &b->set, "auto_connect", "true", set_eval_bool, b ); +	s = set_add( &b->set, "auto_reconnect", "true", set_eval_bool, b ); +	s = set_add( &b->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, b ); +	s = set_add( &b->set, "debug", "false", set_eval_bool, b ); +	s = set_add( &b->set, "save_on_quit", "true", set_eval_bool, b ); +	s = set_add( &b->set, "status", NULL, set_eval_away_status, b ); +	s->flags |= SET_NULL_OK; +	s = set_add( &b->set, "strip_html", "true", NULL, b ); +	 +	b->user = g_malloc( 1 ); +	 +	return b; +} + +void bee_free( bee_t *b ) +{ +	while( b->accounts ) +	{ +		if( b->accounts->ic ) +			imc_logout( b->accounts->ic, FALSE ); +		else if( b->accounts->reconnect ) +			cancel_auto_reconnect( b->accounts ); +		 +		if( b->accounts->ic == NULL ) +			account_del( b, b->accounts ); +		else +			/* Nasty hack, but account_del() doesn't work in this +			   case and we don't want infinite loops, do we? ;-) */ +			b->accounts = b->accounts->next; +	} +	 +	while( b->set ) +		set_del( &b->set, b->set->key ); +	 +	bee_group_free( b ); +	 +	g_free( b->user ); +	g_free( b ); +} + +static char *set_eval_away_status( set_t *set, char *value ) +{ +	bee_t *bee = set->data; +	account_t *a; +	 +	g_free( set->value ); +	set->value = g_strdup( value ); +	 +	for( a = bee->accounts; a; a = a->next ) +	{ +		struct im_connection *ic = a->ic; +		 +		if( ic && ic->flags & OPT_LOGGED_IN ) +			imc_away_send_update( ic ); +	} +	 +	return value; +} diff --git a/protocols/bee.h b/protocols/bee.h new file mode 100644 index 00000000..4b6a1f4a --- /dev/null +++ b/protocols/bee.h @@ -0,0 +1,151 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2010 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Stuff to handle, save and search buddies                             */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#ifndef __BEE_H__ +#define __BEE_H__ + +struct bee_ui_funcs; +struct groupchat; + +typedef struct bee +{ +	struct set *set; +	 +	GSList *users; +	GSList *groups; +	struct account *accounts; /* TODO(wilmer): Use GSList here too? */ +	 +	/* Symbolic, to refer to the local user (who has no real bee_user +	   object). Not to be used by anything except so far imcb_chat_add/ +	   remove_buddy(). This seems slightly cleaner than abusing NULL. */ +	struct bee_user *user; +	 +	const struct bee_ui_funcs *ui; +	void *ui_data; +} bee_t; + +bee_t *bee_new(); +void bee_free( bee_t *b ); + +typedef enum +{ +	BEE_USER_ONLINE = 1,    /* Compatibility with old OPT_LOGGED_IN flag */ +	BEE_USER_AWAY = 4,      /* Compatibility with old OPT_AWAY flag */ +	BEE_USER_LOCAL = 256,   /* Locally-added contacts (not in real contact list) */ +} bee_user_flags_t; + +typedef struct bee_user +{ +	struct im_connection *ic; +	char *handle; +	char *fullname; +	char *nick; +	struct bee_group *group; + +	bee_user_flags_t flags; +	char *status; +	char *status_msg; +	 +	time_t login_time, idle_time; +	 +	bee_t *bee; +	void *ui_data; +} bee_user_t; + +typedef struct bee_group +{ +	char *key; +	char *name; +} bee_group_t; + +typedef struct bee_ui_funcs +{ +	void (*imc_connected)( struct im_connection *ic ); +	void (*imc_disconnected)( struct im_connection *ic ); +	 +	gboolean (*user_new)( bee_t *bee, struct bee_user *bu ); +	gboolean (*user_free)( bee_t *bee, struct bee_user *bu ); +	gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu ); +	gboolean (*user_nick_hint)( bee_t *bee, bee_user_t *bu, const char *hint ); +	gboolean (*user_group)( bee_t *bee, bee_user_t *bu ); +	gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old ); +	gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at ); +	gboolean (*user_typing)( bee_t *bee, bee_user_t *bu, guint32 flags ); +	 +	gboolean (*chat_new)( bee_t *bee, struct groupchat *c ); +	gboolean (*chat_free)( bee_t *bee, struct groupchat *c ); +	gboolean (*chat_log)( bee_t *bee, struct groupchat *c, const char *text ); +	gboolean (*chat_msg)( bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at ); +	gboolean (*chat_add_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); +	gboolean (*chat_remove_user)( bee_t *bee, struct groupchat *c, bee_user_t *bu ); +	gboolean (*chat_topic)( bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu ); +	gboolean (*chat_name_hint)( bee_t *bee, struct groupchat *c, const char *name ); +	 +	struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size ); +	gboolean (*ft_out_start)( struct im_connection *ic, struct file_transfer *ft ); +	void (*ft_close)( struct im_connection *ic, struct file_transfer *ft ); +	void (*ft_finished)( struct im_connection *ic, struct file_transfer *ft ); +} bee_ui_funcs_t; + + +/* bee.c */ +bee_t *bee_new(); +void bee_free( bee_t *b ); + +/* bee_user.c */ +bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags ); +int bee_user_free( bee_t *bee, bee_user_t *bu ); +bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ); +int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ); +bee_group_t *bee_group_by_name( bee_t *bee, const char *name, gboolean creat ); +void bee_group_free( bee_t *bee ); + +/* Callbacks from IM modules to core: */ +/* Buddy activity */ +/* To manipulate the status of a handle. + * - flags can be |='d with OPT_* constants. You will need at least: + *   OPT_LOGGED_IN and OPT_AWAY. + * - 'state' and 'message' can be NULL */ +G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); +G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); +/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ +G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); + +/* bee_chat.c */ +#if 0 +struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ); +void imcb_chat_name_hint( struct groupchat *c, const char *name ); +void imcb_chat_free( struct groupchat *c ); +void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ); +void imcb_chat_log( struct groupchat *c, char *format, ... ); +void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ); +void imcb_chat_add_buddy( struct groupchat *b, const char *handle ); +void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ); +static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); +#endif +int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ); +struct groupchat *bee_chat_by_title( bee_t *bee, struct im_connection *ic, const char *title ); + +#endif /* __BEE_H__ */ diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c new file mode 100644 index 00000000..3be6f189 --- /dev/null +++ b/protocols/bee_chat.c @@ -0,0 +1,234 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2010 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Stuff to handle rooms                                                */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) +{ +	struct groupchat *c = g_new0( struct groupchat, 1 ); +	bee_t *bee = ic->bee; +	 +	/* This one just creates the conversation structure, user won't see +	   anything yet until s/he is joined to the conversation. (This +	   allows you to add other already present participants first.) */ +	 +	ic->groupchats = g_slist_prepend( ic->groupchats, c ); +	c->ic = ic; +	c->title = g_strdup( handle ); +	c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); +	 +	if( set_getbool( &ic->bee->set, "debug" ) ) +		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); +	 +	if( bee->ui->chat_new ) +		bee->ui->chat_new( bee, c ); +	 +	return c; +} + +void imcb_chat_name_hint( struct groupchat *c, const char *name ) +{ +	bee_t *bee = c->ic->bee; +	 +	if( bee->ui->chat_name_hint ) +		bee->ui->chat_name_hint( bee, c, name ); +} + +void imcb_chat_free( struct groupchat *c ) +{ +	struct im_connection *ic = c->ic; +	bee_t *bee = ic->bee; +	GList *ir; +	 +	if( bee->ui->chat_free ) +		bee->ui->chat_free( bee, c ); +	 +	if( set_getbool( &ic->bee->set, "debug" ) ) +		imcb_log( ic, "You were removed from conversation %p", c ); +	 +	ic->groupchats = g_slist_remove( ic->groupchats, c ); +	 +	for( ir = c->in_room; ir; ir = ir->next ) +		g_free( ir->data ); +	g_list_free( c->in_room ); +	g_free( c->title ); +	g_free( c->topic ); +	g_free( c ); +} + +void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ) +{ +	struct im_connection *ic = c->ic; +	bee_t *bee = ic->bee; +	bee_user_t *bu; +	char *s; +	 +	/* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ +	if( g_strcasecmp( who, ic->acc->user ) == 0 ) +		return; +	 +	bu = bee_user_by_handle( bee, ic, who ); +	 +	s = set_getstr( &ic->bee->set, "strip_html" ); +	if( ( g_strcasecmp( s, "always" ) == 0 ) || +	    ( ( ic->flags & OPT_DOES_HTML ) && s ) ) +		strip_html( msg ); +	 +	if( bu && bee->ui->chat_msg ) +		bee->ui->chat_msg( bee, c, bu, msg, sent_at ); +	else +		imcb_chat_log( c, "Message from unknown participant %s: %s", who, msg ); +} + +void imcb_chat_log( struct groupchat *c, char *format, ... ) +{ +	struct im_connection *ic = c->ic; +	bee_t *bee = ic->bee; +	va_list params; +	char *text; +	 +	if( !bee->ui->chat_log ) +		return; +	 +	va_start( params, format ); +	text = g_strdup_vprintf( format, params ); +	va_end( params ); +	 +	bee->ui->chat_log( bee, c, text ); +	g_free( text ); +} + +void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) +{ +	struct im_connection *ic = c->ic; +	bee_t *bee = ic->bee; +	bee_user_t *bu; +	 +	if( !bee->ui->chat_topic ) +		return; +	 +	if( who == NULL) +		bu = NULL; +	else if( g_strcasecmp( who, ic->acc->user ) == 0 ) +		bu = bee->user; +	else +		bu = bee_user_by_handle( bee, ic, who ); +	 +	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || +	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) +		strip_html( topic ); +	 +	bee->ui->chat_topic( bee, c, topic, bu ); +} + +void imcb_chat_add_buddy( struct groupchat *c, const char *handle ) +{ +	struct im_connection *ic = c->ic; +	bee_t *bee = ic->bee; +	bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); +	gboolean me; +	 +	if( set_getbool( &c->ic->bee->set, "debug" ) ) +		imcb_log( c->ic, "User %s added to conversation %p", handle, c ); +	 +	me = ic->acc->prpl->handle_cmp( handle, ic->acc->user ) == 0; +	 +	/* Most protocols allow people to join, even when they're not in +	   your contact list. Try to handle that here */ +	if( !me && !bu ) +		bu = bee_user_new( bee, ic, handle, BEE_USER_LOCAL ); +	 +	/* Add the handle to the room userlist */ +	/* TODO: Use bu instead of a string */ +	c->in_room = g_list_append( c->in_room, g_strdup( handle ) ); +	 +	if( bee->ui->chat_add_user ) +		bee->ui->chat_add_user( bee, c, me ? bee->user : bu ); +	 +	if( me ) +		c->joined = 1; +} + +void imcb_chat_remove_buddy( struct groupchat *c, const char *handle, const char *reason ) +{ +	struct im_connection *ic = c->ic; +	bee_t *bee = ic->bee; +	bee_user_t *bu = NULL; +	 +	if( set_getbool( &bee->set, "debug" ) ) +		imcb_log( ic, "User %s removed from conversation %p (%s)", handle, c, reason ? reason : "" ); +	 +	/* It might be yourself! */ +	if( g_strcasecmp( handle, ic->acc->user ) == 0 ) +	{ +		if( c->joined == 0 ) +			return; +		 +		bu = bee->user; +		c->joined = 0; +	} +	else +	{ +		bu = bee_user_by_handle( bee, ic, handle ); +	} +	 +	if( bee->ui->chat_remove_user ) +		bee->ui->chat_remove_user( bee, c, bu ); +} + +int bee_chat_msg( bee_t *bee, struct groupchat *c, const char *msg, int flags ) +{ +	struct im_connection *ic = c->ic; +	char *buf = NULL; +	 +	if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) ) +	{ +		buf = escape_html( msg ); +		msg = buf; +	} +	else +		buf = g_strdup( msg ); +	 +	ic->acc->prpl->chat_msg( c, buf, flags ); +	g_free( buf ); +	 +	return 1; +} + +struct groupchat *bee_chat_by_title( bee_t *bee, struct im_connection *ic, const char *title ) +{ +	struct groupchat *c; +	GSList *l; +	 +	for( l = ic->groupchats; l; l = l->next ) +	{ +		c = l->data; +		if( strcmp( c->title, title ) == 0 ) +			return c; +	} +	 +	return NULL; +} diff --git a/protocols/bee_ft.c b/protocols/bee_ft.c new file mode 100644 index 00000000..1026eab3 --- /dev/null +++ b/protocols/bee_ft.c @@ -0,0 +1,66 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway                     * +*                                                                    * +* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>             * +\********************************************************************/ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "ft.h" + +file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) +{ +	bee_t *bee = ic->bee;  +	bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); +	 +	if( bee->ui->ft_in_start ) +		return bee->ui->ft_in_start( bee, bu, file_name, file_size ); +	else +		return NULL; +} + +gboolean imcb_file_recv_start( struct im_connection *ic, file_transfer_t *ft ) +{ +	bee_t *bee = ic->bee; +	 +	if( bee->ui->ft_out_start ) +		return bee->ui->ft_out_start( ic, ft ); +	else +		return FALSE; +} + +void imcb_file_canceled( struct im_connection *ic, file_transfer_t *file, char *reason ) +{ +	bee_t *bee = ic->bee; +	 +	if( file->canceled ) +		file->canceled( file, reason ); +	 +	if( bee->ui->ft_close ) +		bee->ui->ft_close( ic, file ); +} + +void imcb_file_finished( struct im_connection *ic, file_transfer_t *file ) +{ +	bee_t *bee = ic->bee; +	 +	if( bee->ui->ft_finished ) +		bee->ui->ft_finished( ic, file ); +} diff --git a/protocols/bee_user.c b/protocols/bee_user.c new file mode 100644 index 00000000..4399a566 --- /dev/null +++ b/protocols/bee_user.c @@ -0,0 +1,248 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2010 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Stuff to handle, save and search buddies                             */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags ) +{ +	bee_user_t *bu; +	 +	if( bee_user_by_handle( bee, ic, handle ) != NULL ) +		return NULL; +	 +	bu = g_new0( bee_user_t, 1 ); +	bu->bee = bee; +	bu->ic = ic; +	bu->flags = flags; +	bu->handle = g_strdup( handle ); +	bee->users = g_slist_prepend( bee->users, bu ); +	 +	if( bee->ui->user_new ) +		bee->ui->user_new( bee, bu ); +	 +	/* Offline by default. This will set the right flags. */ +	imcb_buddy_status( ic, handle, 0, NULL, NULL ); +	 +	return bu; +} + +int bee_user_free( bee_t *bee, bee_user_t *bu ) +{ +	if( !bu ) +		return 0; +	 +	if( bee->ui->user_free ) +		bee->ui->user_free( bee, bu ); +	 +	g_free( bu->handle ); +	g_free( bu->fullname ); +	g_free( bu->nick ); +	g_free( bu->status ); +	g_free( bu->status_msg ); +	g_free( bu ); +	 +	bee->users = g_slist_remove( bee->users, bu ); +	 +	return 1; +} + +bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle ) +{ +	GSList *l; +	 +	for( l = bee->users; l; l = l->next ) +	{ +		bee_user_t *bu = l->data; +		 +		if( bu->ic == ic && ic->acc->prpl->handle_cmp( bu->handle, handle ) == 0 ) +			return bu; +	} +	 +	return NULL; +} + +int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags ) +{ +	char *buf = NULL; +	int st; +	 +	if( ( bu->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) ) +	{ +		buf = escape_html( msg ); +		msg = buf; +	} +	else +		buf = g_strdup( msg ); +	 +	st = bu->ic->acc->prpl->buddy_msg( bu->ic, bu->handle, buf, flags ); +	g_free( buf ); +	 +	return st; +} + + +/* Groups */ +static bee_group_t *bee_group_new( bee_t *bee, const char *name ) +{ +	bee_group_t *bg = g_new0( bee_group_t, 1 ); +	 +	bg->name = g_strdup( name ); +	bg->key = g_utf8_casefold( name, -1 ); +	bee->groups = g_slist_prepend( bee->groups, bg ); +	 +	return bg; +} + +bee_group_t *bee_group_by_name( bee_t *bee, const char *name, gboolean creat ) +{ +	GSList *l; +	char *key; +	 +	if( name == NULL ) +		return NULL; +	 +	key = g_utf8_casefold( name, -1 ); +	for( l = bee->groups; l; l = l->next ) +	{ +		bee_group_t *bg = l->data; +		if( strcmp( bg->key, key ) == 0 ) +			break; +	} +	g_free( key ); +	 +	if( !l ) +		return creat ? bee_group_new( bee, name ) : NULL; +	else +		return l->data; +} + +void bee_group_free( bee_t *bee ) +{ +	while( bee->groups ) +	{ +		bee_group_t *bg = bee->groups->data; +		g_free( bg->name ); +		g_free( bg->key ); +		g_free( bg ); +		bee->groups = g_slist_remove( bee->groups, bee->groups->data ); +	} +} + + +/* IM->UI callbacks */ +void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ) +{ +	bee_t *bee = ic->bee; +	bee_user_t *bu, *old; +	 +	if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) +	{ +		if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "add" ) == 0 ) +		{ +			bu = bee_user_new( bee, ic, handle, BEE_USER_LOCAL ); +		} +		else +		{ +			if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 ) +			{ +				imcb_log( ic, "imcb_buddy_status() for unknown handle %s:\n" +				              "flags = %d, state = %s, message = %s", handle, flags, +				              state ? state : "NULL", message ? message : "NULL" ); +			} +			 +			return; +		} +	} +	 +	/* May be nice to give the UI something to compare against. */ +	old = g_memdup( bu, sizeof( bee_user_t ) ); +	 +	/* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ +	bu->flags = flags; +	bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); +	bu->status_msg = g_strdup( message ); +	 +	if( bee->ui->user_status ) +		bee->ui->user_status( bee, bu, old ); +	 +	g_free( old->status_msg ); +	g_free( old->status ); +	g_free( old ); +} + +void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ) +{ +	bee_t *bee = ic->bee; +	bee_user_t *bu; +	 +	if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) +		return; +	 +	bu->login_time = login; +	bu->idle_time = idle; +} + +void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) +{ +	bee_t *bee = ic->bee; +	bee_user_t *bu; +	 +	bu = bee_user_by_handle( bee, ic, handle ); +	 +	if( !bu ) +	{ +		char *h = set_getstr( &bee->set, "handle_unknown" ); +		 +		if( g_strcasecmp( h, "ignore" ) == 0 ) +		{ +			return; +		} +		else if( g_strncasecmp( h, "add", 3 ) == 0 ) +		{ +			bu = bee_user_new( bee, ic, handle, BEE_USER_LOCAL ); +		} +	} +	 +	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || +	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) ) +		strip_html( msg ); +	 +	if( bee->ui->user_msg && bu ) +		bee->ui->user_msg( bee, bu, msg, sent_at ); +	else +		imcb_log( ic, "Message from unknown handle %s:\n%s", handle, msg ); +} + +void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) +{ +	bee_user_t *bu; +	 +	if( ic->bee->ui->user_typing && +	    ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ) +	{ +		ic->bee->ui->user_typing( ic->bee, bu, flags ); +	} +} diff --git a/protocols/ft.h b/protocols/ft.h new file mode 100644 index 00000000..159f16f2 --- /dev/null +++ b/protocols/ft.h @@ -0,0 +1,176 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway                     * +*                                                                    * +* Copyright 2006 Marijn Kruisselbrink and others                     * +\********************************************************************/ + +/* Generic file transfer header                                     */ + +/* +  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 with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#ifndef _FT_H +#define _FT_H + +/* + * One buffer is needed for each transfer. The receiver stores a message + * in it and gives it to the sender. The sender will stall the receiver + * till the buffer has been sent out. + */ +#define FT_BUFFER_SIZE 2048 + +typedef enum { +	FT_STATUS_LISTENING	= 1, +	FT_STATUS_TRANSFERRING	= 2, +	FT_STATUS_FINISHED	= 4, +	FT_STATUS_CANCELED	= 8, +	FT_STATUS_CONNECTING	= 16 +} file_status_t; + +/* + * This structure holds all irc specific information regarding an incoming (from the point of view of + * the irc client) file transfer. New instances of this struct should only be created by calling the + * imcb_file_send_start() method, which will initialize most of the fields. The data field and the various + * methods are zero-initialized. Instances will automatically be deleted once the transfer is completed, + * canceled, or the connection to the irc client has been lost (note that also if only the irc connection + * and not the file transfer connection is lost, the file transfer will still be canceled and freed). + * + * The following (poor ascii-art) diagram illustrates what methods are called for which status-changes: + * + *	                        /-----------\                    /----------\ + *	               -------> | LISTENING | -----------------> | CANCELED | + *	                        \-----------/  [canceled,]free   \----------/ + *	                              | + *	                              | accept + *	                              V + *	               /------ /-------------\                    /------------------------\ + *	   out_of_data |       | TRANSFERING | -----------------> | TRANSFERING | CANCELED | + *	               \-----> \-------------/  [canceled,]free   \------------------------/ + *	                              | + *	                              | finished,free + *	                              V + *	                 /------------------------\ + *	                 | TRANSFERING | FINISHED | + *	                 \------------------------/ + */ +typedef struct file_transfer { + +	/* Are we sending something? */ +	int sending; + +	/* +	 * The current status of this file transfer. +	 */  +	file_status_t status; +	 +	/* +	 * file size +	 */ +	size_t file_size; +	 +	/* +	 * Number of bytes that have been successfully transferred. +	 */ +	size_t bytes_transferred; + +	/* +	 * Time started. Used to calculate kb/s. +	 */ +	time_t started; + +	/* +	 * file name +	 */ +	char *file_name; + +	/* +	 * A unique local ID for this file transfer. +	 */ +	unsigned int local_id; + +	/* +	 * IM-protocol specific data associated with this file transfer. +	 */ +	gpointer data; +	struct im_connection *ic; +	 +	/* +	 * Private data. +	 */ +	gpointer priv; +	 +	/* +	 * If set, called after succesful connection setup. +	 */ +	void (*accept) ( struct file_transfer *file ); +	 +	/* +	 * If set, called when the transfer is canceled or finished. +	 * Subsequently, this structure will be freed. +	 * +	 */ +	void (*free) ( struct file_transfer *file ); +	 +	/* +	 * If set, called when the transfer is finished and successful. +	 */ +	void (*finished) ( struct file_transfer *file ); +	 +	/* +	 * If set, called when the transfer is canceled. +	 * ( canceled either by the transfer implementation or by +	 *  a call to imcb_file_canceled ) +	 */ +	void (*canceled) ( struct file_transfer *file, char *reason ); +	 +	/* +	 * called by the sending side to indicate that it is writable. +	 * The callee should check if data is available and call the  +	 * function(as seen below) if that is the case. +	 */ +	gboolean (*write_request) ( struct file_transfer *file ); + +	/* +	 * When sending files, protocols register this function to receive data. +	 * This should only be called once after write_request is called. The caller +	 * should not read more data until write_request is called again. This technique +	 * avoids buffering. +	 */ +	gboolean (*write) (struct file_transfer *file, char *buffer, unsigned int len ); + +	/* The send buffer associated with this transfer. +	 * Since receivers always wait for a write_request call one is enough. +	 */ +	char buffer[FT_BUFFER_SIZE]; + +} file_transfer_t; + +/* + * This starts a file transfer from bitlbee to the user. + */ +file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ); + +/* + * This should be called by a protocol when the transfer is canceled. Note that + * the canceled() and free() callbacks given in file will be called by this function. + */ +void imcb_file_canceled( struct im_connection *ic, file_transfer_t *file, char *reason ); + +gboolean imcb_file_recv_start( struct im_connection *ic, file_transfer_t *ft ); + +void imcb_file_finished( struct im_connection *ic, file_transfer_t *file ); +#endif diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index e7a505ba..912ea702 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -7,9 +7,12 @@  ### DEFINITIONS  -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/jabber/ +endif  # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o  CFLAGS += -Wall  LFLAGS += -r @@ -32,7 +35,7 @@ distclean: clean  $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index affe8aef..e04b9792 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -91,18 +91,20 @@ static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_no  struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name )  {  	char *normalized = jabber_normalize( name ); +	GSList *l;  	struct groupchat *ret;  	struct jabber_chat *jc; -	for( ret = ic->groupchats; ret; ret = ret->next ) +	for( l = ic->groupchats; l; l = l->next )  	{ +		ret = l->data;  		jc = ret->data;  		if( strcmp( normalized, jc->name ) == 0 )  			break;  	}  	g_free( normalized ); -	return ret; +	return l ? ret : NULL;  }  void jabber_chat_free( struct groupchat *c ) diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index a14ad21c..d6f92a5f 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -63,7 +63,7 @@ int jabber_write( struct im_connection *ic, char *buf, int len )  		   it via the event handler. If not, add the handler. (In  		   most cases it probably won't be necessary.) */  		if( ( ret = jabber_write_queue( ic ) ) && jd->tx_len > 0 ) -			jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, ic ); +			jd->w_inpa = b_input_add( jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic );  	}  	else  	{ @@ -503,7 +503,7 @@ gboolean jabber_start_stream( struct im_connection *ic )  	jd->xt = xt_new( jabber_handlers, ic );  	if( jd->r_inpa <= 0 ) -		jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic ); +		jd->r_inpa = b_input_add( jd->fd, B_EV_IO_READ, jabber_read_callback, ic );  	greet = g_strdup_printf( "%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "  	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">",  diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 68777b0b..82c90d39 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -90,14 +90,17 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )  				xt_add_attr( reply, "id", s );  			pack = 0;  		} -		else if( strcmp( s, XMLNS_DISCOVER ) == 0 ) +		else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 )  		{ -			const char *features[] = { XMLNS_DISCOVER, +			const char *features[] = { XMLNS_DISCO_INFO,  			                           XMLNS_VERSION,  			                           XMLNS_TIME,  			                           XMLNS_CHATSTATES,  			                           XMLNS_MUC,  			                           XMLNS_PING, +			                           XMLNS_SI, +			                           XMLNS_BYTESTREAMS, +			                           XMLNS_FILETRANSFER,  			                           NULL };  			const char **f; @@ -117,23 +120,28 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )  		else  		{  			xt_free_node( reply ); -			reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" ); +			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, "query" ) ) || -		    !( s = xt_find_attr( c, "xmlns" ) ) ) +		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. */ -		if( strcmp( s, XMLNS_ROSTER ) == 0 ) -		{  			int bare_len = strlen( ic->acc->user );  			if( ( s = xt_find_attr( node, "from" ) ) == NULL || @@ -150,14 +158,19 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )  				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" ); +				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" ); +			reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );  			pack = 0;  		}  	} @@ -368,7 +381,7 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *  	c = query->children;  	while( ( c = xt_find_node( c, "item" ) ) )  	{ -		struct xt_node *group = xt_find_node( node->children, "group" ); +		struct xt_node *group = xt_find_node( c->children, "group" );  		char *jid = xt_find_attr( c, "jid" );  		char *name = xt_find_attr( c, "name" );  		char *sub = xt_find_attr( c, "subscription" ); @@ -377,9 +390,8 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *  		{  			if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )  			{ -				if( initial || imcb_find_buddy( ic, jid ) == NULL ) -					imcb_add_buddy( ic, jid, ( group && group->text_len ) ? -					                           group->text : NULL ); +				imcb_add_buddy( ic, jid, ( group && group->text_len ) ? +				                           group->text : NULL );  				if( name )  					imcb_rename_buddy( ic, jid, name ); @@ -542,7 +554,7 @@ static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_no  static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); -int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ) +int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group )  {  	struct xt_node *node;  	int st; @@ -552,6 +564,8 @@ int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )  	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 ); @@ -575,7 +589,7 @@ static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct  	    ( s = xt_find_attr( node, "type" ) ) &&  	    strcmp( s, "result" ) == 0 )  	{ -		if( imcb_find_buddy( ic, jid ) == NULL ) +		if( bee_user_by_handle( ic->bee, ic, jid ) == NULL )  			imcb_add_buddy( ic, jid, NULL );  	}  	else @@ -607,3 +621,175 @@ int jabber_remove_from_roster( struct im_connection *ic, char *handle )  	xt_free_node( node );  	return st;  } + +xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); + +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; +} + +xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ +	struct xt_node *c; +	struct jabber_buddy *bud; +	char *feature, *xmlns, *from; + +	if( !( from = xt_find_attr( node, "from" ) ) || +	    !( c = xt_find_node( node->children, "query" ) ) || +	    !( xmlns = xt_find_attr( c, "xmlns" ) ) || +	    !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) ) +	{ +		imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); +		return XT_HANDLED; +	} +	if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) +	{ +		/* Who cares about the unknown... */ +		imcb_log( ic, "Couldn't find buddy: %s", from ); +		return XT_HANDLED; +	} +	 +	c = c->children; +	while( ( c = xt_find_node( c, "feature" ) ) ) +	{ +		feature = xt_find_attr( c, "var" ); +		if( feature ) +			bud->features = g_slist_append( bud->features, g_strdup( feature ) ); +		c = c->next; +	} + +	return XT_HANDLED; +} + +xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); + +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; +} + +/* + * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info + */ +xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ +	struct xt_node *c; +	struct jabber_data *jd = ic->proto_data; +	char *xmlns, *from; + +	if( !( c = xt_find_node( node->children, "query" ) ) || +	    !( from = xt_find_attr( node, "from" ) ) || +	    !( xmlns = xt_find_attr( c, "xmlns" ) ) ) +	{ +		imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); +		return XT_HANDLED; +	} + +	jd->have_streamhosts++; + +	if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 ) +	{ +		char *itemjid; + +		/* answer from server */ +	 +		c = c->children; +		while( ( c = xt_find_node( c, "item" ) ) ) +		{ +			itemjid = xt_find_attr( c, "jid" ); +			 +			if( itemjid ) +				jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); + +			c = c->next; +		} +	} +	else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) +	{ +		char *category, *type; + +		/* answer from potential proxy */ + +		c = c->children; +		while( ( c = xt_find_node( c, "identity" ) ) ) +		{ +			category = xt_find_attr( c, "category" ); +			type = xt_find_attr( c, "type" ); + +			if( type && ( strcmp( type, "bytestreams" ) == 0 ) && +			    category && ( strcmp( category, "proxy" ) == 0 ) ) +				jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS ); + +			c = c->next; +		} +	} +	else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 ) +	{ +		char *host, *jid, *port_s; +		int port; + +		/* answer from proxy */ + +		if( ( c = xt_find_node( c->children, "streamhost" ) ) && +		    ( host = xt_find_attr( c, "host" ) ) && +		    ( port_s = xt_find_attr( c, "port" ) ) && +		    ( sscanf( port_s, "%d", &port ) == 1 ) && +		    ( jid = xt_find_attr( c, "jid" ) ) ) +		{ +			jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); +			 +			sh->jid = g_strdup( jid ); +			sh->host = g_strdup( host ); +			g_snprintf( sh->port, sizeof( sh->port ), "%u", port ); + +			imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port ); +			jd->streamhosts = g_slist_append( jd->streamhosts, sh ); +		} +	} + +	if( jd->have_streamhosts == 0 ) +		jd->have_streamhosts++; + +	return XT_HANDLED; +} diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 75351d0d..01353f8e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -64,6 +64,8 @@ static void jabber_init( account_t *acc )  	s->flags |= ACC_SET_OFFLINE_ONLY;  	s = set_add( &acc->set, "priority", "0", set_eval_priority, acc ); + +	s = set_add( &acc->set, "proxy", "<local>;<auto>", NULL, acc );  	s = set_add( &acc->set, "resource", "BitlBee", NULL, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY; @@ -265,11 +267,23 @@ static void jabber_logout( struct im_connection *ic )  {  	struct jabber_data *jd = ic->proto_data; +	while( jd->filetransfers ) +		imcb_file_canceled( ic, ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); + +	while( jd->streamhosts ) +	{ +		jabber_streamhost_t *sh = jd->streamhosts->data; +		jd->streamhosts = g_slist_remove( jd->streamhosts, sh ); +		g_free( sh->jid ); +		g_free( sh->host ); +		g_free( sh ); +	} +  	if( jd->fd >= 0 )  		jabber_end_stream( ic );  	while( ic->groupchats ) -		jabber_chat_free( ic->groupchats ); +		jabber_chat_free( ic->groupchats->data );  	if( jd->r_inpa >= 0 )  		b_event_remove( jd->r_inpa ); @@ -400,7 +414,7 @@ static void jabber_add_buddy( struct im_connection *ic, char *who, char *group )  		return;  	} -	if( jabber_add_to_roster( ic, who, NULL ) ) +	if( jabber_add_to_roster( ic, who, NULL, group ) )  		presence_send_request( ic, who, "subscribe" );  } @@ -426,7 +440,7 @@ static void jabber_remove_buddy( struct im_connection *ic, char *who, char *grou  		presence_send_request( ic, who, "unsubscribe" );  } -static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password ) +static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets )  {  	if( strchr( room, '@' ) == NULL )  		imcb_error( ic, "Invalid room name: %s", room ); @@ -548,6 +562,7 @@ void jabber_initmodule()  	ret->keepalive = jabber_keepalive;  	ret->send_typing = jabber_send_typing;  	ret->handle_cmp = g_strcasecmp; +	ret->transfer_request = jabber_si_transfer_request;  	register_protocol( ret );  } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 3f4144b8..45a1c5c1 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -60,6 +60,14 @@ typedef enum  	                                   have a real JID. */  } jabber_buddy_flags_t; +/* Stores a streamhost's (a.k.a. proxy) data */ +typedef struct +{ +	char *jid; +	char *host; +	char port[6]; +} jabber_streamhost_t; +  typedef enum  {  	JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so @@ -90,6 +98,10 @@ struct jabber_data  	md5_state_t cached_id_prefix;  	GHashTable *node_cache;  	GHashTable *buddies; + +	GSList *filetransfers; +	GSList *streamhosts; +	int have_streamhosts;  };  struct jabber_away_state @@ -126,6 +138,7 @@ struct jabber_buddy  	int priority;  	struct jabber_away_state *away_state;  	char *away_message; +	GSList *features;  	time_t last_msg;  	jabber_buddy_flags_t flags; @@ -141,6 +154,36 @@ struct jabber_chat  	struct jabber_buddy *me;  }; +struct jabber_transfer +{ +	/* bitlbee's handle for this transfer */ +	file_transfer_t *ft; + +	/* the stream's private handle */ +	gpointer streamhandle; + +	/* timeout for discover queries */ +	gint disco_timeout; +	gint disco_timeout_fired; + +	struct im_connection *ic; + +	struct jabber_buddy *bud; + +	int watch_in; +	int watch_out; + +	char *ini_jid; +	char *tgt_jid; +	char *iq_id; +	char *sid; +	int accepted; + +	size_t bytesread, byteswritten; +	int fd; +	struct sockaddr_storage saddr; +}; +  #define JABBER_XMLCONSOLE_HANDLE "xmlconsole"  /* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the @@ -166,17 +209,24 @@ struct jabber_chat  #define XMLNS_ROSTER       "jabber:iq:roster"  /* Some supported extensions/legacy stuff */ -#define XMLNS_AUTH         "jabber:iq:auth"                     /* XEP-0078 */ -#define XMLNS_VERSION      "jabber:iq:version"                  /* XEP-0092 */ -#define XMLNS_TIME         "jabber:iq:time"                     /* XEP-0090 */ -#define XMLNS_PING         "urn:xmpp:ping"                      /* XEP-0199 */ -#define XMLNS_VCARD        "vcard-temp"                         /* XEP-0054 */ -#define XMLNS_DELAY        "jabber:x:delay"                     /* XEP-0091 */ -#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"  /* 0085 */ -#define XMLNS_DISCOVER     "http://jabber.org/protocol/disco#info"  /* 0030 */ -#define XMLNS_MUC          "http://jabber.org/protocol/muc"     /* XEP-0045 */ -#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"/* XEP-0045 */ -#define XMLNS_CAPS         "http://jabber.org/protocol/caps"    /* XEP-0115 */ +#define XMLNS_AUTH         "jabber:iq:auth"                                      /* XEP-0078 */ +#define XMLNS_VERSION      "jabber:iq:version"                                   /* XEP-0092 */ +#define XMLNS_TIME         "jabber:iq:time"                                      /* XEP-0090 */ +#define XMLNS_PING         "urn:xmpp:ping"                                       /* XEP-0199 */ +#define XMLNS_VCARD        "vcard-temp"                                          /* XEP-0054 */ +#define XMLNS_DELAY        "jabber:x:delay"                                      /* XEP-0091 */ +#define XMLNS_XDATA        "jabber:x:data"                                       /* XEP-0004 */ +#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"               /* XEP-0085 */ +#define XMLNS_DISCO_INFO   "http://jabber.org/protocol/disco#info"               /* XEP-0030 */ +#define XMLNS_DISCO_ITEMS  "http://jabber.org/protocol/disco#items"              /* XEP-0030 */ +#define XMLNS_MUC          "http://jabber.org/protocol/muc"                      /* XEP-0045 */ +#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"                 /* XEP-0045 */ +#define XMLNS_CAPS         "http://jabber.org/protocol/caps"                     /* XEP-0115 */ +#define XMLNS_FEATURE      "http://jabber.org/protocol/feature-neg"              /* XEP-0020 */ +#define XMLNS_SI           "http://jabber.org/protocol/si"                       /* XEP-0095 */ +#define XMLNS_FILETRANSFER "http://jabber.org/protocol/si/profile/file-transfer" /* XEP-0096 */ +#define XMLNS_BYTESTREAMS  "http://jabber.org/protocol/bytestreams"              /* XEP-0065 */ +#define XMLNS_IBB          "http://jabber.org/protocol/ibb"                      /* XEP-0047 */  /* iq.c */  xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); @@ -184,8 +234,20 @@ int jabber_init_iq_auth( struct im_connection *ic );  xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );  int jabber_get_roster( struct im_connection *ic );  int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); -int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); +int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group );  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 ); + +/* si.c */ +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode ); +void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); +void jabber_si_free_transfer( file_transfer_t *ft); + +/* s5bytestream.c */ +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); +gboolean jabber_bs_send_start( struct jabber_transfer *tf ); +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len );  /* message.c */  xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); @@ -199,7 +261,7 @@ int presence_send_request( struct im_connection *ic, char *handle, char *request  char *set_eval_priority( set_t *set, char *value );  char *set_eval_tls( set_t *set, char *value );  struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children ); -struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ); +struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code );  void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func );  struct xt_node *jabber_cache_get( struct im_connection *ic, char *id );  void jabber_cache_entry_free( gpointer entry ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 651b7068..ab3e6c38 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -98,7 +98,7 @@ struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_  	return node;  } -struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ) +struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code )  {  	struct xt_node *node, *c;  	char *to; @@ -111,6 +111,10 @@ struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond,  	c = xt_new_node( "error", NULL, c );  	xt_add_attr( c, "type", err_type ); +	/* Add the error code, if present */ +	if (err_code) +		xt_add_attr( c, "code", err_code ); +	  	/* To make the actual error packet, we copy the original packet and  	   add our <error>/type="error" tag. Including the original packet  	   is recommended, so let's just do it. */ @@ -274,8 +278,7 @@ static void jabber_buddy_ask_yes( void *data )  	presence_send_request( bla->ic, bla->handle, "subscribed" ); -	if( imcb_find_buddy( bla->ic, bla->handle ) == NULL ) -		imcb_ask_add( bla->ic, bla->handle, NULL ); +	imcb_ask_add( bla->ic, bla->handle, NULL );  	g_free( bla->handle );  	g_free( bla ); @@ -457,7 +460,7 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  		}  		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && -		    ( bare_exists || imcb_find_buddy( ic, jid ) ) ) +		    ( bare_exists || bee_user_by_handle( ic->bee, ic, jid ) ) )  		{  			*s = '/';  			bud = jabber_buddy_add( ic, jid ); @@ -478,7 +481,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  		if( bud == NULL )  			/* No match. Create it now? */ -			return ( ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid_ ) ) ? +			return ( ( flags & GET_BUDDY_CREAT ) && +			         bee_user_by_handle( ic->bee, ic, jid_ ) ) ?  			           jabber_buddy_add( ic, jid_ ) : NULL;  		else if( bud->resource && ( flags & GET_BUDDY_EXACT ) )  			/* We want an exact match, so in thise case there shouldn't be a /resource. */ diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 006eeead..2875d23e 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -204,7 +204,7 @@ int presence_send_update( struct im_connection *ic )  {  	struct jabber_data *jd = ic->proto_data;  	struct xt_node *node, *cap; -	struct groupchat *c; +	GSList *l;  	int st;  	node = jabber_make_packet( "presence", NULL, NULL, NULL ); @@ -228,8 +228,9 @@ int presence_send_update( struct im_connection *ic )  	/* 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 ) +	for( l = ic->groupchats; l && st; l = l->next )  	{ +		struct groupchat *c = l->data;  		struct jabber_chat *jc = c->data;  		xt_add_attr( node, "to", jc->my_full_jid ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c new file mode 100644 index 00000000..6759b78b --- /dev/null +++ b/protocols/jabber/s5bytestream.c @@ -0,0 +1,1153 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Jabber module - SOCKS5 Bytestreams ( XEP-0065 )                          * +*                                                                           * +*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                         * +*                                                                           * +*  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" +#include "sha1.h" +#include "lib/ftutil.h" +#include <poll.h> + +struct bs_transfer { + +	struct jabber_transfer *tf; + +	jabber_streamhost_t *sh; +	GSList *streamhosts; + +	enum  +	{  +		BS_PHASE_CONNECT,  +		BS_PHASE_CONNECTED,  +		BS_PHASE_REQUEST,  +		BS_PHASE_REPLY +	} phase; + +	/* SHA1( SID + Initiator JID + Target JID) */ +	char *pseudoadr; + +	gint connect_timeout; +	 +	char peek_buf[64]; +	int peek_buf_len; +}; + +struct socks5_message +{ +	unsigned char ver; +	union +	{ +		unsigned char cmd; +		unsigned char rep; +	} cmdrep; +	unsigned char rsv; +	unsigned char atyp; +	unsigned char addrlen; +	unsigned char address[40]; +	in_port_t port; +} __attribute__ ((packed));  + +char *socks5_reply_code[] = { +	"succeeded", +	"general SOCKS server failure", +	"connection not allowed by ruleset", +	"Network unreachable", +	"Host unreachable", +	"Connection refused", +	"TTL expired", +	"Command not supported", +	"Address type not supported", +	"unassigned"}; + +/* connect() timeout in seconds. */ +#define JABBER_BS_CONTIMEOUT 15 +/* listen timeout */ +#define JABBER_BS_LISTEN_TIMEOUT  90 + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ +	if( (op) == -1 ) \ +		return jabber_bs_abort( bt , msg ": %s", strerror( errno ) ); + +gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ); +void jabber_bs_canceled( file_transfer_t *ft , char *reason ); +void jabber_bs_free_transfer( file_transfer_t *ft ); +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ); +gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); + +void jabber_bs_recv_answer_request( struct bs_transfer *bt ); +gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_recv_write_request( file_transfer_t *ft ); +gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ); +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode ); + +gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); +gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ); +gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); +static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); +void jabber_bs_send_activate( struct bs_transfer *bt ); + +/* + * Frees a bs_transfer struct and calls the SI free function + */ +void jabber_bs_free_transfer( file_transfer_t *ft) { +	struct jabber_transfer *tf = ft->data; +	struct bs_transfer *bt = tf->streamhandle; +	jabber_streamhost_t *sh; + +	if ( bt->connect_timeout ) +	{ +		b_event_remove( bt->connect_timeout ); +		bt->connect_timeout = 0; +	} + +	if ( tf->watch_in ) +		b_event_remove( tf->watch_in ); +	 +	if( tf->watch_out ) +		b_event_remove( tf->watch_out ); +	 +	g_free( bt->pseudoadr ); + +	while( bt->streamhosts ) +	{ +		sh = bt->streamhosts->data; +		bt->streamhosts = g_slist_remove( bt->streamhosts, sh ); +		g_free( sh->jid ); +		g_free( sh->host ); +		g_free( sh ); +	} +	 +	g_free( bt ); + +	jabber_si_free_transfer( ft ); +} + +/* + * Checks if buflen data is available on the socket and + * writes it to buffer if that's the case. + */ +gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) +{ +	int ret; +	int fd = bt->tf->fd; + +	if( buflen > sizeof( bt->peek_buf ) ) +		return jabber_bs_abort( bt, "BUG: %d > sizeof(peek_buf)", buflen ); + +	ASSERTSOCKOP( ret = recv( fd, bt->peek_buf + bt->peek_buf_len, +		buflen - bt->peek_buf_len, 0 ), "recv() on SOCKS5 connection" ); + +	if( ret == 0 ) +		return jabber_bs_abort( bt, "Remote end closed connection" ); +	 +	bt->peek_buf_len += ret; +	memcpy( buffer, bt->peek_buf, bt->peek_buf_len ); +	 +	if( bt->peek_buf_len == buflen ) +	{ +		/* If we have everything the caller wanted, reset the peek buffer. */ +		bt->peek_buf_len = 0; +		return buflen; +	} +	else +		return bt->peek_buf_len; +} + + +/*  + * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). + */ +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) +{ +	struct bs_transfer *bt = data; + +	bt->connect_timeout = 0; + +	jabber_bs_abort( bt, "no connection after %d seconds", bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT ); + +	return FALSE; +} + +/*  + * Polls the socket, checks for errors and removes a connect timer + * if there is one. + */ +gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) +{ +	struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; +	 +	if ( bt->connect_timeout ) +	{ +		b_event_remove( bt->connect_timeout ); +		bt->connect_timeout = 0; +	} + +	ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) + +	if( pfd.revents & POLLERR ) +	{ +		int sockerror; +		socklen_t errlen = sizeof( sockerror ); + +		if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) +			return jabber_bs_abort( bt, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); + +		if ( bt->phase == BS_PHASE_CONNECTED ) +			return jabber_bs_abort( bt, "connect failed: %s", strerror( sockerror ) ); + +		return jabber_bs_abort( bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); +	} + +	if( pfd.revents & POLLHUP ) +		return jabber_bs_abort( bt, "Remote end closed connection" ); +	 +	*revents = pfd.revents; +	 +	return TRUE; +} + +/* + * Used for receive and send path. + */ +gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) +{ +	va_list params; +	va_start( params, format ); +	char error[128]; + +	if( vsnprintf( error, 128, format, params ) < 0 ) +		sprintf( error, "internal error parsing error string (BUG)" ); +	va_end( params ); +	if( bt->tf->ft->sending ) +		return jabber_bs_send_handshake_abort( bt, error ); +	else +		return jabber_bs_recv_handshake_abort( bt, error ); +} + +/* Bad luck */ +void jabber_bs_canceled( file_transfer_t *ft , char *reason ) +{ +	struct jabber_transfer *tf = ft->data; + +	imcb_log( tf->ic, "File transfer aborted: %s", reason ); +} + +/* + * Parses an incoming bytestream request and calls jabber_bs_handshake on success. + */ +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) +{ +	char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; +	struct jabber_data *jd = ic->proto_data; +	struct jabber_transfer *tf = NULL; +	GSList *tflist; +	struct bs_transfer *bt; +	GSList *shlist=NULL; +	struct xt_node *shnode; + +	sha1_state_t sha; +	char hash_hex[41]; +	unsigned char hash[20]; +	int i; +	 +	if( !(iq_id   = xt_find_attr( node, "id" ) ) || +	    !(ini_jid = xt_find_attr( node, "from" ) ) || +	    !(tgt_jid = xt_find_attr( node, "to" ) ) || +	    !(sid     = xt_find_attr( qnode, "sid" ) ) ) +	{ +		imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); +		return XT_HANDLED; +	} + +	if( ( mode = xt_find_attr( qnode, "mode" ) ) && +	      ( strcmp( mode, "tcp" ) != 0 ) )  +	{ +		imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); +		return XT_HANDLED; +	} + +	shnode = qnode->children; +	while( ( shnode = xt_find_node( shnode, "streamhost" ) ) ) +	{ +		char *jid, *host, *port_s; +		int port; +		if( ( jid = xt_find_attr( shnode, "jid" ) ) && +		    ( host = xt_find_attr( shnode, "host" ) ) && +		    ( port_s = xt_find_attr( shnode, "port" ) ) && +		    ( sscanf( port_s, "%d", &port ) == 1 ) ) +		{ +			jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); +			sh->jid = g_strdup(jid); +			sh->host = g_strdup(host); +			sprintf( sh->port, "%u", port ); +			shlist = g_slist_append( shlist, sh ); +		} +		shnode = shnode->next; +	} +	 +	if( !shlist ) +	{ +		imcb_log( ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries"); +		return XT_HANDLED; +	} + +	/* Let's see if we can find out what this bytestream should be for... */ + +	for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) +	{ +		struct jabber_transfer *tft = tflist->data; +		if( ( strcmp( tft->sid, sid ) == 0 ) && +		    ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && +		    ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) +		{ +		    	tf = tft; +			break; +		} +	} + +	if (!tf)  +	{ +		imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); +		return XT_HANDLED; +	} + +	/* iq_id and canceled can be reused since SI is done */ +	g_free( tf->iq_id ); +	tf->iq_id = g_strdup( iq_id ); + +	tf->ft->canceled = jabber_bs_canceled; + +	/* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ +	sha1_init( &sha ); +	sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); +	sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); +	sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); +	sha1_finish( &sha, hash ); +	 +	for( i = 0; i < 20; i ++ ) +		sprintf( hash_hex + i * 2, "%02x", hash[i] ); +		 +	bt = g_new0( struct bs_transfer, 1 ); +	bt->tf = tf; +	bt->streamhosts = shlist; +	bt->sh = shlist->data; +	bt->phase = BS_PHASE_CONNECT; +	bt->pseudoadr = g_strdup( hash_hex ); +	tf->streamhandle = bt; +	tf->ft->free = jabber_bs_free_transfer; + +	jabber_bs_recv_handshake( bt, -1, 0 );  + +	return XT_HANDLED; +} + +/* + * This is what a protocol handshake can look like in cooperative multitasking :) + * Might be confusing at first because it's called from different places and is recursing. + * (places being the event thread, bs_request, bs_handshake_abort, and itself) + * + * All in all, it turned out quite nice :) + */ +gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + +	struct bs_transfer *bt = data; +	short revents; +	int gret; + +	if ( ( fd != -1 ) && !jabber_bs_poll( bt, fd, &revents ) ) +		return FALSE; +	 +	switch( bt->phase )  +	{ +	case BS_PHASE_CONNECT: +		{ +			struct addrinfo hints, *rp; + +			memset( &hints, 0, sizeof( struct addrinfo ) ); +			hints.ai_socktype = SOCK_STREAM; + +			if ( ( gret = getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) ) != 0 ) +				return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); + +			ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + +			sock_make_nonblocking( fd ); + +			imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host, bt->sh->port ); + +			if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && +			    ( errno != EINPROGRESS ) ) +				return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); + +			freeaddrinfo( rp ); + +			bt->phase = BS_PHASE_CONNECTED; +			 +			bt->tf->watch_out = b_input_add( fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt ); + +			/* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ +			bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + +			bt->tf->watch_in = 0; +			return FALSE; +		} +	case BS_PHASE_CONNECTED: +		{ +			struct { +				unsigned char ver; +				unsigned char nmethods; +				unsigned char method; +			} socks5_hello = { +				.ver = 5, +				.nmethods = 1, +				.method = 0x00 /* no auth */ +				/* one could also implement username/password. If you know +				 * a jabber client or proxy that actually does it, tell me. +				 */ +			}; +			 +			ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); + +			bt->phase = BS_PHASE_REQUEST; + +			bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt ); + +			bt->tf->watch_out = 0; +			return FALSE; +		} +	case BS_PHASE_REQUEST: +		{ +			struct socks5_message socks5_connect =  +			{ +				.ver = 5, +				.cmdrep.cmd = 0x01, +				.rsv = 0, +				.atyp = 0x03, +				.addrlen = strlen( bt->pseudoadr ), +				.port = 0 +			}; +			int ret; +			char buf[2]; + +			/* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ +			ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); + +			if( !( ret == 2 ) || +			    !( buf[0] == 5 ) || +			    !( buf[1] == 0 ) ) +				return jabber_bs_abort( bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", +									ret, buf[0], buf[1] ); + +			/* copy hash into connect message */ +			memcpy( socks5_connect.address, bt->pseudoadr, socks5_connect.addrlen ); + +			ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_message ), 0 ) , "Sending SOCKS5 Connect" ); + +			bt->phase = BS_PHASE_REPLY; + +			return TRUE; +		} +	case BS_PHASE_REPLY: +		{ +			struct socks5_message socks5_reply; +			int ret; + +			if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) ) +				return FALSE; + +			if ( ret < 5 ) /* header up to address length */ +				return TRUE; +			else if( ret < sizeof( struct socks5_message ) ) +			{ +				/* Either a buggy proxy or just one that doesnt regard +				 * the SHOULD in XEP-0065 saying the reply SHOULD +				 * contain the address. We'll take it, so make sure the +				 * next jabber_bs_peek starts with an empty buffer. */ +				bt->peek_buf_len = 0; +			} + +			if( !( socks5_reply.ver == 5 ) || +			    !( socks5_reply.cmdrep.rep == 0 ) ) { +			    	char errstr[128] = ""; +				if( ( socks5_reply.ver == 5 ) && ( socks5_reply.cmdrep.rep <  +				    ( sizeof( socks5_reply_code ) / sizeof( socks5_reply_code[0] ) ) ) ) { +					sprintf( errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ] ); +				} +				return jabber_bs_abort( bt, "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)",  +					errstr, +					socks5_reply.ver, +					socks5_reply.cmdrep.rep, +					socks5_reply.atyp, +					socks5_reply.addrlen); +			} +			 +			/* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz)  +			 * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). +			 * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy +			 * is sending, it shouldnt matter */ + +			if( bt->tf->ft->sending ) +				jabber_bs_send_activate( bt ); +			else +				jabber_bs_recv_answer_request( bt ); + +			return FALSE; +		} +	default: +		/* BUG */ +		imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + +		bt->tf->watch_in = 0; +		return FALSE; +	} +} + +/* + * If the handshake failed we can try the next streamhost, if there is one. + * An intelligent sender would probably specify himself as the first streamhost and + * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)  + * slow proxy is only used if neccessary. This of course also means, that the timeout + * per streamhost should be kept short. If one or two firewalled adresses are specified, + * they have to timeout first before a proxy is tried. + */ +gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) +{ +	struct jabber_transfer *tf = bt->tf; +	struct xt_node *reply, *iqnode; +	GSList *shlist; + +	imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)",  +		  tf->ft->file_name,  +		  bt->sh->host, +		  bt->sh->port, +		  error ); + +	/* Alright, this streamhost failed, let's try the next... */ +	bt->phase = BS_PHASE_CONNECT; +	shlist = g_slist_find( bt->streamhosts, bt->sh ); +	if( shlist && shlist->next ) +	{ +		bt->sh = shlist->next->data; +		return jabber_bs_recv_handshake( bt, -1, 0 ); +	} + + +	/* out of stream hosts */ + +	iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); +	reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); +	xt_free_node( iqnode ); + +	xt_add_attr( reply, "id", tf->iq_id ); +		 +	if( !jabber_write_packet( tf->ic, reply ) ) +		imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); +	xt_free_node( reply ); + +	imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" ); + +	/* MUST always return FALSE! */ +	return FALSE; +} + +/*  + * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. + * If he is the streamhost himself, he might already know that. However, if it's a proxy, + * the initiator will have to make a connection himself. + */ +void jabber_bs_recv_answer_request( struct bs_transfer *bt ) +{ +	struct jabber_transfer *tf = bt->tf; +	struct xt_node *reply; + +	imcb_log( tf->ic, "File %s: established SOCKS5 connection to %s:%s",  +		  tf->ft->file_name,  +		  bt->sh->host, +		  bt->sh->port ); + +	tf->ft->data = tf; +	tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); +	tf->ft->write_request = jabber_bs_recv_write_request; + +	reply = xt_new_node( "streamhost-used", NULL, NULL ); +	xt_add_attr( reply, "jid", bt->sh->jid ); + +	reply = xt_new_node( "query", NULL, reply ); +	xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); + +	reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); + +	xt_add_attr( reply, "id", tf->iq_id ); +		 +	if( !jabber_write_packet( tf->ic, reply ) ) +		imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream response" ); +	xt_free_node( reply ); +} + +/*  + * This function is called from write_request directly. If no data is available, it will install itself + * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again. + */ +gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) +{ +	int ret; +	struct bs_transfer *bt = data; +	struct jabber_transfer *tf = bt->tf; + +	if( fd != -1 ) /* called via event thread */ +	{ +		tf->watch_in = 0; +		ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" ); +	} +	else +	{ +		/* called directly. There might not be any data available. */ +		if( ( ( ret = recv( tf->fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) ) == -1 ) && +		    ( errno != EAGAIN ) ) +		    return jabber_bs_abort( bt, "Receiving: %s", strerror( errno ) ); + +		if( ( ret == -1 ) && ( errno == EAGAIN ) ) +		{ +			tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); +			return FALSE; +		} +	} + +	/* shouldn't happen since we know the file size */ +	if( ret == 0 ) +		return jabber_bs_abort( bt, "Remote end closed connection" ); +	 +	tf->bytesread += ret; + +	if( tf->bytesread >= tf->ft->file_size ) +		imcb_file_finished( tf->ic, tf->ft ); + +	tf->ft->write( tf->ft, tf->ft->buffer, ret );	 + +	return FALSE; +} + +/*  + * imc callback that is invoked when it is ready to receive some data. + */ +gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) +{ +	struct jabber_transfer *tf = ft->data; + +	if( tf->watch_in ) +	{ +		imcb_file_canceled( tf->ic, ft, "BUG in jabber file transfer: write_request called when already watching for input" ); +		return FALSE; +	} +	 +	jabber_bs_recv_read( tf->streamhandle, -1 , 0 ); + +	return TRUE; +} + +/*  + * Issues a write_request to imc. + * */ +gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition cond ) +{ +	struct bs_transfer *bt = data; + +	bt->tf->watch_out = 0; + +	bt->tf->ft->write_request( bt->tf->ft ); + +	return FALSE; +} + +/* + * This should only be called if we can write, so just do it. + * Add a write watch so we can write more during the next cycle (if possible). + */ +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len ) +{ +	struct jabber_transfer *tf = ft->data; +	struct bs_transfer *bt = tf->streamhandle; +	int ret; + +	if( tf->watch_out ) +		return jabber_bs_abort( bt, "BUG: write() called while watching " ); +	 +	/* TODO: catch broken pipe */ +	ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); + +	tf->byteswritten += ret; +	 +	/* TODO: this should really not be fatal */ +	if( ret < len ) +		return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); + +	if( tf->byteswritten >= ft->file_size ) +		imcb_file_finished( tf->ic, ft ); +	else +		bt->tf->watch_out = b_input_add( tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt ); +		 +	return TRUE; +} + +/* + * Handles the reply by the receiver containing the used streamhost. + */ +static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { +	struct jabber_transfer *tf = NULL; +	struct jabber_data *jd = ic->proto_data; +	struct bs_transfer *bt; +	GSList *tflist; +	struct xt_node *c; +	char *sid, *jid; + +	if( !( c = xt_find_node( node->children, "query" ) ) || +	    !( c = xt_find_node( c->children, "streamhost-used" ) ) || +	    !( jid = xt_find_attr( c, "jid" ) ) ) + +	{ +		imcb_log( ic, "WARNING: Received incomplete bytestream reply" ); +		return XT_HANDLED; +	} +	 +	if( !( c = xt_find_node( orig->children, "query" ) ) || +	    !( sid = xt_find_attr( c, "sid" ) ) ) +	{ +		imcb_log( ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply" ); +		return XT_HANDLED; +	} + +	/* Let's see if we can find out what this bytestream should be for... */ + +	for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) +	{ +		struct jabber_transfer *tft = tflist->data; +		if( ( strcmp( tft->sid, sid ) == 0 ) ) +		{ +		    	tf = tft; +			break; +		} +	} + +	if( !tf ) +	{ +		imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply to unknown request" ); +		return XT_HANDLED; +	} + +	bt = tf->streamhandle; + +	tf->accepted = TRUE; + +	if( strcmp( jid, tf->ini_jid ) == 0 ) +	{ +		/* we're streamhost and target */ +		if( bt->phase == BS_PHASE_REPLY ) +		{ +			/* handshake went through, let's start transferring */ +			tf->ft->write_request( tf->ft ); +		} +	} else +	{ +		/* using a proxy, abort listen */ + +		if( tf->watch_in ) +		{ +			b_event_remove( tf->watch_in ); +			tf->watch_in = 0; +		} +		 +		if( tf->fd != -1 ) { +			closesocket( tf->fd ); +			tf->fd = -1; +		} + +		if ( bt->connect_timeout ) +		{ +			b_event_remove( bt->connect_timeout ); +			bt->connect_timeout = 0; +		} + +		GSList *shlist; +		for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) ) +		{ +			jabber_streamhost_t *sh = shlist->data; +			if( strcmp( sh->jid, jid ) == 0 ) +			{ +				bt->sh = sh; +				jabber_bs_recv_handshake( bt, -1, 0 ); +				return XT_HANDLED; +			} +		} + +		imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid ); +	} + +	return XT_HANDLED; +} + +/*  + * Tell the proxy to activate the stream. Looks like this: + * + * <iq type=set> + * 	<query xmlns=bs sid=sid> + * 		<activate>tgt_jid</activate> + * 	</query> + * </iq> + */ +void jabber_bs_send_activate( struct bs_transfer *bt ) +{ +	struct xt_node *node; + +	node = xt_new_node( "activate", bt->tf->tgt_jid, NULL ); +	node = xt_new_node( "query", NULL, node ); +	xt_add_attr( node, "xmlns", XMLNS_BYTESTREAMS ); +	xt_add_attr( node, "sid", bt->tf->sid ); +	node = jabber_make_packet( "iq", "set", bt->sh->jid, node ); + +	jabber_cache_add( bt->tf->ic, node, jabber_bs_send_handle_activate ); + +	jabber_write_packet( bt->tf->ic, node ); +} + +/* + * The proxy has activated the bytestream. + * We can finally start pushing some data out. + */ +static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ +	char *sid; +	GSList *tflist; +	struct jabber_transfer *tf = NULL; +	struct xt_node *query; +	struct jabber_data *jd = ic->proto_data; + +	query = xt_find_node( orig->children, "query" ); +	sid = xt_find_attr( query, "sid" ); + +	for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) +	{ +		struct jabber_transfer *tft = tflist->data; +		if( ( strcmp( tft->sid, sid ) == 0 ) ) +		{ +		    	tf = tft; +			break; +		} +	} + +	if( !tf ) +	{ +		imcb_log( ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream" ); +		return XT_HANDLED; +	} + +	imcb_log( tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...", tf->ft->file_name ); + +	/* handshake went through, let's start transferring */ +	tf->ft->write_request( tf->ft ); + +	return XT_HANDLED; +} + +jabber_streamhost_t *jabber_si_parse_proxy( struct im_connection *ic, char *proxy ) +{ +	char *host, *port, *jid; +	jabber_streamhost_t *sh; + +	if( ( ( host = strchr( proxy, ',' ) ) == 0 ) || +	     ( ( port = strchr( host+1, ',' ) ) == 0 ) ) { +		imcb_log( ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy ); +		return NULL; +	} +	 +	jid = proxy; +	*host++ = '\0'; +	*port++ = '\0'; + +	sh = g_new0( jabber_streamhost_t, 1 ); +	sh->jid = g_strdup( jid ); +	sh->host = g_strdup( host ); +	strcpy( sh->port, port ); + +	return sh; +} + +void jabber_si_set_proxies( struct bs_transfer *bt ) +{ +	struct jabber_transfer *tf = bt->tf; +	struct jabber_data *jd = tf->ic->proto_data; +	char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); +	char *proxy, *next, *errmsg = NULL; +	char port[6]; +	char host[HOST_NAME_MAX+1]; +	jabber_streamhost_t *sh, *sh2; +	GSList *streamhosts = jd->streamhosts; + +	proxy = proxysetting; +	while ( proxy && ( *proxy!='\0' ) ) { +		if( ( next = strchr( proxy, ';' ) ) ) +			*next++ = '\0';	 +		 +		if( strcmp( proxy, "<local>" ) == 0 ) { +			if( ( tf->fd = ft_listen( &tf->saddr, host, port, FALSE, &errmsg ) ) != -1 ) { +				sh = g_new0( jabber_streamhost_t, 1 ); +				sh->jid = g_strdup( tf->ini_jid ); +				sh->host = g_strdup( host ); +				strcpy( sh->port, port ); +				bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + +				bt->tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); +				bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); +			} else { +				imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", +					  tf->ft->file_name, +					  errmsg ); +			} +		} else if( strcmp( proxy, "<auto>" ) == 0 ) { +			while ( streamhosts ) { +				sh = g_new0( jabber_streamhost_t, 1 ); +				sh2 = streamhosts->data; +				sh->jid = g_strdup( sh2->jid ); +				sh->host = g_strdup( sh2->host ); +				strcpy( sh->port, sh2->port ); +				bt->streamhosts = g_slist_append( bt->streamhosts, sh ); +				streamhosts = g_slist_next( streamhosts ); +			} +		} else if( ( sh = jabber_si_parse_proxy( tf->ic, proxy ) ) ) +			bt->streamhosts = g_slist_append( bt->streamhosts, sh ); +		proxy = next; +	} +} + +/* + * Starts a bytestream. + */ +gboolean jabber_bs_send_start( struct jabber_transfer *tf ) +{ +	struct bs_transfer *bt; +	sha1_state_t sha; +	char hash_hex[41]; +	unsigned char hash[20]; +	int i,ret; + +	/* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ +	sha1_init( &sha ); +	sha1_append( &sha, (unsigned char*) tf->sid, strlen( tf->sid ) ); +	sha1_append( &sha, (unsigned char*) tf->ini_jid, strlen( tf->ini_jid ) ); +	sha1_append( &sha, (unsigned char*) tf->tgt_jid, strlen( tf->tgt_jid ) ); +	sha1_finish( &sha, hash ); +	 +	for( i = 0; i < 20; i ++ ) +		sprintf( hash_hex + i * 2, "%02x", hash[i] ); +		 +	bt = g_new0( struct bs_transfer, 1 ); +	bt->tf = tf; +	bt->phase = BS_PHASE_CONNECT; +	bt->pseudoadr = g_strdup( hash_hex ); +	tf->streamhandle = bt; +	tf->ft->free = jabber_bs_free_transfer; +	tf->ft->canceled = jabber_bs_canceled; + +	jabber_si_set_proxies( bt ); + +	ret = jabber_bs_send_request( tf, bt->streamhosts); + +	return ret; +} + +gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ) +{ +	struct xt_node *shnode, *query, *iq; + +	query = xt_new_node( "query", NULL, NULL ); +	xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS ); +	xt_add_attr( query, "sid", tf->sid ); +	xt_add_attr( query, "mode", "tcp" ); + +	while( streamhosts ) { +		jabber_streamhost_t *sh = streamhosts->data; +		shnode = xt_new_node( "streamhost", NULL, NULL ); +		xt_add_attr( shnode, "jid", sh->jid ); +		xt_add_attr( shnode, "host", sh->host ); +		xt_add_attr( shnode, "port", sh->port ); + +		xt_add_child( query, shnode ); + +		streamhosts = g_slist_next( streamhosts ); +	} + + +	iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); +	xt_add_attr( iq, "from", tf->ini_jid ); + +	jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); + +	if( !jabber_write_packet( tf->ic, iq ) ) +		imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream request" ); +	return TRUE; +} + +gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) +{ +	struct jabber_transfer *tf = bt->tf; +	struct jabber_data *jd = tf->ic->proto_data; + +	/* TODO: did the receiver get here somehow??? */ +	imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s",  +		  tf->ft->file_name,  +		  error ); + +	if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ +		imcb_file_canceled( tf->ic, tf->ft, error ); + +	/* MUST always return FALSE! */ +	return FALSE; +} + +/* + * SOCKS5BYTESTREAM protocol for the sender + */ +gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ) +{ +	struct bs_transfer *bt = data; +	struct jabber_transfer *tf = bt->tf; +	short revents; + +	if ( !jabber_bs_poll( bt, fd, &revents ) ) +		return FALSE; +	 +	switch( bt->phase )  +	{ +	case BS_PHASE_CONNECT: +		{ +			struct sockaddr_storage clt_addr; +			socklen_t ssize = sizeof( clt_addr ); +			 +			/* Connect */ + +			ASSERTSOCKOP( tf->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + +			closesocket( fd ); +			fd = tf->fd; +			sock_make_nonblocking( fd ); +			 +			bt->phase = BS_PHASE_CONNECTED; + +			bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); +			return FALSE; +		} +	case BS_PHASE_CONNECTED: +		{ +			int ret, have_noauth=FALSE; +			struct { +				unsigned char ver; +				unsigned char method; +			} socks5_auth_reply = { .ver = 5, .method = 0 }; +			struct { +				unsigned char ver; +				unsigned char nmethods; +				unsigned char method; +			} socks5_hello; + +			if( !( ret = jabber_bs_peek( bt, &socks5_hello, sizeof( socks5_hello ) ) ) ) +				return FALSE; + +			if( ret < sizeof( socks5_hello ) ) +				return TRUE; + +			if( !( socks5_hello.ver == 5 ) || +			    !( socks5_hello.nmethods >= 1 ) || +			    !( socks5_hello.nmethods < 32 ) ) +				return jabber_bs_abort( bt, "Invalid auth request ver=%d nmethods=%d method=%d", socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method ); + +			have_noauth = socks5_hello.method == 0; + +			if( socks5_hello.nmethods > 1 ) +			{ +				char mbuf[32]; +				int i; +				ASSERTSOCKOP( ret = recv( fd, mbuf, socks5_hello.nmethods - 1, 0 ) , "Receiving auth methods" ); +				if( ret < ( socks5_hello.nmethods - 1 ) ) +					return jabber_bs_abort( bt, "Partial auth request"); +				for( i = 0 ; !have_noauth && ( i < socks5_hello.nmethods - 1 ) ; i ++ ) +					if( mbuf[i] == 0 ) +						have_noauth = TRUE; +			} +			 +			if( !have_noauth ) +				return jabber_bs_abort( bt, "Auth request didn't include no authentication" ); + +			ASSERTSOCKOP( send( fd, &socks5_auth_reply, sizeof( socks5_auth_reply ) , 0 ), "Sending auth reply" ); + +			bt->phase = BS_PHASE_REQUEST; + +			return TRUE; +		} +	case BS_PHASE_REQUEST: +		{ +			struct socks5_message socks5_connect; +			int msgsize = sizeof( struct socks5_message ); +			int ret; + +			if( !( ret = jabber_bs_peek( bt, &socks5_connect, msgsize ) ) ) +				return FALSE; + +			if( ret < msgsize ) +				return TRUE; + +			if( !( socks5_connect.ver == 5) || +			    !( socks5_connect.cmdrep.cmd == 1 ) || +			    !( socks5_connect.atyp == 3 ) || +			    !(socks5_connect.addrlen == 40 ) ) +				return jabber_bs_abort( bt, "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)", socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd, socks5_connect.atyp ); +			if( !( memcmp( socks5_connect.address, bt->pseudoadr, 40 ) == 0 ) ) +				return jabber_bs_abort( bt, "SOCKS5 Connect message contained wrong digest"); + +			socks5_connect.cmdrep.rep = 0; + +			ASSERTSOCKOP( send( fd, &socks5_connect, msgsize, 0 ), "Sending connect reply" ); + +			bt->phase = BS_PHASE_REPLY; + +			imcb_log( tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name ); + +			if( tf->accepted ) +			{ +				/* streamhost-used message came already in(possible?), let's start sending */ +				tf->ft->write_request( tf->ft ); +			} + +			tf->watch_in = 0; +			return FALSE; + +		} +	default: +		/* BUG */ +		imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + +		bt->tf->watch_in = 0; +		return FALSE; +	} +} +#undef ASSERTSOCKOP diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c new file mode 100644 index 00000000..58c0e17f --- /dev/null +++ b/protocols/jabber/si.c @@ -0,0 +1,529 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Jabber module - SI packets                                               * +*                                                                           * +*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                         * +*                                                                           * +*  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" +#include "sha1.h" + +void jabber_si_answer_request( file_transfer_t *ft ); +int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf ); + +/* file_transfer free() callback */ +void jabber_si_free_transfer( file_transfer_t *ft) +{ +	struct jabber_transfer *tf = ft->data; +	struct jabber_data *jd = tf->ic->proto_data; + +	if ( tf->watch_in ) +		b_event_remove( tf->watch_in ); + +	jd->filetransfers = g_slist_remove( jd->filetransfers, tf ); + +	if( tf->fd != -1 ) +	{ +		closesocket( tf->fd ); +		tf->fd = -1; +	} + +	if( tf->disco_timeout ) +		b_event_remove( tf->disco_timeout ); +	 +	g_free( tf->ini_jid ); +	g_free( tf->tgt_jid ); +	g_free( tf->iq_id ); +	g_free( tf->sid ); +	g_free( tf ); +} + +/* 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 ); + +} + +int jabber_si_check_features( struct jabber_transfer *tf, GSList *features ) { +	int foundft = FALSE, foundbt = FALSE, foundsi = FALSE; + +	while ( features ) +	{ +		if( !strcmp( features->data, XMLNS_FILETRANSFER ) ) +			foundft = TRUE; +		if( !strcmp( features->data, XMLNS_BYTESTREAMS ) ) +			foundbt = TRUE; +		if( !strcmp( features->data, XMLNS_SI ) ) +			foundsi = TRUE; + +		features = g_slist_next(features); +	} + +	if( !foundft ) +		imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature file transfers" ); +	else if( !foundbt ) +		imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)" ); +	else if( !foundsi ) +		imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); +		 +	return foundft && foundbt && foundsi; +} + +void jabber_si_transfer_start( struct jabber_transfer *tf ) { + +	if( !jabber_si_check_features( tf, tf->bud->features ) ) +		return; +		 +	/* send the request to our buddy */ +	jabber_si_send_request( tf->ic, tf->bud->full_jid, tf ); + +	/* and start the receive logic */ +	imcb_file_recv_start( tf->ic, tf->ft ); + +} + +gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond ) +{ +	struct jabber_transfer *tf = data; +	struct jabber_data *jd = tf->ic->proto_data; + +	tf->disco_timeout_fired++; + +	if( tf->bud->features && jd->have_streamhosts==1 ) { +		tf->disco_timeout = 0; +		jabber_si_transfer_start( tf ); +		return FALSE; +	} + +	/* 8 seconds should be enough for server and buddy to respond */ +	if ( tf->disco_timeout_fired < 16 ) +		return TRUE; +	 +	if( !tf->bud->features && jd->have_streamhosts!=1 ) +		imcb_log( tf->ic, "Couldn't get buddy's features nor discover all services of the server" ); +	else if( !tf->bud->features ) +		imcb_log( tf->ic, "Couldn't get buddy's features" ); +	else +		imcb_log( tf->ic, "Couldn't discover some of the server's services" ); +	 +	tf->disco_timeout = 0; +	jabber_si_transfer_start( tf ); +	return FALSE; +} + +void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who )  +{ +	struct jabber_transfer *tf; +	struct jabber_data *jd = ic->proto_data; +	struct jabber_buddy *bud; +	char *server = jd->server, *s; + +	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, 0 ); + +	if( bud == NULL ) +	{ +		imcb_file_canceled( ic, ft, "Couldn't find buddy (BUG?)" ); +		return; +	} +	 +	imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); + +	tf = g_new0( struct jabber_transfer, 1 ); + +	tf->ic = ic; +	tf->ft = ft; +	tf->fd = -1; +	tf->ft->data = tf; +	tf->ft->free = jabber_si_free_transfer; +	tf->bud = bud; +	ft->write = jabber_bs_send_write; + +	jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + +	/* query buddy's features and server's streaming proxies if neccessary */ + +	if( !tf->bud->features ) +		jabber_iq_query_features( ic, bud->full_jid ); + +	/* If <auto> is not set don't check for proxies */ +	if( ( jd->have_streamhosts!=1 ) && ( jd->streamhosts==NULL ) && +	    ( strstr( set_getstr( &ic->acc->set, "proxy" ), "<auto>" ) != NULL ) ) { +		jd->have_streamhosts = 0; +		jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); +	} else if ( jd->streamhosts!=NULL ) +		jd->have_streamhosts = 1; + +	/* if we had to do a query, wait for the result.  +	 * Otherwise fire away. */ +	if( !tf->bud->features || jd->have_streamhosts!=1 ) +		tf->disco_timeout = b_timeout_add( 500, jabber_si_waitfor_disco, tf ); +	else +		jabber_si_transfer_start( tf ); +} + +/* + * First function that gets called when a file transfer request comes in. + * A lot to parse. + * + * We choose a stream type from the options given by the initiator. + * Then we wait for imcb to call the accept or cancel callbacks. + */ +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode) +{ +	struct xt_node *c, *d, *reply; +	char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s; +	struct jabber_buddy *bud; +	int requestok = FALSE; +	char *name, *cmp; +	size_t size; +	struct jabber_transfer *tf; +	struct jabber_data *jd = ic->proto_data; +	file_transfer_t *ft; +	 +	/* All this means we expect something like this: ( I think ) +	 * <iq from=... to=... id=...> +	 * 	<si id=id xmlns=si profile=ft> +	 * 		<file xmlns=ft/> +	 * 		<feature xmlns=feature> +	 * 			<x xmlns=xdata type=submit> +	 * 				<field var=stream-method> +	 * +	 */ +	if( !( ini_jid 		= xt_find_attr(   node, "from" ) 			) || +	    !( tgt_jid 		= xt_find_attr(   node, "to" ) 				) || +	    !( iq_id 		= xt_find_attr(   node, "id" ) 				) || +	    !( sid 		= xt_find_attr( sinode, "id" ) 				) || +	    !( cmp              = xt_find_attr( sinode, "profile" )                     ) || +	    !( 0               == strcmp( cmp, XMLNS_FILETRANSFER )			) || +	    !( d 		= xt_find_node( sinode->children, "file" ) 		) || +	    !( cmp = xt_find_attr( d, "xmlns" )						) || +	    !( 0               == strcmp( cmp, XMLNS_FILETRANSFER )			) || +	    !( name 		= xt_find_attr( d, "name" ) 				) || +	    !( size_s           = xt_find_attr( d, "size" )                             ) || +	    !( 1               == sscanf( size_s, "%zd", &size )                        ) || +	    !( d 		= xt_find_node( sinode->children, "feature" ) 		) || +	    !( cmp              = xt_find_attr( d, "xmlns" )				) || +	    !( 0               == strcmp( cmp, XMLNS_FEATURE )				) || +	    !( d 		= xt_find_node( d->children, "x" ) 			) || +	    !( cmp              = xt_find_attr( d, "xmlns" )				) || +	    !( 0               == strcmp( cmp, XMLNS_XDATA )				) || +	    !( cmp              = xt_find_attr( d, "type" )				) || +	    !( 0               == strcmp( cmp, "form" )					) || +	    !( d 		= xt_find_node( d->children, "field" ) 			) || +	    !( cmp              = xt_find_attr( d, "var" )				) || +	    !( 0               == strcmp( cmp, "stream-method" )			) ) +	{ +		imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); +	} +	else +	{ +		/* Check if we support one of the options */ + +		c = d->children; +		while( ( c = xt_find_node( c, "option" ) ) ) +			if( ( d = xt_find_node( c->children, "value" ) ) && +			    ( d->text != NULL ) && +			    ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) +			{ +				requestok = TRUE; +				break; +			} + +		if ( !requestok ) +			imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); +	} +	 +	if( requestok ) +	{ +		/* Figure out who the transfer should come frome... */ + +		ext_jid = ini_jid; +		if( ( s = strchr( ini_jid, '/' ) ) ) +		{ +			if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) +			{ +				bud->last_msg = time( NULL ); +				ext_jid = bud->ext_jid ? : bud->bare_jid; +			} +			else +				*s = 0; /* We need to generate a bare JID now. */ +		} + +		if( !( ft = imcb_file_send_start( ic, ext_jid, name, size ) ) ) +		{  +			imcb_log( ic, "WARNING: Error handling transfer request from %s", ini_jid); +			requestok = FALSE; +		} + +		*s = '/'; +	} +	else +	{  +		reply = jabber_make_error_packet( node, "item-not-found", "cancel", NULL ); +		if (!jabber_write_packet( ic, reply )) +			imcb_log( ic, "WARNING: Error generating reply to file transfer request" ); +		xt_free_node( reply ); +		return XT_HANDLED; +	} + +	/* Request is fine. */ + +	tf = g_new0( struct jabber_transfer, 1 ); + +	tf->ini_jid = g_strdup( ini_jid ); +	tf->tgt_jid = g_strdup( tgt_jid ); +	tf->iq_id = g_strdup( iq_id ); +	tf->sid = g_strdup( sid ); +	tf->ic = ic; +	tf->ft = ft; +	tf->fd = -1; +	tf->ft->data = tf; +	tf->ft->accept = jabber_si_answer_request; +	tf->ft->free = jabber_si_free_transfer; +	tf->ft->canceled = jabber_si_canceled; + +	jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + +	return XT_HANDLED; +} + +/* + * imc called the accept callback which probably means that the user accepted this file transfer. + * We send our response to the initiator. + * In the next step, the initiator will send us a request for the given stream type. + * (currently that can only be a SOCKS5 bytestream) + */ +void jabber_si_answer_request( file_transfer_t *ft ) { +	struct jabber_transfer *tf = ft->data; +	struct xt_node *node, *sinode, *reply; + +	/* generate response, start with the SI tag */ +	sinode = xt_new_node( "si", NULL, NULL ); +	xt_add_attr( sinode, "xmlns", XMLNS_SI ); +	xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER ); +	xt_add_attr( sinode, "id", tf->sid ); + +	/* now the file tag */ +	node = xt_new_node( "file", NULL, NULL ); +	xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER ); + +	xt_add_child( sinode, node ); + +	/* and finally the feature tag */ +	node = xt_new_node( "field", NULL, NULL ); +	xt_add_attr( node, "var", "stream-method" ); +	xt_add_attr( node, "type", "list-single" ); + +	/* Currently all we can do. One could also implement in-band (IBB) */ +	xt_add_child( node, xt_new_node( "value", XMLNS_BYTESTREAMS, NULL ) ); + +	node = xt_new_node( "x", NULL, node ); +	xt_add_attr( node, "xmlns", XMLNS_XDATA ); +	xt_add_attr( node, "type", "submit" ); + +	node = xt_new_node( "feature", NULL, node ); +	xt_add_attr( node, "xmlns", XMLNS_FEATURE ); + +	xt_add_child( sinode, node ); + +	reply = jabber_make_packet( "iq", "result", tf->ini_jid, sinode ); +	xt_add_attr( reply, "id", tf->iq_id ); +	 +	if( !jabber_write_packet( tf->ic, reply ) ) +		imcb_log( tf->ic, "WARNING: Error generating reply to file transfer request" ); +	else +		tf->accepted = TRUE; +	xt_free_node( reply ); +} + +static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ +	struct xt_node *c, *d; +	char *ini_jid, *tgt_jid, *iq_id, *cmp; +	GSList *tflist; +	struct jabber_transfer *tf=NULL; +	struct jabber_data *jd = ic->proto_data; + +	if( !( tgt_jid = xt_find_attr( node, "from" ) ) || +	    !( ini_jid = xt_find_attr( node, "to" ) ) ) +	{ +		imcb_log( ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid ); +		return XT_HANDLED; +	} +	 +	/* All this means we expect something like this: ( I think ) +	 * <iq from=... to=... id=...> +	 * 	<si xmlns=si> +	 * 	[	<file xmlns=ft/>    ] <-- not neccessary +	 * 		<feature xmlns=feature> +	 * 			<x xmlns=xdata type=submit> +	 * 				<field var=stream-method> +	 * 					<value> +	 */ +	if( !( tgt_jid = xt_find_attr( node, "from" ) ) || +	    !( ini_jid = xt_find_attr( node, "to" ) ) || +	    !( iq_id   = xt_find_attr( node, "id" ) ) || +	    !( c = xt_find_node( node->children, "si" ) ) || +	    !( cmp = xt_find_attr( c, "xmlns" ) ) || +	    !( strcmp( cmp, XMLNS_SI ) == 0 ) || +	    !( d = xt_find_node( c->children, "feature" ) ) || +	    !( cmp = xt_find_attr( d, "xmlns" ) ) || +	    !( strcmp( cmp, XMLNS_FEATURE ) == 0 ) || +	    !( d = xt_find_node( d->children, "x" ) ) || +	    !( cmp = xt_find_attr( d, "xmlns" ) ) || +	    !( strcmp( cmp, XMLNS_XDATA ) == 0 ) || +	    !( cmp = xt_find_attr( d, "type" ) ) || +	    !( strcmp( cmp, "submit" ) == 0 ) || +	    !( d = xt_find_node( d->children, "field" ) ) || +	    !( cmp = xt_find_attr( d, "var" ) ) || +	    !( strcmp( cmp, "stream-method" ) == 0 ) || +	    !( d = xt_find_node( d->children, "value" ) ) ) +	{ +		imcb_log( ic, "WARNING: Received incomplete Stream Initiation response" ); +		return XT_HANDLED; +	} + +	if( !( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) {  +		/* since we should only have advertised what we can do and the peer should +		 * only have chosen what we offered, this should never happen */ +		imcb_log( ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text ); +			 +		return XT_HANDLED; +	} +	 +	/* Let's see if we can find out what this bytestream should be for... */ + +	for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) +	{ +		struct jabber_transfer *tft = tflist->data; +		if( ( strcmp( tft->iq_id, iq_id ) == 0 ) ) +		{ +		    	tf = tft; +			break; +		} +	} + +	if (!tf)  +	{ +		imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); +		return XT_HANDLED; +	} + +	tf->ini_jid = g_strdup( ini_jid ); +	tf->tgt_jid = g_strdup( tgt_jid ); + +	imcb_log( ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid ); + +	jabber_bs_send_start( tf ); + +	return XT_HANDLED; +} + +int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf ) +{ +	struct xt_node *node, *sinode; +	struct jabber_buddy *bud; + +	/* who knows how many bits the future holds :) */ +	char filesizestr[ 1 + ( int ) ( 0.301029995663981198f * sizeof( size_t ) * 8 ) ]; + +	const char *methods[] =  +	{  	 +		XMLNS_BYTESTREAMS, +		//XMLNS_IBB, +		NULL  +	}; +	const char **m; +	char *s; + +	/* Maybe we should hash this? */ +	tf->sid = g_strdup_printf( "BitlBeeJabberSID%d", tf->ft->local_id ); +	 +	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, 0 ); + +	/* start with the SI tag */ +	sinode = xt_new_node( "si", NULL, NULL ); +	xt_add_attr( sinode, "xmlns", XMLNS_SI ); +	xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER ); +	xt_add_attr( sinode, "id", tf->sid ); + +/*	if( mimetype )  +		xt_add_attr( node, "mime-type", mimetype ); */ + +	/* now the file tag */ +/*	if( desc ) + 		node = xt_new_node( "desc", descr, NULL ); */ +	node = xt_new_node( "range", NULL, NULL ); + +	sprintf( filesizestr, "%zd", tf->ft->file_size ); +	node = xt_new_node( "file", NULL, node ); +	xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER ); +	xt_add_attr( node, "name", tf->ft->file_name ); +	xt_add_attr( node, "size", filesizestr ); +/*	if (hash) +		xt_add_attr( node, "hash", hash ); +	if (date) +		xt_add_attr( node, "date", date ); */ + +	xt_add_child( sinode, node ); + +	/* and finally the feature tag */ +	node = xt_new_node( "field", NULL, NULL ); +	xt_add_attr( node, "var", "stream-method" ); +	xt_add_attr( node, "type", "list-single" ); + +	for ( m = methods ; *m ; m ++ ) +		xt_add_child( node, xt_new_node( "option", NULL, xt_new_node( "value", (char *)*m, NULL ) ) ); + +	node = xt_new_node( "x", NULL, node ); +	xt_add_attr( node, "xmlns", XMLNS_XDATA ); +	xt_add_attr( node, "type", "form" ); + +	node = xt_new_node( "feature", NULL, node ); +	xt_add_attr( node, "xmlns", XMLNS_FEATURE ); + +	xt_add_child( sinode, node ); + +	/* and we are there... */ +	node = jabber_make_packet( "iq", "set", bud ? bud->full_jid : who, sinode ); +	jabber_cache_add( ic, node, jabber_si_handle_response ); +	tf->iq_id = g_strdup( xt_find_attr( node, "id" ) ); +	 +	return jabber_write_packet( ic, node ); +} diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 6a588613..6c59aedb 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -7,6 +7,9 @@  ### DEFINITIONS  -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/msn/ +endif  # [SH] Program variables  objects = msn.o msn_util.o ns.o passport.o sb.o tables.o @@ -32,7 +35,7 @@ distclean: clean  $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c new file mode 100644 index 00000000..9f8b9a6e --- /dev/null +++ b/protocols/msn/invitation.c @@ -0,0 +1,622 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway                     * +*                                                                    * +* Copyright 2008 Uli Meis					     * +* Copyright 2006 Marijn Kruisselbrink and others                     * +\********************************************************************/ + +/* MSN module - File transfer support             */ + +/* + 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA  02111-1307  USA + */ + +#include "bitlbee.h" +#include "invitation.h" +#include "msn.h" +#include "lib/ftutil.h" + +#ifdef debug +#undef debug +#endif +#define debug(msg...) log_message( LOGLVL_INFO, msg ) + +static void msn_ftp_free( file_transfer_t *file ); +static void msn_ftpr_accept( file_transfer_t *file ); +static void msn_ftp_finished( file_transfer_t *file ); +static void msn_ftp_canceled( file_transfer_t *file, char *reason ); +static gboolean msn_ftpr_write_request( file_transfer_t *file ); + +static gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ); +static gboolean msn_ftp_read( gpointer data, gint fd, b_input_condition cond ); +gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ); + +/* + * Vararg wrapper for imcb_file_canceled(). + */ +gboolean msn_ftp_abort( file_transfer_t *file, char *format, ... ) +{ +        va_list params; +        va_start( params, format ); +        char error[128]; + +        if( vsnprintf( error, 128, format, params ) < 0 ) +                sprintf( error, "internal error parsing error string (BUG)" ); +        va_end( params ); +	imcb_file_canceled( file, error ); +	return FALSE; +} + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ +	if( (op) == -1 ) \ +		return msn_ftp_abort( file , msg ": %s", strerror( errno ) ); + +void msn_ftp_invitation_cmd( struct im_connection *ic, char *who, int cookie, char *icmd, +			     char *trailer ) +{ +	struct msn_message *m = g_new0( struct msn_message, 1 ); +	 +	m->text = g_strdup_printf( "%s" +		    "Invitation-Command: %s\r\n" +		    "Invitation-Cookie: %u\r\n" +		    "%s", +		    MSN_INVITE_HEADERS, +		    icmd, +		    cookie, +		    trailer); +	 +	m->who = g_strdup( who ); + +	msn_sb_write_msg( ic, m ); +} + +void msn_ftp_cancel_invite( struct im_connection *ic, char *who,  int cookie, char *code ) +{ +	char buf[64]; + +	g_snprintf( buf, sizeof( buf ), "Cancel-Code: %s\r\n", code ); +	msn_ftp_invitation_cmd( ic, who, cookie, "CANCEL", buf ); +} + +void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *file, char *who ) +{ +	unsigned int cookie = time( NULL ); /* TODO: randomize */ +	char buf[2048]; + +	msn_filetransfer_t *msn_file = g_new0( msn_filetransfer_t, 1 ); +	file->data = msn_file; +	file->free = msn_ftp_free; +	file->canceled = msn_ftp_canceled; +	file->write = msn_ftps_write; +	msn_file->md = ic->proto_data; +	msn_file->invite_cookie = cookie; +	msn_file->handle = g_strdup( who ); +	msn_file->dcc = file; +	msn_file->md->filetransfers = g_slist_prepend( msn_file->md->filetransfers, msn_file->dcc ); +	msn_file->fd = -1; +	msn_file->sbufpos = 3; + +	g_snprintf( buf, sizeof( buf ),  +		"Application-Name: File Transfer\r\n" +		"Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n" +		"Application-File: %s\r\n" +		"Application-FileSize: %zd\r\n", +		file->file_name, +		file->file_size); + +	msn_ftp_invitation_cmd( msn_file->md->ic, msn_file->handle, cookie, "INVITE", buf ); + +	imcb_file_recv_start( file ); +} + +void msn_invitation_invite( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	char *itype = msn_findheader( body, "Application-GUID:", blen ); +	char *name, *size, *invitecookie, *reject = NULL; +	user_t *u; +	size_t isize; +	file_transfer_t *file; +	 +	if( !itype || strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) != 0 ) { +		/* Don't know what that is - don't care */ +		char *iname = msn_findheader( body, "Application-Name:", blen ); +		imcb_log( sb->ic, "Received unknown MSN invitation %s (%s) from %s", +			  itype ? : "with no GUID", iname ? iname : "no application name", handle ); +		g_free( iname ); +		reject = "REJECT_NOT_INSTALLED"; +	} else if (  +		!( name = msn_findheader( body, "Application-File:", blen )) ||  +		!( size = msn_findheader( body, "Application-FileSize:", blen )) ||  +		!( invitecookie = msn_findheader( body, "Invitation-Cookie:", blen)) || +		!( isize = atoll( size ) ) ) {  +		imcb_log( sb->ic, "Received corrupted transfer request from %s" +			  "(name=%s, size=%s, invitecookie=%s)", +			  handle, name, size, invitecookie ); +		reject = "REJECT"; +	} else if ( !( u = user_findhandle( sb->ic, handle ) ) ) { +		imcb_log( sb->ic, "Error in parsing transfer request, User '%s'" +			  "is not in contact list", handle ); +		reject = "REJECT"; +	} else if ( !( file = imcb_file_send_start( sb->ic, handle, name, isize ) ) ) { +		imcb_log( sb->ic, "Error initiating transfer for request from %s for %s", +			  handle, name ); +		reject = "REJECT"; +	} else { +		msn_filetransfer_t *msn_file = g_new0( msn_filetransfer_t, 1 ); +		file->data = msn_file; +		file->accept = msn_ftpr_accept; +		file->free = msn_ftp_free; +		file->finished = msn_ftp_finished; +		file->canceled = msn_ftp_canceled; +		file->write_request = msn_ftpr_write_request; +		msn_file->md = sb->ic->proto_data; +		msn_file->invite_cookie = cookie; +		msn_file->handle = g_strdup( handle ); +		msn_file->dcc = file; +		msn_file->md->filetransfers = g_slist_prepend( msn_file->md->filetransfers, msn_file->dcc ); +		msn_file->fd = -1; +	} + +	if( reject ) +		msn_ftp_cancel_invite( sb->ic, sb->who, cookie, reject ); + +	g_free( name ); +	g_free( size ); +	g_free( invitecookie ); +	g_free( itype ); +} + +msn_filetransfer_t* msn_find_filetransfer( struct msn_data *md, unsigned int cookie, char *handle ) +{ +	GSList *l; +	 +	for( l = md->filetransfers; l; l = l->next ) { +		msn_filetransfer_t *file = ( (file_transfer_t*) l->data )->data; +		if( file->invite_cookie == cookie && strcmp( handle, file->handle ) == 0 ) { +			return file; +		} +	} +	return NULL; +} + +gboolean msn_ftps_connected( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; +	struct sockaddr_storage clt_addr; +	socklen_t ssize = sizeof( clt_addr ); +	 +	debug( "Connected to MSNFTP client" ); +	 +	ASSERTSOCKOP( msn_file->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + +	closesocket( fd ); +	fd = msn_file->fd; +	sock_make_nonblocking( fd ); + +	msn_file->r_event_id = b_input_add( fd, B_EV_IO_READ, msn_ftp_read, file ); + +	return FALSE; +} + +void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	file_transfer_t *file = msn_file->dcc; +	char buf[1024]; +	unsigned int acookie = time ( NULL ); +	char host[HOST_NAME_MAX+1]; +	char port[6]; +	char *errmsg; + +	msn_file->auth_cookie = acookie; + +	if( ( msn_file->fd = ft_listen( NULL, host, port, FALSE, &errmsg ) ) == -1 ) { +		msn_ftp_abort( file, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); +		return; +	} + +	msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftps_connected, file ); + +	g_snprintf( buf, sizeof( buf ), +		    "IP-Address: %s\r\n" +		    "Port: %s\r\n" +		    "AuthCookie: %d\r\n" +		    "Launch-Application: FALSE\r\n" +		    "Request-Data: IP-Address:\r\n\r\n", +		    host, +		    port, +		    msn_file->auth_cookie ); + +	msn_ftp_invitation_cmd( msn_file->md->ic, handle, msn_file->invite_cookie, "ACCEPT", buf ); +} + +void msn_invitationr_accept( msn_filetransfer_t *msn_file, struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) { +	file_transfer_t *file = msn_file->dcc; +	char *authcookie, *ip, *port; + +	if( !( authcookie = msn_findheader( body, "AuthCookie:", blen ) ) || +	    !( ip = msn_findheader( body, "IP-Address:", blen ) ) || +	    !( port = msn_findheader( body, "Port:", blen ) ) ) { +		msn_ftp_abort( file, "Received invalid accept reply" ); +	} else if(  +		( msn_file->fd = proxy_connect( ip, atoi( port ), msn_ftp_connected, file ) ) +		< 0 ) { +			msn_ftp_abort( file, "Error connecting to MSN client" ); +	} else +		msn_file->auth_cookie = strtoul( authcookie, NULL, 10 ); + +	g_free( authcookie ); +	g_free( ip ); +	g_free( port ); +} + +void msn_invitation_accept( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	msn_filetransfer_t *msn_file = msn_find_filetransfer( sb->ic->proto_data, cookie, handle ); +	file_transfer_t *file = msn_file ? msn_file->dcc : NULL; +	 +	if( !msn_file  ) +		imcb_log( sb->ic, "Received invitation ACCEPT message for unknown invitation (already aborted?)" ); +	else if( file->sending )  +		msn_invitations_accept( msn_file, sb, handle, cookie, body, blen ); +	else +		msn_invitationr_accept( msn_file, sb, handle, cookie, body, blen ); +} + +void msn_invitation_cancel( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	msn_filetransfer_t *msn_file = msn_find_filetransfer( sb->ic->proto_data, cookie, handle ); +	 +	if( !msn_file ) +		imcb_log( sb->ic, "Received invitation CANCEL message for unknown invitation (already aborted?)" ); +	else +		msn_ftp_abort( msn_file->dcc, msn_findheader( body, "Cancel-Code:", blen ) ); +} + +int msn_ftp_write( file_transfer_t *file, char *format, ... ) +{ +	msn_filetransfer_t *msn_file = file->data; +	va_list params; +	int st; +	char *s; +	 +	va_start( params, format ); +	s = g_strdup_vprintf( format, params ); +	va_end( params ); +	 +	st = write( msn_file->fd, s, strlen( s ) ); +	if( st != strlen( s ) ) +		return msn_ftp_abort( file, "Error sending data over MSNFTP connection: %s", +				strerror( errno ) ); +	 +	g_free( s ); +	return 1; +} + +gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; +	 +	debug( "Connected to MSNFTP server, starting authentication" ); +	if( !msn_ftp_write( file, "VER MSNFTP\r\n" ) )  +		return FALSE; +	 +	sock_make_nonblocking( msn_file->fd ); +	msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); +	 +	return FALSE; +} + +gboolean msn_ftp_handle_command( file_transfer_t *file, char* line ) +{ +	msn_filetransfer_t *msn_file = file->data; +	char **cmd = msn_linesplit( line ); +	int count = 0; +	if( cmd[0] ) while( cmd[++count] ); +	 +	if( count < 1 ) +		return msn_ftp_abort( file, "Missing command in MSNFTP communication" ); +	 +	if( strcmp( cmd[0], "VER" ) == 0 ) { +		if( strcmp( cmd[1], "MSNFTP" ) != 0 ) +			return msn_ftp_abort( file, "Unsupported filetransfer protocol: %s", cmd[1] ); +		if( file->sending ) +			msn_ftp_write( file, "VER MSNFTP\r\n" ); +		else  +			msn_ftp_write( file, "USR %s %u\r\n", msn_file->md->ic->acc->user, msn_file->auth_cookie ); +	} else if( strcmp( cmd[0], "FIL" ) == 0 ) { +		if( strtoul( cmd[1], NULL, 10 ) != file->file_size ) +			return msn_ftp_abort( file, "FIL reply contains a different file size than the size in the invitation" ); +		msn_ftp_write( file, "TFR\r\n" ); +		msn_file->status |= MSN_TRANSFER_RECEIVING; +	} else if( strcmp( cmd[0], "USR" ) == 0 ) { +		if( ( strcmp( cmd[1], msn_file->handle ) != 0 ) || +		    ( strtoul( cmd[2], NULL, 10 ) != msn_file->auth_cookie ) ) +			msn_ftp_abort( file, "Authentication failed. " +				"Expected handle: %s (got %s), cookie: %u (got %s)", +				msn_file->handle, cmd[1], +				msn_file->auth_cookie, cmd[2] ); +		msn_ftp_write( file, "FIL %zu\r\n", file->file_size); +	} else if( strcmp( cmd[0], "TFR" ) == 0 ) { +		file->write_request( file ); +	} else if( strcmp( cmd[0], "BYE" ) == 0 ) { +		unsigned int retcode = count > 1 ? atoi(cmd[1]) : 1; + +		if( ( retcode==16777989 ) || ( retcode==16777987 ) ) +			imcb_file_finished( file ); +		else if( retcode==2147942405 ) +			imcb_file_canceled( file, "Failure: receiver is out of disk space" ); +		else if( retcode==2164261682 ) +			imcb_file_canceled( file, "Failure: receiver cancelled the transfer" ); +		else if( retcode==2164261683 ) +			imcb_file_canceled( file, "Failure: sender has cancelled the transfer" ); +		else if( retcode==2164261694 ) +			imcb_file_canceled( file, "Failure: connection is blocked" ); +		else { +			char buf[128]; + +			sprintf( buf, "Failure: unknown BYE code: %d", retcode); +			imcb_file_canceled( file, buf ); +		} +	} else if( strcmp( cmd[0], "CCL" ) == 0 ) { +		imcb_file_canceled( file, "Failure: receiver cancelled the transfer" ); +	} else { +		msn_ftp_abort( file, "Received invalid command %s from msn client", cmd[0] ); +	} +	return TRUE; +} + +gboolean msn_ftp_send( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; + +	msn_file->w_event_id = 0; + +	file->write_request( file ); + +	return FALSE; +} + +/* + * This should only be called if we can write, so just do it. + * Add a write watch so we can write more during the next cycle (if possible). + * This got a bit complicated because (at least) amsn expects packets of size 2045. + */ +gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) +{ +	msn_filetransfer_t *msn_file = file->data; +        int ret, overflow; + +	/* what we can't send now */ +	overflow = msn_file->sbufpos + len - MSNFTP_PSIZE; + +	/* append what we can do the send buffer */ +	memcpy( msn_file->sbuf + msn_file->sbufpos, buffer, MIN( len, MSNFTP_PSIZE - msn_file->sbufpos ) ); +	msn_file->sbufpos += MIN( len, MSNFTP_PSIZE - msn_file->sbufpos ); + +	/* if we don't have enough for a full packet and there's more wait for it */ +	if( ( msn_file->sbufpos < MSNFTP_PSIZE ) &&  +	    ( msn_file->data_sent + msn_file->sbufpos - 3 < file->file_size ) ) { +		if( !msn_file->w_event_id ) +			msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); +		return TRUE; +	} + +	/* Accumulated enough data, lets send something out */ + +	msn_file->sbuf[0] = 0; +	msn_file->sbuf[1] = ( msn_file->sbufpos - 3 ) & 0xff; +	msn_file->sbuf[2] = ( ( msn_file->sbufpos - 3 ) >> 8 ) & 0xff; + +        ASSERTSOCKOP( ret = send( msn_file->fd, msn_file->sbuf, msn_file->sbufpos, 0 ), "Sending" ); + +        msn_file->data_sent += ret - 3; + +        /* TODO: this should really not be fatal */ +        if( ret < msn_file->sbufpos ) +                return msn_ftp_abort( file, "send() sent %d instead of %d (send buffer full!)", ret, msn_file->sbufpos ); + +	msn_file->sbufpos = 3; + +	if( overflow > 0 ) { +		while( overflow > ( MSNFTP_PSIZE - 3 ) ) { +			if( !msn_ftps_write( file, buffer + len - overflow, MSNFTP_PSIZE - 3 ) ) +				return FALSE; +			overflow -= MSNFTP_PSIZE - 3; +		} +		return msn_ftps_write( file, buffer + len - overflow, overflow ); +	} + +	if( msn_file->data_sent == file->file_size ) { +		if( msn_file->w_event_id ) { +			b_event_remove( msn_file->w_event_id ); +			msn_file->w_event_id = 0; +		} +	} else { +		/* we might already be listening if this is data from an overflow */ +		if( !msn_file->w_event_id ) +			msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); +	} + +        return TRUE; +} + +/* Binary part of the file transfer protocol */ +gboolean msn_ftpr_read( file_transfer_t *file )  +{ +	msn_filetransfer_t *msn_file = file->data; +	int st; +	unsigned char buf[3]; + +	if( msn_file->data_remaining ) { +		msn_file->r_event_id = 0; + +		ASSERTSOCKOP( st = read( msn_file->fd, file->buffer, MIN( sizeof( file->buffer ), msn_file->data_remaining ) ), "Receiving" ); + +		if( st == 0 ) +			return msn_ftp_abort( file, "Remote end closed connection"); + +		msn_file->data_sent += st; + +		msn_file->data_remaining -= st; + +		file->write( file, file->buffer, st ); + +		if( msn_file->data_sent >= file->file_size ) +			imcb_file_finished( file ); + +		return FALSE; +	} else { +		ASSERTSOCKOP( st = read( msn_file->fd, buf, 1 ), "Receiving" ); +		if( st == 0 ) { +			return msn_ftp_abort( file, "read returned EOF while reading data header from msn client" ); +		} else if( buf[0] == '\r' || buf[0] == '\n' ) { +			debug( "Discarding extraneous newline" ); +		} else if( buf[0] != 0 ) { +			msn_ftp_abort( file, "Remote end canceled the transfer"); +			/* don't really care about these last 2 (should be 0,0) */ +			read( msn_file->fd, buf, 2 ); +			return FALSE; +		} else { +			unsigned int size; +			ASSERTSOCKOP( st = read( msn_file->fd, buf, 2 ), "Receiving" ); +			if( st < 2 ) +				return msn_ftp_abort( file, "read returned EOF while reading data header from msn client" ); + +			size = buf[0] + ((unsigned int) buf[1] << 8); +			msn_file->data_remaining = size; +		} +	} +	return TRUE; +} + +/* Text mode part of the file transfer protocol */ +gboolean msn_ftp_txtproto( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; +	int i = msn_file->tbufpos, st; +	char *tbuf = msn_file->tbuf; + +	ASSERTSOCKOP( st = read( msn_file->fd,  +				 tbuf + msn_file->tbufpos,  +				 sizeof( msn_file->tbuf ) - msn_file->tbufpos ), +				 "Receiving" ); + +	if( st == 0 ) +		return msn_ftp_abort( file, "read returned EOF while reading text from msn client" ); + +	msn_file->tbufpos += st; + +	do { +		for( ;i < msn_file->tbufpos; i++ ) { +			if( tbuf[i] == '\n' || tbuf[i] == '\r' ) { +				tbuf[i] = '\0'; +				if( i > 0 ) +					msn_ftp_handle_command( file, tbuf ); +				else +					while( tbuf[i] == '\n' || tbuf[i] == '\r' ) i++; +				memmove( tbuf, tbuf + i + 1, msn_file->tbufpos - i - 1 ); +				msn_file->tbufpos -= i + 1; +				i = 0; +				break; +			} +		} +	} while ( i < msn_file->tbufpos ); + +	if( msn_file->tbufpos == sizeof( msn_file->tbuf ) ) +		return msn_ftp_abort( file,  +				      "Line exceeded %d bytes in text protocol",  +				      sizeof( msn_file->tbuf ) ); +	return TRUE; +} + +gboolean msn_ftp_read( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; +	 +	if( msn_file->status & MSN_TRANSFER_RECEIVING ) +		return msn_ftpr_read( file ); +	else +		return msn_ftp_txtproto( file ); +} + +void msn_ftp_free( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; +	 +	if( msn_file->r_event_id ) +		b_event_remove( msn_file->r_event_id ); + +	if( msn_file->w_event_id ) +		b_event_remove( msn_file->w_event_id ); + +	if( msn_file->fd != -1 ) +		closesocket( msn_file->fd ); + +	msn_file->md->filetransfers = g_slist_remove( msn_file->md->filetransfers, msn_file->dcc ); +	 +	g_free( msn_file->handle ); +	 +	g_free( msn_file ); +} + +void msn_ftpr_accept( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; + +	msn_ftp_invitation_cmd( msn_file->md->ic, msn_file->handle, msn_file->invite_cookie, "ACCEPT",  +				"Launch-Application: FALSE\r\n"  +				"Request-Data: IP-Address:\r\n"); +} + +void msn_ftp_finished( file_transfer_t *file ) +{ +	msn_ftp_write( file, "BYE 16777989\r\n" ); +} + +void msn_ftp_canceled( file_transfer_t *file, char *reason ) +{ +	msn_filetransfer_t *msn_file = file->data; + +	msn_ftp_cancel_invite( msn_file->md->ic, msn_file->handle,  +			       msn_file->invite_cookie,  +			       file->status & FT_STATUS_TRANSFERRING ?  +					"FTTIMEOUT" :  +					"FAIL" ); + +	imcb_log( msn_file->md->ic, "File transfer aborted: %s", reason ); +} + +gboolean msn_ftpr_write_request( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; +	if( msn_file->r_event_id != 0 ) { +		msn_ftp_abort( file,  +					"BUG in MSN file transfer:" +					"write_request called when" +					"already watching for input" ); +		return FALSE; +	} + +	msn_file->r_event_id =  +		b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); + +	return TRUE; +} diff --git a/protocols/msn/invitation.h b/protocols/msn/invitation.h new file mode 100644 index 00000000..289efd7b --- /dev/null +++ b/protocols/msn/invitation.h @@ -0,0 +1,82 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway                     * +*                                                                    * +* Copyright 2006 Marijn Kruisselbrink and others                     * +\********************************************************************/ + +/* MSN module - File transfer support             */ + +/* + 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA  02111-1307  USA + */ + +#ifndef _MSN_INVITATION_H +#define _MSN_INVITATION_H + +#include "msn.h" + +#define MSN_INVITE_HEADERS	"MIME-Version: 1.0\r\n" \ +				"Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" \ +				"\r\n" + +#define MSNFTP_PSIZE 2048 + +typedef enum { +	MSN_TRANSFER_RECEIVING	= 1, +	MSN_TRANSFER_SENDING	= 2 +} msn_filetransfer_status_t; + +typedef struct msn_filetransfer +{ +/* Generic invitation data */	 +	/* msn_data instance this invitation was received with. */ +	struct msn_data *md; +	/* Cookie specifying this invitation. */ +	unsigned int invite_cookie; +	/* Handle of user that started this invitation. */ +	char *handle; + +/* File transfer specific data */ +	/* Current status of the file transfer. */ +	msn_filetransfer_status_t status; +	/* Pointer to the dcc structure for this transfer. */ +	file_transfer_t *dcc; +	/* Socket the transfer is taking place over. */ +	int fd; +	/* Cookie received in the original invitation, this must be sent as soon as +	   a connection has been established. */ +	unsigned int auth_cookie; +	/* Data remaining to be received in the current packet. */ +	unsigned int data_remaining; +	/* Buffer containing received, but unprocessed text. */ +	char tbuf[256]; +	unsigned int tbufpos; +	 +	unsigned int data_sent; + +	gint r_event_id; +	gint w_event_id; +	 +	unsigned char sbuf[2048]; +	int sbufpos; + +} msn_filetransfer_t; + +void msn_invitation_invite( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); +void msn_invitation_accept( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); +void msn_invitation_cancel( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); + +#endif diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 7dbdb9d6..8b4f1a81 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -78,6 +78,12 @@ static void msn_logout( struct im_connection *ic )  	if( md )  	{ +		/** Disabling MSN ft support for now. +		while( md->filetransfers ) { +			imcb_file_canceled( md->filetransfers->data, "Closing connection" ); +		} +		*/ +		  		if( md->fd >= 0 )  			closesocket( md->fd ); @@ -97,6 +103,15 @@ static void msn_logout( struct im_connection *ic )  			g_free( md->grouplist[--md->groupcount] );  		g_free( md->grouplist ); +		while( md->grpq ) +		{ +			struct msn_groupadd *ga = md->grpq->data; +			g_free( ga->group ); +			g_free( ga->who ); +			g_free( ga ); +			md->grpq = g_slist_remove( md->grpq, ga ); +		} +		  		g_free( md );  	} @@ -115,6 +130,14 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in  {  	struct msn_switchboard *sb; +#ifdef DEBUG +	if( strcmp( who, "raw" ) == 0 ) +	{ +		msn_write( ic, message, strlen( message ) ); +		msn_write( ic, "\r\n", 2 ); +	} +	else +#endif  	if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{  		return( msn_sb_sendmessage( sb, message ) ); @@ -175,7 +198,7 @@ static void msn_get_info(struct im_connection *ic, char *who)  static void msn_add_buddy( struct im_connection *ic, char *who, char *group )  { -	msn_buddy_list_add( ic, "FL", who, who ); +	msn_buddy_list_add( ic, "FL", who, who, group );  }  static void msn_remove_buddy( struct im_connection *ic, char *who, char *group ) @@ -216,6 +239,7 @@ static void msn_chat_leave( struct groupchat *c )  static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )  {  	struct msn_switchboard *sb; +	struct groupchat *c = imcb_chat_new( ic, who );  	if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{ @@ -233,10 +257,8 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )  		msn_sb_write_msg( ic, m ); -		return NULL; +		return c;  	} -	 -	return NULL;  }  static void msn_keepalive( struct im_connection *ic ) @@ -246,7 +268,7 @@ static void msn_keepalive( struct im_connection *ic )  static void msn_add_permit( struct im_connection *ic, char *who )  { -	msn_buddy_list_add( ic, "AL", who, who ); +	msn_buddy_list_add( ic, "AL", who, who, NULL );  }  static void msn_rem_permit( struct im_connection *ic, char *who ) @@ -258,7 +280,7 @@ static void msn_add_deny( struct im_connection *ic, char *who )  {  	struct msn_switchboard *sb; -	msn_buddy_list_add( ic, "BL", who, who ); +	msn_buddy_list_add( ic, "BL", who, who, NULL );  	/* If there's still a conversation with this person, close it. */  	if( ( sb = msn_sb_by_handle( ic, who ) ) ) @@ -327,6 +349,7 @@ void msn_initmodule()  	ret->rem_deny = msn_rem_deny;  	ret->send_typing = msn_send_typing;  	ret->handle_cmp = g_strcasecmp; +	//ret->transfer_request = msn_ftp_transfer_request;  	register_protocol(ret);  } diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 83a080a3..59036e37 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -69,10 +69,11 @@ struct msn_data  	int trId; -	GSList *msgq; +	GSList *msgq, *grpq;  	GSList *switchboards;  	int sb_failures;  	time_t first_sb_failure; +	GSList *filetransfers;  	const struct msn_away_state *away_state;  	int buddycount; @@ -119,6 +120,12 @@ struct msn_message  	char *text;  }; +struct msn_groupadd +{ +	char *who; +	char *group; +}; +  struct msn_handler_data  {  	int fd; @@ -158,7 +165,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond );  /* msn_util.c */  int msn_write( struct im_connection *ic, char *s, int len );  int msn_logged_in( struct im_connection *ic ); -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname ); +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group );  int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who );  void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname );  char *msn_findheader( char *text, char *header, int len ); @@ -188,4 +195,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m );  void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial );  void msn_sb_stop_keepalives( struct msn_switchboard *sb ); +/* invitation.c */ +void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); +  #endif //_MSN_H diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 9c9d2720..24f7e10e 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -50,24 +50,62 @@ int msn_logged_in( struct im_connection *ic )  	return( 0 );  } -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname_ ) +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group )  {  	struct msn_data *md = ic->proto_data; -	char buf[1024], *realname; +	char buf[1024], *realname, groupid[8]; -	realname = msn_http_encode( realname_ ); -	 -	g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname ); -	if( msn_write( ic, buf, strlen( buf ) ) ) +	*groupid = '\0'; +	if( group )  	{ -		g_free( realname ); +		int i; +		for( i = 0; i < md->groupcount; i ++ ) +			if( g_strcasecmp( md->grouplist[i], group ) == 0 ) +			{ +				g_snprintf( groupid, sizeof( groupid ), " %d", i ); +				break; +			} -		return( 1 ); +		if( *groupid == '\0' ) +		{ +			/* Have to create this group, it doesn't exist yet. */ +			struct msn_groupadd *ga; +			GSList *l; +			 +			for( l = md->grpq; l; l = l->next ) +			{ +				ga = l->data; +				if( g_strcasecmp( ga->group, group ) == 0 ) +					break; +			} +			 +			ga = g_new0( struct msn_groupadd, 1 ); +			ga->who = g_strdup( who ); +			ga->group = g_strdup( group ); +			md->grpq = g_slist_prepend( md->grpq, ga ); +			 +			if( l == NULL ) +			{ +				char *groupname = msn_http_encode( group ); +				g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 ); +				g_free( groupname ); +				return msn_write( ic, buf, strlen( buf ) ); +			} +			else +			{ +				/* This can happen if the user's doing lots of adds to a +				   new group at once; we're still waiting for the server +				   to confirm group creation. */ +				return 1; +			} +		}  	} +	realname = msn_http_encode( realname_ ); +	g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid );  	g_free( realname ); -	return( 0 ); +	return msn_write( ic, buf, strlen( buf ) );  }  int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who ) @@ -93,10 +131,9 @@ static void msn_buddy_ask_yes( void *data )  {  	struct msn_buddy_ask_data *bla = data; -	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname ); +	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL ); -	if( imcb_find_buddy( bla->ic, bla->handle ) == NULL ) -		imcb_ask_add( bla->ic, bla->handle, NULL ); +	imcb_ask_add( bla->ic, bla->handle, NULL );  	g_free( bla->handle );  	g_free( bla->realname ); @@ -107,7 +144,7 @@ static void msn_buddy_ask_no( void *data )  {  	struct msn_buddy_ask_data *bla = data; -	msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname ); +	msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL );  	g_free( bla->handle );  	g_free( bla->realname ); diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 2f656ea5..0be9e727 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -75,7 +75,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )  	g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );  	if( msn_write( ic, s, strlen( s ) ) )  	{ -		ic->inpa = b_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, ic ); +		ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic );  		imcb_log( ic, "Connected to server, waiting for reply" );  	} @@ -532,8 +532,14 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		}  		else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 )  		{ +			const char *group = NULL; +			int num; +			 +			if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount ) +				group = md->grouplist[num]; +			  			http_decode( cmd[5] ); -			imcb_add_buddy( ic, cmd[4], NULL ); +			imcb_add_buddy( ic, cmd[4], group );  			imcb_rename_buddy( ic, cmd[4], cmd[5] );  		}  	} @@ -605,6 +611,50 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			return( 0 );  		}  	} +	else if( strcmp( cmd[0], "ADG" ) == 0 ) +	{ +		char *group = g_strdup( cmd[3] ); +		int groupnum, i; +		GSList *l, *next; +		 +		http_decode( group ); +		if( sscanf( cmd[4], "%d", &groupnum ) == 1 ) +		{ +			if( groupnum >= md->groupcount ) +			{ +				md->grouplist = g_renew( char *, md->grouplist, groupnum + 1 ); +				for( i = md->groupcount; i <= groupnum; i ++ ) +					md->grouplist[i] = NULL; +				md->groupcount = groupnum + 1; +			} +			g_free( md->grouplist[groupnum] ); +			md->grouplist[groupnum] = group; +		} +		else +		{ +			/* Shouldn't happen, but if it does, give up on the group. */ +			g_free( group ); +			imcb_error( ic, "Syntax error" ); +			imc_logout( ic, TRUE ); +			return 0; +		} +		 +		for( l = md->grpq; l; l = next ) +		{ +			struct msn_groupadd *ga = l->data; +			next = l->next; +			if( g_strcasecmp( ga->group, group ) == 0 ) +			{ +				if( !msn_buddy_list_add( ic, "FL", ga->who, ga->who, group ) ) +					return 0; +				 +				g_free( ga->group ); +				g_free( ga->who ); +				g_free( ga ); +				md->grpq = g_slist_remove( md->grpq, ga ); +			} +		} +	}  	else if( isdigit( cmd[0][0] ) )  	{  		int num = atoi( cmd[0] ); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 49eed601..cb5789b8 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -28,6 +28,7 @@  #include "msn.h"  #include "passport.h"  #include "md5.h" +#include "invitation.h"  static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond );  static int msn_sb_command( gpointer data, char **cmd, int num_parts ); @@ -178,6 +179,11 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  			buf = g_strdup( SB_KEEPALIVE_HEADERS );  			i = strlen( buf );  		} +		else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 )  +		{ +			buf = g_strdup( text ); +			i = strlen( buf ); +		}  		else  		{  			buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 ); @@ -226,11 +232,17 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb )  {  	struct im_connection *ic = sb->ic; +	struct groupchat *c = NULL;  	char buf[1024];  	/* Create the groupchat structure. */  	g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session ); -	sb->chat = imcb_chat_new( ic, buf ); +	if( sb->who ) +		c = bee_chat_by_title( ic->bee, ic, sb->who ); +	if( c && !msn_sb_by_chat( c ) ) +		sb->chat = c; +	else +		sb->chat = imcb_chat_new( ic, buf );  	/* Populate the channel. */  	if( sb->who ) imcb_chat_add_buddy( sb->chat, sb->who ); @@ -314,7 +326,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  		g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session );  	if( msn_sb_write( sb, buf, strlen( buf ) ) ) -		sb->inp = b_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb ); +		sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb );  	else  		debug( "Error %d while connecting to switchboard server", 2 ); @@ -691,64 +703,46 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int  				/* PANIC! */  			}  		} +#if 0 +		// Disable MSN ft support for now.  		else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 )  		{ -			char *itype = msn_findheader( body, "Application-GUID:", blen ); -			char buf[1024]; +			char *command = msn_findheader( body, "Invitation-Command:", blen ); +			char *cookie = msn_findheader( body, "Invitation-Cookie:", blen ); +			unsigned int icookie;  			g_free( ct ); -			*buf = 0; -			 -			if( !itype ) -				return( 1 ); -			 -			/* File transfer. */ -			if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 ) -			{ -				char *name = msn_findheader( body, "Application-File:", blen ); -				char *size = msn_findheader( body, "Application-FileSize:", blen ); -				 -				if( name && size ) -				{ -					g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n" -					            "Filetransfers are not supported by BitlBee for now...", name, size ); -				} -				else -				{ -					strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" ); -				} -				 -				if( name ) g_free( name ); -				if( size ) g_free( size ); -			} -			else -			{ -				char *iname = msn_findheader( body, "Application-Name:", blen ); -				 -				g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>", -				                                itype, iname ? iname : "no name" ); -				 -				if( iname ) g_free( iname ); +			/* Every invite should have both a Command and Cookie header */ +			if( !command || !cookie ) { +				g_free( command ); +				g_free( cookie ); +				imcb_log( ic, "Warning: No command or cookie from %s", sb->who ); +				return 1;  			} -			g_free( itype ); -			 -			if( !*buf ) -				return( 1 ); +			icookie = strtoul( cookie, NULL, 10 ); +			g_free( cookie ); -			if( sb->who ) -			{ -				imcb_buddy_msg( ic, cmd[1], buf, 0, 0 ); -			} -			else if( sb->chat ) -			{ -				imcb_chat_msg( sb->chat, cmd[1], buf, 0, 0 ); -			} -			else -			{ -				/* PANIC! */ +			if( g_strncasecmp( command, "INVITE", 6 ) == 0 ) { +				msn_invitation_invite( sb, cmd[1], icookie, body, blen ); +			} else if( g_strncasecmp( command, "ACCEPT", 6 ) == 0 ) { +				msn_invitation_accept( sb, cmd[1], icookie, body, blen ); +			} else if( g_strncasecmp( command, "CANCEL", 6 ) == 0 ) { +				msn_invitation_cancel( sb, cmd[1], icookie, body, blen ); +			} else { +				imcb_log( ic, "Warning: Received invalid invitation with " +						"command %s from %s", command, sb->who );  			} +			 +			g_free( command ); +		} +#endif +		else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 )  +		{ +			imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not " +					"support msnmsgrp2p yet.", sb->who ); +			g_free( ct );  		}  		else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 )  		{ @@ -779,10 +773,11 @@ static gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition  void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial )  { -	struct buddy *b; +	bee_user_t *bu;  	if( sb && sb->who && sb->keepalive == 0 && -	    ( b = imcb_find_buddy( sb->ic, sb->who ) ) && !b->present && +	    ( bu = bee_user_by_handle( sb->ic->bee, sb->ic, sb->who ) ) && +	    !( bu->flags & BEE_USER_ONLINE ) &&  	    set_getbool( &sb->ic->acc->set, "switchboard_keepalives" ) )  	{  		if( initial ) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 2248d11e..e628126f 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -35,10 +35,6 @@  #include <ctype.h>  #include "nogaim.h" -#include "chat.h" - -static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); -static char *format_timestamp( irc_t *irc, time_t msg_ts );  GSList *connections; @@ -92,8 +88,6 @@ void load_plugins(void)  }  #endif -/* nogaim.c */ -  GList *protocols = NULL;  void register_protocol (struct prpl *p) @@ -116,16 +110,18 @@ void register_protocol (struct prpl *p)  struct prpl *find_protocol(const char *name)  {  	GList *gl; -	for (gl = protocols; gl; gl = gl->next)  +	 +	for( gl = protocols; gl; gl = gl->next )   	{   		struct prpl *proto = gl->data; - 		if(!g_strcasecmp(proto->name, name))  + 		 + 		if( g_strcasecmp( proto->name, name ) == 0 )  			return proto;   	} + 	   	return NULL;  } -/* nogaim.c */  void nogaim_init()  {  	extern void msn_initmodule(); @@ -133,6 +129,7 @@ void nogaim_init()  	extern void byahoo_initmodule();  	extern void jabber_initmodule();  	extern void twitter_initmodule(); +	extern void purple_initmodule();  #ifdef WITH_MSN  	msn_initmodule(); @@ -154,6 +151,10 @@ void nogaim_init()  	twitter_initmodule();  #endif +#ifdef WITH_PURPLE +	purple_initmodule(); +#endif +  #ifdef WITH_PLUGINS  	load_plugins();  #endif @@ -161,15 +162,13 @@ void nogaim_init()  GSList *get_connections() { return connections; } -/* multi.c */ -  struct im_connection *imcb_new( account_t *acc )  {  	struct im_connection *ic;  	ic = g_new0( struct im_connection, 1 ); -	ic->irc = acc->irc; +	ic->bee = acc->bee;  	ic->acc = acc;  	acc->ic = ic; @@ -183,7 +182,7 @@ void imc_free( struct im_connection *ic )  	account_t *a;  	/* Destroy the pointer to this connection from the account list */ -	for( a = ic->irc->accounts; a; a = a->next ) +	for( a = ic->bee->accounts; a; a = a->next )  		if( a->ic == ic )  		{  			a->ic = NULL; @@ -204,20 +203,21 @@ static void serv_got_crap( struct im_connection *ic, char *format, ... )  	text = g_strdup_vprintf( format, params );  	va_end( params ); -	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || -	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) +	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) || +	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) )  		strip_html( text );  	/* Try to find a different connection on the same protocol. */ -	for( a = ic->irc->accounts; a; a = a->next ) +	for( a = ic->bee->accounts; a; a = a->next )  		if( a->prpl == ic->acc->prpl && a->ic != ic )  			break;  	/* If we found one, include the screenname in the message. */  	if( a ) -		irc_usermsg( ic->irc, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text ); +		/* FIXME(wilmer): ui_log callback or so */ +		irc_usermsg( ic->bee->ui_data, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text );  	else -		irc_usermsg( ic->irc, "%s - %s", ic->acc->prpl->name, text ); +		irc_usermsg( ic->bee->ui_data, "%s - %s", ic->acc->prpl->name, text );  	g_free( text );  } @@ -268,18 +268,12 @@ static gboolean send_keepalive( gpointer d, gint fd, b_input_condition cond )  void imcb_connected( struct im_connection *ic )  { -	irc_t *irc = ic->irc; -	struct chat *c; -	user_t *u; -	  	/* MSN servers sometimes redirect you to a different server and do  	   the whole login sequence again, so these "late" calls to this  	   function should be handled correctly. (IOW, ignored) */  	if( ic->flags & OPT_LOGGED_IN )  		return; -	u = user_find( ic->irc, ic->irc->nick ); -	  	imcb_log( ic, "Logged in" );  	ic->keepalive = b_timeout_add( 60000, send_keepalive, ic ); @@ -292,6 +286,10 @@ void imcb_connected( struct im_connection *ic )  	   exponential backoff timer. */  	ic->acc->auto_reconnect_delay = 0; +	if( ic->bee->ui->imc_connected ) +		ic->bee->ui->imc_connected( ic ); +	 +	/*  	for( c = irc->chatrooms; c; c = c->next )  	{  		if( c->acc != ic->acc ) @@ -300,6 +298,7 @@ void imcb_connected( struct im_connection *ic )  		if( set_getbool( &c->set, "auto_join" ) )  			chat_join( irc, c, NULL );  	} +	*/  }  gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ) @@ -307,7 +306,7 @@ gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond )  	account_t *a = data;  	a->reconnect = 0; -	account_on( a->irc, a ); +	account_on( a->bee, a );  	return( FALSE );	/* Only have to run the timeout once */  } @@ -320,9 +319,9 @@ void cancel_auto_reconnect( account_t *a )  void imc_logout( struct im_connection *ic, int allow_reconnect )  { -	irc_t *irc = ic->irc; -	user_t *t, *u; +	bee_t *bee = ic->bee;  	account_t *a; +	GSList *l;  	int delay;  	/* Nested calls might happen sometimes, this is probably the best @@ -332,6 +331,9 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	else  		ic->flags |= OPT_LOGGING_OUT; +	if( ic->bee->ui->imc_disconnected ) +		ic->bee->ui->imc_disconnected( ic ); +	  	imcb_log( ic, "Signing off.." );  	b_event_remove( ic->keepalive ); @@ -342,22 +344,20 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	g_free( ic->away );  	ic->away = NULL; -	u = irc->users; -	while( u ) +	for( l = bee->users; l; )  	{ -		if( u->ic == ic ) -		{ -			t = u->next; -			user_del( irc, u->nick ); -			u = t; -		} -		else -			u = u->next; +		bee_user_t *bu = l->data; +		GSList *next = l->next; +		 +		if( bu->ic == ic ) +			bee_user_free( bee, bu ); +		 +		l = next;  	} -	query_del_by_conn( ic->irc, ic ); +	query_del_by_conn( (irc_t*) ic->bee->ui_data, ic ); -	for( a = irc->accounts; a; a = a->next ) +	for( a = bee->accounts; a; a = a->next )  		if( a->ic == ic )  			break; @@ -365,7 +365,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	{  		/* Uhm... This is very sick. */  	} -	else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) && +	else if( allow_reconnect && set_getbool( &bee->set, "auto_reconnect" ) &&  	         set_getbool( &a->set, "auto_reconnect" ) &&  	         ( delay = account_reconnect_delay( a ) ) > 0 )  	{ @@ -376,170 +376,68 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	imc_free( ic );  } - -/* dialogs.c */ -  void imcb_ask( struct im_connection *ic, char *msg, void *data,                 query_callback doit, query_callback dont )  { -	query_add( ic->irc, ic, msg, doit, dont, data ); +	query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, g_free, data );  } - -/* list.c */ - -void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group ) +void imcb_ask_with_free( struct im_connection *ic, char *msg, void *data, +                         query_callback doit, query_callback dont, query_callback myfree )  { -	user_t *u; -	char nick[MAX_NICK_LENGTH+1], *s; -	irc_t *irc = ic->irc; -	 -	if( user_findhandle( ic, handle ) ) -	{ -		if( set_getbool( &irc->set, "debug" ) ) -			imcb_log( ic, "User already exists, ignoring add request: %s", handle ); -		 -		return; -		 -		/* Buddy seems to exist already. Let's ignore this request then... -		   Eventually subsequent calls to this function *should* be possible -		   when a buddy is in multiple groups. But for now BitlBee doesn't -		   even support groups so let's silently ignore this for now. */ -	} -	 -	memset( nick, 0, MAX_NICK_LENGTH + 1 ); -	strcpy( nick, nick_get( ic->acc, handle ) ); -	 -	u = user_add( ic->irc, nick ); -	 -//	if( !realname || !*realname ) realname = nick; -//	u->realname = g_strdup( realname ); -	 -	if( ( s = strchr( handle, '@' ) ) ) -	{ -		u->host = g_strdup( s + 1 ); -		u->user = g_strndup( handle, s - handle ); -	} -	else if( ic->acc->server ) -	{ -		u->host = g_strdup( ic->acc->server ); -		u->user = g_strdup( handle ); -		 -		/* s/ /_/ ... important for AOL screennames */ -		for( s = u->user; *s; s ++ ) -			if( *s == ' ' ) -				*s = '_'; -	} -	else -	{ -		u->host = g_strdup( ic->acc->prpl->name ); -		u->user = g_strdup( handle ); -	} -	 -	u->ic = ic; -	u->handle = g_strdup( handle ); -	if( group ) u->group = g_strdup( group ); -	u->send_handler = buddy_send_handler; -	u->last_typing_notice = 0; +	query_add( (irc_t *) ic->bee->ui_data, ic, msg, doit, dont, myfree, data );  } -struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle ) +void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group )  { -	static struct buddy b[1]; -	user_t *u; -	 -	u = user_findhandle( ic, handle ); +	bee_user_t *bu; +	bee_t *bee = ic->bee; -	if( !u ) -		return( NULL ); +	if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) +		bu = bee_user_new( bee, ic, handle, 0 ); -	memset( b, 0, sizeof( b ) ); -	strncpy( b->name, handle, 80 ); -	strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN ); -	b->present = u->online; -	b->ic = u->ic; +	bu->group = bee_group_by_name( bee, group, TRUE ); -	return( b ); +	if( bee->ui->user_group ) +		bee->ui->user_group( bee, bu );  } -void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname ) +void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *fullname )  { -	user_t *u = user_findhandle( ic, handle ); -	char *set; +	bee_t *bee = ic->bee; +	bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); -	if( !u || !realname ) return; +	if( !bu || !fullname ) return; -	if( g_strcasecmp( u->realname, realname ) != 0 ) +	if( !bu->fullname || strcmp( bu->fullname, fullname ) != 0 )  	{ -		if( u->realname != u->nick ) g_free( u->realname ); +		g_free( bu->fullname ); +		bu->fullname = g_strdup( fullname ); -		u->realname = g_strdup( realname ); -		 -		if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) ) -			imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname ); -	} -	 -	set = set_getstr( &ic->acc->set, "nick_source" ); -	if( strcmp( set, "handle" ) != 0 ) -	{ -		char *name = g_strdup( realname ); -		 -		if( strcmp( set, "first_name" ) == 0 ) -		{ -			int i; -			for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {} -			name[i] = '\0'; -		} -		 -		imcb_buddy_nick_hint( ic, handle, name ); -		 -		g_free( name ); +		if( bee->ui->user_fullname ) +			bee->ui->user_fullname( bee, bu );  	}  }  void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group )  { -	user_t *u; -	 -	if( ( u = user_findhandle( ic, handle ) ) ) -		user_del( ic->irc, u->nick ); +	bee_user_free( ic->bee, bee_user_by_handle( ic->bee, ic, handle ) );  }  /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM     modules to suggest a nickname for a handle. */  void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )  { -	user_t *u = user_findhandle( ic, handle ); -	char newnick[MAX_NICK_LENGTH+1], *orig_nick; +	bee_t *bee = ic->bee; +	bee_user_t *bu = bee_user_by_handle( bee, ic, handle ); -	if( u && !u->online && !nick_saved( ic->acc, handle ) ) -	{ -		/* Only do this if the person isn't online yet (which should -		   be the case if we just added it) and if the user hasn't -		   assigned a nickname to this buddy already. */ -		 -		strncpy( newnick, nick, MAX_NICK_LENGTH ); -		newnick[MAX_NICK_LENGTH] = 0; -		 -		/* Some processing to make sure this string is a valid IRC nickname. */ -		nick_strip( newnick ); -		if( set_getbool( &ic->irc->set, "lcnicks" ) ) -			nick_lc( newnick ); -		 -		if( strcmp( u->nick, newnick ) != 0 ) -		{ -			/* Only do this if newnick is different from the current one. -			   If rejoining a channel, maybe we got this nick already -			   (and dedupe would only add an underscore. */ -			nick_dedupe( ic->acc, handle, newnick ); -			 -			/* u->nick will be freed halfway the process, so it can't be -			   passed as an argument. */ -			orig_nick = g_strdup( u->nick ); -			user_rename( ic->irc, orig_nick, newnick ); -			g_free( orig_nick ); -		} -	} +	if( !bu || !nick ) return; +	 +	g_free( bu->nick ); +	bu->nick = g_strdup( nick ); +	 +	if( bee->ui->user_nick_hint ) +		bee->ui->user_nick_hint( bee, bu, nick );  } @@ -584,7 +482,8 @@ void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *re  	data->ic = ic;  	data->handle = g_strdup( handle ); -	query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data ); +	query_add( (irc_t *) ic->bee->ui_data, ic, s, +	           imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, g_free, data );  } @@ -609,672 +508,25 @@ void imcb_ask_add( struct im_connection *ic, const char *handle, const char *rea  	char *s;  	/* TODO: Make a setting for this! */ -	if( user_findhandle( ic, handle ) != NULL ) +	if( bee_user_by_handle( ic->bee, ic, handle ) != NULL )  		return;  	s = g_strdup_printf( "The user %s is not in your buddy list yet. Do you want to add him/her now?", handle );  	data->ic = ic;  	data->handle = g_strdup( handle ); -	query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data ); -} - - -/* server.c */                     - -void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ) -{ -	user_t *u; -	int oa, oo; -	 -	u = user_findhandle( ic, (char*) handle ); -	 -	if( !u ) -	{ -		if( g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "add" ) == 0 ) -		{ -			imcb_add_buddy( ic, (char*) handle, NULL ); -			u = user_findhandle( ic, (char*) handle ); -		} -		else -		{ -			if( set_getbool( &ic->irc->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "ignore" ) != 0 ) -			{ -				imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle ); -				imcb_log( ic, "flags = %d, state = %s, message = %s", flags, -				          state ? state : "NULL", message ? message : "NULL" ); -			} -			 -			return; -		} -	} -	 -	oa = u->away != NULL; -	oo = u->online; -	 -	g_free( u->away ); -	g_free( u->status_msg ); -	u->away = u->status_msg = NULL; -	 -	if( set_getbool( &ic->irc->set, "show_offline" ) && !u->online ) -	{ -		/* always set users as online */ -		irc_spawn( ic->irc, u ); -		u->online = 1; -		if( !( flags & OPT_LOGGED_IN ) ) -		{ -			/* set away message if user isn't really online */ -			u->away = g_strdup( "User is offline" ); -		} -	} -	else if( ( flags & OPT_LOGGED_IN ) && !u->online ) -	{ -		irc_spawn( ic->irc, u ); -		u->online = 1; -	} -	else if( !( flags & OPT_LOGGED_IN ) && u->online ) -	{ -		struct groupchat *c; -		 -		if( set_getbool( &ic->irc->set, "show_offline" ) ) -		{ -			/* keep offline users in channel and set away message to "offline" */ -			u->away = g_strdup( "User is offline" ); - -			/* Keep showing him/her in the control channel but not in groupchats. */ -			for( c = ic->groupchats; c; c = c->next ) -			{ -				if( remove_chat_buddy_silent( c, handle ) && c->joined ) -					irc_part( c->ic->irc, u, c->channel ); -			} -		} -		else -		{ -			/* kill offline users */ -			irc_kill( ic->irc, u ); -			u->online = 0; - -			/* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ -			for( c = ic->groupchats; c; c = c->next ) -				remove_chat_buddy_silent( c, handle ); -		} -	} - -	if( flags & OPT_AWAY ) -	{ -		if( state && message ) -		{ -			u->away = g_strdup_printf( "%s (%s)", state, message ); -		} -		else if( state ) -		{ -			u->away = g_strdup( state ); -		} -		else if( message ) -		{ -			u->away = g_strdup( message ); -		} -		else -		{ -			u->away = g_strdup( "Away" ); -		} -	} -	else -	{ -		u->status_msg = g_strdup( message ); -	} -	 -	/* early if-clause for show_offline even if there is some redundant code here because this isn't LISP but C ;) */ -	if( set_getbool( &ic->irc->set, "show_offline" ) && set_getbool( &ic->irc->set, "away_devoice" ) ) -	{ -		char *from; -		 -		if( set_getbool( &ic->irc->set, "simulate_netsplit" ) ) -		{ -			from = g_strdup( ic->irc->myhost ); -		} -		else -		{ -			from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, -			                                    ic->irc->myhost ); -		} - -		/* if we use show_offline, we op online users, voice away users, and devoice/deop offline users */ -		if( flags & OPT_LOGGED_IN ) -		{ -			/* user is "online" (either really online or away) */ -			irc_write( ic->irc, ":%s MODE %s %cv%co %s %s", from, ic->irc->channel, -			                                          u->away?'+':'-', u->away?'-':'+', u->nick, u->nick ); -		} -		else -		{ -			/* user is offline */ -			irc_write( ic->irc, ":%s MODE %s -vo %s %s", from, ic->irc->channel, u->nick, u->nick ); -		} -	} -	else -	{  -		/* LISPy... */ -		if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) &&		/* Don't do a thing when user doesn't want it */ -		    ( u->online ) &&						/* Don't touch offline people */ -		    ( ( ( u->online != oo ) && !u->away ) ||			/* Voice joining people */ -		      ( ( u->online == oo ) && ( oa == !u->away ) ) ) )		/* (De)voice people changing state */ -		{ -			char *from; - -			if( set_getbool( &ic->irc->set, "simulate_netsplit" ) ) -			{ -				from = g_strdup( ic->irc->myhost ); -			} -			else -			{ -				from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, -				                                    ic->irc->myhost ); -			} -			irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, -			                                          u->away?'-':'+', u->nick ); -			g_free( from ); -		} -	} -} - -void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) -{ -	irc_t *irc = ic->irc; -	char *wrapped, *ts = NULL; -	user_t *u; -	 -	u = user_findhandle( ic, handle ); -	 -	if( !u ) -	{ -		char *h = set_getstr( &irc->set, "handle_unknown" ); -		 -		if( g_strcasecmp( h, "ignore" ) == 0 ) -		{ -			if( set_getbool( &irc->set, "debug" ) ) -				imcb_log( ic, "Ignoring message from unknown handle %s", handle ); -			 -			return; -		} -		else if( g_strncasecmp( h, "add", 3 ) == 0 ) -		{ -			int private = set_getbool( &irc->set, "private" ); -			 -			if( h[3] ) -			{ -				if( g_strcasecmp( h + 3, "_private" ) == 0 ) -					private = 1; -				else if( g_strcasecmp( h + 3, "_channel" ) == 0 ) -					private = 0; -			} -			 -			imcb_add_buddy( ic, handle, NULL ); -			u = user_findhandle( ic, handle ); -			u->is_private = private; -		} -		else -		{ -			imcb_log( ic, "Message from unknown handle %s:", handle ); -			u = user_find( irc, irc->mynick ); -		} -	} -	 -	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || -	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) -		strip_html( msg ); -	 -	if( set_getbool( &ic->irc->set, "display_timestamps" ) && -	    ( ts = format_timestamp( irc, sent_at ) ) ) -	{ -		char *new = g_strconcat( ts, msg, NULL ); -		g_free( ts ); -		ts = msg = new; -	} -	 -	wrapped = word_wrap( msg, 425 ); -	irc_msgfrom( irc, u->nick, wrapped ); -	g_free( wrapped ); -	g_free( ts ); -} - -void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) -{ -	user_t *u; -	 -	if( !set_getbool( &ic->irc->set, "typing_notice" ) ) -		return; -	 -	if( ( u = user_findhandle( ic, handle ) ) ) -	{ -		char buf[256];  -		 -		g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 ); -		irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf ); -	} -} - -struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) -{ -	struct groupchat *c; -	 -	/* This one just creates the conversation structure, user won't see anything yet */ -	 -	if( ic->groupchats ) -	{ -		for( c = ic->groupchats; c->next; c = c->next ); -		c = c->next = g_new0( struct groupchat, 1 ); -	} -	else -		ic->groupchats = c = g_new0( struct groupchat, 1 ); -	 -	c->ic = ic; -	c->title = g_strdup( handle ); -	c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ ); -	c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); -	 -	if( set_getbool( &ic->irc->set, "debug" ) ) -		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); -	 -	return c; +	query_add( (irc_t *) ic->bee->ui_data, ic, s, +	           imcb_ask_add_cb_yes, imcb_ask_add_cb_no, g_free, data );  } -void imcb_chat_name_hint( struct groupchat *c, const char *name ) +struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle )  { -	if( !c->joined ) -	{ -		struct im_connection *ic = c->ic; -		char stripped[MAX_NICK_LENGTH+1], *full_name; -		 -		strncpy( stripped, name, MAX_NICK_LENGTH ); -		stripped[MAX_NICK_LENGTH] = '\0'; -		nick_strip( stripped ); -		if( set_getbool( &ic->irc->set, "lcnicks" ) ) -			nick_lc( stripped ); -		 -		full_name = g_strdup_printf( "&%s", stripped ); -		 -		if( stripped[0] && -		    nick_cmp( stripped, ic->irc->channel + 1 ) != 0 && -		    irc_chat_by_channel( ic->irc, full_name ) == NULL ) -		{ -			g_free( c->channel ); -			c->channel = full_name; -		} -		else -		{ -			g_free( full_name ); -		} -	} -} - -void imcb_chat_free( struct groupchat *c ) -{ -	struct im_connection *ic = c->ic; -	struct groupchat *l; -	GList *ir; -	 -	if( set_getbool( &ic->irc->set, "debug" ) ) -		imcb_log( ic, "You were removed from conversation %p", c ); -	 -	if( c ) -	{ -		if( c->joined ) -		{ -			user_t *u, *r; -			 -			r = user_find( ic->irc, ic->irc->mynick ); -			irc_privmsg( ic->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" ); -			 -			u = user_find( ic->irc, ic->irc->nick ); -			irc_kick( ic->irc, u, c->channel, r ); -			/* irc_part( ic->irc, u, c->channel ); */ -		} -		 -		/* Find the previous chat in the linked list. */ -		for( l = ic->groupchats; l && l->next != c; l = l->next ); -		 -		if( l ) -			l->next = c->next; -		else -			ic->groupchats = c->next; -		 -		for( ir = c->in_room; ir; ir = ir->next ) -			g_free( ir->data ); -		g_list_free( c->in_room ); -		g_free( c->channel ); -		g_free( c->title ); -		g_free( c->topic ); -		g_free( c ); -	} -} - -void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at ) -{ -	struct im_connection *ic = c->ic; -	char *wrapped; -	user_t *u; -	 -	/* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ -	if( g_strcasecmp( who, ic->acc->user ) == 0 ) -		return; -	 -	u = user_findhandle( ic, who ); -	 -	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || -	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) -		strip_html( msg ); -	 -	wrapped = word_wrap( msg, 425 ); -	if( c && u ) -	{ -		char *ts = NULL; -		if( set_getbool( &ic->irc->set, "display_timestamps" ) ) -			ts = format_timestamp( ic->irc, sent_at ); -		irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped ); -		g_free( ts ); -	} -	else -	{ -		imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); -	} -	g_free( wrapped ); -} - -void imcb_chat_log( struct groupchat *c, char *format, ... ) -{ -	irc_t *irc = c->ic->irc; -	va_list params; -	char *text; -	user_t *u; -	 -	va_start( params, format ); -	text = g_strdup_vprintf( format, params ); -	va_end( params ); -	 -	u = user_find( irc, irc->mynick ); -	 -	irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text ); -	 -	g_free( text ); -} - -void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at ) -{ -	struct im_connection *ic = c->ic; -	user_t *u = NULL; -	 -	if( who == NULL) -		u = user_find( ic->irc, ic->irc->mynick ); -	else if( g_strcasecmp( who, ic->acc->user ) == 0 ) -		u = user_find( ic->irc, ic->irc->nick ); -	else -		u = user_findhandle( ic, who ); -	 -	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || -	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) -		strip_html( topic ); -	 -	g_free( c->topic ); -	c->topic = g_strdup( topic ); -	 -	if( c->joined && u ) -		irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic ); -} - - -/* buddy_chat.c */ - -void imcb_chat_add_buddy( struct groupchat *b, const char *handle ) -{ -	user_t *u = user_findhandle( b->ic, handle ); -	int me = 0; -	 -	if( set_getbool( &b->ic->irc->set, "debug" ) ) -		imcb_log( b->ic, "User %s added to conversation %p", handle, b ); -	 -	/* It might be yourself! */ -	if( b->ic->acc->prpl->handle_cmp( handle, b->ic->acc->user ) == 0 ) -	{ -		u = user_find( b->ic->irc, b->ic->irc->nick ); -		if( !b->joined ) -			irc_join( b->ic->irc, u, b->channel ); -		b->joined = me = 1; -	} -	 -	/* Most protocols allow people to join, even when they're not in -	   your contact list. Try to handle that here */ -	if( !u ) -	{ -		imcb_add_buddy( b->ic, handle, NULL ); -		u = user_findhandle( b->ic, handle ); -	} -	 -	/* Add the handle to the room userlist, if it's not 'me' */ -	if( !me ) -	{ -		if( b->joined ) -			irc_join( b->ic->irc, u, b->channel ); -		b->in_room = g_list_append( b->in_room, g_strdup( handle ) ); -	} -} - -/* This function is one BIG hack... :-( EREWRITE */ -void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ) -{ -	user_t *u; -	int me = 0; -	 -	if( set_getbool( &b->ic->irc->set, "debug" ) ) -		imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" ); -	 -	/* It might be yourself! */ -	if( g_strcasecmp( handle, b->ic->acc->user ) == 0 ) -	{ -		if( b->joined == 0 ) -			return; -		 -		u = user_find( b->ic->irc, b->ic->irc->nick ); -		b->joined = 0; -		me = 1; -	} -	else -	{ -		u = user_findhandle( b->ic, handle ); -	} -	 -	if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) ) -		irc_part( b->ic->irc, u, b->channel ); -} - -static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) -{ -	GList *i; -	 -	/* Find the handle in the room userlist and shoot it */ -	i = b->in_room; -	while( i ) -	{ -		if( g_strcasecmp( handle, i->data ) == 0 ) -		{ -			g_free( i->data ); -			b->in_room = g_list_remove( b->in_room, i->data ); -			return( 1 ); -		} -		 -		i = i->next; -	} -	 -	return( 0 ); -} - - -/* Misc. BitlBee stuff which shouldn't really be here */ - -char *set_eval_away_devoice( set_t *set, char *value ) -{ -	irc_t *irc = set->data; -	int st; -	 -	if( !is_bool( value ) ) -		return SET_INVALID; -	 -	st = bool2int( value ); -	 -	/* Horror.... */ -	 -	if( st != set_getbool( &irc->set, "away_devoice" ) ) -	{ -		char list[80] = ""; -		user_t *u = irc->users; -		int i = 0, count = 0; -		char pm; -		char v[80]; -		 -		if( st ) -			pm = '+'; -		else -			pm = '-'; -		 -		while( u ) -		{ -			if( u->ic && u->online && !u->away ) -			{ -				if( ( strlen( list ) + strlen( u->nick ) ) >= 79 ) -				{ -					for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; -					irc_write( irc, ":%s MODE %s %c%s%s", -					           irc->myhost, -		        			   irc->channel, pm, v, list ); -					 -					*list = 0; -					count = 0; -				} -				 -				sprintf( list + strlen( list ), " %s", u->nick ); -				count ++; -			} -			u = u->next; -		} -		 -		/* $v = 'v' x $i */ -		for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; -		irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost, -		                                            irc->channel, pm, v, list ); -	} -	 -	return value; -} - -char *set_eval_timezone( set_t *set, char *value ) -{ -	char *s; -	 -	if( strcmp( value, "local" ) == 0 || -	    strcmp( value, "gmt" ) == 0 || strcmp( value, "utc" ) == 0 ) -		return value; -	 -	/* Otherwise: +/- at the beginning optional, then one or more numbers, -	   possibly followed by a colon and more numbers. Don't bother bound- -	   checking them since users are free to shoot themselves in the foot. */ -	s = value; -	if( *s == '+' || *s == '-' ) -		s ++; -	 -	/* \d+ */ -	if( !isdigit( *s ) ) -		return SET_INVALID; -	while( *s && isdigit( *s ) ) s ++; -	 -	/* EOS? */ -	if( *s == '\0' ) -		return value; -	 -	/* Otherwise, colon */ -	if( *s != ':' ) -		return SET_INVALID; -	s ++; -	 -	/* \d+ */ -	if( !isdigit( *s ) ) -		return SET_INVALID; -	while( *s && isdigit( *s ) ) s ++; -	 -	/* EOS */ -	return *s == '\0' ? value : SET_INVALID; -} - -static char *format_timestamp( irc_t *irc, time_t msg_ts ) -{ -	time_t now_ts = time( NULL ); -	struct tm now, msg; -	char *set; -	 -	/* If the timestamp is <= 0 or less than a minute ago, discard it as -	   it doesn't seem to add to much useful info and/or might be noise. */ -	if( msg_ts <= 0 || msg_ts > now_ts - 60 ) -		return NULL; -	 -	set = set_getstr( &irc->set, "timezone" ); -	if( strcmp( set, "local" ) == 0 ) -	{ -		localtime_r( &now_ts, &now ); -		localtime_r( &msg_ts, &msg ); -	} -	else -	{ -		int hr, min = 0, sign = 60; -		 -		if( set[0] == '-' ) -		{ -			sign *= -1; -			set ++; -		} -		else if( set[0] == '+' ) -		{ -			set ++; -		} -		 -		if( sscanf( set, "%d:%d", &hr, &min ) >= 1 ) -		{ -			msg_ts += sign * ( hr * 60 + min ); -			now_ts += sign * ( hr * 60 + min ); -		} -		 -		gmtime_r( &now_ts, &now ); -		gmtime_r( &msg_ts, &msg ); -	} -	 -	if( msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday ) -		return g_strdup_printf( "\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ", -		                        msg.tm_hour, msg.tm_min, msg.tm_sec ); -	else -		return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d " -		                        "%02d:%02d:%02d\x02]\x02 ", -		                        msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday, -		                        msg.tm_hour, msg.tm_min, msg.tm_sec ); +	return bee_user_by_handle( ic->bee, ic, handle );  }  /* The plan is to not allow straight calls to prpl functions anymore, but do     them all from some wrappers. We'll start to define some down here: */ -int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags ) -{ -	char *buf = NULL; -	int st; -	 -	if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) ) -	{ -		buf = escape_html( msg ); -		msg = buf; -	} -	 -	st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags ); -	g_free( buf ); -	 -	return st; -} -  int imc_chat_msg( struct groupchat *c, char *msg, int flags )  {  	char *buf = NULL; @@ -1302,7 +554,7 @@ int imc_away_send_update( struct im_connection *ic )  		return 0;  	away = set_getstr( &ic->acc->set, "away" ) ? -	     : set_getstr( &ic->irc->set, "away" ); +	     : set_getstr( &ic->bee->set, "away" );  	if( away && *away )  	{  		GList *m = ic->acc->prpl->away_states( ic ); @@ -1313,7 +565,7 @@ int imc_away_send_update( struct im_connection *ic )  	{  		away = NULL;  		msg = set_getstr( &ic->acc->set, "status" ) ? -		    : set_getstr( &ic->irc->set, "status" ); +		    : set_getstr( &ic->bee->set, "status" );  	}  	ic->acc->prpl->set_away( ic, away, msg ); diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 48a80413..46f6535a 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -1,7 +1,7 @@    /********************************************************************\    * BitlBee -- An IRC to other IM-networks gateway                     *    *                                                                    * -  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* @@ -44,6 +44,8 @@  #include "account.h"  #include "proxy.h"  #include "query.h" +#include "md5.h" +#include "ft.h"  #define BUDDY_ALIAS_MAXLEN 388   /* because MSN names can be 387 characters */ @@ -84,9 +86,9 @@ struct im_connection  	int evil;  	/* BitlBee */ -	irc_t *irc; +	bee_t *bee; -	struct groupchat *groupchats; +	GSList *groupchats;  };  struct groupchat { @@ -97,10 +99,9 @@ struct groupchat {  	 * "nick list". This is how you can check who is in the group chat  	 * already, for example to avoid adding somebody two times. */  	GList *in_room; -	GList *ignored; +	//GList *ignored; -	struct groupchat *next; -	char *channel; +	//struct groupchat *next;  	/* The title variable contains the ID you gave when you created the  	 * chat using imcb_chat_new(). */  	char *title; @@ -111,6 +112,7 @@ struct groupchat {  	/* This is for you, you can add your own structure here to extend this  	 * structure for your protocol's needs. */  	void *data; +	void *ui_data;  };  struct buddy { @@ -131,6 +133,7 @@ struct prpl {  	/* You should set this to the name of your protocol.  	 * - The user sees this name ie. when imcb_log() is used. */  	const char *name; +	void *data;  	/* Added this one to be able to add per-account settings, don't think  	 * it should be used for anything else. You are supposed to use the @@ -208,13 +211,19 @@ struct prpl {  	 * your protocol does not support publicly named group chats, then do  	 * not implement this. */  	struct groupchat * -	     (* chat_join)	(struct im_connection *, const char *room, const char *nick, const char *password); +	     (* chat_join)	(struct im_connection *, const char *room, +	                         const char *nick, const char *password, set_t **sets);  	/* Change the topic, if supported. Note that BitlBee expects the IM  	   server to confirm the topic change with a regular topic change  	   event. If it doesn't do that, you have to fake it to make it  	   visible to the user. */  	void (* chat_topic)	(struct groupchat *, char *topic); +	/* If your protocol module needs any special info for joining chatrooms +	   other than a roomname + nickname, add them here. */ +	void (* chat_add_settings)	(account_t *acc, set_t **head); +	void (* chat_free_settings)	(account_t *acc, set_t **head); +	  	/* You can tell what away states your protocol supports, so that  	 * BitlBee will try to map the IRC away reasons to them. If your  	 * protocol doesn't have any, just return one generic "Away". */ @@ -227,6 +236,16 @@ struct prpl {  	/* Implement these callbacks if you want to use imcb_ask_auth() */  	void (* auth_allow)	(struct im_connection *, const char *who);  	void (* auth_deny)	(struct im_connection *, const char *who); + +	/* Incoming transfer request */ +	void (* transfer_request) (struct im_connection *, file_transfer_t *ft, char *handle ); +	 +	/* Some placeholders so eventually older plugins may cooperate with newer BitlBees. */ +	void *resv1; +	void *resv2; +	void *resv3; +	void *resv4; +	void *resv5;  };  /* im_api core stuff. */ @@ -262,6 +281,7 @@ G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G   * - 'doit' or 'dont' will be called depending of the answer of the user.   */  G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont ); +G_MODULE_EXPORT void imcb_ask_with_free( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont, query_callback myfree );  /* Two common questions you may want to ask:   * - X added you to his contact list, allow? @@ -280,16 +300,8 @@ G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *h  G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname );  G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick ); -/* Buddy activity */ -/* To manipulate the status of a handle. - * - flags can be |='d with OPT_* constants. You will need at least: - *   OPT_LOGGED_IN and OPT_AWAY. - * - 'state' and 'message' can be NULL */ -G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); -/* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); -/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ -G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at );  G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ); +G_MODULE_EXPORT struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle );  G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );  /* Groupchats */ @@ -315,7 +327,6 @@ G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c );  /* Actions, or whatever. */  int imc_away_send_update( struct im_connection *ic ); -int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags );  int imc_chat_msg( struct groupchat *c, char *msg, int flags );  void imc_add_allow( struct im_connection *ic, char *handle ); @@ -325,7 +336,6 @@ void imc_rem_block( struct im_connection *ic, char *handle );  /* Misc. stuff */  char *set_eval_timezone( set_t *set, char *value ); -char *set_eval_away_devoice( set_t *set, char *value );  gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond );  void cancel_auto_reconnect( struct account *a ); diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile index 2792f22a..0ec7436b 100644 --- a/protocols/oscar/Makefile +++ b/protocols/oscar/Makefile @@ -7,6 +7,10 @@  ### DEFINITIONS  -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/oscar/ +CFLAGS += -I$(SRCDIR) +endif  # [SH] Program variables  objects = admin.o auth.o bos.o buddylist.o chat.o chatnav.o conn.o icq.o im.o info.o misc.o msgcookie.o rxhandlers.o rxqueue.o search.o service.o snac.o ssi.o stats.o tlv.o txqueue.o oscar_util.o oscar.o @@ -32,7 +36,7 @@ distclean: clean  $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index f98fbe6f..0310a27e 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -253,8 +253,6 @@ static char *normalize(const char *s)  	g_return_val_if_fail((s != NULL), NULL);  	u = t = g_strdup(s); - -	strcpy(t, s);  	g_strdown(t);  	while (*t && (x < BUF_LEN - 1)) { @@ -289,7 +287,7 @@ static gboolean oscar_callback(gpointer data, gint source,  	odata = (struct oscar_data *)ic->proto_data; -	if (condition & GAIM_INPUT_READ) { +	if (condition & B_EV_IO_READ) {  		if (aim_get_command(odata->sess, conn) >= 0) {  			aim_rxdispatch(odata->sess);                                 if (odata->killme) @@ -361,7 +359,7 @@ static gboolean oscar_login_connect(gpointer data, gint source, b_input_conditio  	}  	aim_conn_completeconnect(sess, conn); -	ic->inpa = b_input_add(conn->fd, GAIM_INPUT_READ, +	ic->inpa = b_input_add(conn->fd, B_EV_IO_READ,  			oscar_callback, conn);  	return FALSE; @@ -492,7 +490,7 @@ static gboolean oscar_bos_connect(gpointer data, gint source, b_input_condition  	}  	aim_conn_completeconnect(sess, bosconn); -	ic->inpa = b_input_add(bosconn->fd, GAIM_INPUT_READ, +	ic->inpa = b_input_add(bosconn->fd, B_EV_IO_READ,  			oscar_callback, bosconn);  	imcb_log(ic, _("Connection established, cookie sent")); @@ -651,6 +649,7 @@ static int gaim_parse_logout(aim_session_t *sess, aim_frame_t *fr, ...) {  static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {  	struct im_connection *ic = sess->aux_data;  	struct chat_connection *chatcon; +	struct groupchat *c = NULL;  	static int id = 1;  	aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0); @@ -663,7 +662,12 @@ static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {  	chatcon = find_oscar_chat_by_conn(ic, fr->conn);  	chatcon->id = id; -	chatcon->cnv = imcb_chat_new(ic, chatcon->show); +	 +	c = bee_chat_by_title(ic->bee, ic, chatcon->show); +	if (c && !c->data) +		chatcon->cnv = c; +	else +		chatcon->cnv = imcb_chat_new(ic, chatcon->show);  	chatcon->cnv->data = chatcon;  	return 1; @@ -702,7 +706,7 @@ static gboolean oscar_chatnav_connect(gpointer data, gint source, b_input_condit  	}  	aim_conn_completeconnect(sess, tstconn); -	odata->cnpa = b_input_add(tstconn->fd, GAIM_INPUT_READ, +	odata->cnpa = b_input_add(tstconn->fd, B_EV_IO_READ,  					oscar_callback, tstconn);  	return FALSE; @@ -730,7 +734,7 @@ static gboolean oscar_auth_connect(gpointer data, gint source, b_input_condition  	}  	aim_conn_completeconnect(sess, tstconn); -	odata->paspa = b_input_add(tstconn->fd, GAIM_INPUT_READ, +	odata->paspa = b_input_add(tstconn->fd, B_EV_IO_READ,  				oscar_callback, tstconn);  	return FALSE; @@ -766,7 +770,7 @@ static gboolean oscar_chat_connect(gpointer data, gint source, b_input_condition  	aim_conn_completeconnect(sess, ccon->conn);  	ccon->inpa = b_input_add(tstconn->fd, -			GAIM_INPUT_READ, +			B_EV_IO_READ,  			oscar_callback, tstconn);  	odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon); @@ -933,7 +937,7 @@ static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {  	tmp = normalize(info->sn);  	imcb_buddy_status(ic, tmp, flags, state_string, NULL); -	/* imcb_buddy_times(ic, tmp, signon, time_idle); */ +	imcb_buddy_times(ic, tmp, signon, time_idle);  	return 1; @@ -1059,8 +1063,7 @@ static void gaim_icq_authgrant(void *data_) {  	message = 0;  	aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");  	// aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message); -	if(imcb_find_buddy(data->ic, uin) == NULL) -		imcb_ask_add(data->ic, uin, NULL); +	imcb_ask_add(data->ic, uin, NULL);  	g_free(uin);  	g_free(data); @@ -1821,11 +1824,13 @@ static void oscar_get_info(struct im_connection *g, char *name) {  static void oscar_get_away(struct im_connection *g, char *who) {  	struct oscar_data *odata = (struct oscar_data *)g->proto_data;  	if (odata->icq) { +		/** FIXME(wilmer): Hmm, lost the ability to get away msgs here, do we care to get that back?  		struct buddy *budlight = imcb_find_buddy(g, who);  		if (budlight)  			if ((budlight->uc & 0xff80) >> 7)  				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)  					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7); +		*/  	} else  		aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);  } @@ -1952,7 +1957,7 @@ static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {  static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {  	struct im_connection *ic = sess->aux_data; -	struct aim_ssi_item *curitem; +	struct aim_ssi_item *curitem, *curgroup;  	int tmp;  	char *nrm; @@ -1963,13 +1968,13 @@ static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {  		switch (curitem->type) {  			case 0x0000: /* Buddy */ -				if ((curitem->name) && (!imcb_find_buddy(ic, nrm))) { +				if ((curitem->name) && (!imcb_buddy_by_handle(ic, nrm))) {  					char *realname = NULL;  					if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))  						    realname = aim_gettlv_str(curitem->data, 0x0131, 1); -						 -					imcb_add_buddy(ic, nrm, NULL); +					 +					imcb_add_buddy(ic, nrm, curgroup->gid == curitem->gid ? curgroup->name : NULL);  					if (realname) {  						imcb_buddy_nick_hint(ic, nrm, realname); @@ -1979,6 +1984,10 @@ static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {  				}  				break; +			case 0x0001: /* Group */ +				curgroup = curitem; +				break; +  			case 0x0002: /* Permit buddy */  				if (curitem->name) {  					GSList *list; @@ -2421,7 +2430,8 @@ void oscar_chat_msg(struct groupchat *c, char *message, int msgflags)  	guint16 flags;  	char *s; -	ccon = c->data; +	if (!(ccon = c->data)) +		return;  	for (s = message; *s; s++)  		if (*s & 128) @@ -2462,7 +2472,10 @@ void oscar_chat_invite(struct groupchat *c, char *who, char *message)  {  	struct im_connection *ic = c->ic;  	struct oscar_data * od = (struct oscar_data *)ic->proto_data; -	struct chat_connection *ccon = c->data; +	struct chat_connection *ccon; +	 +	if (!(ccon = c->data)) +		return;  	aim_chat_invite(od->sess, od->conn, who, message ? message : "",  					ccon->exchange, ccon->name, 0x0); @@ -2487,44 +2500,59 @@ void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)  void oscar_chat_leave(struct groupchat *c)  { +	if (!c->data) +		return;  	oscar_chat_kill(c->ic, c->data);  } -struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, const char * nick, const char * password ) +struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char *room, +	const char *nick, const char *password, int exchange_number)  {  	struct oscar_data * od = (struct oscar_data *)ic->proto_data; +	struct groupchat *ret = imcb_chat_new(ic, room);  	aim_conn_t * cur;  	if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {  		int st; -		st = aim_chatnav_createroom(od->sess, cur, room, 4); +		st = aim_chatnav_createroom(od->sess, cur, room, exchange_number); -		return NULL; +		return ret;  	} else {  		struct create_room * cr = g_new0(struct create_room, 1); -		cr->exchange = 4; +		cr->exchange = exchange_number;  		cr->name = g_strdup(room);  		od->create_rooms = g_slist_append(od->create_rooms, cr);  		aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); -		return NULL; +		return ret;  	}  } +struct groupchat *oscar_chat_join(struct im_connection *ic, const char *room, +	const char *nick, const char *password, set_t **sets) +{ +	return oscar_chat_join_internal(ic, room, nick, password, set_getint(sets, "exchange_number")); +} +  struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)  {  	struct oscar_data * od = (struct oscar_data *)ic->proto_data;  	struct groupchat *ret;  	static int chat_id = 0; -	char * chatname; +	char * chatname, *s; +	struct groupchat *c;  	chatname = g_strdup_printf("%s%s%d", isdigit(*ic->acc->user) ? "icq" : "",  	                           ic->acc->user, chat_id++); -   -	ret = oscar_chat_join(ic, chatname, NULL, NULL); - +	 +	for (s = chatname; *s; s ++) +		if (!isalnum(*s)) +			*s = '0'; +	 +	c = imcb_chat_new(ic, chatname); +	ret = oscar_chat_join_internal(ic, chatname, NULL, NULL, 4);  	aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);  	g_free(chatname); @@ -2536,7 +2564,7 @@ void oscar_accept_chat(void *data)  {  	struct aim_chat_invitation * inv = data; -	oscar_chat_join(inv->ic, inv->name, NULL, NULL); +	oscar_chat_join_internal(inv->ic, inv->name, NULL, NULL, 4);  	g_free(inv->name);  	g_free(inv);  } @@ -2549,6 +2577,16 @@ void oscar_reject_chat(void *data)  	g_free(inv);  } +void oscar_chat_add_settings(account_t *acc, set_t **head) +{ +	set_add(head, "exchange_number", "4", set_eval_int, NULL); +} + +void oscar_chat_free_settings(account_t *acc, set_t **head) +{ +	set_del(head, "exchange_number"); +} +  void oscar_initmodule()   {  	struct prpl *ret = g_new0(struct prpl, 1); @@ -2569,6 +2607,8 @@ void oscar_initmodule()  	ret->chat_leave = oscar_chat_leave;  	ret->chat_with = oscar_chat_with;  	ret->chat_join = oscar_chat_join; +	ret->chat_add_settings = oscar_chat_add_settings; +	ret->chat_free_settings = oscar_chat_free_settings;  	ret->add_permit = oscar_add_permit;  	ret->add_deny = oscar_add_deny;  	ret->rem_permit = oscar_rem_permit; diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile new file mode 100644 index 00000000..97a5bb6a --- /dev/null +++ b/protocols/purple/Makefile @@ -0,0 +1,44 @@ +########################### +## Makefile for BitlBee  ## +##                       ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/purple/ +endif + +# [SH] Program variables +objects = ft.o purple.o + +CFLAGS += -Wall $(PURPLE_CFLAGS) +LFLAGS += -r + +# [SH] Phony targets +all: purple_mod.o +check: all +lcov: check +gcov:  +	gcov *.c + +.PHONY: all clean distclean + +clean: +	rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: $(SRCDIR)%.c +	@echo '*' Compiling $< +	@$(CC) -c $(CFLAGS) $< -o $@ + +purple_mod.o: $(objects) +	@echo '*' Linking purple_mod.o +	$(LD) $(LFLAGS) $(objects) -o purple_mod.o diff --git a/protocols/purple/ft-direct.c b/protocols/purple/ft-direct.c new file mode 100644 index 00000000..98a16d75 --- /dev/null +++ b/protocols/purple/ft-direct.c @@ -0,0 +1,239 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  libpurple module - File transfer stuff                                   * +*                                                                           * +*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              * +*                                                                           * +*  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.              * +*                                                                           * +\***************************************************************************/ + +/* This code tries to do direct file transfers, i.e. without caching the file +   locally on disk first. Since libpurple can only do this since version 2.6.0 +   and even then very unreliably (and not with all IM modules), I'm canning +   this code for now. */ + +#include "bitlbee.h" + +#include <stdarg.h> + +#include <glib.h> +#include <purple.h> + +struct prpl_xfer_data +{ +	PurpleXfer *xfer; +	file_transfer_t *ft; +	gint ready_timer; +	char *buf; +	int buf_len; +}; + +static file_transfer_t *next_ft; + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); + +/* Glorious hack: We seem to have to remind at least some libpurple plugins +   that we're ready because this info may get lost if we give it too early. +   So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ +	struct prpl_xfer_data *px = data; +	 +	purple_xfer_ui_ready( px->xfer ); +	 +	return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ +	struct prpl_xfer_data *px = ft->data; +	px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); +	return TRUE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ +	struct prpl_xfer_data *px = ft->data; +	 +	px->buf = g_memdup( buffer, len ); +	px->buf_len = len; + +	//purple_xfer_ui_ready( px->xfer ); +	px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); +	 +	return TRUE; +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ +	struct prpl_xfer_data *px = ft->data; +	purple_xfer_request_accepted( px->xfer, NULL ); +	prpl_xfer_write_request( ft ); +} + +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) +{ +	struct prpl_xfer_data *px = ft->data; +	purple_xfer_request_denied( px->xfer ); +} + +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ +	PurpleXfer *xfer = data; +	struct im_connection *ic = purple_ic_by_pa( xfer->account ); +	struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); +	PurpleBuddy *buddy; +	const char *who; +	 +	buddy = purple_find_buddy( xfer->account, xfer->who ); +	who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; +	 +	/* TODO(wilmer): After spreading some more const goodness in BitlBee, +	   remove the evil cast below. */ +	px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); +	px->ft->data = px; +	px->xfer = data; +	px->xfer->ui_data = px; +	 +	px->ft->accept = prpl_xfer_accept; +	px->ft->canceled = prpl_xfer_canceled; +	px->ft->write_request = prpl_xfer_write_request; +	 +	return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ +	if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) +	{ +		/* This should suppress the stupid file dialog. */ +		purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); +		 +		/* Sadly the xfer struct is still empty ATM so come back after +		   the caller is done. */ +		b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); +	} +	else +	{ +		struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); +		 +		px->ft = next_ft; +		px->ft->data = px; +		px->xfer = xfer; +		px->xfer->ui_data = px; +		 +		purple_xfer_set_filename( xfer, px->ft->file_name ); +		purple_xfer_set_size( xfer, px->ft->file_size ); +		 +		next_ft = NULL; +	} +} + +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) +{ +	fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ +	fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ +	struct prpl_xfer_data *px = xfer->ui_data; +	gboolean st; +	 +	fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); +	 +	b_event_remove( px->ready_timer ); +	px->ready_timer = 0; +	 +	st = px->ft->write( px->ft, (char*) buffer, size ); +	 +	if( st && xfer->bytes_remaining == size ) +		imcb_file_finished( px->ft ); +	 +	return st ? size : 0; +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ +	struct prpl_xfer_data *px = xfer->ui_data; +	 +	fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + +	if( px->buf ) +	{ +		*buffer = px->buf; +		px->buf = NULL; +		 +		px->ft->write_request( px->ft ); +		 +		return px->buf_len; +	} +	 +	return 0; +} + +PurpleXferUiOps bee_xfer_uiops = +{ +	prplcb_xfer_new, +	prplcb_xfer_dbg, +	prplcb_xfer_dbg, +	prplcb_xfer_progress, +	prplcb_xfer_dbg, +	prplcb_xfer_dbg, +	prplcb_xfer_write, +	prplcb_xfer_read, +	prplcb_xfer_dbg, +}; + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ +	PurpleAccount *pa = ic->proto_data; +	struct prpl_xfer_data *px; +	 +	/* xfer_new() will pick up this variable. It's a hack but we're not +	   multi-threaded anyway. */ +	next_ft = ft; +	serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); +	 +	ft->write = prpl_xfer_write; +	 +	px = ft->data; +	imcb_file_recv_start( ft ); +	 +	px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ +	struct prpl_xfer_data *px = data; +	 +	if( px->ft->status & FT_STATUS_TRANSFERRING ) +	{ +		fprintf( stderr, "The ft, it is ready...\n" ); +		px->ft->write_request( px->ft ); +		 +		return FALSE; +	} +	 +	return TRUE; +} diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c new file mode 100644 index 00000000..c4efc657 --- /dev/null +++ b/protocols/purple/ft.c @@ -0,0 +1,355 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  libpurple module - File transfer stuff                                   * +*                                                                           * +*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              * +*                                                                           * +*  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.              * +*                                                                           * +\***************************************************************************/ + +/* Do file transfers via disk for now, since libpurple was really designed +   for straight-to/from disk fts and is only just learning how to pass the +   file contents the the UI instead (2.6.0 and higher it seems, and with +   varying levels of success). */ + +#include "bitlbee.h" + +#include <stdarg.h> + +#include <glib.h> +#include <purple.h> + +struct prpl_xfer_data +{ +	PurpleXfer *xfer; +	file_transfer_t *ft; +	struct im_connection *ic; +	int fd; +	char *fn, *handle; +	gboolean ui_wants_data; +}; + +static file_transfer_t *next_ft; + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); +static gboolean prpl_xfer_write_request( struct file_transfer *ft ); + + +/* Receiving files (IM->UI): */ +static void prpl_xfer_accept( struct file_transfer *ft ) +{ +	struct prpl_xfer_data *px = ft->data; +	purple_xfer_request_accepted( px->xfer, NULL ); +	prpl_xfer_write_request( ft ); +} + +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) +{ +	struct prpl_xfer_data *px = ft->data; +	purple_xfer_request_denied( px->xfer ); +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ +	if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) +	{ +		struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); +		 +		xfer->ui_data = px; +		px->xfer = xfer; +		px->fn = mktemp( g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ) ); +		px->fd = -1; +		px->ic = purple_ic_by_pa( xfer->account ); +		 +		purple_xfer_set_local_filename( xfer, px->fn ); +		 +		/* Sadly the xfer struct is still empty ATM so come back after +		   the caller is done. */ +		b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); +	} +	else +	{ +		struct file_transfer *ft = next_ft; +		struct prpl_xfer_data *px = ft->data; +		 +		xfer->ui_data = px; +		px->xfer = xfer; +		 +		next_ft = NULL; +	} +} + +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ +	PurpleXfer *xfer = data; +	struct im_connection *ic = purple_ic_by_pa( xfer->account ); +	struct prpl_xfer_data *px = xfer->ui_data; +	PurpleBuddy *buddy; +	const char *who; +	 +	buddy = purple_find_buddy( xfer->account, xfer->who ); +	who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; +	 +	/* TODO(wilmer): After spreading some more const goodness in BitlBee, +	   remove the evil cast below. */ +	px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); +	px->ft->data = px; +	 +	px->ft->accept = prpl_xfer_accept; +	px->ft->canceled = prpl_xfer_canceled; +	px->ft->write_request = prpl_xfer_write_request; +	 +	return FALSE; +} + +gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) +{ +	struct file_transfer *ft = data; +	struct prpl_xfer_data *px = ft->data; +	struct stat fs; +	off_t tx_bytes; +	 +	/* If we don't have the file opened yet, there's no data so wait. */ +	if( px->fd < 0 || !px->ui_wants_data ) +		return FALSE; +	 +	tx_bytes = lseek( px->fd, 0, SEEK_CUR ); +	fstat( px->fd, &fs ); +	 +	if( fs.st_size > tx_bytes ) +	{ +		char buf[1024]; +		size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); +		 +		if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) +		{ +			px->ui_wants_data = FALSE; +		} +		else +		{ +			purple_xfer_cancel_local( px->xfer ); +			imcb_file_canceled( px->ic, ft, "Read error" ); +		} +	} +	 +	if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) +	{ +		/*purple_xfer_end( px->xfer );*/ +		imcb_file_finished( px->ic, ft ); +	} +	 +	return FALSE; +} + +/* UI calls this when its buffer is empty and wants more data to send to the user. */ +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ +	struct prpl_xfer_data *px = ft->data; +	 +	px->ui_wants_data = TRUE; +	try_write_to_ui( ft, 0, 0 ); +	 +	return FALSE; +} + + +/* Generic (IM<>UI): */ +static void prplcb_xfer_destroy( PurpleXfer *xfer ) +{ +	struct prpl_xfer_data *px = xfer->ui_data; +	 +	g_free( px->fn ); +	g_free( px->handle ); +	if( px->fd >= 0 ) +		close( px->fd ); +	g_free( px ); +} + +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) +{ +	struct prpl_xfer_data *px = xfer->ui_data; +	 +	if( px == NULL ) +		return; +	 +	if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) +	{ +		if( *px->fn ) +		{ +			char *slash; +			 +			unlink( px->fn ); +			if( ( slash = strrchr( px->fn, '/' ) ) ) +			{ +				*slash = '\0'; +				rmdir( px->fn ); +			} +			*px->fn = '\0'; +		} +		 +		return; +	} +	 +	if( px->fd == -1 && percent > 0 ) +	{ +		/* Weeeeeeeee, we're getting data! That means the file exists +		   by now so open it and start sending to the UI. */ +		px->fd = open( px->fn, O_RDONLY ); +		 +		/* Unlink it now, because we don't need it after this. */ +		unlink( px->fn ); +	} +	 +	if( percent < 1 ) +		try_write_to_ui( px->ft, 0, 0 ); +	else +		/* Another nice problem: If we have the whole file, it only +		   gets closed when we return. Problem: There may still be +		   stuff buffered and not written, we'll only see it after +		   the caller close()s the file. So poll the file after that. */ +		b_timeout_add( 0, try_write_to_ui, px->ft ); +} + +static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) +{ +	struct prpl_xfer_data *px = xfer->ui_data; +	 +	if( px->ft ) +		imcb_file_canceled( px->ic, px->ft, "Canceled by remote end" ); +	else +		/* px->ft == NULL for sends, because of the two stages. :-/ */ +		imcb_error( px->ic, "File transfer cancelled by remote end" ); +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ +	fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + + +/* Sending files (UI->IM): */ +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ); +static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ +	struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); +	char *dir, *basename; +	 +	ft->data = px; +	px->ft = ft; +	 +	dir = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); +	if( !mkdtemp( dir ) ) +	{ +		imcb_error( ic, "Could not create temporary file for file transfer" ); +		g_free( px ); +		g_free( dir ); +		return; +	} +	 +	if( ( basename = strrchr( ft->file_name, '/' ) ) ) +		basename++; +	else +		basename = ft->file_name; +	px->fn = g_strdup_printf( "%s/%s", dir, basename ); +	px->fd = open( px->fn, O_WRONLY | O_CREAT, 0600 ); +	g_free( dir ); +	 +	if( px->fd < 0 ) +	{ +		imcb_error( ic, "Could not create temporary file for file transfer" ); +		g_free( px ); +		g_free( px->fn ); +		return; +	} +	 +	px->ic = ic; +	px->handle = g_strdup( handle ); +	 +	imcb_log( ic, "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait..." ); +	 +	b_timeout_add( 0, purple_transfer_request_cb, ft ); +} + +static void purple_transfer_forward( struct file_transfer *ft ) +{ +	struct prpl_xfer_data *px = ft->data; +	PurpleAccount *pa = px->ic->proto_data; +	 +	/* xfer_new() will pick up this variable. It's a hack but we're not +	   multi-threaded anyway. */ +	next_ft = ft; +	serv_send_file( purple_account_get_connection( pa ), px->handle, px->fn ); +} + +static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *ft = data; +	struct prpl_xfer_data *px = ft->data; +	 +	if( ft->write == NULL ) +	{ +		ft->write = prpl_xfer_write; +		imcb_file_recv_start( px->ic, ft ); +	} +	 +	ft->write_request( ft ); +	 +	return FALSE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ +	struct prpl_xfer_data *px = ft->data; +	 +	if( write( px->fd, buffer, len ) != len ) +	{ +		imcb_file_canceled( px->ic, ft, "Error while writing temporary file" ); +		return FALSE; +	} +	 +	if( lseek( px->fd, 0, SEEK_CUR ) >= ft->file_size ) +	{ +		close( px->fd ); +		px->fd = -1; +		 +		purple_transfer_forward( ft ); +		imcb_file_finished( px->ic, ft ); +		px->ft = NULL; +	} +	else +		b_timeout_add( 0, purple_transfer_request_cb, ft ); +	 +	return TRUE; +} + + + +PurpleXferUiOps bee_xfer_uiops = +{ +	prplcb_xfer_new, +	prplcb_xfer_destroy, +	NULL, /* prplcb_xfer_add, */ +	prplcb_xfer_progress, +	prplcb_xfer_dbg, +	prplcb_xfer_cancel_remote, +	NULL, +	NULL, +	prplcb_xfer_dbg, +}; diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c new file mode 100644 index 00000000..c7cfcfda --- /dev/null +++ b/protocols/purple/purple.c @@ -0,0 +1,1201 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  libpurple module - Main file                                             * +*                                                                           * +*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              * +*                                                                           * +*  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 "bitlbee.h" +#include "help.h" + +#include <stdarg.h> + +#include <glib.h> +#include <purple.h> + +GSList *purple_connections; + +/* This makes me VERY sad... :-( But some libpurple callbacks come in without +   any context so this is the only way to get that. Don't want to support +   libpurple in daemon mode anyway. */ +static bee_t *local_bee; + +static char *set_eval_display_name( set_t *set, char *value ); + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) +{ +	GSList *i; +	 +	for( i = purple_connections; i; i = i->next ) +		if( ((struct im_connection *)i->data)->proto_data == pa ) +			return i->data; +	 +	return NULL; +} + +static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +{ +	return purple_ic_by_pa( purple_connection_get_account( gc ) ); +} + +static gboolean purple_menu_cmp( const char *a, const char *b ) +{ +	while( *a && *b ) +	{ +		while( *a == '_' ) a ++; +		while( *b == '_' ) b ++; +		if( tolower( *a ) != tolower( *b ) ) +			return FALSE; +		 +		a ++; +		b ++; +	} +	 +	return ( *a == '\0' && *b == '\0' ); +} + +static void purple_init( account_t *acc ) +{ +	PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); +	PurplePluginProtocolInfo *pi = prpl->info->extra_info; +	PurpleAccount *pa; +	GList *i, *st; +	set_t *s; +	char help_title[64]; +	GString *help; +	 +	help = g_string_new( "" ); +	g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:", +	                        (char*) acc->prpl->name, prpl->info->name ); +	 +	/* Convert all protocol_options into per-account setting variables. */ +	for( i = pi->protocol_options; i; i = i->next ) +	{ +		PurpleAccountOption *o = i->data; +		const char *name; +		char *def = NULL; +		set_eval eval = NULL; +		void *eval_data = NULL; +		GList *io = NULL; +		GSList *opts = NULL; +		 +		name = purple_account_option_get_setting( o ); +		 +		switch( purple_account_option_get_type( o ) ) +		{ +		case PURPLE_PREF_STRING: +			def = g_strdup( purple_account_option_get_default_string( o ) ); +			 +			g_string_append_printf( help, "\n* %s (%s), %s, default: %s", +			                        name, purple_account_option_get_text( o ), +			                        "string", def ); +			 +			break; +		 +		case PURPLE_PREF_INT: +			def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) ); +			eval = set_eval_int; +			 +			g_string_append_printf( help, "\n* %s (%s), %s, default: %s", +			                        name, purple_account_option_get_text( o ), +			                        "integer", def ); +			 +			break; +		 +		case PURPLE_PREF_BOOLEAN: +			if( purple_account_option_get_default_bool( o ) ) +				def = g_strdup( "true" ); +			else +				def = g_strdup( "false" ); +			eval = set_eval_bool; +			 +			g_string_append_printf( help, "\n* %s (%s), %s, default: %s", +			                        name, purple_account_option_get_text( o ), +			                        "boolean", def ); +			 +			break; +		 +		case PURPLE_PREF_STRING_LIST: +			def = g_strdup( purple_account_option_get_default_list_value( o ) ); +			 +			g_string_append_printf( help, "\n* %s (%s), %s, default: %s", +			                        name, purple_account_option_get_text( o ), +			                        "list", def ); +			g_string_append( help, "\n  Possible values: " ); +			 +			for( io = purple_account_option_get_list( o ); io; io = io->next ) +			{ +				PurpleKeyValuePair *kv = io->data; +				opts = g_slist_append( opts, kv->value ); +				/* TODO: kv->value is not a char*, WTF? */ +				if( strcmp( kv->value, kv->key ) != 0 ) +					g_string_append_printf( help, "%s (%s), ", (char*) kv->value, kv->key ); +				else +					g_string_append_printf( help, "%s, ", (char*) kv->value ); +			} +			g_string_truncate( help, help->len - 2 ); +			eval = set_eval_list; +			eval_data = opts; +			 +			break; +			 +		default: +			/** No way to talk to the user right now, invent one when +			this becomes important. +			irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n", +			             name, purple_account_option_get_type( o ) ); +			*/ +			name = NULL; +		} +		 +		if( name != NULL ) +		{ +			s = set_add( &acc->set, name, def, eval, acc ); +			s->flags |= ACC_SET_OFFLINE_ONLY; +			s->eval_data = eval_data; +			g_free( def ); +		} +	} +	 +	g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name ); +	help_add_mem( &global.help, help_title, help->str ); +	g_string_free( help, TRUE ); +	 +	s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); +	s->flags |= ACC_SET_ONLINE_ONLY; +	 +	if( pi->options & OPT_PROTO_MAIL_CHECK ) +	{ +		s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); +		s->flags |= ACC_SET_OFFLINE_ONLY; +	} +	 +	/* Go through all away states to figure out if away/status messages +	   are possible. */ +	pa = purple_account_new( acc->user, (char*) acc->prpl->data ); +	for( st = purple_account_get_status_types( pa ); st; st = st->next ) +	{ +		PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data ); +		 +		if( prim == PURPLE_STATUS_AVAILABLE ) +		{ +			if( purple_status_type_get_attr( st->data, "message" ) ) +				acc->flags |= ACC_FLAG_STATUS_MESSAGE; +		} +		else if( prim != PURPLE_STATUS_OFFLINE ) +		{ +			if( purple_status_type_get_attr( st->data, "message" ) ) +				acc->flags |= ACC_FLAG_AWAY_MESSAGE; +		} +	} +	purple_accounts_remove( pa ); +} + +static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) +{ +	PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); +	PurplePluginProtocolInfo *pi = prpl->info->extra_info; +	GList *i; +	 +	for( i = pi->protocol_options; i; i = i->next ) +	{ +		PurpleAccountOption *o = i->data; +		const char *name; +		set_t *s; +		 +		name = purple_account_option_get_setting( o ); +		s = set_find( &acc->set, name ); +		if( s->value == NULL ) +			continue; +		 +		switch( purple_account_option_get_type( o ) ) +		{ +		case PURPLE_PREF_STRING: +		case PURPLE_PREF_STRING_LIST: +			purple_account_set_string( pa, name, set_getstr( &acc->set, name ) ); +			break; +		 +		case PURPLE_PREF_INT: +			purple_account_set_int( pa, name, set_getint( &acc->set, name ) ); +			break; +		 +		case PURPLE_PREF_BOOLEAN: +			purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) ); +			break; +		 +		default: +			break; +		} +	} +	 +	if( pi->options & OPT_PROTO_MAIL_CHECK ) +		purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) ); +} + +static void purple_login( account_t *acc ) +{ +	struct im_connection *ic = imcb_new( acc ); +	PurpleAccount *pa; +	 +	if( local_bee != NULL && local_bee != acc->bee ) +	{ +		imcb_error( ic,  "Daemon mode detected. Do *not* try to use libpurple in daemon mode! " +		                 "Please use inetd or ForkDaemon mode instead." ); +		imc_logout( ic, FALSE ); +		return; +	} +	local_bee = acc->bee; +	 +	/* For now this is needed in the _connected() handlers if using +	   GLib event handling, to make sure we're not handling events +	   on dead connections. */ +	purple_connections = g_slist_prepend( purple_connections, ic ); +	 +	ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data ); +	purple_account_set_password( pa, acc->pass ); +	purple_sync_settings( acc, pa ); +	 +	purple_account_set_enabled( pa, "BitlBee", TRUE ); +} + +static void purple_logout( struct im_connection *ic ) +{ +	PurpleAccount *pa = ic->proto_data; +	 +	purple_account_set_enabled( pa, "BitlBee", FALSE ); +	purple_connections = g_slist_remove( purple_connections, ic ); +	purple_accounts_remove( pa ); +} + +static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) +{ +	PurpleConversation *conv; +	 +	if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, +	                                                    who, ic->proto_data ) ) == NULL ) +	{ +		conv = purple_conversation_new( PURPLE_CONV_TYPE_IM, +		                                ic->proto_data, who ); +	} +	 +	purple_conv_im_send( purple_conversation_get_im_data( conv ), message ); +	 +	return 1; +} + +static GList *purple_away_states( struct im_connection *ic ) +{ +	PurpleAccount *pa = ic->proto_data; +	GList *st, *ret = NULL; +	 +	for( st = purple_account_get_status_types( pa ); st; st = st->next ) +	{ +		PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data ); +		if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE ) +			ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); +	} +	 +	return ret; +} + +static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) +{ +	PurpleAccount *pa = ic->proto_data; +	GList *status_types = purple_account_get_status_types( pa ), *st; +	PurpleStatusType *pst = NULL; +	GList *args = NULL; +	 +	for( st = status_types; st; st = st->next ) +	{ +		pst = st->data; +		 +		if( state_txt == NULL && +		    purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE ) +			break; + +		if( state_txt != NULL && +		    g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) +			break; +	} +	 +	if( message && purple_status_type_get_attr( pst, "message" ) ) +	{ +		args = g_list_append( args, "message" ); +		args = g_list_append( args, message ); +	} +	 +	purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away", +		                        TRUE, args ); + +	g_list_free( args ); +} + +static char *set_eval_display_name( set_t *set, char *value ) +{ +	account_t *acc = set->data; +	struct im_connection *ic = acc->ic; +	 +	return NULL; +} + +static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) +{ +	PurpleBuddy *pb; +	PurpleGroup *pg = NULL; +	 +	if( group && !( pg = purple_find_group( group ) ) ) +	{ +		pg = purple_group_new( group ); +		purple_blist_add_group( pg, NULL ); +	} +	 +	pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL ); +	purple_blist_add_buddy( pb, NULL, pg, NULL ); +	purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb ); +} + +static void purple_remove_buddy( struct im_connection *ic, char *who, char *group ) +{ +	PurpleBuddy *pb; +	 +	pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); +	if( pb != NULL ) +	{ +		PurpleGroup *group; +		 +		group = purple_buddy_get_group( pb ); +		purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, group ); +		 +		purple_blist_remove_buddy( pb ); +	} +} + +static void purple_add_permit( struct im_connection *ic, char *who ) +{ +	PurpleAccount *pa = ic->proto_data; +	 +	purple_privacy_permit_add( pa, who, FALSE ); +} + +static void purple_add_deny( struct im_connection *ic, char *who ) +{ +	PurpleAccount *pa = ic->proto_data; +	 +	purple_privacy_deny_add( pa, who, FALSE ); +} + +static void purple_rem_permit( struct im_connection *ic, char *who ) +{ +	PurpleAccount *pa = ic->proto_data; +	 +	purple_privacy_permit_remove( pa, who, FALSE ); +} + +static void purple_rem_deny( struct im_connection *ic, char *who ) +{ +	PurpleAccount *pa = ic->proto_data; +	 +	purple_privacy_deny_remove( pa, who, FALSE ); +} + +static void purple_get_info( struct im_connection *ic, char *who ) +{ +	serv_get_info( purple_account_get_connection( ic->proto_data ), who ); +} + +static void purple_keepalive( struct im_connection *ic ) +{ +} + +static int purple_send_typing( struct im_connection *ic, char *who, int flags ) +{ +	PurpleTypingState state = PURPLE_NOT_TYPING; +	PurpleConversation *conv; +	 +	if( flags & OPT_TYPING ) +		state = PURPLE_TYPING; +	else if( flags & OPT_THINKING ) +		state = PURPLE_TYPED; +	 +	if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, +	                                                    who, ic->proto_data ) ) == NULL ) +	{ +		purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state ); +		return 1; +	} +	else +	{ +		return 0; +	} +} + +static void purple_chat_msg( struct groupchat *gc, char *message, int flags ) +{ +	PurpleConversation *pc = gc->data; +	 +	purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message ); +} + +struct groupchat *purple_chat_with( struct im_connection *ic, char *who ) +{ +	/* No, "of course" this won't work this way. Or in fact, it almost +	   does, but it only lets you send msgs to it, you won't receive +	   any. Instead, we have to click the virtual menu item. +	PurpleAccount *pa = ic->proto_data; +	PurpleConversation *pc; +	PurpleConvChat *pcc; +	struct groupchat *gc; +	 +	gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" ); +	gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" ); +	pc->ui_data = gc; +	 +	pcc = PURPLE_CONV_CHAT( pc ); +	purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE ); +	purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE ); +	//purple_conv_chat_add_user( pcc, who, "", 0, TRUE ); +	*/ +	 +	/* There went my nice afternoon. :-( */ +	 +	PurpleAccount *pa = ic->proto_data; +	PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); +	PurplePluginProtocolInfo *pi = prpl->info->extra_info; +	PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); +	PurpleMenuAction *mi; +	GList *menu; +	void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */ +	 +	if( !pb || !pi || !pi->blist_node_menu ) +		return NULL; +	 +	menu = pi->blist_node_menu( &pb->node ); +	while( menu ) +	{ +		mi = menu->data; +		if( purple_menu_cmp( mi->label, "initiate chat" ) || +		    purple_menu_cmp( mi->label, "initiate conference" ) ) +			break; +		menu = menu->next; +	} +	 +	if( menu == NULL ) +		return NULL; +	 +	/* Call the fucker. */ +	callback = (void*) mi->callback; +	callback( &pb->node, menu->data ); +	 +	return NULL; +} + +void purple_chat_invite( struct groupchat *gc, char *who, char *message ) +{ +	PurpleConversation *pc = gc->data; +	PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc ); +	 +	serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ), +	                  purple_conv_chat_get_id( pcc ),  +	                  message && *message ? message : "Please join my chat", +	                  who ); +} + +void purple_chat_leave( struct groupchat *gc ) +{ +	PurpleConversation *pc = gc->data; +	 +	purple_conversation_destroy( pc ); +} + +struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password ) +{ +	PurpleAccount *pa = ic->proto_data; +	PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); +	PurplePluginProtocolInfo *pi = prpl->info->extra_info; +	GHashTable *chat_hash; +	PurpleConversation *conv; +	GList *info, *l; +	 +	if( !pi->chat_info || !pi->chat_info_defaults || +	    !( info = pi->chat_info( purple_account_get_connection( pa ) ) ) ) +	{ +		imcb_error( ic, "Joining chatrooms not supported by this protocol" ); +		return NULL; +	} +	 +	if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_CHAT, room, pa ) ) ) +		purple_conversation_destroy( conv ); +	 +	chat_hash = pi->chat_info_defaults( purple_account_get_connection( pa ), room ); +	 +	for( l = info; l; l = l->next ) +	{ +		struct proto_chat_entry *pce = l->data; +		 +		if( strcmp( pce->identifier, "handle" ) == 0 ) +			g_hash_table_replace( chat_hash, "handle", g_strdup( nick ) ); +		else if( strcmp( pce->identifier, "password" ) == 0 ) +			g_hash_table_replace( chat_hash, "password", g_strdup( password ) ); +		else if( strcmp( pce->identifier, "passwd" ) == 0 ) +			g_hash_table_replace( chat_hash, "passwd", g_strdup( password ) ); +	} +	 +	serv_join_chat( purple_account_get_connection( pa ), chat_hash ); +	 +	return NULL; +} + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); + +static void purple_ui_init(); + +GHashTable *prplcb_ui_info() +{ +	static GHashTable *ret; +	 +	if( ret == NULL ) +	{ +		ret = g_hash_table_new(g_str_hash, g_str_equal); +		g_hash_table_insert( ret, "name", "BitlBee" ); +		g_hash_table_insert( ret, "version", BITLBEE_VERSION ); +	} +	 +	return ret; +} + +static PurpleCoreUiOps bee_core_uiops =  +{ +	NULL, +	NULL, +	purple_ui_init, +	NULL, +	prplcb_ui_info, +}; + +static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	 +	imcb_log( ic, "%s", text ); +} + +static void prplcb_conn_connected( PurpleConnection *gc ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	const char *dn; +	set_t *s; +	 +	imcb_connected( ic ); +	 +	if( ( dn = purple_connection_get_display_name( gc ) ) && +	    ( s = set_find( &ic->acc->set, "display_name" ) ) ) +	{ +		g_free( s->value ); +		s->value = g_strdup( dn ); +	} +	 +	if( gc->flags & PURPLE_CONNECTION_HTML ) +		ic->flags |= OPT_DOES_HTML; +} + +static void prplcb_conn_disconnected( PurpleConnection *gc ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	 +	if( ic != NULL ) +	{ +		imc_logout( ic, !gc->wants_to_die ); +	} +} + +static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	 +	if( ic != NULL ) +		imcb_log( ic, "%s", text ); +} + +static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	 +	/* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login, +	   should probably handle that. */ +	if( ic != NULL ) +		imcb_error( ic, "%s", text ); +} + +static PurpleConnectionUiOps bee_conn_uiops = +{ +	prplcb_conn_progress, +	prplcb_conn_connected, +	prplcb_conn_disconnected, +	prplcb_conn_notice, +	NULL, +	NULL, +	NULL, +	prplcb_conn_report_disconnect_reason, +}; + +static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) +{ +	if( node->type == PURPLE_BLIST_BUDDY_NODE ) +	{ +		PurpleBuddy *bud = (PurpleBuddy*) node; +		PurpleGroup *group = purple_buddy_get_group( bud ); +		struct im_connection *ic = purple_ic_by_pa( bud->account ); +		PurpleStatus *as; +		int flags = 0; +		 +		if( ic == NULL ) +			return; +		 +		if( bud->server_alias ) +			imcb_rename_buddy( ic, bud->name, bud->server_alias ); +		 +		if( group ) +			imcb_add_buddy( ic, bud->name, purple_group_get_name( group ) ); +		 +		flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; +		flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; +		 +		as = purple_presence_get_active_status( bud->presence ); +		 +		imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ), +		                   purple_status_get_attr_string( as, "message" ) ); +		 +		imcb_buddy_times( ic, bud->name, +		                  purple_presence_get_login_time( bud->presence ), +		                  purple_presence_get_idle_time( bud->presence ) ); +	} +} + +static void prplcb_blist_new( PurpleBlistNode *node ) +{ +	if( node->type == PURPLE_BLIST_BUDDY_NODE ) +	{ +		PurpleBuddy *bud = (PurpleBuddy*) node; +		struct im_connection *ic = purple_ic_by_pa( bud->account ); +		 +		if( ic == NULL ) +			return; +		 +		imcb_add_buddy( ic, bud->name, NULL ); +		 +		prplcb_blist_update( NULL, node ); +	} +} + +static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) +{ +/* +	PurpleBuddy *bud = (PurpleBuddy*) node; +	 +	if( node->type == PURPLE_BLIST_BUDDY_NODE ) +	{ +		struct im_connection *ic = purple_ic_by_pa( bud->account ); +		 +		if( ic == NULL ) +			return; +		 +		imcb_remove_buddy( ic, bud->name, NULL ); +	} +*/ +} + +static PurpleBlistUiOps bee_blist_uiops = +{ +	NULL, +	prplcb_blist_new, +	NULL, +	prplcb_blist_update, +	prplcb_blist_remove, +}; + +void prplcb_conv_new( PurpleConversation *conv ) +{ +	if( conv->type == PURPLE_CONV_TYPE_CHAT ) +	{ +		struct im_connection *ic = purple_ic_by_pa( conv->account ); +		struct groupchat *gc; +		 +		gc = imcb_chat_new( ic, conv->name ); +		conv->ui_data = gc; +		gc->data = conv; +		 +		/* libpurple brokenness: Whatever. Show that we join right away, +		   there's no clear "This is you!" signaling in _add_users so +		   don't even try. */ +		imcb_chat_add_buddy( gc, gc->ic->acc->user ); +	} +} + +void prplcb_conv_free( PurpleConversation *conv ) +{ +	struct groupchat *gc = conv->ui_data; +	 +	imcb_chat_free( gc ); +} + +void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals ) +{ +	struct groupchat *gc = conv->ui_data; +	GList *b; +	 +	for( b = cbuddies; b; b = b->next ) +	{ +		PurpleConvChatBuddy *pcb = b->data; +		 +		imcb_chat_add_buddy( gc, pcb->name ); +	} +} + +void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies ) +{ +	struct groupchat *gc = conv->ui_data; +	GList *b; +	 +	for( b = cbuddies; b; b = b->next ) +		imcb_chat_remove_buddy( gc, b->data, "" ); +} + +void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) +{ +	struct groupchat *gc = conv->ui_data; +	PurpleBuddy *buddy; +	 +	/* ..._SEND means it's an outgoing message, no need to echo those. */ +	if( flags & PURPLE_MESSAGE_SEND ) +		return; +	 +	buddy = purple_find_buddy( conv->account, who ); +	if( buddy != NULL ) +		who = purple_buddy_get_name( buddy ); +	 +	imcb_chat_msg( gc, who, (char*) message, 0, mtime ); +} + +static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) +{ +	struct im_connection *ic = purple_ic_by_pa( conv->account ); +	PurpleBuddy *buddy; +	 +	/* ..._SEND means it's an outgoing message, no need to echo those. */ +	if( flags & PURPLE_MESSAGE_SEND ) +		return; +	 +	buddy = purple_find_buddy( conv->account, who ); +	if( buddy != NULL ) +		who = purple_buddy_get_name( buddy ); +	 +	imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); +} + +static PurpleConversationUiOps bee_conv_uiops =  +{ +	prplcb_conv_new,           /* create_conversation  */ +	prplcb_conv_free,          /* destroy_conversation */ +	prplcb_conv_chat_msg,      /* write_chat           */ +	prplcb_conv_im,            /* write_im             */ +	NULL,                      /* write_conv           */ +	prplcb_conv_add_users,     /* chat_add_users       */ +	NULL,                      /* chat_rename_user     */ +	prplcb_conv_del_users,     /* chat_remove_users    */ +	NULL,                      /* chat_update_user     */ +	NULL,                      /* present              */ +	NULL,                      /* has_focus            */ +	NULL,                      /* custom_smiley_add    */ +	NULL,                      /* custom_smiley_write  */ +	NULL,                      /* custom_smiley_close  */ +	NULL,                      /* send_confirm         */ +}; + +struct prplcb_request_action_data +{ +	void *user_data, *bee_data; +	PurpleRequestActionCb yes, no; +	int yes_i, no_i; +}; + +static void prplcb_request_action_yes( void *data ) +{ +	struct prplcb_request_action_data *pqad = data; +	 +	if( pqad->yes ) +		pqad->yes( pqad->user_data, pqad->yes_i ); +	g_free( pqad ); +} + +static void prplcb_request_action_no( void *data ) +{ +	struct prplcb_request_action_data *pqad = data; +	 +	if( pqad->no ) +		pqad->no( pqad->user_data, pqad->no_i ); +	g_free( pqad ); +} + +static void *prplcb_request_action( const char *title, const char *primary, const char *secondary, +                                    int default_action, PurpleAccount *account, const char *who, +                                    PurpleConversation *conv, void *user_data, size_t action_count, +                                    va_list actions ) +{ +	struct prplcb_request_action_data *pqad;  +	int i; +	char *q; +	 +	pqad = g_new0( struct prplcb_request_action_data, 1 ); +	 +	for( i = 0; i < action_count; i ++ ) +	{ +		char *caption; +		void *fn; +		 +		caption = va_arg( actions, char* ); +		fn = va_arg( actions, void* ); +		 +		if( strstr( caption, "Accept" ) || strstr( caption, "OK" ) ) +		{ +			pqad->yes = fn; +			pqad->yes_i = i; +		} +		else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) ) +		{ +			pqad->no = fn; +			pqad->no_i = i; +		} +	} +	 +	pqad->user_data = user_data; +	 +	/* TODO: IRC stuff here :-( */ +	q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary ); +	pqad->bee_data = query_add( local_bee->ui_data, purple_ic_by_pa( account ), q, +		prplcb_request_action_yes, prplcb_request_action_no, g_free, pqad ); +	 +	g_free( q ); +	 +	return pqad; +} + +/* +static void prplcb_request_test() +{ +	fprintf( stderr, "bla\n" ); +} +*/ + +static PurpleRequestUiOps bee_request_uiops = +{ +	NULL, +	NULL, +	prplcb_request_action, +	NULL, +	NULL, +	NULL, +	NULL, +}; + +static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name ) +{ +	struct im_connection *ic = purple_ic_by_pa( account ); +	 +	if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) +		ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) ); +} + +static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name ) +{ +	struct im_connection *ic = purple_ic_by_pa( account ); +	void *n; +	 +	n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ); +	ic->permit = g_slist_remove( ic->permit, n ); +} + +static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name ) +{ +	struct im_connection *ic = purple_ic_by_pa( account ); +	 +	if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) +		ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) ); +} + +static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name ) +{ +	struct im_connection *ic = purple_ic_by_pa( account ); +	void *n; +	 +	n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ); +	ic->deny = g_slist_remove( ic->deny, n ); +} + +static PurplePrivacyUiOps bee_privacy_uiops = +{ +	prplcb_privacy_permit_added, +	prplcb_privacy_permit_removed, +	prplcb_privacy_deny_added, +	prplcb_privacy_deny_removed, +}; + +static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) +{ +	fprintf( stderr, "DEBUG %s: %s", category, arg_s ); +} + +static PurpleDebugUiOps bee_debug_uiops = +{ +	prplcb_debug_print, +}; + +static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) +{ +	return b_timeout_add( interval, (b_event_handler) func, udata ); +} + +static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) +{ +	return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); +} + +static gboolean prplcb_ev_remove( guint id ) +{ +	b_event_remove( (gint) id ); +	return TRUE; +} + +static PurpleEventLoopUiOps glib_eventloops =  +{ +	prplcb_ev_timeout_add, +	prplcb_ev_remove, +	prplcb_ev_input_add, +	prplcb_ev_remove, +}; + +static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from, +                                  const char *to, const char *url ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	 +	imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url ); +	 +	return NULL; +} + +static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info ) +{ +	struct im_connection *ic = purple_ic_by_gc( gc ); +	GString *info = g_string_new( "" ); +	GList *l = purple_notify_user_info_get_entries( user_info ); +	char *key; +	const char *value; +	int n; +	 +	while( l ) +	{ +		PurpleNotifyUserInfoEntry *e = l->data; +		 +		switch( purple_notify_user_info_entry_get_type( e ) ) +		{ +		case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR: +		case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER: +			key = g_strdup( purple_notify_user_info_entry_get_label( e ) ); +			value = purple_notify_user_info_entry_get_value( e ); +			 +			if( key ) +			{ +				strip_html( key ); +				g_string_append_printf( info, "%s: ", key ); +				 +				if( value ) +				{ +					n = strlen( value ) - 1; +					while( isspace( value[n] ) ) +						n --; +					g_string_append_len( info, value, n + 1 ); +				} +				g_string_append_c( info, '\n' ); +				g_free( key ); +			} +			 +			break; +		case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK: +			g_string_append( info, "------------------------\n" ); +			break; +		} +		 +		l = l->next; +	} +	 +	imcb_log( ic, "User %s info:\n%s", who, info->str ); +	g_string_free( info, TRUE ); +	 +	return NULL; +} + +static PurpleNotifyUiOps bee_notify_uiops = +{ +        NULL, +        prplcb_notify_email, +        NULL, +        NULL, +        NULL, +        NULL, +        prplcb_notify_userinfo, +}; + +static void *prplcb_account_request_authorize( PurpleAccount *account, const char *remote_user, +	const char *id, const char *alias, const char *message, gboolean on_list, +	PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data ) +{ +	struct im_connection *ic = purple_ic_by_pa( account ); +	char *q; +	 +	if( alias ) +		q = g_strdup_printf( "%s (%s) wants to add you to his/her contact " +		                     "list. (%s)", alias, remote_user, message ); +	else +		q = g_strdup_printf( "%s wants to add you to his/her contact " +		                     "list. (%s)", remote_user, message ); +	 +	imcb_ask_with_free( ic, q, user_data, authorize_cb, deny_cb, NULL ); +	g_free( q ); +	 +	return NULL; +} + +static PurpleAccountUiOps bee_account_uiops = +{ +	NULL, +	NULL, +	NULL, +	prplcb_account_request_authorize, +	NULL, +}; + +extern PurpleXferUiOps bee_xfer_uiops; + +static void purple_ui_init() +{ +	purple_connections_set_ui_ops( &bee_conn_uiops ); +	purple_blist_set_ui_ops( &bee_blist_uiops ); +	purple_conversations_set_ui_ops( &bee_conv_uiops ); +	purple_request_set_ui_ops( &bee_request_uiops ); +	purple_privacy_set_ui_ops( &bee_privacy_uiops ); +	purple_notify_set_ui_ops( &bee_notify_uiops ); +	purple_accounts_set_ui_ops( &bee_account_uiops ); +	purple_xfers_set_ui_ops( &bee_xfer_uiops ); +	 +	if( getenv( "BITLBEE_DEBUG" ) ) +		purple_debug_set_ui_ops( &bee_debug_uiops ); +} + +void purple_initmodule() +{ +	struct prpl funcs; +	GList *prots; +	GString *help; +	 +	if( B_EV_IO_READ != PURPLE_INPUT_READ || +	    B_EV_IO_WRITE != PURPLE_INPUT_WRITE ) +	{ +		/* FIXME FIXME FIXME FIXME FIXME :-) */ +		exit( 1 ); +	} +	 +	purple_util_set_user_dir( "/tmp" ); +	purple_debug_set_enabled( FALSE ); +	purple_core_set_ui_ops( &bee_core_uiops ); +	purple_eventloop_set_ui_ops( &glib_eventloops ); +	if( !purple_core_init( "BitlBee") ) +	{ +		/* Initializing the core failed. Terminate. */ +		fprintf( stderr, "libpurple initialization failed.\n" ); +		abort(); +	} +	 +	/* This seems like stateful shit we don't want... */ +	purple_set_blist( purple_blist_new() ); +	purple_blist_load(); +	 +	/* Meh? */ +	purple_prefs_load(); +	 +	memset( &funcs, 0, sizeof( funcs ) ); +	funcs.login = purple_login; +	funcs.init = purple_init; +	funcs.logout = purple_logout; +	funcs.buddy_msg = purple_buddy_msg; +	funcs.away_states = purple_away_states; +	funcs.set_away = purple_set_away; +	funcs.add_buddy = purple_add_buddy; +	funcs.remove_buddy = purple_remove_buddy; +	funcs.add_permit = purple_add_permit; +	funcs.add_deny = purple_add_deny; +	funcs.rem_permit = purple_rem_permit; +	funcs.rem_deny = purple_rem_deny; +	funcs.get_info = purple_get_info; +	funcs.keepalive = purple_keepalive; +	funcs.send_typing = purple_send_typing; +	funcs.handle_cmp = g_strcasecmp; +	/* TODO(wilmer): Set these only for protocols that support them? */ +	funcs.chat_msg = purple_chat_msg; +	funcs.chat_with = purple_chat_with; +	funcs.chat_invite = purple_chat_invite; +	funcs.chat_leave = purple_chat_leave; +	funcs.chat_join = purple_chat_join; +	funcs.transfer_request = purple_transfer_request; +	 +	help = g_string_new( "BitlBee libpurple module supports the following IM protocols:\n" ); +	 +	/* Add a protocol entry to BitlBee's structures for every protocol +	   supported by this libpurple instance. */	 +	for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) +	{ +		PurplePlugin *prot = prots->data; +		struct prpl *ret; +		 +		ret = g_memdup( &funcs, sizeof( funcs ) ); +		ret->name = ret->data = prot->info->id; +		if( strncmp( ret->name, "prpl-", 5 ) == 0 ) +			ret->name += 5; +		register_protocol( ret ); +		 +		g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name ); +		 +		/* libpurple doesn't define a protocol called OSCAR, but we +		   need it to be compatible with normal BitlBee. */ +		if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) +		{ +			ret = g_memdup( &funcs, sizeof( funcs ) ); +			ret->name = "oscar"; +			ret->data = prot->info->id; +			register_protocol( ret ); +		} +	} +	 +	g_string_append( help, "\n\nFor used protocols, more information about available " +	                 "settings can be found using \x02help purple <protocol name>\x02" ); +	 +	/* Add a simple dynamically-generated help item listing all +	   the supported protocols. */ +	help_add_mem( &global.help, "purple", help->str ); +	g_string_free( help, TRUE ); +} diff --git a/protocols/twitter/Makefile b/protocols/twitter/Makefile index ca1e4695..8a4b97f9 100644 --- a/protocols/twitter/Makefile +++ b/protocols/twitter/Makefile @@ -7,6 +7,9 @@  ### DEFINITIONS  -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/twitter/ +endif  # [SH] Program variables  objects = twitter.o twitter_http.o twitter_lib.o @@ -32,7 +35,7 @@ distclean: clean  $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 2e3ab634..f718eeb7 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -118,7 +118,7 @@ static gboolean twitter_oauth_callback( struct oauth_info *info )  			return FALSE;  		} -		sprintf( name, "twitter_%s", ic->acc->user ); +		sprintf( name, "%s_%s", td->prefix, ic->acc->user );  		msg = g_strdup_printf( "To finish OAuth authentication, please visit "  		                       "%s and respond with the resulting PIN code.",  		                       info->auth_url ); @@ -171,8 +171,21 @@ static gboolean twitter_length_check( struct im_connection *ic, gchar *msg )  static void twitter_init( account_t *acc )  {  	set_t *s; +	char *def_url; +	char *def_oauth; -	s = set_add( &acc->set, "base_url", TWITTER_API_URL, NULL, acc ); +	if( strcmp( acc->prpl->name, "twitter" ) == 0 ) +	{ +		def_url = TWITTER_API_URL; +		def_oauth = "true"; +	} +	else /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ +	{ +		def_url = IDENTICA_API_URL; +		def_oauth = "false"; +	} +	 +	s = set_add( &acc->set, "base_url", def_url, NULL, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY;  	s = set_add( &acc->set, "message_length", "140", set_eval_int, acc ); @@ -180,7 +193,7 @@ static void twitter_init( account_t *acc )  	s = set_add( &acc->set, "mode", "one", set_eval_mode, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY; -	s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc ); +	s = set_add( &acc->set, "oauth", def_oauth, set_eval_bool, acc );  }  /** @@ -213,12 +226,16 @@ static void twitter_login( account_t *acc )  		td->url_path = g_strdup( url.file );  	else  		td->url_path = g_strdup( "" ); +	if( g_str_has_suffix( url.host, ".com" ) ) +		td->prefix = g_strndup( url.host, strlen( url.host ) - 4 ); +	else +		td->prefix = g_strdup( url.host );  	td->user = acc->user;  	if( strstr( acc->pass, "oauth_token=" ) )  		td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth ); -	sprintf( name, "twitter_%s", acc->user ); +	sprintf( name, "%s_%s", td->prefix, acc->user );  	imcb_add_buddy( ic, name, NULL );  	imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); @@ -246,6 +263,7 @@ static void twitter_logout( struct im_connection *ic )  	if( td )  	{  		oauth_info_free( td->oauth_info ); +		g_free( td->prefix );  		g_free( td->url_host );  		g_free( td->url_path );  		g_free( td->pass ); @@ -261,9 +279,10 @@ static void twitter_logout( struct im_connection *ic )  static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )  {  	struct twitter_data *td = ic->proto_data; +	int plen = strlen( td->prefix ); -	if (g_strncasecmp(who, "twitter_", 8) == 0 && -	    g_strcasecmp(who + 8, ic->acc->user) == 0) +	if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' && +	    g_strcasecmp(who + plen + 1, ic->acc->user) == 0)  	{  		if( set_getbool( &ic->acc->set, "oauth" ) &&  		    td->oauth_info && td->oauth_info->token == NULL ) @@ -315,8 +334,28 @@ static void twitter_remove_buddy( struct im_connection *ic, char *who, char *gro  static void twitter_chat_msg( struct groupchat *c, char *message, int flags )  { -	if( c && message && twitter_length_check(c->ic, message)) -		twitter_post_status(c->ic, message); +	if( c && message && twitter_length_check( c->ic, message ) ) +	{ +		char *s, *new = NULL; +		 +		if( ( s = strchr( message, ':' ) ) || +		    ( s = strchr( message, ',' ) ) ) +		{ +			bee_user_t *bu; +			 +			new = g_strdup( message ); +			new[s-message] = '\0'; +			if( ( bu = bee_user_by_handle( c->ic->bee, c->ic, new ) ) ) +			{ +				sprintf( new, "@%s", bu->handle ); +				new[s-message+1] = ' '; +				message = new; +			} +		} +		 +		twitter_post_status( c->ic, message ); +		g_free( new ); +	}  }  static void twitter_chat_invite( struct groupchat *c, char *who, char *message ) @@ -395,10 +434,14 @@ void twitter_initmodule()  	ret->rem_deny = twitter_rem_deny;  	ret->send_typing = twitter_send_typing;  	ret->handle_cmp = g_strcasecmp; +	 +	register_protocol(ret); +	/* And an identi.ca variant: */ +	ret = g_memdup(ret, sizeof(struct prpl)); +	ret->name = "identica";  	register_protocol(ret);  	// Initialise the twitter_connections GSList.  	twitter_connections = NULL;  } - diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index e61d32be..b7e41fc5 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -52,6 +52,8 @@ struct twitter_data  	int url_port;  	char *url_host;  	char *url_path; + +	char *prefix; /* Used to generate contact + channel name. */  };  /** diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 0578c5e0..620850ab 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -116,7 +116,7 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char *  	struct twitter_data *td = ic->proto_data;  	// Check if the buddy is allready in the buddy list. -	if (!imcb_find_buddy( ic, name )) +	if (!bee_user_by_handle( ic->bee, ic, name ))  	{  		char *mode = set_getstr(&ic->acc->set, "mode"); @@ -124,7 +124,12 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char *  		imcb_add_buddy( ic, name, NULL );  		imcb_rename_buddy( ic, name, fullname );  		if (g_strcasecmp(mode, "chat") == 0) +		{ +			/* Necessary so that nicks always get translated to the +			   exact Twitter username. */ +			imcb_buddy_nick_hint( ic, name, name );  			imcb_chat_add_buddy( td->home_timeline_gc, name ); +		}  		else if (g_strcasecmp(mode, "many") == 0)  			imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );  	} @@ -458,7 +463,7 @@ static void twitter_groupchat_init(struct im_connection *ic)  	td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); -	name_hint = g_strdup_printf( "Twitter_%s", ic->acc->user ); +	name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user );  	imcb_chat_name_hint( gc, name_hint );  	g_free( name_hint );  } @@ -518,7 +523,7 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list)  	if( mode_one )  	{ -		g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user ); +		g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user );  		from[MAX_STRING-1] = '\0';  	} diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 6b90f9bb..5a3c3f68 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -29,6 +29,7 @@  #include "twitter_http.h"  #define TWITTER_API_URL "http://twitter.com" +#define IDENTICA_API_URL "http://identi.ca/api"  /* Status URLs */  #define TWITTER_STATUS_UPDATE_URL "/statuses/update.xml" diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index b4fe56e2..e5374538 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -7,11 +7,14 @@  ### DEFINITIONS  -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/yahoo/ +endif  # [SH] Program variables  objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_util.o -CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB +CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB -Wno-pointer-to-int-cast  LFLAGS += -r  # [SH] Phony targets @@ -32,7 +35,7 @@ distclean: clean  $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index 1bfc2e59..bd111654 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -2,6 +2,8 @@   * libyahoo2: libyahoo2.c   *   * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * YMSG16 code copyright (C) 2009,  + * 		Siddhesh Poyarekar <siddhesh dot poyarekar at gmail dot com>   *   * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>   * @@ -26,6 +28,8 @@   *     Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto   *     <hiro-y@kcn.ne.jp>   * + * YMSG16 authentication code based mostly on write-up at: + * 	http://www.carbonize.co.uk/ymsg16.html   *   * 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 @@ -91,9 +95,9 @@ char *strchr (), *strrchr ();  #include "http_client.h"  #ifdef USE_STRUCT_CALLBACKS -struct yahoo_callbacks *yc=NULL; +struct yahoo_callbacks *yc = NULL; -void yahoo_register_callbacks(struct yahoo_callbacks * tyc) +void yahoo_register_callbacks(struct yahoo_callbacks *tyc)  {  	yc = tyc;  } @@ -103,21 +107,23 @@ void yahoo_register_callbacks(struct yahoo_callbacks * tyc)  #define YAHOO_CALLBACK(x)	x  #endif -static int yahoo_send_data(int fd, void *data, int len); +static int yahoo_send_data(void *fd, void *data, int len); +static void _yahoo_http_connected(int id, void *fd, int error, void *data); +static void yahoo_connected(void *fd, int error, void *data); -int yahoo_log_message(char * fmt, ...) +int yahoo_log_message(char *fmt, ...)  {  	char out[1024];  	va_list ap;  	va_start(ap, fmt);  	vsnprintf(out, sizeof(out), fmt, ap);  	va_end(ap); -	return YAHOO_CALLBACK(ext_yahoo_log)("%s", out); +	return YAHOO_CALLBACK(ext_yahoo_log) ("%s", out);  } -int yahoo_connect(char * host, int port) +int yahoo_connect(char *host, int port)  { -	return YAHOO_CALLBACK(ext_yahoo_connect)(host, port); +	return YAHOO_CALLBACK(ext_yahoo_connect) (host, port);  }  static enum yahoo_log_level log_level = YAHOO_LOG_NONE; @@ -135,97 +141,30 @@ int yahoo_set_log_level(enum yahoo_log_level level)  }  /* default values for servers */ -static char pager_host[] = "scs.msg.yahoo.com";  +static char *default_pager_hosts[] = {	"scs.msg.yahoo.com", +					"scsa.msg.yahoo.com", +					"scsb.msg.yahoo.com", +					"scsc.msg.yahoo.com", +					NULL}; +  static int pager_port = 5050; -static int fallback_ports[]={23, 25, 80, 20, 119, 8001, 8002, 5050, 0}; -static char filetransfer_host[]="filetransfer.msg.yahoo.com"; -static int filetransfer_port=80; -static char webcam_host[]="webcam.yahoo.com"; -static int webcam_port=5100; -static char webcam_description[]=""; -static char local_host[]=""; -static int conn_type=Y_WCM_DSL; +static int fallback_ports[] = { 23, 25, 80, 20, 119, 8001, 8002, 5050, 0 }; + +static char filetransfer_host[] = "filetransfer.msg.yahoo.com"; +static int filetransfer_port = 80; +static char webcam_host[] = "webcam.yahoo.com"; +static int webcam_port = 5100; +static char webcam_description[] = ""; +static char local_host[] = ""; +static int conn_type = Y_WCM_DSL;  static char profile_url[] = "http://profiles.yahoo.com/"; -enum yahoo_service { /* these are easier to see in hex */ -	YAHOO_SERVICE_LOGON = 1, -	YAHOO_SERVICE_LOGOFF, -	YAHOO_SERVICE_ISAWAY, -	YAHOO_SERVICE_ISBACK, -	YAHOO_SERVICE_IDLE, /* 5 (placemarker) */ -	YAHOO_SERVICE_MESSAGE, -	YAHOO_SERVICE_IDACT, -	YAHOO_SERVICE_IDDEACT, -	YAHOO_SERVICE_MAILSTAT, -	YAHOO_SERVICE_USERSTAT, /* 0xa */ -	YAHOO_SERVICE_NEWMAIL, -	YAHOO_SERVICE_CHATINVITE, -	YAHOO_SERVICE_CALENDAR, -	YAHOO_SERVICE_NEWPERSONALMAIL, -	YAHOO_SERVICE_NEWCONTACT, -	YAHOO_SERVICE_ADDIDENT, /* 0x10 */ -	YAHOO_SERVICE_ADDIGNORE, -	YAHOO_SERVICE_PING, -	YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */ -	YAHOO_SERVICE_SYSMESSAGE = 0x14, -	YAHOO_SERVICE_SKINNAME = 0x15, -	YAHOO_SERVICE_PASSTHROUGH2 = 0x16, -	YAHOO_SERVICE_CONFINVITE = 0x18, -	YAHOO_SERVICE_CONFLOGON, -	YAHOO_SERVICE_CONFDECLINE, -	YAHOO_SERVICE_CONFLOGOFF, -	YAHOO_SERVICE_CONFADDINVITE, -	YAHOO_SERVICE_CONFMSG, -	YAHOO_SERVICE_CHATLOGON, -	YAHOO_SERVICE_CHATLOGOFF, -	YAHOO_SERVICE_CHATMSG = 0x20, -	YAHOO_SERVICE_GAMELOGON = 0x28, -	YAHOO_SERVICE_GAMELOGOFF, -	YAHOO_SERVICE_GAMEMSG = 0x2a, -	YAHOO_SERVICE_FILETRANSFER = 0x46, -	YAHOO_SERVICE_VOICECHAT = 0x4A, -	YAHOO_SERVICE_NOTIFY, -	YAHOO_SERVICE_VERIFY, -	YAHOO_SERVICE_P2PFILEXFER, -	YAHOO_SERVICE_PEERTOPEER = 0x4F,	/* Checks if P2P possible */ -	YAHOO_SERVICE_WEBCAM, -	YAHOO_SERVICE_AUTHRESP = 0x54, -	YAHOO_SERVICE_LIST, -	YAHOO_SERVICE_AUTH = 0x57, -	YAHOO_SERVICE_AUTHBUDDY = 0x6d, -	YAHOO_SERVICE_ADDBUDDY = 0x83, -	YAHOO_SERVICE_REMBUDDY, -	YAHOO_SERVICE_IGNORECONTACT,	/* > 1, 7, 13 < 1, 66, 13, 0*/ -	YAHOO_SERVICE_REJECTCONTACT, -	YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */ -	YAHOO_SERVICE_Y7_PING = 0x8A, /* 0 - id and that's it?? */ -	YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/ -	YAHOO_SERVICE_CHATGOTO, -	YAHOO_SERVICE_CHATJOIN,	/* > 1 104-room 129-1600326591 62-2 */ -	YAHOO_SERVICE_CHATLEAVE, -	YAHOO_SERVICE_CHATEXIT = 0x9b, -	YAHOO_SERVICE_CHATADDINVITE = 0x9d, -	YAHOO_SERVICE_CHATLOGOUT = 0xa0, -	YAHOO_SERVICE_CHATPING, -	YAHOO_SERVICE_COMMENT = 0xa8, -	YAHOO_SERVICE_STEALTH = 0xb9, -	YAHOO_SERVICE_PICTURE_CHECKSUM = 0xbd, -	YAHOO_SERVICE_PICTURE = 0xbe, -	YAHOO_SERVICE_PICTURE_UPDATE = 0xc1, -	YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2, -	YAHOO_SERVICE_Y6_VISIBILITY=0xc5, -	YAHOO_SERVICE_Y6_STATUS_UPDATE=0xc6, -	YAHOO_PHOTOSHARE_INIT=0xd2,	 -	YAHOO_SERVICE_CONTACT_YMSG13=0xd6, -	YAHOO_PHOTOSHARE_PREV=0xd7, -	YAHOO_PHOTOSHARE_KEY=0xd8, -	YAHOO_PHOTOSHARE_TRANS=0xda, -	YAHOO_FILE_TRANSFER_INIT_YMSG13=0xdc, -	YAHOO_FILE_TRANSFER_GET_YMSG13=0xdd, -	YAHOO_FILE_TRANSFER_PUT_YMSG13=0xde, -	YAHOO_SERVICE_YMSG15_STATUS=0xf0, -	YAHOO_SERVICE_YMSG15_BUDDY_LIST=0xf1, +struct connect_callback_data { +	struct yahoo_data *yd; +	int tag; +	int i; +	int server_i;  };  struct yahoo_pair { @@ -241,15 +180,15 @@ struct yahoo_packet {  };  struct yahoo_search_state { -	int   lsearch_type; -	char  *lsearch_text; -	int   lsearch_gender; -	int   lsearch_agerange; -	int   lsearch_photo; -	int   lsearch_yahoo_only; -	int   lsearch_nstart; -	int   lsearch_nfound; -	int   lsearch_ntotal; +	int lsearch_type; +	char *lsearch_text; +	int lsearch_gender; +	int lsearch_agerange; +	int lsearch_photo; +	int lsearch_yahoo_only; +	int lsearch_nstart; +	int lsearch_nfound; +	int lsearch_ntotal;  };  struct data_queue { @@ -263,34 +202,52 @@ struct yahoo_input_data {  	struct yahoo_webcam_data *wcd;  	struct yahoo_search_state *ys; -	int   fd; +	void *fd;  	enum yahoo_connection_type type; -	 -	unsigned char	*rxqueue; -	int   rxlen; -	int   read_tag; + +	unsigned char *rxqueue; +	int rxlen; +	int read_tag;  	YList *txqueues; -	int   write_tag; +	int write_tag;  };  struct yahoo_server_settings {  	char *pager_host; -	int   pager_port; +	int pager_port;  	char *filetransfer_host; -	int   filetransfer_port; +	int filetransfer_port;  	char *webcam_host; -	int   webcam_port; +	int webcam_port;  	char *webcam_description;  	char *local_host; -	int   conn_type; +	int conn_type; +	char **pager_host_list;  }; -static void * _yahoo_default_server_settings() +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over); + +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt); +static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt); +static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt); + +static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn); + +static void *_yahoo_default_server_settings()  { -	struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1); +	struct yahoo_server_settings *yss = +		y_new0(struct yahoo_server_settings, 1); + +	/* Give preference to the default host list +	 * Make sure that only one of the two is set at any time  +	 */ +	yss->pager_host = NULL; +	yss->pager_host_list = default_pager_hosts; -	yss->pager_host = strdup(pager_host);  	yss->pager_port = pager_port;  	yss->filetransfer_host = strdup(filetransfer_host);  	yss->filetransfer_port = filetransfer_port; @@ -303,54 +260,61 @@ static void * _yahoo_default_server_settings()  	return yss;  } -static void * _yahoo_assign_server_settings(va_list ap) +static void *_yahoo_assign_server_settings(va_list ap)  {  	struct yahoo_server_settings *yss = _yahoo_default_server_settings();  	char *key;  	char *svalue; -	int   nvalue; +	int nvalue; +	char **pvalue; -	while(1) { +	while (1) {  		key = va_arg(ap, char *); -		if(key == NULL) +		if (key == NULL)  			break; -		if(!strcmp(key, "pager_host")) { +		if (!strcmp(key, "pager_host")) {  			svalue = va_arg(ap, char *);  			free(yss->pager_host);  			yss->pager_host = strdup(svalue); -		} else if(!strcmp(key, "pager_port")) { +			yss->pager_host_list = NULL; +		} else if (!strcmp(key, "pager_host_list")) { +			pvalue = va_arg(ap, char **); +			yss->pager_host_list = pvalue; +			free(yss->pager_host); +			yss->pager_host = NULL; +		} else if (!strcmp(key, "pager_port")) {  			nvalue = va_arg(ap, int);  			yss->pager_port = nvalue; -		} else if(!strcmp(key, "filetransfer_host")) { +		} else if (!strcmp(key, "filetransfer_host")) {  			svalue = va_arg(ap, char *);  			free(yss->filetransfer_host);  			yss->filetransfer_host = strdup(svalue); -		} else if(!strcmp(key, "filetransfer_port")) { +		} else if (!strcmp(key, "filetransfer_port")) {  			nvalue = va_arg(ap, int);  			yss->filetransfer_port = nvalue; -		} else if(!strcmp(key, "webcam_host")) { +		} else if (!strcmp(key, "webcam_host")) {  			svalue = va_arg(ap, char *);  			free(yss->webcam_host);  			yss->webcam_host = strdup(svalue); -		} else if(!strcmp(key, "webcam_port")) { +		} else if (!strcmp(key, "webcam_port")) {  			nvalue = va_arg(ap, int);  			yss->webcam_port = nvalue; -		} else if(!strcmp(key, "webcam_description")) { +		} else if (!strcmp(key, "webcam_description")) {  			svalue = va_arg(ap, char *);  			free(yss->webcam_description);  			yss->webcam_description = strdup(svalue); -		} else if(!strcmp(key, "local_host")) { +		} else if (!strcmp(key, "local_host")) {  			svalue = va_arg(ap, char *);  			free(yss->local_host);  			yss->local_host = strdup(svalue); -		} else if(!strcmp(key, "conn_type")) { +		} else if (!strcmp(key, "conn_type")) {  			nvalue = va_arg(ap, int);  			yss->conn_type = nvalue;  		} else {  			WARNING(("Unknown key passed to yahoo_init, " -				"perhaps you didn't terminate the list " -				"with NULL")); +					"perhaps you didn't terminate the list " +					"with NULL"));  		}  	} @@ -359,7 +323,7 @@ static void * _yahoo_assign_server_settings(va_list ap)  static void yahoo_free_server_settings(struct yahoo_server_settings *yss)  { -	if(!yss) +	if (!yss)  		return;  	free(yss->pager_host); @@ -371,30 +335,33 @@ static void yahoo_free_server_settings(struct yahoo_server_settings *yss)  	free(yss);  } -static YList *conns=NULL; -static YList *inputs=NULL; -static int last_id=0; +static YList *conns = NULL; +static YList *inputs = NULL; +static int last_id = 0;  static void add_to_list(struct yahoo_data *yd)  {  	conns = y_list_prepend(conns, yd);  } -static struct yahoo_data * find_conn_by_id(int id) + +static struct yahoo_data *find_conn_by_id(int id)  {  	YList *l; -	for(l = conns; l; l = y_list_next(l)) { +	for (l = conns; l; l = y_list_next(l)) {  		struct yahoo_data *yd = l->data; -		if(yd->client_id == id) +		if (yd->client_id == id)  			return yd;  	}  	return NULL;  } +  static void del_from_list(struct yahoo_data *yd)  {  	conns = y_list_remove(conns, yd);  }  /* call repeatedly to get the next one */ +/*  static struct yahoo_input_data * find_input_by_id(int id)  {  	YList *l; @@ -405,41 +372,45 @@ static struct yahoo_input_data * find_input_by_id(int id)  	}  	return NULL;  } +*/ -static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who) +static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id, +	const char *who)  {  	YList *l;  	LOG(("find_input_by_id_and_webcam_user")); -	for(l = inputs; l; l = y_list_next(l)) { +	for (l = inputs; l; l = y_list_next(l)) {  		struct yahoo_input_data *yid = l->data; -		if(yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id  -				&& yid->wcm &&  -				((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) || -				 !(yid->wcm->user && !who))) +		if (yid->type == YAHOO_CONNECTION_WEBCAM +			&& yid->yd->client_id == id && yid->wcm && ((who +					&& yid->wcm->user +					&& !strcmp(who, yid->wcm->user)) +				|| !(yid->wcm->user && !who)))  			return yid;  	}  	return NULL;  } -static struct yahoo_input_data * find_input_by_id_and_type(int id, enum yahoo_connection_type type) +static struct yahoo_input_data *find_input_by_id_and_type(int id, +	enum yahoo_connection_type type)  {  	YList *l;  	LOG(("find_input_by_id_and_type")); -	for(l = inputs; l; l = y_list_next(l)) { +	for (l = inputs; l; l = y_list_next(l)) {  		struct yahoo_input_data *yid = l->data; -		if(yid->type == type && yid->yd->client_id == id) +		if (yid->type == type && yid->yd->client_id == id)  			return yid;  	}  	return NULL;  } -static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd) +static struct yahoo_input_data *find_input_by_id_and_fd(int id, void *fd)  {  	YList *l;  	LOG(("find_input_by_id_and_fd")); -	for(l = inputs; l; l = y_list_next(l)) { +	for (l = inputs; l; l = y_list_next(l)) {  		struct yahoo_input_data *yid = l->data; -		if(yid->fd == fd && yid->yd->client_id == id) +		if (yid->fd == fd && yid->yd->client_id == id)  			return yid;  	}  	return NULL; @@ -447,36 +418,34 @@ static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd)  static int count_inputs_with_id(int id)  { -	int c=0; +	int c = 0;  	YList *l;  	LOG(("counting %d", id)); -	for(l = inputs; l; l = y_list_next(l)) { +	for (l = inputs; l; l = y_list_next(l)) {  		struct yahoo_input_data *yid = l->data; -		if(yid->yd->client_id == id) +		if (yid->yd->client_id == id)  			c++;  	}  	LOG(("%d", c));  	return c;  } -  extern char *yahoo_crypt(char *, char *);  /* Free a buddy list */ -static void yahoo_free_buddies(YList * list) +static void yahoo_free_buddies(YList *list)  {  	YList *l; -	for(l = list; l; l = l->next) -	{ +	for (l = list; l; l = l->next) {  		struct yahoo_buddy *bud = l->data; -		if(!bud) +		if (!bud)  			continue;  		FREE(bud->group);  		FREE(bud->id);  		FREE(bud->real_name); -		if(bud->yab_entry) { +		if (bud->yab_entry) {  			FREE(bud->yab_entry->fname);  			FREE(bud->yab_entry->lname);  			FREE(bud->yab_entry->nname); @@ -495,7 +464,7 @@ static void yahoo_free_buddies(YList * list)  }  /* Free an identities list */ -static void yahoo_free_identities(YList * list) +static void yahoo_free_identities(YList *list)  {  	while (list) {  		YList *n = list; @@ -524,6 +493,7 @@ static void yahoo_free_data(struct yahoo_data *yd)  	FREE(yd->password);  	FREE(yd->cookie_y);  	FREE(yd->cookie_t); +	FREE(yd->cookie_b);  	FREE(yd->cookie_c);  	FREE(yd->login_cookie);  	FREE(yd->login_id); @@ -539,8 +509,8 @@ static void yahoo_free_data(struct yahoo_data *yd)  #define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4) -static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service,  -		enum yahoo_status status, int id) +static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, +	enum ypacket_status status, int id)  {  	struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1); @@ -551,7 +521,8 @@ static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service,  	return pkt;  } -static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value) +static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, +	const char *value)  {  	struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);  	pair->key = key; @@ -596,7 +567,8 @@ static int yahoo_packet_length(struct yahoo_packet *pkt)  			 (((*((buf)+2))&0xff)<< 8) + \  			 (((*((buf)+3))&0xff))) -static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int len) +static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, +	int len)  {  	int pos = 0; @@ -649,7 +621,8 @@ static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int  			pair->value = strdup(value);  			FREE(value);  			pkt->hash = y_list_append(pkt->hash, pair); -			DEBUG_MSG(("Key: %d  \tValue: %s", pair->key, pair->value)); +			DEBUG_MSG(("Key: %d  \tValue: %s", pair->key, +					pair->value));  		} else {  			FREE(pair);  		} @@ -689,30 +662,29 @@ static void yahoo_dump_unhandled(struct yahoo_packet *pkt)  	}  } -  static void yahoo_packet_dump(unsigned char *data, int len)  { -	if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { +	if (yahoo_get_log_level() >= YAHOO_LOG_DEBUG) {  		int i;  		for (i = 0; i < len; i++) {  			if ((i % 8 == 0) && i) -				YAHOO_CALLBACK(ext_yahoo_log)(" "); +				YAHOO_CALLBACK(ext_yahoo_log) (" ");  			if ((i % 16 == 0) && i) -				YAHOO_CALLBACK(ext_yahoo_log)("\n"); -			YAHOO_CALLBACK(ext_yahoo_log)("%02x ", data[i]); +				YAHOO_CALLBACK(ext_yahoo_log) ("\n"); +			YAHOO_CALLBACK(ext_yahoo_log) ("%02x ", data[i]);  		} -		YAHOO_CALLBACK(ext_yahoo_log)("\n"); +		YAHOO_CALLBACK(ext_yahoo_log) ("\n");  		for (i = 0; i < len; i++) {  			if ((i % 8 == 0) && i) -				YAHOO_CALLBACK(ext_yahoo_log)(" "); +				YAHOO_CALLBACK(ext_yahoo_log) (" ");  			if ((i % 16 == 0) && i) -				YAHOO_CALLBACK(ext_yahoo_log)("\n"); +				YAHOO_CALLBACK(ext_yahoo_log) ("\n");  			if (isprint(data[i])) -				YAHOO_CALLBACK(ext_yahoo_log)(" %c ", data[i]); +				YAHOO_CALLBACK(ext_yahoo_log) (" %c ", data[i]);  			else -				YAHOO_CALLBACK(ext_yahoo_log)(" . "); +				YAHOO_CALLBACK(ext_yahoo_log) (" . ");  		} -		YAHOO_CALLBACK(ext_yahoo_log)("\n"); +		YAHOO_CALLBACK(ext_yahoo_log) ("\n");  	}  } @@ -722,7 +694,8 @@ static void to_y64(unsigned char *out, const unsigned char *in, int inlen)  	base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-");  } -static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length) +static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, +	int length)  {  	struct data_queue *tx = y_new0(struct data_queue, 1);  	tx->queue = y_new0(unsigned char, length); @@ -731,15 +704,17 @@ static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, in  	yid->txqueues = y_list_append(yid->txqueues, tx); -	if(!yid->write_tag) -		yid->write_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_WRITE, yid); +	if (!yid->write_tag) +		yid->write_tag = +			YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd-> +			client_id, yid->fd, YAHOO_INPUT_WRITE, yid);  } -static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet *pkt, int extra_pad) +static void yahoo_send_packet(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt, int extra_pad)  {  	int pktlen = yahoo_packet_length(pkt);  	int len = YAHOO_PACKET_HDRLEN + pktlen; -  	unsigned char *data;  	int pos = 0; @@ -748,19 +723,20 @@ static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet  	data = y_new0(unsigned char, len + 1); -	memcpy(data + pos, "YMSG", 4); pos += 4; -	pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); -	pos += yahoo_put16(data + pos, 0x0000); -	pos += yahoo_put16(data + pos, pktlen + extra_pad); -	pos += yahoo_put16(data + pos, pkt->service); -	pos += yahoo_put32(data + pos, pkt->status); -	pos += yahoo_put32(data + pos, pkt->id); +	memcpy(data + pos, "YMSG", 4); +	pos += 4; +	pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);	/* version [latest 12 0x000c] */ +	pos += yahoo_put16(data + pos, 0x0000);	/* HIWORD pkt length??? */ +	pos += yahoo_put16(data + pos, pktlen + extra_pad);	/* LOWORD pkt length? */ +	pos += yahoo_put16(data + pos, pkt->service);	/* service */ +	pos += yahoo_put32(data + pos, pkt->status);	/* status [4bytes] */ +	pos += yahoo_put32(data + pos, pkt->id);	/* session [4bytes] */  	yahoo_packet_write(pkt, data + pos);  	yahoo_packet_dump(data, len); -	 -	if( yid->type == YAHOO_CONNECTION_FT ) + +	if (yid->type == YAHOO_CONNECTION_FT)  		yahoo_send_data(yid->fd, data, len);  	else  		yahoo_add_to_send_queue(yid, data, len); @@ -781,92 +757,94 @@ static void yahoo_packet_free(struct yahoo_packet *pkt)  	FREE(pkt);  } -static int yahoo_send_data(int fd, void *data, int len) +static int yahoo_send_data(void *fd, void *data, int len)  {  	int ret;  	int e; -	if (fd < 0) +	if (fd == NULL)  		return -1;  	yahoo_packet_dump(data, len);  	do { -		ret = write(fd, data, len); -	} while(ret == -1 && errno==EINTR); -	e=errno; +		ret = YAHOO_CALLBACK(ext_yahoo_write) (fd, data, len); +	} while (ret == -1 && errno == EINTR); +	e = errno; -	if (ret == -1)  { +	if (ret == -1) {  		LOG(("wrote data: ERR %s", strerror(errno)));  	} else {  		LOG(("wrote data: OK"));  	} -	errno=e; +	errno = e;  	return ret;  } -void yahoo_close(int id)  +void yahoo_close(int id)  {  	struct yahoo_data *yd = find_conn_by_id(id); -	 -	if(!yd) +	if (!yd)  		return;  	del_from_list(yd);  	yahoo_free_data(yd); -	if(id == last_id) +	if (id == last_id)  		last_id--;  } -static void yahoo_input_close(struct yahoo_input_data *yid)  +static void yahoo_input_close(struct yahoo_input_data *yid)  {  	inputs = y_list_remove(inputs, yid); -	LOG(("yahoo_input_close(read)"));  -	YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->read_tag); -	LOG(("yahoo_input_close(write)"));  -	YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->write_tag); +	LOG(("yahoo_input_close(read)")); +	YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id, +		yid->read_tag); +	LOG(("yahoo_input_close(write)")); +	YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id, +		yid->write_tag);  	yid->read_tag = yid->write_tag = 0; -	if(yid->fd) -		close(yid->fd); +	if (yid->fd) +		YAHOO_CALLBACK(ext_yahoo_close) (yid->fd);  	yid->fd = 0;  	FREE(yid->rxqueue); -	if(count_inputs_with_id(yid->yd->client_id) == 0) { +	if (count_inputs_with_id(yid->yd->client_id) == 0) {  		LOG(("closing %d", yid->yd->client_id));  		yahoo_close(yid->yd->client_id);  	}  	yahoo_free_webcam(yid->wcm); -	if(yid->wcd) +	if (yid->wcd)  		FREE(yid->wcd); -	if(yid->ys) { +	if (yid->ys) {  		FREE(yid->ys->lsearch_text);  		FREE(yid->ys);  	}  	FREE(yid);  } -static int is_same_bud(const void * a, const void * b) { +static int is_same_bud(const void *a, const void *b) +{  	const struct yahoo_buddy *subject = a;  	const struct yahoo_buddy *object = b;  	return strcmp(subject->id, object->id);  } -static char * getcookie(char *rawcookie) +static char *getcookie(char *rawcookie)  { -	char * cookie=NULL; -	char * tmpcookie;  -	char * cookieend; +	char *cookie = NULL; +	char *tmpcookie; +	char *cookieend; -	if (strlen(rawcookie) < 2)  +	if (strlen(rawcookie) < 2)  		return NULL; -	tmpcookie = strdup(rawcookie+2); +	tmpcookie = strdup(rawcookie + 2);  	cookieend = strchr(tmpcookie, ';'); -	if(cookieend) +	if (cookieend)  		*cookieend = '\0';  	cookie = strdup(tmpcookie); @@ -876,18 +854,18 @@ static char * getcookie(char *rawcookie)  	return cookie;  } -static char * getlcookie(char *cookie) +static char *getlcookie(char *cookie)  {  	char *tmp;  	char *tmpend;  	char *login_cookie = NULL;  	tmpend = strstr(cookie, "n="); -	if(tmpend) { -		tmp = strdup(tmpend+2); +	if (tmpend) { +		tmp = strdup(tmpend + 2);  		tmpend = strchr(tmp, '&'); -		if(tmpend) -			*tmpend='\0'; +		if (tmpend) +			*tmpend = '\0';  		login_cookie = strdup(tmp);  		FREE(tmp);  	} @@ -895,7 +873,8 @@ static char * getlcookie(char *cookie)  	return login_cookie;  } -static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_notify(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *msg = NULL; @@ -926,84 +905,31 @@ static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_pack  	if (!msg)  		return; -	 -	if (!strncasecmp(msg, "TYPING", strlen("TYPING")))  -		YAHOO_CALLBACK(ext_yahoo_typing_notify)(yd->client_id, to, from, stat); -	else if (!strncasecmp(msg, "GAME", strlen("GAME")))  -		YAHOO_CALLBACK(ext_yahoo_game_notify)(yd->client_id, to, from, stat); -	else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE")))  -	{ + +	if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) +		YAHOO_CALLBACK(ext_yahoo_typing_notify) (yd->client_id, to, +			from, stat); +	else if (!strncasecmp(msg, "GAME", strlen("GAME"))) +		YAHOO_CALLBACK(ext_yahoo_game_notify) (yd->client_id, to, from, +			stat, ind); +	else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {  		if (!strcmp(ind, " ")) { -			YAHOO_CALLBACK(ext_yahoo_webcam_invite)(yd->client_id, to, from); +			YAHOO_CALLBACK(ext_yahoo_webcam_invite) (yd->client_id, +				to, from);  		} else {  			accept = atoi(ind);  			/* accept the invitation (-1 = deny 1 = accept) */  			if (accept < 0)  				accept = 0; -			YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply)(yd->client_id, to, from, accept); +			YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply) (yd-> +				client_id, to, from, accept);  		} -	} -	else +	} else  		LOG(("Got unknown notification: %s", msg));  } -static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt) -{ -	struct yahoo_data *yd = yid->yd; -	char *from=NULL; -	char *to=NULL; -	char *msg=NULL; -	char *url=NULL; -	long expires=0; - -	char *service=NULL; - -	char *filename=NULL; -	unsigned long filesize=0L; - -	YList *l; -	for (l = pkt->hash; l; l = l->next) { -		struct yahoo_pair *pair = l->data; -		if (pair->key == 4) -			from = pair->value; -		if (pair->key == 5) -			to = pair->value; -		if (pair->key == 14) -			msg = pair->value; -		if (pair->key == 20) -			url = pair->value; -		if (pair->key == 38) -			expires = atol(pair->value); - -		if (pair->key == 27) -			filename = pair->value; -		if (pair->key == 28) -			filesize = atol(pair->value); - -		if (pair->key == 49) -			service = pair->value; -	} - -	if(pkt->service == YAHOO_SERVICE_P2PFILEXFER) { -		if(strcmp("FILEXFER", service) != 0) { -			WARNING(("unhandled service 0x%02x", pkt->service)); -			yahoo_dump_unhandled(pkt); -			return; -		} -	} - -	if(msg) { -		char *tmp; -		tmp = strchr(msg, '\006'); -		if(tmp) -			*tmp = '\0'; -	} -	if(url && from) -		YAHOO_CALLBACK(ext_yahoo_got_file)(yd->client_id, to, from, url, expires, msg, filename, filesize); - -} - -static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_conference(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *msg = NULL; @@ -1011,98 +937,102 @@ static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_  	char *who = NULL;  	char *room = NULL;  	char *id = NULL; -	int  utf8 = 0; +	int utf8 = 0;  	YList *members = NULL;  	YList *l; -	 +  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data;  		if (pair->key == 50)  			host = pair->value; -		 -		if (pair->key == 52) {		/* invite */ + +		if (pair->key == 52) {	/* invite */  			members = y_list_append(members, strdup(pair->value));  		} -		if (pair->key == 53)		/* logon */ +		if (pair->key == 53)	/* logon */  			who = pair->value; -		if (pair->key == 54)		/* decline */ +		if (pair->key == 54)	/* decline */  			who = pair->value; -		if (pair->key == 55)		/* unavailable (status == 2) */ +		if (pair->key == 55)	/* unavailable (status == 2) */  			who = pair->value; -		if (pair->key == 56)		/* logoff */ +		if (pair->key == 56)	/* logoff */  			who = pair->value;  		if (pair->key == 57)  			room = pair->value; -		if (pair->key == 58)		/* join message */ +		if (pair->key == 58)	/* join message */  			msg = pair->value; -		if (pair->key == 14)		/* decline/conf message */ +		if (pair->key == 14)	/* decline/conf message */  			msg = pair->value; -		if (pair->key == 13) -			; -		if (pair->key == 16)		/* error */ +		if (pair->key == 13) ; +		if (pair->key == 16)	/* error */  			msg = pair->value; -		if (pair->key == 1)		/* my id */ +		if (pair->key == 1)	/* my id */  			id = pair->value; -		if (pair->key == 3)		/* message sender */ +		if (pair->key == 3)	/* message sender */  			who = pair->value;  		if (pair->key == 97)  			utf8 = atoi(pair->value);  	} -	if(!room) +	if (!room)  		return; -	if(host) { -		for(l = members; l; l = l->next) { -			char * w = l->data; -			if(!strcmp(w, host)) +	if (host) { +		for (l = members; l; l = l->next) { +			char *w = l->data; +			if (!strcmp(w, host))  				break;  		} -		if(!l) +		if (!l)  			members = y_list_append(members, strdup(host));  	}  	/* invite, decline, join, left, message -> status == 1 */ -	switch(pkt->service) { +	switch (pkt->service) {  	case YAHOO_SERVICE_CONFINVITE: -		if(pkt->status == 2) -			; -		else if(members) -			YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, id, host, room, msg, members); -		else if(msg) -			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, msg, 0, E_CONFNOTAVAIL); +		if (pkt->status == 2) ; +		else if (members) +			YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd-> +				client_id, id, host, room, msg, members); +		else if (msg) +			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, msg, 0, +				E_CONFNOTAVAIL);  		break;  	case YAHOO_SERVICE_CONFADDINVITE: -		if(pkt->status == 2) -			; -		else -			YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, id, host, room, msg, members); +		if (pkt->status == 1) +			YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd-> +				client_id, id, host, room, msg, members);  		break;  	case YAHOO_SERVICE_CONFDECLINE: -		if(who) -			YAHOO_CALLBACK(ext_yahoo_conf_userdecline)(yd->client_id, id, who, room, msg); +		if (who) +			YAHOO_CALLBACK(ext_yahoo_conf_userdecline) (yd-> +				client_id, id, who, room, msg);  		break;  	case YAHOO_SERVICE_CONFLOGON: -		if(who) -			YAHOO_CALLBACK(ext_yahoo_conf_userjoin)(yd->client_id, id, who, room); +		if (who) +			YAHOO_CALLBACK(ext_yahoo_conf_userjoin) (yd->client_id, +				id, who, room);  		break;  	case YAHOO_SERVICE_CONFLOGOFF: -		if(who) -			YAHOO_CALLBACK(ext_yahoo_conf_userleave)(yd->client_id, id, who, room); +		if (who) +			YAHOO_CALLBACK(ext_yahoo_conf_userleave) (yd->client_id, +				id, who, room);  		break;  	case YAHOO_SERVICE_CONFMSG: -		if(who) -			YAHOO_CALLBACK(ext_yahoo_conf_message)(yd->client_id, id, who, room, msg, utf8); +		if (who) +			YAHOO_CALLBACK(ext_yahoo_conf_message) (yd->client_id, +				id, who, room, msg, utf8);  		break;  	}  } -static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_chat(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	char *msg = NULL;  	char *id = NULL; @@ -1111,13 +1041,13 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet  	char *topic = NULL;  	YList *members = NULL;  	struct yahoo_chat_member *currentmember = NULL; -	int  msgtype = 1; -	int  utf8 = 0; -	int  firstjoin = 0; -	int  membercount = 0; -	int  chaterr=0; +	int msgtype = 1; +	int utf8 = 0; +	int firstjoin = 0; +	int membercount = 0; +	int chaterr = 0;  	YList *l; -	 +  	yahoo_dump_unhandled(pkt);  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data; @@ -1147,7 +1077,8 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet  			who = pair->value;  			if (pkt->service == YAHOO_SERVICE_CHATJOIN) { -				currentmember = y_new0(struct yahoo_chat_member, 1); +				currentmember = +					y_new0(struct yahoo_chat_member, 1);  				currentmember->id = strdup(pair->value);  				members = y_list_append(members, currentmember);  			} @@ -1177,7 +1108,6 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet  				currentmember->location = strdup(pair->value);  		} -  		if (pair->key == 130) {  			/* first join */  			firstjoin = 1; @@ -1195,17 +1125,19 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet  		if (pair->key == 114) {  			/* message error not sure what all the pair values mean */  			/* but -1 means no session in room */ -			chaterr= atoi(pair->value); +			chaterr = atoi(pair->value);  		}  	} -	if(!room) { -		if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */ -			YAHOO_CALLBACK(ext_yahoo_chat_yahoologout)(yid->yd->client_id, id); -			return ; +	if (!room) { +		if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) {	/* yahoo originated chat logout */ +			YAHOO_CALLBACK(ext_yahoo_chat_yahoologout) (yid->yd-> +				client_id, id); +			return;  		} -		if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr)  { -			YAHOO_CALLBACK(ext_yahoo_chat_yahooerror)(yid->yd->client_id, id); +		if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) { +			YAHOO_CALLBACK(ext_yahoo_chat_yahooerror) (yid->yd-> +				client_id, id);  			return;  		} @@ -1213,103 +1145,156 @@ static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet  		return;  	} -	switch(pkt->service) { +	switch (pkt->service) {  	case YAHOO_SERVICE_CHATJOIN: -		if(y_list_length(members) != membercount) { +		if (y_list_length(members) != membercount) {  			WARNING(("Count of members doesn't match No. of members we got"));  		} -		if(firstjoin && members) { -			YAHOO_CALLBACK(ext_yahoo_chat_join)(yid->yd->client_id, id, room, topic, members, yid->fd); -		} else if(who) { -			if(y_list_length(members) != 1) { +		if (firstjoin && members) { +			YAHOO_CALLBACK(ext_yahoo_chat_join) (yid->yd->client_id, +				id, room, topic, members, yid->fd); +		} else if (who) { +			if (y_list_length(members) != 1) {  				WARNING(("Got more than 1 member on a normal join"));  			}  			/* this should only ever have one, but just in case */ -			while(members) { +			while (members) {  				YList *n = members->next;  				currentmember = members->data; -				YAHOO_CALLBACK(ext_yahoo_chat_userjoin)(yid->yd->client_id, id, room, currentmember); +				YAHOO_CALLBACK(ext_yahoo_chat_userjoin) (yid-> +					yd->client_id, id, room, currentmember);  				y_list_free_1(members); -				members=n; +				members = n;  			}  		}  		break;  	case YAHOO_SERVICE_CHATEXIT: -		if(who) { -			YAHOO_CALLBACK(ext_yahoo_chat_userleave)(yid->yd->client_id, id, room, who); +		if (who) { +			YAHOO_CALLBACK(ext_yahoo_chat_userleave) (yid->yd-> +				client_id, id, room, who);  		}  		break;  	case YAHOO_SERVICE_COMMENT: -		if(who) { -			YAHOO_CALLBACK(ext_yahoo_chat_message)(yid->yd->client_id, id, who, room, msg, msgtype, utf8); +		if (who) { +			YAHOO_CALLBACK(ext_yahoo_chat_message) (yid->yd-> +				client_id, id, who, room, msg, msgtype, utf8);  		}  		break;  	}  } -static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_message(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	YList *l; -	YList * messages = NULL; +	YList *messages = NULL;  	struct m { -		int  i_31; -		int  i_32; +		int i_31; +		int i_32;  		char *to;  		char *from;  		long tm;  		char *msg; -		int  utf8; +		int utf8; +		char *gunk;  	} *message = y_new0(struct m, 1);  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data; -		if (pair->key == 1 || pair->key == 4) -		{ -			if(!message->from) +		if (pair->key == 1 || pair->key == 4) { +			if (!message->from)  				message->from = pair->value; -		} -		else if (pair->key == 5) +		} else if (pair->key == 5)  			message->to = pair->value;  		else if (pair->key == 15)  			message->tm = strtol(pair->value, NULL, 10);  		else if (pair->key == 97)  			message->utf8 = atoi(pair->value); -			/* user message */  /* sys message */ +		/* This comes when the official client sends us a message */ +		else if (pair->key == 429) +			message->gunk = pair->value; +		/* user message *//* sys message */  		else if (pair->key == 14 || pair->key == 16)  			message->msg = pair->value;  		else if (pair->key == 31) { -			if(message->i_31) { +			if (message->i_31) {  				messages = y_list_append(messages, message);  				message = y_new0(struct m, 1);  			}  			message->i_31 = atoi(pair->value); -		} -		else if (pair->key == 32) +		} else if (pair->key == 32)  			message->i_32 = atoi(pair->value);  		else -			LOG(("yahoo_process_message: status: %d, key: %d, value: %s", -					pkt->status, pair->key, pair->value)); +			LOG(("yahoo_process_message: status: %d, key: %d, value: %s", pkt->status, pair->key, pair->value));  	}  	messages = y_list_append(messages, message); -	for (l = messages; l; l=l->next) { +	for (l = messages; l; l = l->next) {  		message = l->data;  		if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) { -			YAHOO_CALLBACK(ext_yahoo_system_message)(yd->client_id, message->msg); +			YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id, +				message->to, message->from, message->msg);  		} else if (pkt->status <= 2 || pkt->status == 5) { -			YAHOO_CALLBACK(ext_yahoo_got_im)(yd->client_id, message->to, message->from, message->msg, message->tm, pkt->status, message->utf8); +			/* Confirm message receipt if we got the gunk */ +			if(message->gunk) { +				struct yahoo_packet *outpkt; +                         +				outpkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_CONFIRM, +					YPACKET_STATUS_DEFAULT, 0); +				yahoo_packet_hash(outpkt, 1, yd->user); +				yahoo_packet_hash(outpkt, 5, message->from); +				yahoo_packet_hash(outpkt, 302, "430"); +				yahoo_packet_hash(outpkt, 430, message->gunk); +				yahoo_packet_hash(outpkt, 303, "430"); +				yahoo_packet_hash(outpkt, 450, "0"); +				yahoo_send_packet(yid, outpkt, 0); +                         +				yahoo_packet_free(outpkt); +			} + +			if (!strcmp(message->msg, "<ding>")) +				YAHOO_CALLBACK(ext_yahoo_got_buzz) (yd->client_id, +					message->to, message->from, message->tm); +			else +				YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id, +					message->to, message->from, message->msg, +					message->tm, pkt->status, message->utf8);  		} else if (pkt->status == 0xffffffff) { -			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, message->msg, 0, E_SYSTEM); +			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, +				message->msg, 0, E_SYSTEM);  		} -		free(message); +		FREE(message);  	}  	y_list_free(messages);  } +/* + * Here's what multi-level packets look like. Data in brackets is the value. + * + * 3 level: + * ======= + *  + * 302 (318) - Beginning level 1 + * 	300 (318) - Begin level 2 + * 	302 (319) - End level 2 header + * 		300 (319) - Begin level 3 + * 		301 (319) - End level 3 + * 	303 (319) - End level 2 + * 303 (318) - End level 1 + * + * 2 level: + * ======= + * + * 302 (315) - Beginning level 1 + * 	300 (315) - Begin level 2 + * 	301 (315) - End level 2 + * 303 (315) - End level 1 + * + */  static void yahoo_process_status(struct yahoo_input_data *yid,  	struct yahoo_packet *pkt)  { @@ -1326,10 +1311,12 @@ static void yahoo_process_status(struct yahoo_input_data *yid,  		return;  	} -	/* Status updates may be spread accross multiple packets and not -	   even on buddy boundaries, so keeping some state is important. -	   So, continue where we left off, and only add a user entry to -	   the list once it's complete (301-315 End buddy). */ +	/*  +	 * Status updates may be spread accross multiple packets and not  +	 * even on buddy boundaries, so keeping some state is important.  +	 * So, continue where we left off, and only add a user entry to  +	 * the list once it's complete (301-315 End buddy). +	 */  	u = yd->half_user;  	for (l = pkt->hash; l; l = l->next) { @@ -1593,17 +1580,20 @@ static void yahoo_process_list(struct yahoo_input_data *yid,  		YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);  } -static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_verify(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd; -	if(pkt->status != 0x01) { +	if (pkt->status != 0x01) {  		DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status)); -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_LOCK, ""); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +			YAHOO_LOGIN_LOCK, "");  		return;  	} -	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_send_packet(yid, pkt, 0); @@ -1612,7 +1602,8 @@ static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_pack  } -static void yahoo_process_picture_checksum( struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_picture_checksum(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *from = NULL; @@ -1620,30 +1611,30 @@ static void yahoo_process_picture_checksum( struct yahoo_input_data *yid, struct  	int checksum = 0;  	YList *l; -	for(l = pkt->hash; l; l = l->next) -	{ +	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data; -		switch(pair->key) -		{ -			case 1: -			case 4: -				from = pair->value; -			case 5: -				to = pair->value; -				break; -			case 212: -				break; -			case 192: -				checksum = atoi( pair->value ); -				break; +		switch (pair->key) { +		case 1: +		case 4: +			from = pair->value; +		case 5: +			to = pair->value; +			break; +		case 212: +			break; +		case 192: +			checksum = atoi(pair->value); +			break;  		}  	} -	YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum)(yd->client_id,to,from,checksum); +	YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum) (yd->client_id, to, +		from, checksum);  } -static void yahoo_process_picture(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_picture(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *url = NULL; @@ -1652,607 +1643,108 @@ static void yahoo_process_picture(struct yahoo_input_data *yid, struct yahoo_pac  	int status = 0;  	int checksum = 0;  	YList *l; -	 -	for(l = pkt->hash; l; l = l->next) -	{ + +	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data; -		switch(pair->key) -		{ +		switch (pair->key) {  		case 1: -		case 4:		/* sender */ +		case 4:	/* sender */  			from = pair->value;  			break; -		case 5:		/* we */ +		case 5:	/* we */  			to = pair->value;  			break; -		case 13:		/* request / sending */ -			status = atoi( pair->value ); +		case 13:	/* request / sending */ +			status = atoi(pair->value);  			break; -		case 20:		/* url */ +		case 20:	/* url */  			url = pair->value;  			break;  		case 192:	/*checksum */ -			checksum = atoi( pair->value ); +			checksum = atoi(pair->value);  			break;  		}  	} -	switch( status ) -	{ -		case 1:	/* this is a request, ignore for now */ -			YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request)(yd->client_id, to, from); -			break; -		case 2:	/* this is cool - we get a picture :) */ -			YAHOO_CALLBACK(ext_yahoo_got_buddyicon)(yd->client_id,to, from, url, checksum); -			break; +	switch (status) { +	case 1:		/* this is a request, ignore for now */ +		YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request) (yd->client_id, +			to, from); +		break; +	case 2:		/* this is cool - we get a picture :) */ +		YAHOO_CALLBACK(ext_yahoo_got_buddyicon) (yd->client_id, to, +			from, url, checksum); +		break;  	}  } -static void yahoo_process_picture_upload(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_picture_upload(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	YList *l;  	char *url = NULL; -	if ( pkt->status != 1 ) +	if (pkt->status != 1)  		return;		/* something went wrong */ -	 -	for(l = pkt->hash; l; l = l->next) -	{ -		struct yahoo_pair *pair = l->data; -		switch(pair->key) -		{ -			case 5:		/* we */ -				break; -			case 20:		/* url */ -				url = pair->value; -				break; -			case 27:		/* local filename */ -				break; -			case 38:		/* time */ -				break; -		} -	} - -	YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded)(yd->client_id, url); -} - -static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid,  -		const char *seed, const char *sn) -{ -	struct yahoo_data *yd = yid->yd; -	 -	/* So, Yahoo has stopped supporting its older clients in India, and  -	 * undoubtedly will soon do so in the rest of the world. -	 *  -	 * The new clients use this authentication method.  I warn you in  -	 * advance, it's bizzare, convoluted, inordinately complicated.   -	 * It's also no more secure than crypt() was.  The only purpose this  -	 * scheme could serve is to prevent third part clients from connecting  -	 * to their servers. -	 * -	 * Sorry, Yahoo. -	 */ - -	struct yahoo_packet *pack; -	 -	md5_byte_t result[16]; -	md5_state_t ctx; -	char *crypt_result; -	unsigned char *password_hash = malloc(25); -	unsigned char *crypt_hash = malloc(25); -	unsigned char *hash_string_p = malloc(50 + strlen(sn)); -	unsigned char *hash_string_c = malloc(50 + strlen(sn)); -	 -	char checksum; -	 -	int sv; -	 -	unsigned char *result6 = malloc(25); -	unsigned char *result96 = malloc(25); - -	sv = seed[15]; -	sv = (sv % 8) % 5; - -	md5_init(&ctx); -	md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password)); -	md5_finish(&ctx, result); -	to_y64(password_hash, result, 16); -	 -	md5_init(&ctx); -	crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$");   -	md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result)); -	md5_finish(&ctx, result); -	to_y64(crypt_hash, result, 16); -	free(crypt_result); - -	switch (sv) { -	case 0: -		checksum = seed[seed[7] % 16]; -		snprintf((char *)hash_string_p, strlen(sn) + 50, -			"%c%s%s%s", checksum, password_hash, yd->user, seed); -		snprintf((char *)hash_string_c, strlen(sn) + 50, -			"%c%s%s%s", checksum, crypt_hash, yd->user, seed); -		break; -	case 1: -		checksum = seed[seed[9] % 16]; -		snprintf((char *)hash_string_p, strlen(sn) + 50, -			"%c%s%s%s", checksum, yd->user, seed, password_hash); -		snprintf((char *)hash_string_c, strlen(sn) + 50, -			"%c%s%s%s", checksum, yd->user, seed, crypt_hash); -		break; -	case 2: -		checksum = seed[seed[15] % 16]; -		snprintf((char *)hash_string_p, strlen(sn) + 50, -			"%c%s%s%s", checksum, seed, password_hash, yd->user); -		snprintf((char *)hash_string_c, strlen(sn) + 50, -			"%c%s%s%s", checksum, seed, crypt_hash, yd->user); -		break; -	case 3: -		checksum = seed[seed[1] % 16]; -		snprintf((char *)hash_string_p, strlen(sn) + 50, -			"%c%s%s%s", checksum, yd->user, password_hash, seed); -		snprintf((char *)hash_string_c, strlen(sn) + 50, -			"%c%s%s%s", checksum, yd->user, crypt_hash, seed); -		break; -	case 4: -		checksum = seed[seed[3] % 16]; -		snprintf((char *)hash_string_p, strlen(sn) + 50, -			"%c%s%s%s", checksum, password_hash, seed, yd->user); -		snprintf((char *)hash_string_c, strlen(sn) + 50, -			"%c%s%s%s", checksum, crypt_hash, seed, yd->user); -		break; -	} -		 -	md5_init(&ctx);   -	md5_append(&ctx, (md5_byte_t *)hash_string_p, strlen((char *)hash_string_p)); -	md5_finish(&ctx, result); -	to_y64(result6, result, 16); - -	md5_init(&ctx);   -	md5_append(&ctx, (md5_byte_t *)hash_string_c, strlen((char *)hash_string_c)); -	md5_finish(&ctx, result); -	to_y64(result96, result, 16); - -	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); -	yahoo_packet_hash(pack, 0, yd->user); -	yahoo_packet_hash(pack, 6, (char *)result6); -	yahoo_packet_hash(pack, 96, (char *)result96); -	yahoo_packet_hash(pack, 1, yd->user); -		 -	yahoo_send_packet(yid, pack, 0); -		 -	FREE(result6); -	FREE(result96); -	FREE(password_hash); -	FREE(crypt_hash); -	FREE(hash_string_p); -	FREE(hash_string_c); - -	yahoo_packet_free(pack); - -} - -/* - * New auth protocol cracked by Cerulean Studios and sent in to Gaim - */ -static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *seed, const char *sn) -{ -	struct yahoo_packet *pack = NULL; -	struct yahoo_data *yd = yid->yd; - -	md5_byte_t         result[16]; -	md5_state_t        ctx; - -	sha1_state_t       ctx1; -	sha1_state_t       ctx2; - -	char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ"; -	char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop"; - -	char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5"; -	char *operand_lookup = "+|&%/*^-"; -	char *delimit_lookup = ",;"; - -	unsigned char *password_hash = malloc(25); -	unsigned char *crypt_hash = malloc(25); -	char *crypt_result = NULL; -	unsigned char pass_hash_xor1[64]; -	unsigned char pass_hash_xor2[64]; -	unsigned char crypt_hash_xor1[64]; -	unsigned char crypt_hash_xor2[64]; -	unsigned char chal[7]; -	char resp_6[100]; -	char resp_96[100]; - -	unsigned char digest1[20]; -	unsigned char digest2[20]; -	unsigned char magic_key_char[4]; -	const unsigned char *magic_ptr; - -	unsigned int  magic[64]; -	unsigned int  magic_work=0; - -	char comparison_src[20]; - -	int x, j, i; -	int cnt = 0; -	int magic_cnt = 0; -	int magic_len; -	int depth =0, table =0; - -	memset(&pass_hash_xor1, 0, 64); -	memset(&pass_hash_xor2, 0, 64); -	memset(&crypt_hash_xor1, 0, 64); -	memset(&crypt_hash_xor2, 0, 64); -	memset(&digest1, 0, 20); -	memset(&digest2, 0, 20); -	memset(&magic, 0, 64); -	memset(&resp_6, 0, 100); -	memset(&resp_96, 0, 100); -	memset(&magic_key_char, 0, 4); - -	/*  -	 * Magic: Phase 1.  Generate what seems to be a 30  -	 * byte value (could change if base64 -	 * ends up differently?  I don't remember and I'm  -	 * tired, so use a 64 byte buffer. -	 */ - -	magic_ptr = (unsigned char *)seed; - -	while (*magic_ptr != 0) { -		char *loc; - -		/* Ignore parentheses.  */ - -		if (*magic_ptr == '(' || *magic_ptr == ')') { -			magic_ptr++; -			continue; -		} - -		/* Characters and digits verify against  -		   the challenge lookup. -		*/ - -		if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) { -			loc = strchr(challenge_lookup, *magic_ptr); -			if (!loc) { -				/* This isn't good */ -				continue; -			} - -			/* Get offset into lookup table and lsh 3. */ - -			magic_work = loc - challenge_lookup; -			magic_work <<= 3; - -			magic_ptr++; -			continue; -		} else { -			unsigned int local_store; - -			loc = strchr(operand_lookup, *magic_ptr); -			if (!loc) { -				/* Also not good. */ -				continue; -			} - -			local_store = loc - operand_lookup; - -			/* Oops; how did this happen? */ -			if (magic_cnt >= 64)  -				break; - -			magic[magic_cnt++] = magic_work | local_store; -			magic_ptr++; -			continue; -		} -	} - -	magic_len = magic_cnt; -	magic_cnt = 0; - -	/* Magic: Phase 2.  Take generated magic value and  -	 * sprinkle fairy dust on the values. */ - -	for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) { -		unsigned char byte1; -		unsigned char byte2; - -		/* Bad.  Abort. -		 */ -		if (magic_cnt >= magic_len) { -			WARNING(("magic_cnt(%d)  magic_len(%d)", magic_cnt, magic_len)) -			break; -		} - -		byte1 = magic[magic_cnt]; -		byte2 = magic[magic_cnt+1]; - -		byte1 *= 0xcd; -		byte1 ^= byte2; - -		magic[magic_cnt+1] = byte1; -	} - -	/* Magic: Phase 3.  This computes 20 bytes.  The first 4 bytes are used as our magic  -	 * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key  -	 * plus 3 bytes.  The 3 bytes are found by looping, and they represent the offsets  -	 * into particular functions we'll later call to potentially alter the magic key.  -	 *  -	 * %-)  -	 */  -	 -	magic_cnt = 1;  -	x = 0;  -	 -	do {  -		unsigned int     bl = 0;   -		unsigned int     cl = magic[magic_cnt++];  -		 -		if (magic_cnt >= magic_len)  -			break;  -		 -		if (cl > 0x7F) {  -			if (cl < 0xe0)   -				bl = cl = (cl & 0x1f) << 6;   -			else {  -				bl = magic[magic_cnt++];   -                              cl = (cl & 0x0f) << 6;   -                              bl = ((bl & 0x3f) + cl) << 6;   -			}   -			 -			cl = magic[magic_cnt++];   -			bl = (cl & 0x3f) + bl;   -		} else  -			bl = cl;   -		 -		comparison_src[x++] = (bl & 0xff00) >> 8;   -		comparison_src[x++] = bl & 0xff;   -	} while (x < 20);  - -	/* Dump magic key into a char for SHA1 action. */ -	 -		 -	for(x = 0; x < 4; x++)  -		magic_key_char[x] = comparison_src[x]; - -	/* Compute values for recursive function table! */ -	memcpy( chal, magic_key_char, 4 ); -        x = 1; -	for( i = 0; i < 0xFFFF && x; i++ ) -	{ -		for( j = 0; j < 5 && x; j++ ) -		{ -			chal[4] = i; -			chal[5] = i >> 8; -			chal[6] = j; -			md5_init( &ctx ); -			md5_append( &ctx, chal, 7 ); -			md5_finish( &ctx, result ); -			if( memcmp( comparison_src + 4, result, 16 ) == 0 ) -			{ -				depth = i; -				table = j; -				x = 0; -			} -		} -	} - -	/* Transform magic_key_char using transform table */ -	x = magic_key_char[3] << 24  | magic_key_char[2] << 16  -		| magic_key_char[1] << 8 | magic_key_char[0]; -	x = yahoo_xfrm( table, depth, x ); -	x = yahoo_xfrm( table, depth, x ); -	magic_key_char[0] = x & 0xFF; -	magic_key_char[1] = x >> 8 & 0xFF; -	magic_key_char[2] = x >> 16 & 0xFF; -	magic_key_char[3] = x >> 24 & 0xFF; - -	/* Get password and crypt hashes as per usual. */ -	md5_init(&ctx); -	md5_append(&ctx, (md5_byte_t *)yd->password,  strlen(yd->password)); -	md5_finish(&ctx, result); -	to_y64(password_hash, result, 16); - -	md5_init(&ctx); -	crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$");   -	md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result)); -	md5_finish(&ctx, result); -	to_y64(crypt_hash, result, 16); -	free(crypt_result); - -	/* Our first authentication response is based off  -	 * of the password hash. */ - -	for (x = 0; x < (int)strlen((char *)password_hash); x++)  -		pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36; - -	if (cnt < 64)  -		memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt); - -	cnt = 0; - -	for (x = 0; x < (int)strlen((char *)password_hash); x++)  -		pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c; - -	if (cnt < 64)  -		memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt); - -	sha1_init(&ctx1); -	sha1_init(&ctx2); - -	/* The first context gets the password hash XORed  -	 * with 0x36 plus a magic value -	 * which we previously extrapolated from our  -	 * challenge. */ - -	sha1_append(&ctx1, pass_hash_xor1, 64); -	if (j >= 3 ) -		ctx1.Length_Low = 0x1ff; -	sha1_append(&ctx1, magic_key_char, 4); -	sha1_finish(&ctx1, digest1); - -	 /* The second context gets the password hash XORed  -	  * with 0x5c plus the SHA-1 digest -	  * of the first context. */ - -	sha1_append(&ctx2, pass_hash_xor2, 64); -	sha1_append(&ctx2, digest1, 20); -	sha1_finish(&ctx2, digest2); - -	/* Now that we have digest2, use it to fetch  -	 * characters from an alphabet to construct -	 * our first authentication response. */ - -	for (x = 0; x < 20; x += 2) { -		unsigned int    val = 0; -		unsigned int    lookup = 0; -		char            byte[6]; - -		memset(&byte, 0, 6); - -		/* First two bytes of digest stuffed  -		 *  together. -		 */ - -		val = digest2[x]; -		val <<= 8; -		val += digest2[x+1]; +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; -		lookup = (val >> 0x0b); -		lookup &= 0x1f; -		if (lookup >= strlen(alphabet1)) +		switch (pair->key) { +		case 5:	/* we */  			break; -		sprintf(byte, "%c", alphabet1[lookup]); -		strcat(resp_6, byte); -		strcat(resp_6, "="); - -		lookup = (val >> 0x06); -		lookup &= 0x1f; -		if (lookup >= strlen(alphabet2)) +		case 20:	/* url */ +			url = pair->value;  			break; -		sprintf(byte, "%c", alphabet2[lookup]); -		strcat(resp_6, byte); - -		lookup = (val >> 0x01); -		lookup &= 0x1f; -		if (lookup >= strlen(alphabet2)) +		case 27:	/* local filename */  			break; -		sprintf(byte, "%c", alphabet2[lookup]); -		strcat(resp_6, byte); - -		lookup = (val & 0x01); -		if (lookup >= strlen(delimit_lookup)) +		case 38:	/* time */  			break; -		sprintf(byte, "%c", delimit_lookup[lookup]); -		strcat(resp_6, byte); +		}  	} -	/* Our second authentication response is based off  -	 * of the crypto hash. */ - -	cnt = 0; -	memset(&digest1, 0, 20); -	memset(&digest2, 0, 20); - -	for (x = 0; x < (int)strlen((char *)crypt_hash); x++)  -		crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36; - -	if (cnt < 64)  -		memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt); - -	cnt = 0; - -	for (x = 0; x < (int)strlen((char *)crypt_hash); x++)  -		crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c; - -	if (cnt < 64)  -		memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt); - -	sha1_init(&ctx1); -	sha1_init(&ctx2); - -	/* The first context gets the password hash XORed  -	 * with 0x36 plus a magic value -	 * which we previously extrapolated from our  -	 * challenge. */ - -	sha1_append(&ctx1, crypt_hash_xor1, 64); -	if (j >= 3 ) -		ctx1.Length_Low = 0x1ff; -	sha1_append(&ctx1, magic_key_char, 4); -	sha1_finish(&ctx1, digest1); - -	/* The second context gets the password hash XORed  -	 * with 0x5c plus the SHA-1 digest -	 * of the first context. */ - -	sha1_append(&ctx2, crypt_hash_xor2, 64); -	sha1_append(&ctx2, digest1, 20); -	sha1_finish(&ctx2, digest2); - -	/* Now that we have digest2, use it to fetch  -	 * characters from an alphabet to construct -	 * our first authentication response.  */ - -	for (x = 0; x < 20; x += 2) { -		unsigned int val = 0; -		unsigned int lookup = 0; +	YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded) (yd->client_id, url); +} -		char byte[6]; +void yahoo_login(int id, int initial) +{ +	struct yahoo_data *yd = find_conn_by_id(id); +	struct connect_callback_data *ccd; +	struct yahoo_server_settings *yss; +	int tag; -		memset(&byte, 0, 6); +	char *host; -		/* First two bytes of digest stuffed  -		 *  together. */ +	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); +	yid->yd = yd; +	yid->type = YAHOO_CONNECTION_PAGER; +	inputs = y_list_prepend(inputs, yid); -		val = digest2[x]; -		val <<= 8; -		val += digest2[x+1]; +	yd->initial_status = initial; +	yss = yd->server_settings; -		lookup = (val >> 0x0b); -		lookup &= 0x1f; -		if (lookup >= strlen(alphabet1)) -			break; -		sprintf(byte, "%c", alphabet1[lookup]); -		strcat(resp_96, byte); -		strcat(resp_96, "="); +	ccd = y_new0(struct connect_callback_data, 1); +	ccd->yd = yd; -		lookup = (val >> 0x06); -		lookup &= 0x1f; -		if (lookup >= strlen(alphabet2)) -			break; -		sprintf(byte, "%c", alphabet2[lookup]); -		strcat(resp_96, byte); +	host = yss->pager_host; -		lookup = (val >> 0x01); -		lookup &= 0x1f; -		if (lookup >= strlen(alphabet2)) -			break; -		sprintf(byte, "%c", alphabet2[lookup]); -		strcat(resp_96, byte); +	if (!host) +		host = yss->pager_host_list[0]; -		lookup = (val & 0x01); -		if (lookup >= strlen(delimit_lookup)) -			break; -		sprintf(byte, "%c", delimit_lookup[lookup]); -		strcat(resp_96, byte); -	} +	tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, +		host, yss->pager_port, yahoo_connected, ccd, 0); -	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); -	yahoo_packet_hash(pack, 0, sn); -	yahoo_packet_hash(pack, 6, resp_6); -	yahoo_packet_hash(pack, 96, resp_96); -	yahoo_packet_hash(pack, 1, sn); -	yahoo_send_packet(yid, pack, 0); -	yahoo_packet_free(pack); - -	free(password_hash); -	free(crypt_hash); +	/* +	 * if tag <= 0, then callback has already been called +	 * so ccd will have been freed +	 */ +	if (tag > 0) +		ccd->tag = tag; +	else if (tag < 0) +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +			YAHOO_LOGIN_SOCK, NULL);  }  struct yahoo_https_auth_data @@ -2302,7 +1794,7 @@ static enum yahoo_status yahoo_https_status_parse(int code)  	}  } -static void yahoo_process_auth_0x10(struct yahoo_input_data *yid, const char *seed, const char *sn) +static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn)  {  	struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1); @@ -2352,17 +1844,17 @@ static void yahoo_https_auth_token_finish(struct http_request *req)  	yd = yid->yd;  	if (req->status_code != 200) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL);  		goto fail;  	}  	if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL);  		goto fail;  	}  	if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3001, NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3001, NULL);  		goto fail;  	} @@ -2408,19 +1900,19 @@ static void yahoo_https_auth_finish(struct http_request *req)  	unsigned char yhash[32];  	if (req->status_code != 200) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL);  		goto fail;  	}  	if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL);  		goto fail;  	}  	if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL ||  	    (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL ||  	    (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3002, NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3002, NULL);  		goto fail;  	} @@ -2453,52 +1945,53 @@ fail:  	g_free(had);  } -static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_auth(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	char *seed = NULL; -	char *sn   = NULL; +	char *sn = NULL;  	YList *l = pkt->hash;  	int m = 0; +	struct yahoo_data *yd = yid->yd;  	while (l) {  		struct yahoo_pair *pair = l->data; -		if (pair->key == 94) + +		switch (pair->key) { +		case 94:  			seed = pair->value; -		if (pair->key == 1) +			break; +		case 1:  			sn = pair->value; -		if (pair->key == 13) +			break; +		case 13:  			m = atoi(pair->value); +			break; +		}  		l = l->next;  	} -	if (!seed)  +	if (!seed)  		return; -	switch (m) { -		case 0: -			yahoo_process_auth_pre_0x0b(yid, seed, sn); -			break; -		case 1: -			yahoo_process_auth_0x0b(yid, seed, sn); -			break; -		case 2: -			yahoo_process_auth_0x10(yid, seed, sn); -			break; -		default: -			/* call error */ -			WARNING(("unknown auth type %d", m)); -			yahoo_process_auth_0x0b(yid, seed, sn); -			break; +	if (m==2) +		yahoo_https_auth(yid, seed, sn); +	else { +		/* call error */ +		WARNING(("unknown auth type %d", m)); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +			YAHOO_LOGIN_UNKNOWN, NULL);  	}  } -static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_auth_resp(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *login_id;  	char *handle; -	char *url=NULL; -	int  login_status=0; +	char *url = NULL; +	int login_status = -1;  	YList *l; @@ -2514,13 +2007,15 @@ static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_p  			login_status = atoi(pair->value);  	} -	if(pkt->status == 0xffffffff) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, login_status, url); -	/*	yahoo_logoff(yd->client_id);*/ +	if (pkt->status == YPACKET_STATUS_DISCONNECTED) { +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +			login_status, url); +		/*      yahoo_logoff(yd->client_id); */  	}  } -static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_mail(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *who = NULL; @@ -2546,12 +2041,46 @@ static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet  	if (who && email && subj) {  		char from[1024];  		snprintf(from, sizeof(from), "%s (%s)", who, email); -		YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, from, subj, count); -	} else if(count > 0) -		YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, NULL, NULL, count); +		YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, from, +			subj, count); +	} else if (count > 0) +		YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, NULL, +			NULL, count);  } -static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_new_contact(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt) +{ +	struct yahoo_data *yd = yid->yd; +	char *me = NULL; +	char *who = NULL; +	char *msg = NULL; +	int online = -1; + +	YList *l; + +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; +		if (pair->key == 4) +			who = pair->value; +		else if (pair->key == 5) +			me = pair->value; +		else if (pair->key == 14) +			msg = pair->value; +		else if (pair->key == 13) +			online = strtol(pair->value, NULL, 10); +	} + +	if (who && online < 0) +		YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who, +			msg); +	else if (online == 2) +		YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg); +} + +/* UNUSED? */ +static void yahoo_process_contact(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *id = NULL; @@ -2560,7 +2089,7 @@ static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_pac  	char *name = NULL;  	long tm = 0L;  	int state = YAHOO_STATUS_AVAILABLE; -	int online = FALSE; +	int online = 0;  	int away = 0;  	int idle = 0;  	int mobile = 0; @@ -2589,18 +2118,21 @@ static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_pac  			idle = strtol(pair->value, NULL, 10);  		else if (pair->key == 60)  			mobile = strtol(pair->value, NULL, 10); -		 +  	}  	if (id) -		YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, id, who, msg); +		YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, id, who, +			msg);  	else if (name) -		YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away, idle, mobile); -	else if(pkt->status == 0x07) -		YAHOO_CALLBACK(ext_yahoo_rejected)(yd->client_id, who, msg); +		YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, name, +			state, msg, away, idle, mobile); +	else if (pkt->status == 0x07) +		YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);  } -static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_buddyadd(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *who = NULL; @@ -2608,7 +2140,7 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa  	int status = 0;  	char *me = NULL; -	struct yahoo_buddy *bud=NULL; +	struct yahoo_buddy *bud = NULL;  	YList *l;  	for (l = pkt->hash; l; l = l->next) { @@ -2623,51 +2155,30 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa  			status = strtol(pair->value, NULL, 10);  	} -	yahoo_dump_unhandled(pkt); - -	if(!who) +	if (!who)  		return; -	if(!where) +	if (!where)  		where = "Unknown"; -	/* status: 0 == Successful, 1 == Error (does not exist), 2 == Already in list */ -	if( status == 0 ) { -		bud = y_new0(struct yahoo_buddy, 1); -		bud->id = strdup(who); -		bud->group = strdup(where); -		bud->real_name = NULL; -		 -		yd->buddies = y_list_append(yd->buddies, bud); -	 -		/* Possibly called already, but at least the call above doesn't -		   seem to happen every time (not anytime I tried). */ -		YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, me, who, NULL); -	} +	bud = y_new0(struct yahoo_buddy, 1); +	bud->id = strdup(who); +	bud->group = strdup(where); +	bud->real_name = NULL; -/*	YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */ -} +	yd->buddies = y_list_append(yd->buddies, bud); -static void yahoo_process_contact_ymsg13(struct yahoo_input_data *yid, struct yahoo_packet *pkt) -{ -	char* who=NULL; -	char* me=NULL;	 -	char* msg=NULL; -	YList *l; -	for (l = pkt->hash; l; l = l->next) { -		struct yahoo_pair *pair = l->data; -		if (pair->key == 4) -			who = pair->value; -		else if (pair->key == 5) -			me = pair->value; -		else -			DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value)); +	/* A non-zero status (i've seen 2) seems to mean the buddy is already  +	 * added and is online */ +	if (status) { +		LOG(("Setting online see packet for info")); +		yahoo_dump_unhandled(pkt); +		YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who, +			YAHOO_STATUS_AVAILABLE, NULL, 0, 0, 0);  	} - -	if(pkt->status==3) -		YAHOO_CALLBACK(ext_yahoo_contact_auth_request)(yid->yd->client_id, me, who, msg);  } -static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_buddydel(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	char *who = NULL; @@ -2690,12 +2201,13 @@ static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_pa  		else if (pair->key == 66)  			unk_66 = strtol(pair->value, NULL, 10);  		else -			DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value)); +			DEBUG_MSG(("unknown key: %d = %s", pair->key, +					pair->value));  	} -	if(!who || !where) +	if (!who || !where)  		return; -	 +  	bud = y_new0(struct yahoo_buddy, 1);  	bud->id = strdup(who);  	bud->group = strdup(where); @@ -2706,7 +2218,7 @@ static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_pa  	FREE(bud->group);  	FREE(bud); -	if(buddy) { +	if (buddy) {  		bud = buddy->data;  		yd->buddies = y_list_remove_link(yd->buddies, buddy);  		y_list_free_1(buddy); @@ -2716,16 +2228,17 @@ static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_pa  		FREE(bud->real_name);  		FREE(bud); -		bud=NULL; +		bud = NULL;  	}  } -static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_ignore(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	char *who = NULL; -	int  status = 0; +	int status = 0;  	char *me = NULL; -	int  un_ignore = 0; +	int un_ignore = 0;  	YList *l;  	for (l = pkt->hash; l; l = l->next) { @@ -2734,27 +2247,27 @@ static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_pack  			who = pair->value;  		if (pair->key == 1)  			me = pair->value; -		if (pair->key == 13) /* 1 == ignore, 2 == unignore */  +		if (pair->key == 13)	/* 1 == ignore, 2 == unignore */  			un_ignore = strtol(pair->value, NULL, 10); -		if (pair->key == 66)  +		if (pair->key == 66)  			status = strtol(pair->value, NULL, 10);  	} -  	/*  	 * status -	 * 	0  - ok -	 * 	2  - already in ignore list, could not add -	 * 	3  - not in ignore list, could not delete -	 * 	12 - is a buddy, could not add +	 *      0  - ok +	 *      2  - already in ignore list, could not add +	 *      3  - not in ignore list, could not delete +	 *      12 - is a buddy, could not add  	 */  /*	if(status)  		YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status); -*/	 +*/  } -static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_voicechat(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	char *who = NULL;  	char *me = NULL; @@ -2769,12 +2282,13 @@ static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_p  		if (pair->key == 5)  			me = pair->value;  		if (pair->key == 13) -			voice_room=pair->value; -		if (pair->key == 57)  -			room=pair->value; +			voice_room = pair->value; +		if (pair->key == 57) +			room = pair->value;  	} -	NOTICE(("got voice chat invite from %s in %s to identity %s", who, room, me)); +	NOTICE(("got voice chat invite from %s in %s to identity %s", who, room, +			me));  	/*   	 * send: s:0 1:me 5:who 57:room 13:1  	 * ????  s:4 5:who 10:99 19:-1615114531 @@ -2786,33 +2300,59 @@ static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_p  	 */  } -static void yahoo_process_ping(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_ping(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	char *errormsg = NULL; -	 +  	YList *l;  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data;  		if (pair->key == 16)  			errormsg = pair->value;  	} -	 +  	NOTICE(("got ping packet")); -	YAHOO_CALLBACK(ext_yahoo_got_ping)(yid->yd->client_id, errormsg); +	YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg);  } -static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) +static void yahoo_process_buddy_change_group(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt) +{ +	YList *l; +	char *me = NULL; +	char *who = NULL; +	char *old_group = NULL; +	char *new_group = NULL; + +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; +		if (pair->key == 1) +			me = pair->value; +		if (pair->key == 7) +			who = pair->value; +		if (pair->key == 224) +			old_group = pair->value; +		if (pair->key == 264) +			new_group = pair->value; +	} + +	YAHOO_CALLBACK(ext_yahoo_got_buddy_change_group) (yid->yd->client_id, +		me, who, old_group, new_group); +} + +static void _yahoo_webcam_get_server_connected(void *fd, int error, void *d)  {  	struct yahoo_input_data *yid = d;  	char *who = yid->wcm->user;  	char *data = NULL;  	char *packet = NULL; -	unsigned char magic_nr[] = {0, 1, 0}; +	unsigned char magic_nr[] = { 0, 1, 0 };  	unsigned char header_len = 8;  	unsigned int len = 0;  	unsigned int pos = 0; -	if(error || fd <= 0) { +	if (error || !fd) {  		FREE(who);  		FREE(yid);  		return; @@ -2820,7 +2360,7 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d)  	yid->fd = fd;  	inputs = y_list_prepend(inputs, yid); -	 +  	/* send initial packet */  	if (who)  		data = strdup("<RVWCFG>"); @@ -2830,8 +2370,7 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d)  	FREE(data);  	/* send data */ -	if (who) -	{ +	if (who) {  		data = strdup("g=");  		data = y_string_append(data, who);  		data = y_string_append(data, "\r\n"); @@ -2850,10 +2389,13 @@ static void _yahoo_webcam_get_server_connected(int fd, int error, void *d)  	FREE(packet);  	FREE(data); -	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); +	yid->read_tag = +		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, +		YAHOO_INPUT_READ, yid);  } -static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char *key) +static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, +	char *key)  {  	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);  	struct yahoo_server_settings *yss = y->yd->server_settings; @@ -2861,34 +2403,36 @@ static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char  	yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;  	yid->yd = y->yd;  	yid->wcm = y_new0(struct yahoo_webcam, 1); -	yid->wcm->user = who?strdup(who):NULL; -	yid->wcm->direction = who?YAHOO_WEBCAM_DOWNLOAD:YAHOO_WEBCAM_UPLOAD; +	yid->wcm->user = who ? strdup(who) : NULL; +	yid->wcm->direction = who ? YAHOO_WEBCAM_DOWNLOAD : YAHOO_WEBCAM_UPLOAD;  	yid->wcm->key = strdup(key); -	YAHOO_CALLBACK(ext_yahoo_connect_async)(yid->yd->client_id, yss->webcam_host, yss->webcam_port,  -			_yahoo_webcam_get_server_connected, yid); +	YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id, +		yss->webcam_host, yss->webcam_port, +		_yahoo_webcam_get_server_connected, yid, 0);  } -static YList *webcam_queue=NULL; -static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static YList *webcam_queue = NULL; +static void yahoo_process_webcam_key(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	char *me = NULL;  	char *key = NULL;  	char *who = NULL;  	YList *l; -	// yahoo_dump_unhandled(pkt); +	yahoo_dump_unhandled(pkt);  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data;  		if (pair->key == 5)  			me = pair->value; -		if (pair->key == 61)  -			key=pair->value; +		if (pair->key == 61) +			key = pair->value;  	}  	l = webcam_queue; -	if(!l) +	if (!l)  		return;  	who = l->data;  	webcam_queue = y_list_remove_link(webcam_queue, webcam_queue); @@ -2897,12 +2441,11 @@ static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_  	FREE(who);  } -static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_packet_process(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service)); -	yahoo_dump_unhandled(pkt); -	switch (pkt->service) -	{ +	switch (pkt->service) {  	case YAHOO_SERVICE_USERSTAT:  	case YAHOO_SERVICE_LOGON:  	case YAHOO_SERVICE_LOGOFF: @@ -2913,7 +2456,7 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	case YAHOO_SERVICE_IDACT:  	case YAHOO_SERVICE_IDDEACT:  	case YAHOO_SERVICE_Y6_STATUS_UPDATE: -	case YAHOO_SERVICE_YMSG15_STATUS: +	case YAHOO_SERVICE_Y8_STATUS:  		yahoo_process_status(yid, pkt);  		break;  	case YAHOO_SERVICE_NOTIFY: @@ -2927,7 +2470,9 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	case YAHOO_SERVICE_NEWMAIL:  		yahoo_process_mail(yid, pkt);  		break; -	case YAHOO_SERVICE_REJECTCONTACT: +	case YAHOO_SERVICE_Y7_AUTHORIZATION: +		yahoo_process_new_contact(yid, pkt); +		break;  	case YAHOO_SERVICE_NEWCONTACT:  		yahoo_process_contact(yid, pkt);  		break; @@ -2962,15 +2507,18 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  		yahoo_process_chat(yid, pkt);  		break;  	case YAHOO_SERVICE_P2PFILEXFER: -	case YAHOO_SERVICE_FILETRANSFER: +	case YAHOO_SERVICE_Y7_FILETRANSFER:  		yahoo_process_filetransfer(yid, pkt);  		break; +	case YAHOO_SERVICE_Y7_FILETRANSFERINFO: +		yahoo_process_filetransferinfo(yid, pkt); +		break; +	case YAHOO_SERVICE_Y7_FILETRANSFERACCEPT: +		yahoo_process_filetransferaccept(yid, pkt); +		break;  	case YAHOO_SERVICE_ADDBUDDY:  		yahoo_process_buddyadd(yid, pkt);  		break; -	case YAHOO_SERVICE_CONTACT_YMSG13: -		yahoo_process_contact_ymsg13(yid,pkt); -		break;  	case YAHOO_SERVICE_REMBUDDY:  		yahoo_process_buddydel(yid, pkt);  		break; @@ -2986,6 +2534,9 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	case YAHOO_SERVICE_PING:  		yahoo_process_ping(yid, pkt);  		break; +	case YAHOO_SERVICE_Y7_CHANGE_GROUP: +		yahoo_process_buddy_change_group(yid, pkt); +		break;  	case YAHOO_SERVICE_IDLE:  	case YAHOO_SERVICE_MAILSTAT:  	case YAHOO_SERVICE_CHATINVITE: @@ -2999,6 +2550,7 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	case YAHOO_SERVICE_CHATLOGON:  	case YAHOO_SERVICE_CHATLOGOFF:  	case YAHOO_SERVICE_CHATMSG: +	case YAHOO_SERVICE_REJECTCONTACT:  	case YAHOO_SERVICE_PEERTOPEER:  		WARNING(("unhandled service 0x%02x", pkt->service));  		yahoo_dump_unhandled(pkt); @@ -3011,9 +2563,10 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  		break;  	case YAHOO_SERVICE_PICTURE_UPLOAD:  		yahoo_process_picture_upload(yid, pkt); -		break;	 -	case YAHOO_SERVICE_YMSG15_BUDDY_LIST:	/* Buddy List */ +		break; +	case YAHOO_SERVICE_Y8_LIST:	/* Buddy List */  		yahoo_process_buddy_list(yid, pkt); +		break;  	default:  		WARNING(("unknown service 0x%02x", pkt->service));  		yahoo_dump_unhandled(pkt); @@ -3021,14 +2574,14 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	}  } -static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) +static struct yahoo_packet *yahoo_getdata(struct yahoo_input_data *yid)  {  	struct yahoo_packet *pkt;  	struct yahoo_data *yd = yid->yd;  	int pos = 0;  	int pktlen; -	if(!yd) +	if (!yd)  		return NULL;  	DEBUG_MSG(("rxlen is %d", yid->rxlen)); @@ -3037,13 +2590,13 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)  		return NULL;  	} -	pos += 4; /* YMSG */ +	pos += 4;		/* YMSG */  	pos += 2;  	pos += 2; -	pktlen = yahoo_get16(yid->rxqueue + pos); pos += 2; -	DEBUG_MSG(("%d bytes to read, rxlen is %d",  -			pktlen, yid->rxlen)); +	pktlen = yahoo_get16(yid->rxqueue + pos); +	pos += 2; +	DEBUG_MSG(("%d bytes to read, rxlen is %d", pktlen, yid->rxlen));  	if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {  		DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen")); @@ -3055,11 +2608,14 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)  	pkt = yahoo_packet_new(0, 0, 0); -	pkt->service = yahoo_get16(yid->rxqueue + pos); pos += 2; -	pkt->status = yahoo_get32(yid->rxqueue + pos); pos += 4; +	pkt->service = yahoo_get16(yid->rxqueue + pos); +	pos += 2; +	pkt->status = yahoo_get32(yid->rxqueue + pos); +	pos += 4;  	DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service, -				pkt->status)); -	pkt->id = yahoo_get32(yid->rxqueue + pos); pos += 4; +			pkt->status)); +	pkt->id = yahoo_get32(yid->rxqueue + pos); +	pos += 4;  	yd->session_id = pkt->id; @@ -3067,12 +2623,13 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)  	yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;  	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); -	if (yid->rxlen>0) { -		unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN  -				+ pktlen, yid->rxlen); +	if (yid->rxlen > 0) { +		unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN +			+ pktlen, yid->rxlen);  		FREE(yid->rxqueue);  		yid->rxqueue = tmp; -		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); +		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, +				yid->rxqueue));  	} else {  		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));  		FREE(yid->rxqueue); @@ -3081,136 +2638,166 @@ static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)  	return pkt;  } -static void yahoo_yab_read(struct yab *yab, unsigned char *d, int len) +static struct yab *yahoo_yab_read(unsigned char *d, int len)  {  	char *st, *en;  	char *data = (char *)d; -	data[len]='\0'; +	struct yab *yab = NULL; + +	data[len] = '\0';  	DEBUG_MSG(("Got yab: %s", data)); -	st = en = strstr(data, "userid=\""); -	if(st) { -		st += strlen("userid=\""); -		en = strchr(st, '"'); *en++ = '\0'; -		yab->id = yahoo_xmldecode(st); +	st = en = strstr(data, "e0=\""); +	if (st) { +		yab = y_new0(struct yab, 1); + +		st += strlen("e0=\""); +		en = strchr(st, '"'); +		*en++ = '\0'; +		yab->email = yahoo_xmldecode(st);  	} -	st = strstr(en, "fname=\""); -	if(st) { -		st += strlen("fname=\""); -		en = strchr(st, '"'); *en++ = '\0'; +	if (!en) +		return NULL; + +	st = strstr(en, "id=\""); +	if (st) { +		st += strlen("id=\""); +		en = strchr(st, '"'); +		*en++ = '\0'; +		yab->yid = atoi(yahoo_xmldecode(st)); +	} + +	st = strstr(en, "fn=\""); +	if (st) { +		st += strlen("fn=\""); +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->fname = yahoo_xmldecode(st);  	} -	st = strstr(en, "lname=\""); -	if(st) { -		st += strlen("lname=\""); -		en = strchr(st, '"'); *en++ = '\0'; +	st = strstr(en, "ln=\""); +	if (st) { +		st += strlen("ln=\""); +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->lname = yahoo_xmldecode(st);  	} -	st = strstr(en, "nname=\""); -	if(st) { -		st += strlen("nname=\""); -		en = strchr(st, '"'); *en++ = '\0'; +	st = strstr(en, "nn=\""); +	if (st) { +		st += strlen("nn=\""); +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->nname = yahoo_xmldecode(st);  	} -	st = strstr(en, "email=\""); -	if(st) { -		st += strlen("email=\""); -		en = strchr(st, '"'); *en++ = '\0'; -		yab->email = yahoo_xmldecode(st); +	st = strstr(en, "yi=\""); +	if (st) { +		st += strlen("yi=\""); +		en = strchr(st, '"'); +		*en++ = '\0'; +		yab->id = yahoo_xmldecode(st);  	}  	st = strstr(en, "hphone=\""); -	if(st) { +	if (st) {  		st += strlen("hphone=\""); -		en = strchr(st, '"'); *en++ = '\0'; +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->hphone = yahoo_xmldecode(st);  	}  	st = strstr(en, "wphone=\""); -	if(st) { +	if (st) {  		st += strlen("wphone=\""); -		en = strchr(st, '"'); *en++ = '\0'; +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->wphone = yahoo_xmldecode(st);  	}  	st = strstr(en, "mphone=\""); -	if(st) { +	if (st) {  		st += strlen("mphone=\""); -		en = strchr(st, '"'); *en++ = '\0'; +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->mphone = yahoo_xmldecode(st);  	}  	st = strstr(en, "dbid=\""); -	if(st) { +	if (st) {  		st += strlen("dbid=\""); -		en = strchr(st, '"'); *en++ = '\0'; +		en = strchr(st, '"'); +		*en++ = '\0';  		yab->dbid = atoi(st);  	} + +	return yab;  } -static struct yab * yahoo_getyab(struct yahoo_input_data *yid) +static struct yab *yahoo_getyab(struct yahoo_input_data *yid)  {  	struct yab *yab = NULL; -	int pos = 0, end=0; +	int pos = 0, end = 0;  	struct yahoo_data *yd = yid->yd; -	if(!yd) -		return NULL; - -	DEBUG_MSG(("rxlen is %d", yid->rxlen)); - -	if(yid->rxlen <= strlen("<record")) +	if (!yd)  		return NULL; -	/* start with <record */ -	while(pos < yid->rxlen-strlen("<record")+1  -			&& memcmp(yid->rxqueue + pos, "<record", strlen("<record"))) -		pos++; - -	if(pos >= yid->rxlen-1) -		return NULL; - -	end = pos+2; -	/* end with /> */ -	while(end < yid->rxlen-strlen("/>")+1 && memcmp(yid->rxqueue + end, "/>", strlen("/>"))) -		end++; - -	if(end >= yid->rxlen-1) -		return NULL; - -	yab = y_new0(struct yab, 1); -	yahoo_yab_read(yab, yid->rxqueue + pos, end+2-pos); -	 - -	yid->rxlen -= end+1; -	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); -	if (yid->rxlen>0) { -		unsigned char *tmp = y_memdup(yid->rxqueue + end + 1, yid->rxlen); -		FREE(yid->rxqueue); -		yid->rxqueue = tmp; -		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); -	} else { -		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); -		FREE(yid->rxqueue); -	} +	do { +		DEBUG_MSG(("rxlen is %d", yid->rxlen)); + +		if (yid->rxlen <= strlen("<ct")) +			return NULL; + +		/* start with <ct */ +		while (pos < yid->rxlen - strlen("<ct") + 1 +			&& memcmp(yid->rxqueue + pos, "<ct", strlen("<ct"))) +			pos++; + +		if (pos >= yid->rxlen - 1) +			return NULL; + +		end = pos + 2; +		/* end with > */ +		while (end < yid->rxlen - strlen(">") +			&& memcmp(yid->rxqueue + end, ">", strlen(">"))) +			end++; + +		if (end >= yid->rxlen - 1) +			return NULL; + +		yab = yahoo_yab_read(yid->rxqueue + pos, end + 2 - pos); + +		yid->rxlen -= end + 1; +		DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, +				yid->rxqueue)); +		if (yid->rxlen > 0) { +			unsigned char *tmp = +				y_memdup(yid->rxqueue + end + 1, yid->rxlen); +			FREE(yid->rxqueue); +			yid->rxqueue = tmp; +			DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, +					yid->rxqueue)); +		} else { +			DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); +			FREE(yid->rxqueue); +		} +	} while (!yab && end < yid->rxlen - 1);  	return yab;  } -static char * yahoo_getwebcam_master(struct yahoo_input_data *yid) +static char *yahoo_getwebcam_master(struct yahoo_input_data *yid)  { -	unsigned int pos=0; -	unsigned int len=0; -	unsigned int status=0; -	char *server=NULL; +	unsigned int pos = 0; +	unsigned int len = 0; +	unsigned int status = 0; +	char *server = NULL;  	struct yahoo_data *yd = yid->yd; -	if(!yid || !yd) +	if (!yid || !yd)  		return NULL;  	DEBUG_MSG(("rxlen is %d", yid->rxlen)); @@ -3222,14 +2809,11 @@ static char * yahoo_getwebcam_master(struct yahoo_input_data *yid)  	/* extract status (0 = ok, 6 = webcam not online) */  	status = yid->rxqueue[pos++]; -	if (status == 0) -	{ -		pos += 2; /* skip next 2 bytes */ -		server =  y_memdup(yid->rxqueue+pos, 16); +	if (status == 0) { +		pos += 2;	/* skip next 2 bytes */ +		server = y_memdup(yid->rxqueue + pos, 16);  		pos += 16; -	} -	else if (status == 6) -	{ +	} else if (status == 6) {  		YAHOO_CALLBACK(ext_yahoo_webcam_closed)  			(yd->client_id, yid->wcm->user, 4);  	} @@ -3238,11 +2822,12 @@ static char * yahoo_getwebcam_master(struct yahoo_input_data *yid)  	yid->rxlen -= len;  	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); -	if (yid->rxlen>0) { +	if (yid->rxlen > 0) {  		unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);  		FREE(yid->rxqueue);  		yid->rxqueue = tmp; -		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); +		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, +				yid->rxqueue));  	} else {  		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));  		FREE(yid->rxqueue); @@ -3253,35 +2838,33 @@ static char * yahoo_getwebcam_master(struct yahoo_input_data *yid)  static int yahoo_get_webcam_data(struct yahoo_input_data *yid)  { -	unsigned char reason=0; -	unsigned int pos=0; -	unsigned int begin=0; -	unsigned int end=0; -	unsigned int closed=0; -	unsigned char header_len=0; +	unsigned char reason = 0; +	unsigned int pos = 0; +	unsigned int begin = 0; +	unsigned int end = 0; +	unsigned int closed = 0; +	unsigned char header_len = 0;  	char *who; -	int connect=0; +	int connect = 0;  	struct yahoo_data *yd = yid->yd; -	if(!yd) +	if (!yd)  		return -1; -	if(!yid->wcm || !yid->wcd || !yid->rxlen) +	if (!yid->wcm || !yid->wcd || !yid->rxlen)  		return -1;  	DEBUG_MSG(("rxlen is %d", yid->rxlen));  	/* if we are not reading part of image then read header */ -	if (!yid->wcd->to_read) -	{ -		header_len=yid->rxqueue[pos++]; -		yid->wcd->packet_type=0; +	if (!yid->wcd->to_read) { +		header_len = yid->rxqueue[pos++]; +		yid->wcd->packet_type = 0;  		if (yid->rxlen < header_len)  			return 0; -		if (header_len >= 8) -		{ +		if (header_len >= 8) {  			reason = yid->rxqueue[pos++];  			/* next 2 bytes should always be 05 00 */  			pos += 2; @@ -3289,8 +2872,7 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid)  			pos += 4;  			yid->wcd->to_read = yid->wcd->data_size;  		} -		if (header_len >= 13) -		{ +		if (header_len >= 13) {  			yid->wcd->packet_type = yid->rxqueue[pos++];  			yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);  			pos += 4; @@ -3302,7 +2884,8 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid)  	begin = pos;  	pos += yid->wcd->to_read; -	if (pos > yid->rxlen) pos = yid->rxlen; +	if (pos > yid->rxlen) +		pos = yid->rxlen;  	/* if it is not an image then make sure we have the whole packet */  	if (yid->wcd->packet_type != 0x02) { @@ -3315,93 +2898,95 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid)  	}  	DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type, -		yid->wcd->data_size)); +			yid->wcd->data_size));  	/* find out what kind of packet we got */ -	switch (yid->wcd->packet_type) -	{ -		case 0x00: -			/* user requests to view webcam (uploading) */ -			if (yid->wcd->data_size && -				yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) { -				end = begin; -				while (end <= yid->rxlen && -					yid->rxqueue[end++] != 13); -				if (end > begin) -				{ -					who = y_memdup(yid->rxqueue + begin, end - begin); -					who[end - begin - 1] = 0; -					YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who + 2, 2); -					FREE(who); -				} +	switch (yid->wcd->packet_type) { +	case 0x00: +		/* user requests to view webcam (uploading) */ +		if (yid->wcd->data_size && +			yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) { +			end = begin; +			while (end <= yid->rxlen && yid->rxqueue[end++] != 13) ; +			if (end > begin) { +				who = y_memdup(yid->rxqueue + begin, +					end - begin); +				who[end - begin - 1] = 0; +				YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd-> +					client_id, who + 2, 2); +				FREE(who);  			} +		} -			if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) { -				/* timestamp/status field */ -				/* 0 = declined viewing permission */ -				/* 1 = accepted viewing permission */ -				if (yid->wcd->timestamp == 0) { -					YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, 3); -				} -			} -			break; -		case 0x01: /* status packets?? */ -			/* timestamp contains status info */ -			/* 00 00 00 01 = we have data?? */ -			break; -		case 0x02: /* image data */ -			YAHOO_CALLBACK(ext_yahoo_got_webcam_image)(yd->client_id,  -					yid->wcm->user, yid->rxqueue + begin, -					yid->wcd->data_size, pos - begin, -					yid->wcd->timestamp); -			break; -		case 0x05: /* response packets when uploading */ -			if (!yid->wcd->data_size) { -				YAHOO_CALLBACK(ext_yahoo_webcam_data_request)(yd->client_id, yid->wcd->timestamp); +		if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) { +			/* timestamp/status field */ +			/* 0 = declined viewing permission */ +			/* 1 = accepted viewing permission */ +			if (yid->wcd->timestamp == 0) { +				YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd-> +					client_id, yid->wcm->user, 3);  			} +		} +		break; +	case 0x01:		/* status packets?? */ +		/* timestamp contains status info */ +		/* 00 00 00 01 = we have data?? */ +		break; +	case 0x02:		/* image data */ +		YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id, +			yid->wcm->user, yid->rxqueue + begin, +			yid->wcd->data_size, pos - begin, yid->wcd->timestamp); +		break; +	case 0x05:		/* response packets when uploading */ +		if (!yid->wcd->data_size) { +			YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd-> +				client_id, yid->wcd->timestamp); +		} +		break; +	case 0x07:		/* connection is closing */ +		switch (reason) { +		case 0x01:	/* user closed connection */ +			closed = 1;  			break; -		case 0x07: /* connection is closing */ -			switch(reason) -			{ -				case 0x01: /* user closed connection */ -					closed = 1; -					break; -				case 0x0F: /* user cancelled permission */ -					closed = 2; -					break; -			} -			YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, closed); -			break; -		case 0x0C: /* user connected */ -		case 0x0D: /* user disconnected */ -			if (yid->wcd->data_size) { -				who = y_memdup(yid->rxqueue + begin, pos - begin + 1); -				who[pos - begin] = 0; -				if (yid->wcd->packet_type == 0x0C) -					connect=1; -				else -					connect=0; -				YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who, connect); -				FREE(who); -			} -			break; -		case 0x13: /* user data */ -			/* i=user_ip (ip of the user we are viewing) */ -			/* j=user_ext_ip (external ip of the user we */ - 			/*                are viewing) */ -			break; -		case 0x17: /* ?? */ +		case 0x0F:	/* user cancelled permission */ +			closed = 2;  			break; +		} +		YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, +			yid->wcm->user, closed); +		break; +	case 0x0C:		/* user connected */ +	case 0x0D:		/* user disconnected */ +		if (yid->wcd->data_size) { +			who = y_memdup(yid->rxqueue + begin, pos - begin + 1); +			who[pos - begin] = 0; +			if (yid->wcd->packet_type == 0x0C) +				connect = 1; +			else +				connect = 0; +			YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, +				who, connect); +			FREE(who); +		} +		break; +	case 0x13:		/* user data */ +		/* i=user_ip (ip of the user we are viewing) */ +		/* j=user_ext_ip (external ip of the user we */ +		/*                are viewing) */ +		break; +	case 0x17:		/* ?? */ +		break;  	}  	yid->wcd->to_read -= pos - begin;  	yid->rxlen -= pos;  	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); -	if (yid->rxlen>0) { +	if (yid->rxlen > 0) {  		unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);  		FREE(yid->rxqueue);  		yid->rxqueue = tmp; -		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); +		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, +				yid->rxqueue));  	} else {  		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));  		FREE(yid->rxqueue); @@ -3414,60 +2999,64 @@ static int yahoo_get_webcam_data(struct yahoo_input_data *yid)  	return 0;  } -int yahoo_write_ready(int id, int fd, void *data) +int yahoo_write_ready(int id, void *fd, void *data)  {  	struct yahoo_input_data *yid = data;  	int len;  	struct data_queue *tx; -	LOG(("write callback: id=%d fd=%d data=%p", id, fd, data)); -	if(!yid || !yid->txqueues || !find_conn_by_id(id)) +	LOG(("write callback: id=%d fd=%p data=%p", id, fd, data)); +	if (!yid || !yid->txqueues)  		return -2; -	 +  	tx = yid->txqueues->data;  	LOG(("writing %d bytes", tx->len));  	len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len)); -	if(len == -1 && errno == EAGAIN) +	if (len == -1 && errno == EAGAIN)  		return 1; -	if(len <= 0) { +	if (len <= 0) {  		int e = errno;  		DEBUG_MSG(("len == %d (<= 0)", len)); -		while(yid->txqueues) { -			YList *l=yid->txqueues; +		while (yid->txqueues) { +			YList *l = yid->txqueues;  			tx = l->data;  			free(tx->queue);  			free(tx); -			yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); +			yid->txqueues = +				y_list_remove_link(yid->txqueues, +				yid->txqueues);  			y_list_free_1(l);  		} -		LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd)); -		YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag); +		LOG(("yahoo_write_ready(%d, %p) len < 0", id, fd)); +		YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag);  		yid->write_tag = 0; -		errno=e; +		errno = e;  		return 0;  	}  	tx->len -= len; -	if(tx->len > 0) { +	if (tx->len > 0) {  		unsigned char *tmp = y_memdup(tx->queue + len, tx->len);  		FREE(tx->queue);  		tx->queue = tmp;  	} else { -		YList *l=yid->txqueues; +		YList *l = yid->txqueues;  		free(tx->queue);  		free(tx); -		yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); +		yid->txqueues = +			y_list_remove_link(yid->txqueues, yid->txqueues);  		y_list_free_1(l);  		/* -		if(!yid->txqueues)  -			LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); -		*/ -		if(!yid->txqueues) { -			LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); -			YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag); +		   if(!yid->txqueues)  +		   LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); +		 */ +		if (!yid->txqueues) { +			LOG(("yahoo_write_ready(%d, %p) !txqueues", id, fd)); +			YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, +				yid->write_tag);  			yid->write_tag = 0;  		}  	} @@ -3475,17 +3064,18 @@ int yahoo_write_ready(int id, int fd, void *data)  	return 1;  } -static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_pager_connection(struct yahoo_input_data *yid, +	int over)  {  	struct yahoo_packet *pkt;  	struct yahoo_data *yd = yid->yd;  	int id = yd->client_id; -	if(over) +	if (over)  		return; -	while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER)  -			&& (pkt = yahoo_getdata(yid)) != NULL) { +	while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) +		&& (pkt = yahoo_getdata(yid)) != NULL) {  		yahoo_packet_process(yid, pkt); @@ -3493,17 +3083,15 @@ static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int ove  	}  } -static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) -{ -} - -static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, +	int over)  { -	if(over) +	if (over)  		return; -	if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "</content>")) { -		YAHOO_CALLBACK(ext_yahoo_chat_cat_xml)(yid->yd->client_id, (char*)yid->rxqueue); +	if (strstr((char *)yid->rxqueue + (yid->rxlen - 20), "</content>")) { +		YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id, +			(char *)yid->rxqueue);  	}  } @@ -3512,111 +3100,143 @@ static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)  	struct yahoo_data *yd = yid->yd;  	struct yab *yab;  	YList *buds; -	int changed=0; +	int changed = 0;  	int id = yd->client_id; +	int yab_used = 0; + +	LOG(("Got data for YAB")); -	if(over) +	if (over)  		return; -	while(find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB)  -			&& (yab = yahoo_getyab(yid)) != NULL) { -		if(!yab->id) +	while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) +		&& (yab = yahoo_getyab(yid)) != NULL) { +		if (!yab->id)  			continue; -		changed=1; -		for(buds = yd->buddies; buds; buds=buds->next) { -			struct yahoo_buddy * bud = buds->data; -			if(!strcmp(bud->id, yab->id)) { + +		changed = 1; +		yab_used = 0; +		for (buds = yd->buddies; buds; buds = buds->next) { +			struct yahoo_buddy *bud = buds->data; +			if (!strcmp(bud->id, yab->id)) { +				yab_used = 1;  				bud->yab_entry = yab; -				if(yab->nname) { +				if (yab->nname) {  					bud->real_name = strdup(yab->nname); -				} else if(yab->fname && yab->lname) { -					bud->real_name = y_new0(char,  -							strlen(yab->fname)+ -							strlen(yab->lname)+2 -							); +				} else if (yab->fname && yab->lname) { +					bud->real_name = y_new0(char, +						strlen(yab->fname) + +						strlen(yab->lname) + 2);  					sprintf(bud->real_name, "%s %s", -							yab->fname, yab->lname); -				} else if(yab->fname) { +						yab->fname, yab->lname); +				} else if (yab->fname) {  					bud->real_name = strdup(yab->fname);  				} -				break; /* for */ +				break;	/* for */  			}  		} + +		if (!yab_used) { +			FREE(yab->fname); +			FREE(yab->lname); +			FREE(yab->nname); +			FREE(yab->id); +			FREE(yab->email); +			FREE(yab->hphone); +			FREE(yab->wphone); +			FREE(yab->mphone); +			FREE(yab); +		} +  	} -	if(changed) -		YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies); +	if (changed) +		YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, +			yd->buddies);  } -static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_search_connection(struct yahoo_input_data *yid, +	int over)  { -	struct yahoo_found_contact *yct=NULL; +	struct yahoo_found_contact *yct = NULL;  	char *p = (char *)yid->rxqueue, *np, *cp;  	int k, n; -	int start=0, found=0, total=0; -	YList *contacts=NULL; -	struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER); +	int start = 0, found = 0, total = 0; +	YList *contacts = NULL; +	struct yahoo_input_data *pyid = +		find_input_by_id_and_type(yid->yd->client_id, +		YAHOO_CONNECTION_PAGER); -	if(!over || !pyid) +	if (!over || !pyid)  		return; -	if(p && (p=strstr(p, "\r\n\r\n"))) { +	if (p && (p = strstr(p, "\r\n\r\n"))) {  		p += 4; -		for(k = 0; (p = strchr(p, 4)) && (k < 4); k++) { +		for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) {  			p++;  			n = atoi(p); -			switch(k) { -				case 0: found = pyid->ys->lsearch_nfound = n; break; -				case 2: start = pyid->ys->lsearch_nstart = n; break; -				case 3: total = pyid->ys->lsearch_ntotal = n; break; +			switch (k) { +			case 0: +				found = pyid->ys->lsearch_nfound = n; +				break; +			case 2: +				start = pyid->ys->lsearch_nstart = n; +				break; +			case 3: +				total = pyid->ys->lsearch_ntotal = n; +				break;  			}  		} -		if(p) +		if (p)  			p++; -		k=0; -		while(p && *p) { +		k = 0; +		while (p && *p) {  			cp = p;  			np = strchr(p, 4); -			if(!np) +			if (!np)  				break;  			*np = 0; -			p = np+1; - -			switch(k++) { -				case 1: -					if(strlen(cp) > 2 && y_list_length(contacts) < total) { -						yct = y_new0(struct yahoo_found_contact, 1); -						contacts = y_list_append(contacts, yct); -						yct->id = cp+2; -					} else { -						*p = 0; -					} -					break; -				case 2:  -					yct->online = !strcmp(cp, "2") ? 1 : 0; -					break; -				case 3:  -					yct->gender = cp; -					break; -				case 4:  -					yct->age = atoi(cp); -					break; -				case 5:  -					if(strcmp(cp, "5") != 0) -						yct->location = cp; -					k = 0; -					break; +			p = np + 1; + +			switch (k++) { +			case 1: +				if (strlen(cp) > 2 +					&& y_list_length(contacts) < total) { +					yct = y_new0(struct yahoo_found_contact, +						1); +					contacts = y_list_append(contacts, yct); +					yct->id = cp + 2; +				} else { +					*p = 0; +				} +				break; +			case 2: +				yct->online = !strcmp(cp, "2") ? 1 : 0; +				break; +			case 3: +				yct->gender = cp; +				break; +			case 4: +				yct->age = atoi(cp); +				break; +			case 5: +				/* not worth the context switch for strcmp */ +				if (cp[0] != '\005' || cp[1] != '\000') +					yct->location = cp; +				k = 0; +				break;  			}  		}  	} -	YAHOO_CALLBACK(ext_yahoo_got_search_result)(yid->yd->client_id, found, start, total, contacts); +	YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found, +		start, total, contacts); -	while(contacts) { +	while (contacts) {  		YList *node = contacts;  		contacts = y_list_remove_link(contacts, node);  		free(node->data); @@ -3624,20 +3244,20 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov  	}  } -static void _yahoo_webcam_connected(int fd, int error, void *d) +static void _yahoo_webcam_connected(void *fd, int error, void *d)  {  	struct yahoo_input_data *yid = d;  	struct yahoo_webcam *wcm = yid->wcm;  	struct yahoo_data *yd = yid->yd;  	char conn_type[100]; -	char *data=NULL; -	char *packet=NULL; -	unsigned char magic_nr[] = {1, 0, 0, 0, 1}; -	unsigned header_len=0; -	unsigned int len=0; -	unsigned int pos=0; - -	if(error || fd <= 0) { +	char *data = NULL; +	char *packet = NULL; +	unsigned char magic_nr[] = { 1, 0, 0, 0, 1 }; +	unsigned header_len = 0; +	unsigned int len = 0; +	unsigned int pos = 0; + +	if (error || !fd) {  		FREE(yid);  		return;  	} @@ -3647,74 +3267,70 @@ static void _yahoo_webcam_connected(int fd, int error, void *d)  	LOG(("Connected"));  	/* send initial packet */ -	switch (wcm->direction) -	{ -		case YAHOO_WEBCAM_DOWNLOAD: -			data = strdup("<REQIMG>"); -			break; -		case YAHOO_WEBCAM_UPLOAD:	 -			data = strdup("<SNDIMG>"); -			break; -		default: -			return; +	switch (wcm->direction) { +	case YAHOO_WEBCAM_DOWNLOAD: +		data = strdup("<REQIMG>"); +		break; +	case YAHOO_WEBCAM_UPLOAD: +		data = strdup("<SNDIMG>"); +		break; +	default: +		return;  	}  	yahoo_add_to_send_queue(yid, data, strlen(data));  	FREE(data);  	/* send data */ -	switch (wcm->direction) -	{ -		case YAHOO_WEBCAM_DOWNLOAD: -			header_len = 8; -			data = strdup("a=2\r\nc=us\r\ne=21\r\nu="); -			data = y_string_append(data, yd->user); -			data = y_string_append(data, "\r\nt="); -			data = y_string_append(data, wcm->key); -			data = y_string_append(data, "\r\ni="); -			data = y_string_append(data, wcm->my_ip); -			data = y_string_append(data, "\r\ng="); -			data = y_string_append(data, wcm->user); -			data = y_string_append(data, "\r\no=w-2-5-1\r\np="); -			snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); -			data = y_string_append(data, conn_type); -			data = y_string_append(data, "\r\n"); -			break; -		case YAHOO_WEBCAM_UPLOAD: -			header_len = 13; -			data = strdup("a=2\r\nc=us\r\nu="); -			data = y_string_append(data, yd->user); -			data = y_string_append(data, "\r\nt="); -			data = y_string_append(data, wcm->key); -			data = y_string_append(data, "\r\ni="); -			data = y_string_append(data, wcm->my_ip); -			data = y_string_append(data, "\r\no=w-2-5-1\r\np="); -			snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); -			data = y_string_append(data, conn_type); -			data = y_string_append(data, "\r\nb="); -			data = y_string_append(data, wcm->description); -			data = y_string_append(data, "\r\n"); -			break; +	switch (wcm->direction) { +	case YAHOO_WEBCAM_DOWNLOAD: +		header_len = 8; +		data = strdup("a=2\r\nc=us\r\ne=21\r\nu="); +		data = y_string_append(data, yd->user); +		data = y_string_append(data, "\r\nt="); +		data = y_string_append(data, wcm->key); +		data = y_string_append(data, "\r\ni="); +		data = y_string_append(data, wcm->my_ip); +		data = y_string_append(data, "\r\ng="); +		data = y_string_append(data, wcm->user); +		data = y_string_append(data, "\r\no=w-2-5-1\r\np="); +		snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); +		data = y_string_append(data, conn_type); +		data = y_string_append(data, "\r\n"); +		break; +	case YAHOO_WEBCAM_UPLOAD: +		header_len = 13; +		data = strdup("a=2\r\nc=us\r\nu="); +		data = y_string_append(data, yd->user); +		data = y_string_append(data, "\r\nt="); +		data = y_string_append(data, wcm->key); +		data = y_string_append(data, "\r\ni="); +		data = y_string_append(data, wcm->my_ip); +		data = y_string_append(data, "\r\no=w-2-5-1\r\np="); +		snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); +		data = y_string_append(data, conn_type); +		data = y_string_append(data, "\r\nb="); +		data = y_string_append(data, wcm->description); +		data = y_string_append(data, "\r\n"); +		break;  	}  	len = strlen(data);  	packet = y_new0(char, header_len + len);  	packet[pos++] = header_len;  	packet[pos++] = 0; -	switch (wcm->direction) -	{ -		case YAHOO_WEBCAM_DOWNLOAD: -			packet[pos++] = 1; -			packet[pos++] = 0; -			break; -		case YAHOO_WEBCAM_UPLOAD: -			packet[pos++] = 5; -			packet[pos++] = 0; -			break; +	switch (wcm->direction) { +	case YAHOO_WEBCAM_DOWNLOAD: +		packet[pos++] = 1; +		packet[pos++] = 0; +		break; +	case YAHOO_WEBCAM_UPLOAD: +		packet[pos++] = 5; +		packet[pos++] = 0; +		break;  	}  	pos += yahoo_put32(packet + pos, len); -	if (wcm->direction == YAHOO_WEBCAM_UPLOAD) -	{ +	if (wcm->direction == YAHOO_WEBCAM_UPLOAD) {  		memcpy(packet + pos, magic_nr, sizeof(magic_nr));  		pos += sizeof(magic_nr);  	} @@ -3723,7 +3339,9 @@ static void _yahoo_webcam_connected(int fd, int error, void *d)  	FREE(packet);  	FREE(data); -	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); +	yid->read_tag = +		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, +		yid->fd, YAHOO_INPUT_READ, yid);  }  static void yahoo_webcam_connect(struct yahoo_input_data *y) @@ -3748,23 +3366,23 @@ static void yahoo_webcam_connect(struct yahoo_input_data *y)  	yid->wcd = y_new0(struct yahoo_webcam_data, 1);  	LOG(("Connecting to: %s:%d", wcm->server, wcm->port)); -	YAHOO_CALLBACK(ext_yahoo_connect_async)(y->yd->client_id, wcm->server, wcm->port, -			_yahoo_webcam_connected, yid); +	YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server, +		wcm->port, _yahoo_webcam_connected, yid, 0);  } -static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, +	int over)  { -	char* server; +	char *server;  	struct yahoo_server_settings *yss; -	if(over) +	if (over)  		return;  	server = yahoo_getwebcam_master(yid); -	if (server) -	{ +	if (server) {  		yss = yid->yd->server_settings;  		yid->wcm->server = strdup(server);  		yid->wcm->port = yss->webcam_port; @@ -3777,72 +3395,74 @@ static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid,  	}  } -static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int over) +static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, +	int over)  {  	int id = yid->yd->client_id; -	int fd = yid->fd; +	void *fd = yid->fd; -	if(over) +	if (over)  		return;  	/* as long as we still have packets available keep processing them */ -	while (find_input_by_id_and_fd(id, fd)  -			&& yahoo_get_webcam_data(yid) == 1); -} - -static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) = { -	yahoo_process_pager_connection, -	yahoo_process_ft_connection, -	yahoo_process_yab_connection, -	yahoo_process_webcam_master_connection, -	yahoo_process_webcam_connection, -	yahoo_process_chatcat_connection, -	yahoo_process_search_connection, -}; +	while (find_input_by_id_and_fd(id, fd) +		&& yahoo_get_webcam_data(yid) == 1) ; +} + +static void (*yahoo_process_connection[]) (struct yahoo_input_data *, +	int over) = { +yahoo_process_pager_connection, yahoo_process_ft_connection, +		yahoo_process_yab_connection, +		yahoo_process_webcam_master_connection, +		yahoo_process_webcam_connection, +		yahoo_process_chatcat_connection, +		yahoo_process_search_connection}; -int yahoo_read_ready(int id, int fd, void *data) +int yahoo_read_ready(int id, void *fd, void *data)  {  	struct yahoo_input_data *yid = data;  	char buf[1024];  	int len; -	LOG(("read callback: id=%d fd=%d data=%p", id, fd, data)); -	if(!yid) +	LOG(("read callback: id=%d fd=%p data=%p", id, fd, data)); +	if (!yid)  		return -2; -	  	do { -		len = read(fd, buf, sizeof(buf)); -	} while(len == -1 && errno == EINTR); +		len = YAHOO_CALLBACK(ext_yahoo_read) (fd, buf, sizeof(buf)); +	} while (len == -1 && errno == EINTR); -	if(len == -1 && (errno == EAGAIN||errno == EINTR))	/* we'll try again later */ +	if (len == -1 && (errno == EAGAIN || errno == EINTR))	/* we'll try again later */  		return 1;  	if (len <= 0) {  		int e = errno;  		DEBUG_MSG(("len == %d (<= 0)", len)); -		if(yid->type == YAHOO_CONNECTION_PAGER) { -			YAHOO_CALLBACK(ext_yahoo_error)(yid->yd->client_id, "Connection closed by server", 1, E_CONNECTION); +		if (yid->type == YAHOO_CONNECTION_PAGER) { +			YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd-> +				client_id, YAHOO_LOGIN_SOCK, NULL);  		} -		yahoo_process_connection[yid->type](yid, 1); +		yahoo_process_connection[yid->type] (yid, 1);  		yahoo_input_close(yid);  		/* no need to return an error, because we've already fixed it */ -		if(len == 0) +		if (len == 0)  			return 1; -		errno=e; +		errno = e;  		LOG(("read error: %s", strerror(errno)));  		return -1;  	} -	yid->rxqueue = y_renew(unsigned char, yid->rxqueue, len + yid->rxlen); +	yid->rxqueue = +		y_renew(unsigned char, yid->rxqueue, len + yid->rxlen + 1);  	memcpy(yid->rxqueue + yid->rxlen, buf, len);  	yid->rxlen += len; +	yid->rxqueue[yid->rxlen] = 0; -	yahoo_process_connection[yid->type](yid, 0); +	yahoo_process_connection[yid->type] (yid, 0);  	return len;  } @@ -3854,7 +3474,7 @@ int yahoo_init_with_attributes(const char *username, const char *password, ...)  	yd = y_new0(struct yahoo_data, 1); -	if(!yd) +	if (!yd)  		return 0;  	yd->user = strdup(username); @@ -3879,13 +3499,7 @@ int yahoo_init(const char *username, const char *password)  	return yahoo_init_with_attributes(username, password, NULL);  } -struct connect_callback_data { -	struct yahoo_data *yd; -	int tag; -	int i; -}; - -static void yahoo_connected(int fd, int error, void *data) +static void yahoo_connected(void *fd, int error, void *data)  {  	struct connect_callback_data *ccd = data;  	struct yahoo_data *yd = ccd->yd; @@ -3893,114 +3507,112 @@ static void yahoo_connected(int fd, int error, void *data)  	struct yahoo_input_data *yid;  	struct yahoo_server_settings *yss = yd->server_settings; -	if(error) { -		if(fallback_ports[ccd->i]) { -			int tag; -			yss->pager_port = fallback_ports[ccd->i++]; -			tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, -					yss->pager_port, yahoo_connected, ccd); +	if (error) { +		int tag; +		if (fallback_ports[ccd->i]) { +			char *host = yss->pager_host; + +			if (!host) +				host = yss->pager_host_list[ccd->server_i]; -			if(tag > 0) -				ccd->tag=tag; +			yss->pager_port = fallback_ports[ccd->i++]; +			tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd-> +				client_id, host, yss->pager_port, +				yahoo_connected, ccd, 0); + +			if (tag > 0) +				ccd->tag = tag; +		} else if (yss->pager_host_list +				&& yss->pager_host_list[ccd->server_i]) { + +			/* Get back to the default port */ +			yss->pager_port = pager_port; +			ccd->server_i++; +			LOG(("Fallback: Connecting to %s:%d", yss->pager_host_list[ccd->server_i], yss->pager_port)); + +			ccd->i = 0; +			tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, +				yss->pager_host_list[ccd->server_i], yss->pager_port, +				yahoo_connected, ccd, 0);  		} else {  			FREE(ccd); -			YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL); +			YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +				YAHOO_LOGIN_SOCK, NULL);  		}  		return;  	}  	FREE(ccd); -	/* fd < 0 && error == 0 means connect was cancelled */ -	if(fd < 0) +	/* fd == NULL && error == 0 means connect was cancelled */ +	if (!fd)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	NOTICE(("Sending initial packet"));  	yahoo_packet_hash(pkt, 1, yd->user); -	yid = y_new0(struct yahoo_input_data, 1); -	yid->yd = yd; +	yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);  	yid->fd = fd; -	inputs = y_list_prepend(inputs, yid);  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt); -	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); +	yid->read_tag = +		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, +		yid->fd, YAHOO_INPUT_READ, yid);  } -void yahoo_login(int id, int initial) +void *yahoo_get_fd(int id)  { -	struct yahoo_data *yd = find_conn_by_id(id); -	struct connect_callback_data *ccd; -	struct yahoo_server_settings *yss; -	int tag; - -	if(!yd) -		return; - -	yss = yd->server_settings; - -	yd->initial_status = initial; - -	ccd = y_new0(struct connect_callback_data, 1); -	ccd->yd = yd; -	tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, yss->pager_port,  -			yahoo_connected, ccd); - -	/* -	 * if tag <= 0, then callback has already been called -	 * so ccd will have been freed -	 */ -	if(tag > 0) -		ccd->tag = tag; -	else if(tag < 0) -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL); -} - - -int yahoo_get_fd(int id) -{ -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); -	if(!yid) +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	if (!yid)  		return 0;  	else  		return yid->fd;  } -void yahoo_send_im(int id, const char *from, const char *who, const char *what, int utf8, int picture) +void yahoo_send_buzz(int id, const char *from, const char *who)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	yahoo_send_im(id, from, who, "<ding>", 1, 0); +} + +void yahoo_send_im(int id, const char *from, const char *who, const char *what, +	int utf8, int picture) +{ +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_packet *pkt = NULL;  	struct yahoo_data *yd;  	char pic_str[10]; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, +		yd->session_id);  	snprintf(pic_str, sizeof(pic_str), "%d", picture); -	 -	if(from && strcmp(from, yd->user)) + +	if (from && strcmp(from, yd->user))  		yahoo_packet_hash(pkt, 0, yd->user); -	yahoo_packet_hash(pkt, 1, from?from:yd->user); +	yahoo_packet_hash(pkt, 1, from ? from : yd->user);  	yahoo_packet_hash(pkt, 5, who);  	yahoo_packet_hash(pkt, 14, what); -	if(utf8) +	if (utf8)  		yahoo_packet_hash(pkt, 97, "1");  	yahoo_packet_hash(pkt, 63, ";0");	/* imvironment name; or ;0 */  	yahoo_packet_hash(pkt, 64, "0");  	yahoo_packet_hash(pkt, 206, pic_str); -  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt); @@ -4008,17 +3620,19 @@ void yahoo_send_im(int id, const char *from, const char *who, const char *what,  void yahoo_send_typing(int id, const char *from, const char *who, int typ)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY, +		yd->session_id);  	yahoo_packet_hash(pkt, 5, who); -	yahoo_packet_hash(pkt, 1, from?from:yd->user); +	yahoo_packet_hash(pkt, 1, from ? from : yd->user);  	yahoo_packet_hash(pkt, 14, " ");  	yahoo_packet_hash(pkt, 13, typ ? "1" : "0");  	yahoo_packet_hash(pkt, 49, "TYPING"); @@ -4030,22 +3644,25 @@ void yahoo_send_typing(int id, const char *from, const char *who, int typ)  void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL;  	int old_status;  	char s[4]; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; +  	old_status = yd->current_status;  	yd->current_status = state;  	/* Thank you libpurple :) */  	if (yd->current_status == YAHOO_STATUS_INVISIBLE) { -		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0); +		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, +			YAHOO_STATUS_AVAILABLE, 0);  		yahoo_packet_hash(pkt, 13, "2");  		yahoo_send_packet(yid, pkt, 0);  		yahoo_packet_free(pkt); @@ -4053,7 +3670,8 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)  		return;  	} -	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, yd->current_status, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, +		yd->current_status, yd->session_id);  	snprintf(s, sizeof(s), "%d", yd->current_status);  	yahoo_packet_hash(pkt, 10, s);  	yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : ""); @@ -4061,8 +3679,9 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt); -	if(old_status == YAHOO_STATUS_INVISIBLE) { -		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0); +	if (old_status == YAHOO_STATUS_INVISIBLE) { +		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, +			YAHOO_STATUS_AVAILABLE, 0);  		yahoo_packet_hash(pkt, 13, "1");  		yahoo_send_packet(yid, pkt, 0);  		yahoo_packet_free(pkt); @@ -4071,21 +3690,23 @@ void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)  void yahoo_logoff(int id)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd;  	LOG(("yahoo_logoff: current status: %d", yd->current_status)); -	if(yd->current_status != -1 && 0) { +	if (yd->current_status != -1 && 0) {  		/* Meh. Don't send this. The event handlers are not going to  		   get to do this so it'll just leak memory. And the TCP  		   connection reset will hopefully be clear enough. */ -		pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); +		pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, +			YPACKET_STATUS_DEFAULT, yd->session_id);  		yd->current_status = -1;  		if (pkt) { @@ -4094,22 +3715,25 @@ void yahoo_logoff(int id)  		}  	} -	do { +/*	do {  		yahoo_input_close(yid); -	} while((yid = find_input_by_id(id))); +	} while((yid = find_input_by_id(id)));*/ +  }  void yahoo_get_list(int id)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	if (pkt) {  		yahoo_send_packet(yid, pkt, 0); @@ -4117,139 +3741,136 @@ void yahoo_get_list(int id)  	}  } -static void _yahoo_http_connected(int id, int fd, int error, void *data) +static void _yahoo_http_connected(int id, void *fd, int error, void *data)  {  	struct yahoo_input_data *yid = data; -	if(fd <= 0) { +	if (fd == NULL || error) {  		inputs = y_list_remove(inputs, yid);  		FREE(yid);  		return;  	}  	yid->fd = fd; -	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); +	yid->read_tag = +		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, +		YAHOO_INPUT_READ, yid);  } +/* FIXME Get address book from address.yahoo.com instead */  void yahoo_get_yab(int id)  {  	struct yahoo_data *yd = find_conn_by_id(id);  	struct yahoo_input_data *yid;  	char url[1024]; -	char buff[1024]; +	char buff[2048]; -	if(!yd) +	if (!yd)  		return;  	yid = y_new0(struct yahoo_input_data, 1);  	yid->yd = yd;  	yid->type = YAHOO_CONNECTION_YAB; -	snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?ab2=0"); +	LOG(("Sending request for Address Book")); + +	snprintf(url, 1024, +		"http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us" +		"&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252"); -	snprintf(buff, sizeof(buff), "Y=%s; T=%s", -			yd->cookie_y, yd->cookie_t); +	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);  	inputs = y_list_prepend(inputs, yid); -	yahoo_http_get(yid->yd->client_id, url, buff,  -			_yahoo_http_connected, yid); +	yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, +		_yahoo_http_connected, yid); +} + +struct yahoo_post_data { +	struct yahoo_input_data *yid; +	char *data; +}; + +static void _yahoo_http_post_connected(int id, void *fd, int error, void *data) +{ +	struct yahoo_post_data *yad = data; +	struct yahoo_input_data *yid = yad->yid; +	char *buff = yad->data; + +	if (!fd) { +		inputs = y_list_remove(inputs, yid); +		FREE(yid); +		return; +	} + +	YAHOO_CALLBACK(ext_yahoo_write) (fd, buff, strlen(buff)); + +	yid->fd = fd; +	yid->read_tag = +		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, +		YAHOO_INPUT_READ, yid); + +	FREE(buff); +	FREE(yad);  } -void yahoo_set_yab(int id, struct yab * yab) +/* FIXME This is also likely affected */ +void yahoo_set_yab(int id, struct yab *yab)  { +	struct yahoo_post_data *yad = y_new0(struct yahoo_post_data, 1);  	struct yahoo_data *yd = find_conn_by_id(id);  	struct yahoo_input_data *yid;  	char url[1024];  	char buff[1024]; -	char *temp; -	int size = sizeof(url)-1; +	char post[1024]; +	int size = 0; -	if(!yd) +	if (!yd)  		return;  	yid = y_new0(struct yahoo_input_data, 1);  	yid->type = YAHOO_CONNECTION_YAB;  	yid->yd = yd; -	strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size); - -	if(yab->dbid) { -		/* change existing yab */ -		char tmp[32]; -		strncat(url, "&ee=1&ow=1&id=", size - strlen(url)); -		snprintf(tmp, sizeof(tmp), "%d", yab->dbid); -		strncat(url, tmp, size - strlen(url)); -	} - -	if(yab->fname) { -		strncat(url, "&fn=", size - strlen(url)); -		temp = yahoo_urlencode(yab->fname); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	if(yab->lname) { -		strncat(url, "&ln=", size - strlen(url)); -		temp = yahoo_urlencode(yab->lname); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	strncat(url, "&yid=", size - strlen(url)); -	temp = yahoo_urlencode(yab->id); -	strncat(url, temp, size - strlen(url)); -	free(temp); -	if(yab->nname) { -		strncat(url, "&nn=", size - strlen(url)); -		temp = yahoo_urlencode(yab->nname); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	if(yab->email) { -		strncat(url, "&e=", size - strlen(url)); -		temp = yahoo_urlencode(yab->email); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	if(yab->hphone) { -		strncat(url, "&hp=", size - strlen(url)); -		temp = yahoo_urlencode(yab->hphone); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	if(yab->wphone) { -		strncat(url, "&wp=", size - strlen(url)); -		temp = yahoo_urlencode(yab->wphone); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	if(yab->mphone) { -		strncat(url, "&mp=", size - strlen(url)); -		temp = yahoo_urlencode(yab->mphone); -		strncat(url, temp, size - strlen(url)); -		free(temp); -	} -	strncat(url, "&pp=0", size - strlen(url)); - -	snprintf(buff, sizeof(buff), "Y=%s; T=%s", -			yd->cookie_y, yd->cookie_t); +	if(yab->yid) +		size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +			"<ab k=\"%s\" cc=\"%d\">" +			"<ct id=\"%d\" e=\"1\" yi=\"%s\" nn=\"%s\" />" +			"</ab>", yd->user, 9, yab->yid,	/* Don't know why */ +			yab->id, yab->nname?yab->nname:""); +	else +		size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +			"<ab k=\"%s\" cc=\"%d\">" +			"<ct a=\"1\" yi=\"%s\" nn=\"%s\" />" +			"</ab>", yd->user, 1,	/* Don't know why */ +			yab->id, yab->nname?yab->nname:""); + +	yad->yid = yid; +	yad->data = strdup(post); + +	strcpy(url, "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us" +		"&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252"); + +	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);  	inputs = y_list_prepend(inputs, yid); -	yahoo_http_get(yid->yd->client_id, url, buff,  -			_yahoo_http_connected, yid); +	yahoo_http_post(yid->yd->client_id, url, buff, size, +		_yahoo_http_post_connected, yad);  } -void yahoo_set_identity_status(int id, const char * identity, int active) +void yahoo_set_identity_status(int id, const char *identity, int active)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(active?YAHOO_SERVICE_IDACT:YAHOO_SERVICE_IDDEACT, -			YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(active ? YAHOO_SERVICE_IDACT : +		YAHOO_SERVICE_IDDEACT, YPACKET_STATUS_DEFAULT, yd->session_id);  	yahoo_packet_hash(pkt, 3, identity);  	if (pkt) {  		yahoo_send_packet(yid, pkt, 0); @@ -4259,15 +3880,17 @@ void yahoo_set_identity_status(int id, const char * identity, int active)  void yahoo_refresh(int id)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	if (pkt) {  		yahoo_send_packet(yid, pkt, 0);  		yahoo_packet_free(pkt); @@ -4276,54 +3899,59 @@ void yahoo_refresh(int id)  void yahoo_keepalive(int id)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd; -	struct yahoo_packet *pkt=NULL; -	if(!yid) +	struct yahoo_packet *pkt = NULL; +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  } -void yahoo_chat_keepalive (int id) +void yahoo_chat_keepalive(int id)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type (id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL;  	if (!yid) -	    return; +		return;  	yd = yid->yd; -	pkt = yahoo_packet_new (YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id); -	yahoo_send_packet (yid, pkt, 0); -	yahoo_packet_free (pkt); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YPACKET_STATUS_DEFAULT, +		yd->session_id); +	yahoo_send_packet(yid, pkt, 0); +	yahoo_packet_free(pkt);  } -void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg) +void yahoo_add_buddy(int id, const char *who, const char *group, +	const char *msg)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd;  	if (!yd->logged_in)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, yd->session_id); - -	if (msg != NULL) /* add message/request "it's me add me" */ +	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, +		yd->session_id); +	if (msg != NULL)	/* add message/request "it's me add me" */  		yahoo_packet_hash(pkt, 14, msg);  	else -		yahoo_packet_hash(pkt,14,""); - +		yahoo_packet_hash(pkt, 14, "");  	yahoo_packet_hash(pkt, 65, group);  	yahoo_packet_hash(pkt, 97, "1");  	yahoo_packet_hash(pkt, 1, yd->user); @@ -4333,23 +3961,23 @@ void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg  	yahoo_packet_hash(pkt, 334, "0");  	yahoo_packet_hash(pkt, 301, "319");  	yahoo_packet_hash(pkt, 303, "319"); - -  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  }  void yahoo_remove_buddy(int id, const char *who, const char *group)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 7, who); @@ -4358,151 +3986,129 @@ void yahoo_remove_buddy(int id, const char *who, const char *group)  	yahoo_packet_free(pkt);  } -void yahoo_accept_buddy_ymsg13(int id,const char* me,const char* who){ -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); -	struct yahoo_data *yd; - -	if(!yid) -		return; -	yd = yid->yd; - -	struct yahoo_packet* pkt=NULL; -	pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0); - -	yahoo_packet_hash(pkt,1,me ?: yd->user);	 -	yahoo_packet_hash(pkt,5,who); -	yahoo_packet_hash(pkt,13,"1"); -	yahoo_packet_hash(pkt,334,"0"); -	yahoo_send_packet(yid, pkt, 0); -	yahoo_packet_free(pkt); -} - -void yahoo_reject_buddy_ymsg13(int id,const char* me,const char* who,const char* msg){ -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); -	struct yahoo_data *yd; - -	if(!yid) -		return; -	yd = yid->yd; - -	struct yahoo_packet* pkt=NULL; -	pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0); - -	yahoo_packet_hash(pkt,1,me ?: yd->user);	 -	yahoo_packet_hash(pkt,5,who); -//	yahoo_packet_hash(pkt,241,YAHOO_PROTO_VER); -	yahoo_packet_hash(pkt,13,"2"); -	yahoo_packet_hash(pkt,334,"0"); -	yahoo_packet_hash(pkt,97,"1"); -	yahoo_packet_hash(pkt,14,msg?:""); - -	yahoo_send_packet(yid, pkt, 0); -	yahoo_packet_free(pkt); - -} - -void yahoo_reject_buddy(int id, const char *who, const char *msg) +void yahoo_confirm_buddy(int id, const char *who, int reject, const char *msg)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd;  	if (!yd->logged_in)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_AUTHORIZATION, +		YPACKET_STATUS_DEFAULT, yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user); -	yahoo_packet_hash(pkt, 7, who); -	yahoo_packet_hash(pkt, 14, msg); +	yahoo_packet_hash(pkt, 5, who); +	if (reject) +		yahoo_packet_hash(pkt, 13, "2"); +	else { +		yahoo_packet_hash(pkt, 241, "0"); +		yahoo_packet_hash(pkt, 13, "1"); +	} + +	yahoo_packet_hash(pkt, 334, "0"); + +	if (reject) { +		yahoo_packet_hash(pkt, 14, msg ? msg : ""); +		yahoo_packet_hash(pkt, 97, "1"); +	} +  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  }  void yahoo_ignore_buddy(int id, const char *who, int unignore)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd;  	if (!yd->logged_in)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, +		YPACKET_STATUS_DEFAULT, yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 7, who); -	yahoo_packet_hash(pkt, 13, unignore?"2":"1"); +	yahoo_packet_hash(pkt, 13, unignore ? "2" : "1");  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  }  void yahoo_stealth_buddy(int id, const char *who, int unstealth)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd;  	if (!yd->logged_in)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH_PERM, +		YPACKET_STATUS_DEFAULT, yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 7, who); -	yahoo_packet_hash(pkt, 31, unstealth?"2":"1"); +	yahoo_packet_hash(pkt, 31, unstealth ? "2" : "1");  	yahoo_packet_hash(pkt, 13, "2");  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  } -void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group) +void yahoo_change_buddy_group(int id, const char *who, const char *old_group, +	const char *new_group)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_CHANGE_GROUP, +		YPACKET_STATUS_DEFAULT, yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user); +	yahoo_packet_hash(pkt, 302, "240"); +	yahoo_packet_hash(pkt, 300, "240");  	yahoo_packet_hash(pkt, 7, who); -	yahoo_packet_hash(pkt, 65, new_group); -	yahoo_packet_hash(pkt, 14, " "); - -	yahoo_send_packet(yid, pkt, 0); -	yahoo_packet_free(pkt); +	yahoo_packet_hash(pkt, 224, old_group); +	yahoo_packet_hash(pkt, 264, new_group); +	yahoo_packet_hash(pkt, 301, "240"); +	yahoo_packet_hash(pkt, 303, "240"); -	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); -	yahoo_packet_hash(pkt, 1, yd->user); -	yahoo_packet_hash(pkt, 7, who); -	yahoo_packet_hash(pkt, 65, old_group);  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  }  void yahoo_group_rename(int id, const char *old_group, const char *new_group)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt = NULL; -	if(!yid) +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, +		YPACKET_STATUS_DEFAULT, yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 65, old_group);  	yahoo_packet_hash(pkt, 67, new_group); @@ -4511,24 +4117,27 @@ void yahoo_group_rename(int id, const char *old_group, const char *new_group)  	yahoo_packet_free(pkt);  } -void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg) +void yahoo_conference_addinvite(int id, const char *from, const char *who, +	const char *room, const YList *members, const char *msg)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, +		YPACKET_STATUS_DEFAULT, yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));  	yahoo_packet_hash(pkt, 51, who);  	yahoo_packet_hash(pkt, 57, room);  	yahoo_packet_hash(pkt, 58, msg);  	yahoo_packet_hash(pkt, 13, "0"); -	for(; members; members = members->next) { +	for (; members; members = members->next) {  		yahoo_packet_hash(pkt, 52, (char *)members->data);  		yahoo_packet_hash(pkt, 53, (char *)members->data);  	} @@ -4539,21 +4148,24 @@ void yahoo_conference_addinvite(int id, const char * from, const char *who, cons  	yahoo_packet_free(pkt);  } -void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg) +void yahoo_conference_invite(int id, const char *from, YList *who, +	const char *room, const char *msg)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));  	yahoo_packet_hash(pkt, 50, yd->user); -	for(; who; who = who->next) { +	for (; who; who = who->next) {  		yahoo_packet_hash(pkt, 52, (char *)who->data);  	}  	yahoo_packet_hash(pkt, 57, room); @@ -4565,45 +4177,51 @@ void yahoo_conference_invite(int id, const char * from, YList *who, const char *  	yahoo_packet_free(pkt);  } -void yahoo_conference_logon(int id, const char *from, YList *who, const char *room) +void yahoo_conference_logon(int id, const char *from, YList *who, +	const char *room)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); -	for(; who; who = who->next) { -		yahoo_packet_hash(pkt, 3, (char *)who->data); -	} +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); +	yahoo_packet_hash(pkt, 3, (from ? from : yd->user));  	yahoo_packet_hash(pkt, 57, room); +	for (; who; who = who->next) +		yahoo_packet_hash(pkt, 3, (char *)who->data);  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  } -void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg) +void yahoo_conference_decline(int id, const char *from, YList *who, +	const char *room, const char *msg)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, +		YPACKET_STATUS_DEFAULT, yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); -	for(; who; who = who->next) { +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); +	yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); +	for (; who; who = who->next)  		yahoo_packet_hash(pkt, 3, (char *)who->data); -	}  	yahoo_packet_hash(pkt, 57, room);  	yahoo_packet_hash(pkt, 14, msg); @@ -4612,22 +4230,26 @@ void yahoo_conference_decline(int id, const char * from, YList *who, const char  	yahoo_packet_free(pkt);  } -void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room) +void yahoo_conference_logoff(int id, const char *from, YList *who, +	const char *room)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); -	for(; who; who = who->next) { +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); +	yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); +	for (; who; who = who->next)  		yahoo_packet_hash(pkt, 3, (char *)who->data); -	} +  	yahoo_packet_hash(pkt, 57, room);  	yahoo_send_packet(yid, pkt, 0); @@ -4635,26 +4257,30 @@ void yahoo_conference_logoff(int id, const char * from, YList *who, const char *  	yahoo_packet_free(pkt);  } -void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8) +void yahoo_conference_message(int id, const char *from, YList *who, +	const char *room, const char *msg, int utf8)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); -	for(; who; who = who->next) { +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); +	yahoo_packet_hash(pkt, 53, (from ? from : yd->user)); +	for (; who; who = who->next)  		yahoo_packet_hash(pkt, 53, (char *)who->data); -	} +  	yahoo_packet_hash(pkt, 57, room);  	yahoo_packet_hash(pkt, 14, msg); -	if(utf8) +	if (utf8)  		yahoo_packet_hash(pkt, 97, "1");  	yahoo_send_packet(yid, pkt, 0); @@ -4669,7 +4295,7 @@ void yahoo_get_chatrooms(int id, int chatroomid)  	char url[1024];  	char buff[1024]; -	if(!yd) +	if (!yd)  		return;  	yid = y_new0(struct yahoo_input_data, 1); @@ -4677,32 +4303,39 @@ void yahoo_get_chatrooms(int id, int chatroomid)  	yid->type = YAHOO_CONNECTION_CHATCAT;  	if (chatroomid == 0) { -		snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatcat=0"); +		snprintf(url, 1024, +			"http://insider.msg.yahoo.com/ycontent/?chatcat=0");  	} else { -		snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",chatroomid); +		snprintf(url, 1024, +			"http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0", +			chatroomid);  	}  	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);  	inputs = y_list_prepend(inputs, yid); -	yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid); +	yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, +		_yahoo_http_connected, yid);  } -void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid) +void yahoo_chat_logon(int id, const char *from, const char *room, +	const char *roomid)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));  	yahoo_packet_hash(pkt, 109, yd->user);  	yahoo_packet_hash(pkt, 6, "abcde"); @@ -4710,41 +4343,44 @@ void yahoo_chat_logon(int id, const char *from, const char *room, const char *ro  	yahoo_packet_free(pkt); -	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));  	yahoo_packet_hash(pkt, 104, room);  	yahoo_packet_hash(pkt, 129, roomid); -	yahoo_packet_hash(pkt, 62, "2"); /* ??? */ +	yahoo_packet_hash(pkt, 62, "2");	/* ??? */  	yahoo_send_packet(yid, pkt, 0);  	yahoo_packet_free(pkt);  } - -void  yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8) +void yahoo_chat_message(int id, const char *from, const char *room, +	const char *msg, const int msgtype, const int utf8)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt;  	char buf[2]; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));  	yahoo_packet_hash(pkt, 104, room);  	yahoo_packet_hash(pkt, 117, msg); -	 +  	snprintf(buf, sizeof(buf), "%d", msgtype);  	yahoo_packet_hash(pkt, 124, buf); -	if(utf8) +	if (utf8)  		yahoo_packet_hash(pkt, 97, "1");  	yahoo_send_packet(yid, pkt, 0); @@ -4752,21 +4388,22 @@ void  yahoo_chat_message(int id, const char *from, const char *room, const char  	yahoo_packet_free(pkt);  } -  void yahoo_chat_logoff(int id, const char *from)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YPACKET_STATUS_DEFAULT, +		yd->session_id); -	yahoo_packet_hash(pkt, 1, (from?from:yd->user)); +	yahoo_packet_hash(pkt, 1, (from ? from : yd->user));  	yahoo_send_packet(yid, pkt, 0); @@ -4775,16 +4412,18 @@ void yahoo_chat_logoff(int id, const char *from)  void yahoo_buddyicon_request(int id, const char *who)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -	if( !yid ) +	if (!yid)  		return;  	yd = yid->yd; -	 -	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); + +	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT, +		0);  	yahoo_packet_hash(pkt, 4, yd->user);  	yahoo_packet_hash(pkt, 5, who);  	yahoo_packet_hash(pkt, 13, "1"); @@ -4793,21 +4432,24 @@ void yahoo_buddyicon_request(int id, const char *who)  	yahoo_packet_free(pkt);  } -void yahoo_send_picture_info(int id, const char *who, const char *url, int checksum) +void yahoo_send_picture_info(int id, const char *who, const char *url, +	int checksum)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt;  	char checksum_str[10]; -	if( !yid ) +	if (!yid)  		return;  	yd = yid->yd;  	snprintf(checksum_str, sizeof(checksum_str), "%d", checksum); -	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); +	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT, +		0);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 4, yd->user);  	yahoo_packet_hash(pkt, 5, who); @@ -4821,19 +4463,21 @@ void yahoo_send_picture_info(int id, const char *who, const char *url, int check  void yahoo_send_picture_update(int id, const char *who, int type)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt;  	char type_str[10]; -	if( !yid ) +	if (!yid)  		return;  	yd = yid->yd;  	snprintf(type_str, sizeof(type_str), "%d", type); -	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE, YAHOO_STATUS_AVAILABLE, 0); +	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE, +		YPACKET_STATUS_DEFAULT, 0);  	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 5, who);  	yahoo_packet_hash(pkt, 206, type_str); @@ -4844,21 +4488,23 @@ void yahoo_send_picture_update(int id, const char *who, int type)  void yahoo_send_picture_checksum(int id, const char *who, int checksum)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt;  	char checksum_str[10]; -	if( !yid ) +	if (!yid)  		return;  	yd = yid->yd; -	 +  	snprintf(checksum_str, sizeof(checksum_str), "%d", checksum); -	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, YAHOO_STATUS_AVAILABLE, 0); +	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, +		YPACKET_STATUS_DEFAULT, 0);  	yahoo_packet_hash(pkt, 1, yd->user); -	if( who != 0 ) +	if (who != 0)  		yahoo_packet_hash(pkt, 5, who);  	yahoo_packet_hash(pkt, 192, checksum_str);  	yahoo_packet_hash(pkt, 212, "1"); @@ -4869,19 +4515,21 @@ void yahoo_send_picture_checksum(int id, const char *who, int checksum)  void yahoo_webcam_close_feed(int id, const char *who)  { -	struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who); +	struct yahoo_input_data *yid = +		find_input_by_id_and_webcam_user(id, who); -	if(yid) +	if (yid)  		yahoo_input_close(yid);  }  void yahoo_webcam_get_feed(int id, const char *who)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_data *yd;  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return;  	/*  @@ -4891,11 +4539,12 @@ void yahoo_webcam_get_feed(int id, const char *who)  	 * order that we request them.  	 * The queue is popped in yahoo_process_webcam_key  	 */ -	webcam_queue = y_list_append(webcam_queue, who?strdup(who):NULL); +	webcam_queue = y_list_append(webcam_queue, who ? strdup(who) : NULL);  	yd = yid->yd; -	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YPACKET_STATUS_DEFAULT, +		yd->session_id);  	yahoo_packet_hash(pkt, 1, yd->user);  	if (who != NULL) @@ -4905,9 +4554,11 @@ void yahoo_webcam_get_feed(int id, const char *who)  	yahoo_packet_free(pkt);  } -void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp) +void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, +	unsigned int timestamp)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);  	unsigned char *packet;  	unsigned char header_len = 13;  	unsigned int pos = 0; @@ -4919,10 +4570,10 @@ void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length,  	packet[pos++] = header_len;  	packet[pos++] = 0; -	packet[pos++] = 5; /* version byte?? */ +	packet[pos++] = 5;	/* version byte?? */  	packet[pos++] = 0;  	pos += yahoo_put32(packet + pos, length); -	packet[pos++] = 2; /* packet type, image */ +	packet[pos++] = 2;	/* packet type, image */  	pos += yahoo_put32(packet + pos, timestamp);  	yahoo_add_to_send_queue(yid, packet, header_len);  	FREE(packet); @@ -4931,9 +4582,10 @@ void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length,  		yahoo_add_to_send_queue(yid, image, length);  } -void yahoo_webcam_accept_viewer(int id, const char* who, int accept) +void yahoo_webcam_accept_viewer(int id, const char *who, int accept)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);  	char *packet = NULL;  	char *data = NULL;  	unsigned char header_len = 13; @@ -4944,17 +4596,17 @@ void yahoo_webcam_accept_viewer(int id, const char* who, int accept)  		return;  	data = strdup("u="); -	data = y_string_append(data, (char*)who); +	data = y_string_append(data, (char *)who);  	data = y_string_append(data, "\r\n");  	len = strlen(data);  	packet = y_new0(char, header_len + len);  	packet[pos++] = header_len;  	packet[pos++] = 0; -	packet[pos++] = 5; /* version byte?? */ +	packet[pos++] = 5;	/* version byte?? */  	packet[pos++] = 0;  	pos += yahoo_put32(packet + pos, len); -	packet[pos++] = 0; /* packet type */ +	packet[pos++] = 0;	/* packet type */  	pos += yahoo_put32(packet + pos, accept);  	memcpy(packet + pos, data, len);  	FREE(data); @@ -4964,13 +4616,15 @@ void yahoo_webcam_accept_viewer(int id, const char* who, int accept)  void yahoo_webcam_invite(int id, const char *who)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_packet *pkt; -		 -	if(!yid) + +	if (!yid)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY, +		yid->yd->session_id);  	yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");  	yahoo_packet_hash(pkt, 14, " "); @@ -4982,7 +4636,8 @@ void yahoo_webcam_invite(int id, const char *who)  	yahoo_packet_free(pkt);  } -static void yahoo_search_internal(int id, int t, const char *text, int g, int ar, int photo, int yahoo_only, int startpos, int total) +static void yahoo_search_internal(int id, int t, const char *text, int g, +	int ar, int photo, int yahoo_only, int startpos, int total)  {  	struct yahoo_data *yd = find_conn_by_id(id);  	struct yahoo_input_data *yid; @@ -4990,7 +4645,7 @@ static void yahoo_search_internal(int id, int t, const char *text, int g, int ar  	char buff[1024];  	char *ctext, *p; -	if(!yd) +	if (!yd)  		return;  	yid = y_new0(struct yahoo_input_data, 1); @@ -4998,38 +4653,43 @@ static void yahoo_search_internal(int id, int t, const char *text, int g, int ar  	yid->type = YAHOO_CONNECTION_SEARCH;  	/* -	age range -	.ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+ -	*/ +	   age range +	   .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+ +	 */ -	snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos); +	snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, +		startpos);  	ctext = strdup(text); -	while((p = strchr(ctext, ' '))) +	while ((p = strchr(ctext, ' ')))  		*p = '+'; -	snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", -			ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "", -			startpos ? buff : ""); +	snprintf(url, 1024, +		"http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", +		ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "", +		startpos ? buff : "");  	FREE(ctext);  	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);  	inputs = y_list_prepend(inputs, yid); -	yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid); +	yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, +		_yahoo_http_connected, yid);  } -void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar,  -		int photo, int yahoo_only) +void yahoo_search(int id, enum yahoo_search_type t, const char *text, +	enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo, +	int yahoo_only)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_search_state *yss; -	if(!yid) +	if (!yid)  		return; -	if(!yid->ys) +	if (!yid->ys)  		yid->ys = y_new0(struct yahoo_search_state, 1);  	yss = yid->ys; @@ -5047,273 +4707,708 @@ void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo  void yahoo_search_again(int id, int start)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	struct yahoo_input_data *yid = +		find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);  	struct yahoo_search_state *yss; -	if(!yid || !yid->ys) +	if (!yid || !yid->ys)  		return;  	yss = yid->ys; -	if(start == -1) +	if (start == -1)  		start = yss->lsearch_nstart + yss->lsearch_nfound; -	yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text,  -			yss->lsearch_gender, yss->lsearch_agerange,  -			yss->lsearch_photo, yss->lsearch_yahoo_only,  -			start, yss->lsearch_ntotal); +	yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, +		yss->lsearch_gender, yss->lsearch_agerange, +		yss->lsearch_photo, yss->lsearch_yahoo_only, +		start, yss->lsearch_ntotal); +} + +void yahoo_send_picture(int id, const char *name, unsigned long size, +	yahoo_get_fd_callback callback, void *data) +{ +	/* Not Implemented */  } +/* File Transfer */ +static YList *active_file_transfers = NULL; + +enum { +	FT_STATE_HEAD = 1, +	FT_STATE_RECV, +	FT_STATE_RECV_START, +	FT_STATE_SEND +}; +  struct send_file_data { -	struct yahoo_packet *pkt; +	int client_id; +	char *id; +	char *who; +	char *filename; +	char *ip_addr; +	char *token; +	int size; + +	struct yahoo_input_data *yid; +	int state; +  	yahoo_get_fd_callback callback; -	void *user_data; +	void *data;  }; -static void _yahoo_send_picture_connected(int id, int fd, int error, void *data) +static char *yahoo_get_random(void) +{ +	int i = 0; +	int r = 0; +	int c = 0; +	char out[25]; + +	out[24] = '\0'; +	out[23] = '$'; +	out[22] = '$'; +	 +	for (i = 0; i < 22; i++) { +		if(r == 0) +			r = rand(); + +		c = r%61; + +		if(c<26) +			out[i] = c + 'a'; +		else if (c<52) +			out[i] = c - 26 + 'A'; +		else +			out[i] = c - 52 + '0'; + +		r /= 61; +	} + +	return strdup(out); +} + +static int _are_same_id(const void *sfd1, const void *id) +{ +	return strcmp(((struct send_file_data *)sfd1)->id, (char *)id); +} + +static int _are_same_yid(const void *sfd1, const void *yid) +{ +	if(((struct send_file_data *)sfd1)->yid == yid) +		return 0; +	else +		return 1; +} + +static struct send_file_data *yahoo_get_active_transfer(char *id) +{ +	YList *l = y_list_find_custom(active_file_transfers, id, +		_are_same_id); + +	if(l) +		return (struct send_file_data *)l->data; +	 +	return NULL; +} + +static struct send_file_data *yahoo_get_active_transfer_with_yid(void *yid) +{ +	YList *l = y_list_find_custom(active_file_transfers, yid, +		_are_same_yid); + +	if(l) +		return (struct send_file_data *)l->data; +	 +	return NULL; +} + +static void yahoo_add_active_transfer(struct send_file_data *sfd) +{ +	active_file_transfers = y_list_prepend(active_file_transfers, sfd); +} + +static void yahoo_remove_active_transfer(struct send_file_data *sfd) +{ +	active_file_transfers = y_list_remove(active_file_transfers, sfd); +	free(sfd->id); +	free(sfd->who); +	free(sfd->filename); +	free(sfd->ip_addr); +	FREE(sfd); +} + +static void _yahoo_ft_upload_connected(int id, void *fd, int error, void *data)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT);  	struct send_file_data *sfd = data; -	struct yahoo_packet *pkt = sfd->pkt; -	unsigned char buff[1024]; +	struct yahoo_input_data *yid = sfd->yid; -	if(fd <= 0) { -		sfd->callback(id, fd, error, sfd->user_data); -		FREE(sfd); -		yahoo_packet_free(pkt); +	if (!fd) {  		inputs = y_list_remove(inputs, yid);  		FREE(yid);  		return;  	} +	sfd->callback(id, fd, error, sfd->data); +  	yid->fd = fd; -	yahoo_send_packet(yid, pkt, 8); -	yahoo_packet_free(pkt); +	yid->read_tag = +		YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, +		YAHOO_INPUT_READ, yid); +} -	snprintf((char *)buff, sizeof(buff), "29"); -	buff[2] = 0xc0; -	buff[3] = 0x80; -	 -	write(yid->fd, buff, 4); +static void yahoo_file_transfer_upload(struct yahoo_data *yd, +	struct send_file_data *sfd) +{ +	char url[256]; +	char buff[4096]; +	char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; -	/*	YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */ +	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); -	sfd->callback(id, fd, error, sfd->user_data); -	FREE(sfd); -	inputs = y_list_remove(inputs, yid); -	/* -	while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { -	if(!strcmp(buff, "")) -	break; +	yid->yd = yd; +	yid->type = YAHOO_CONNECTION_FT; + +	inputs = y_list_prepend(inputs, yid); +	sfd->yid = yid; +	sfd->state = FT_STATE_SEND; + +	token_enc = yahoo_urlencode(sfd->token); +	sender_enc = yahoo_urlencode(yd->user); +	recv_enc = yahoo_urlencode(sfd->who); + +	snprintf(url, sizeof(url),  +		"http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, +		token_enc, sender_enc, recv_enc); + +	snprintf(buff, sizeof(buff), "T=%s; Y=%s", yd->cookie_t, yd->cookie_y); + +	yahoo_http_post(yd->client_id, url, buff, sfd->size, +		_yahoo_ft_upload_connected, sfd); + +	FREE(token_enc); +	FREE(sender_enc); +	FREE(recv_enc);  } -	*/ -	yahoo_input_close(yid); +static void yahoo_init_ft_recv(struct yahoo_data *yd, +	struct send_file_data *sfd) +{ +	char url[256]; +	char buff[1024]; +	char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + +	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + +	yid->yd = yd; +	yid->type = YAHOO_CONNECTION_FT; + +	inputs = y_list_prepend(inputs, yid); +	sfd->yid = yid; +	sfd->state = FT_STATE_HEAD; + +	token_enc = yahoo_urlencode(sfd->token); +	sender_enc = yahoo_urlencode(sfd->who); +	recv_enc = yahoo_urlencode(yd->user); + +	snprintf(url, sizeof(url),  +		"http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, +		token_enc, sender_enc, recv_enc); + +	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + +	yahoo_http_head(yid->yd->client_id, url, buff, 0, NULL, +		_yahoo_http_connected, yid); + +	FREE(token_enc); +	FREE(sender_enc); +	FREE(recv_enc);  } -void yahoo_send_picture(int id, const char *name, unsigned long size, -							yahoo_get_fd_callback callback, void *data) +static void yahoo_file_transfer_accept(struct yahoo_input_data *yid, +	struct send_file_data *sfd)  { -	struct yahoo_data *yd = find_conn_by_id(id); -	struct yahoo_input_data *yid; -	struct yahoo_server_settings *yss; -	struct yahoo_packet *pkt = NULL; -	char size_str[10]; -	char expire_str[10]; -	long content_length=0; -	unsigned char buff[1024]; -	char url[255]; +	struct yahoo_packet *pkt; + +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERACCEPT, +		YPACKET_STATUS_DEFAULT, yid->yd->session_id); + +	yahoo_packet_hash(pkt, 1, yid->yd->user); +	yahoo_packet_hash(pkt, 5, sfd->who); +	yahoo_packet_hash(pkt, 265, sfd->id); +	yahoo_packet_hash(pkt, 27, sfd->filename); +	yahoo_packet_hash(pkt, 249, "3"); +	yahoo_packet_hash(pkt, 251, sfd->token); + +	yahoo_send_packet(yid, pkt, 0); + +	yahoo_packet_free(pkt); + +	yahoo_init_ft_recv(yid->yd, sfd); +} + +static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt) +{ +	YList *l;  	struct send_file_data *sfd; +	char *who = NULL; +	char *filename = NULL; +	char *id = NULL; +	char *token = NULL; -	if(!yd) -		return; +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; +		switch (pair->key) { +		case 4: +			who = pair->value; +			break; +		case 5: +			/* Me... don't care */ +			break; +		case 249: +			break; +		case 265: +			id = pair->value; +			break; +		case 251: +			token = pair->value; +			break; +		case 27: +			filename = pair->value; +			break; +		} +	} -	yss = yd->server_settings; +	sfd = yahoo_get_active_transfer(id); -	yid = y_new0(struct yahoo_input_data, 1); -	yid->yd = yd; -	yid->type = YAHOO_CONNECTION_FT; +	if (sfd) { +		sfd->token = strdup(token); -	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD, YAHOO_STATUS_AVAILABLE, yd->session_id); +		yahoo_file_transfer_upload(yid->yd, sfd); +	} +	else { +		YAHOO_CALLBACK(ext_yahoo_file_transfer_done) +			(yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN, +			sfd->data); -	snprintf(size_str, sizeof(size_str), "%ld", size); -	snprintf(expire_str, sizeof(expire_str), "%ld", (long)604800); +		yahoo_remove_active_transfer(sfd); +	} +} -	yahoo_packet_hash(pkt, 0, yd->user); -	yahoo_packet_hash(pkt, 1, yd->user); -	yahoo_packet_hash(pkt, 14, ""); -	yahoo_packet_hash(pkt, 27, name); -	yahoo_packet_hash(pkt, 28, size_str); -	yahoo_packet_hash(pkt, 38, expire_str); -	 +static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt) +{ +	YList *l; +	char *who = NULL; +	char *filename = NULL; +	char *id = NULL; +	char *token = NULL; +	char *ip_addr = NULL; -	content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); +	struct send_file_data *sfd; -	snprintf(url, sizeof(url), "http://%s:%d/notifyft", -				yss->filetransfer_host, yss->filetransfer_port); -	snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s", -				 yd->cookie_y, yd->cookie_t); -	inputs = y_list_prepend(inputs, yid); +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; +		switch (pair->key) { +		case 1: +		case 4: +			who = pair->value; +			break; +		case 5: +			/* Me... don't care */ +			break; +		case 249: +			break; +		case 265: +			id = pair->value; +			break; +		case 250: +			ip_addr = pair->value; +			break; +		case 251: +			token = pair->value; +			break; +		case 27: +			filename = pair->value; +			break; +		} +	} -	sfd = y_new0(struct send_file_data, 1); -	sfd->pkt = pkt; -	sfd->callback = callback; -	sfd->user_data = data; -	yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size, -						_yahoo_send_picture_connected, sfd); +	sfd = yahoo_get_active_transfer(id); + +	if (sfd) { +		sfd->token = strdup(token); +		sfd->ip_addr = strdup(ip_addr); + +		yahoo_file_transfer_accept(yid, sfd); +	} +	else { +		YAHOO_CALLBACK(ext_yahoo_file_transfer_done) +			(yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN, +			sfd->data); + +		yahoo_remove_active_transfer(sfd); +	}  } -static void _yahoo_send_file_connected(int id, int fd, int error, void *data) +static void yahoo_send_filetransferinfo(struct yahoo_data *yd, +	struct send_file_data *sfd)  { -	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT); -	struct send_file_data *sfd = data; -	struct yahoo_packet *pkt = sfd->pkt; -	unsigned char buff[1024]; +	struct yahoo_input_data *yid; +	struct yahoo_packet *pkt; + +	yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER); +	sfd->ip_addr = YAHOO_CALLBACK(ext_yahoo_get_ip_addr)("relay.yahoo.com"); + +	if (!sfd->ip_addr) { +		YAHOO_CALLBACK(ext_yahoo_file_transfer_done) +			(yd->client_id, YAHOO_FILE_TRANSFER_RELAY, sfd->data); + +		yahoo_remove_active_transfer(sfd); -	if(fd <= 0) { -		sfd->callback(id, fd, error, sfd->user_data); -		FREE(sfd); -		yahoo_packet_free(pkt); -		inputs = y_list_remove(inputs, yid); -		FREE(yid);  		return;  	} -	yid->fd = fd; -	yahoo_send_packet(yid, pkt, 8); +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERINFO, +		YPACKET_STATUS_DEFAULT, yd->session_id); + +	yahoo_packet_hash(pkt, 1, yd->user); +	yahoo_packet_hash(pkt, 5, sfd->who); +	yahoo_packet_hash(pkt, 265, sfd->id); +	yahoo_packet_hash(pkt, 27, sfd->filename); +	yahoo_packet_hash(pkt, 249, "3"); +	yahoo_packet_hash(pkt, 250, sfd->ip_addr); + +	yahoo_send_packet(yid, pkt, 0); +  	yahoo_packet_free(pkt); +} -	snprintf((char *)buff, sizeof(buff), "29"); -	buff[2] = 0xc0; -	buff[3] = 0x80; -	 -	write(yid->fd, buff, 4); +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt) +{ +	YList *l; +	char *who = NULL; +	char *filename = NULL; +	char *msg = NULL; +	char *id = NULL; +	int action = 0; +	int size = 0; +	struct yahoo_data *yd = yid->yd; -/*	YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */ +	struct send_file_data *sfd; -	sfd->callback(id, fd, error, sfd->user_data); -	FREE(sfd); -	inputs = y_list_remove(inputs, yid); -	/* -	while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { -		if(!strcmp(buff, "")) +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; +		switch (pair->key) { +		case 4: +			who = pair->value; +			break; +		case 5: +			/* Me... don't care */ +			break; +		case 222: +			action = atoi(pair->value); +			break; +		case 265: +			id = pair->value; +			break; +		case 266: /* Don't know */ +			break; +		case 302: /* Start Data? */ +			break; +		case 300: +			break; +		case 27: +			filename = pair->value;  			break; +		case 28: +			size = atoi(pair->value); +			break; +		case 14: +			msg = pair->value; +		case 301: /* End Data? */ +			break; +		case 303: +			break; + +		} +	} + +	if (action == YAHOO_FILE_TRANSFER_INIT) { +		/* Received a FT request from buddy */ +		sfd = y_new0(struct send_file_data, 1); +         +		sfd->client_id = yd->client_id; +		sfd->id = strdup(id); +		sfd->who = strdup(who); +		sfd->filename = strdup(filename); +		sfd->size = size; +         +		yahoo_add_active_transfer(sfd); + +		YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, yd->user, +			who, msg, filename, size, sfd->id);  	} +	else { +		/* Response to our request */ +		sfd = yahoo_get_active_transfer(id); -	*/ -	yahoo_input_close(yid); +		if (sfd && action == YAHOO_FILE_TRANSFER_ACCEPT) { +			yahoo_send_filetransferinfo(yd, sfd); +		} +		else if (!sfd || action == YAHOO_FILE_TRANSFER_REJECT) { +			YAHOO_CALLBACK(ext_yahoo_file_transfer_done) +				(yd->client_id, YAHOO_FILE_TRANSFER_REJECT, +				sfd->data); + +			yahoo_remove_active_transfer(sfd); +		} +	}  } -void yahoo_send_file(int id, const char *who, const char *msg,  -		const char *name, unsigned long size,  -		yahoo_get_fd_callback callback, void *data) +void yahoo_send_file(int id, const char *who, const char *msg, +	const char *name, unsigned long size, +	yahoo_get_fd_callback callback, void *data)  { -	struct yahoo_data *yd = find_conn_by_id(id); -	struct yahoo_input_data *yid; -	struct yahoo_server_settings *yss;  	struct yahoo_packet *pkt = NULL;  	char size_str[10]; -	long content_length=0; -	unsigned char buff[1024]; -	char url[255]; +	struct yahoo_input_data *yid; +	struct yahoo_data *yd;  	struct send_file_data *sfd; +	 +	yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); +	yd = find_conn_by_id(id); +	sfd = y_new0(struct send_file_data, 1); -	if(!yd) -		return; +	sfd->client_id = id; +	sfd->id = yahoo_get_random(); +	sfd->who = strdup(who); +	sfd->filename = strdup(name); +	sfd->size = size; +	sfd->callback = callback; +	sfd->data = data; -	yss = yd->server_settings; +	yahoo_add_active_transfer(sfd); -	yid = y_new0(struct yahoo_input_data, 1); -	yid->yd = yd; -	yid->type = YAHOO_CONNECTION_FT; +	if (!yd) +		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id); +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, +		YPACKET_STATUS_DEFAULT, yd->session_id);  	snprintf(size_str, sizeof(size_str), "%ld", size); -	yahoo_packet_hash(pkt, 0, yd->user); +	yahoo_packet_hash(pkt, 1, yd->user);  	yahoo_packet_hash(pkt, 5, who); -	yahoo_packet_hash(pkt, 14, msg); +	yahoo_packet_hash(pkt, 265, sfd->id); +	yahoo_packet_hash(pkt, 222, "1"); +	yahoo_packet_hash(pkt, 266, "1"); +	yahoo_packet_hash(pkt, 302, "268"); +	yahoo_packet_hash(pkt, 300, "268");  	yahoo_packet_hash(pkt, 27, name);  	yahoo_packet_hash(pkt, 28, size_str); +	yahoo_packet_hash(pkt, 301, "268"); +	yahoo_packet_hash(pkt, 303, "268"); -	content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); +	yahoo_send_packet(yid, pkt, 0); -	snprintf(url, sizeof(url), "http://%s:%d/notifyft",  -			yss->filetransfer_host, yss->filetransfer_port); -	snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s", -			yd->cookie_y, yd->cookie_t); -	inputs = y_list_prepend(inputs, yid); +	yahoo_packet_free(pkt); +} -	sfd = y_new0(struct send_file_data, 1); -	sfd->pkt = pkt; -	sfd->callback = callback; -	sfd->user_data = data; -	yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size, -			_yahoo_send_file_connected, sfd); +void yahoo_send_file_transfer_response(int client_id, int response, char *id, void *data) +{ +	struct yahoo_packet *pkt = NULL; +	char resp[2]; +	struct yahoo_input_data *yid; +	 +	struct send_file_data *sfd = yahoo_get_active_transfer(id); + +	sfd->data = data; + +	yid = find_input_by_id_and_type(client_id, YAHOO_CONNECTION_PAGER); + +	pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, +		YPACKET_STATUS_DEFAULT, yid->yd->session_id); + +	snprintf(resp, sizeof(resp), "%d", response); + +	yahoo_packet_hash(pkt, 1, yid->yd->user); +	yahoo_packet_hash(pkt, 5, sfd->who); +	yahoo_packet_hash(pkt, 265, sfd->id); +	yahoo_packet_hash(pkt, 222, resp); + +	yahoo_send_packet(yid, pkt, 0); + +	yahoo_packet_free(pkt); + +	if(response == YAHOO_FILE_TRANSFER_REJECT) +		yahoo_remove_active_transfer(sfd); +} + +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) +{ +	struct send_file_data *sfd; +	struct yahoo_data *yd = yid->yd; +	 +	sfd = yahoo_get_active_transfer_with_yid(yid); + +	if (!sfd) { +		LOG(("Something funny happened. yid %p has no sfd.\n", yid)); +		return; +	} + +	/*  +	 * We want to handle only the complete data with HEAD since we don't +	 * want a situation where both the GET and HEAD are active. +	 * With SEND, we really can't do much with partial response +	 */ +	if ((sfd->state == FT_STATE_HEAD || sfd->state == FT_STATE_SEND)  +			&& !over) +		return; + +	if (sfd->state == FT_STATE_HEAD) { +		/* Do a GET */ +		char url[256]; +		char buff[1024]; +		char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + +		struct yahoo_input_data *yid_ft =  +			y_new0(struct yahoo_input_data, 1); +         +		yid_ft->yd = yid->yd; +		yid_ft->type = YAHOO_CONNECTION_FT; +         +		inputs = y_list_prepend(inputs, yid_ft); +		sfd->yid = yid_ft; +		sfd->state = FT_STATE_RECV; + +		token_enc = yahoo_urlencode(sfd->token); +		sender_enc = yahoo_urlencode(sfd->who); +		recv_enc = yahoo_urlencode(yd->user); +         +		snprintf(url, sizeof(url),  +			"http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, +			token_enc, sender_enc, recv_enc); + +		snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, +			yd->cookie_t); + + +		yahoo_http_get(yd->client_id, url, buff, 1, 1, +			_yahoo_http_connected, yid_ft); + +		FREE(token_enc); +		FREE(sender_enc); +		FREE(recv_enc); +	} +	else if (sfd->state == FT_STATE_RECV ||  +		sfd->state == FT_STATE_RECV_START) { + +		unsigned char *data_begin = NULL; + +		if (yid->rxlen == 0) +			yahoo_remove_active_transfer(sfd); + +		if (sfd->state != FT_STATE_RECV_START && +			(data_begin =  +				(unsigned char *)strstr((char *)yid->rxqueue, +				"\r\n\r\n"))) { + +			sfd->state = FT_STATE_RECV_START; + +			yid->rxlen -= 4+(data_begin-yid->rxqueue)/sizeof(char); +			data_begin += 4; + +			if (yid->rxlen > 0) +				YAHOO_CALLBACK(ext_yahoo_got_ft_data)  +					(yd->client_id, data_begin, +					yid->rxlen, sfd->data); +		} +		else if (sfd->state == FT_STATE_RECV_START) +			YAHOO_CALLBACK(ext_yahoo_got_ft_data) (yd->client_id, +				yid->rxqueue, yid->rxlen, sfd->data); + +		FREE(yid->rxqueue); +		yid->rxqueue = NULL; +		yid->rxlen = 0; +	} +	else if (sfd->state == FT_STATE_SEND) { +		/* Sent file completed */ +		int len = 0; +		char *off = strstr((char *)yid->rxqueue, "Content-Length: "); + +		if (off) { +			off += 16; +			len = atoi(off); +		} + +		if (len < sfd->size) +			YAHOO_CALLBACK(ext_yahoo_file_transfer_done) +				(yd->client_id, +				YAHOO_FILE_TRANSFER_FAILED, sfd->data); +		else +			YAHOO_CALLBACK(ext_yahoo_file_transfer_done) +				(yd->client_id, +				YAHOO_FILE_TRANSFER_DONE, sfd->data); + +		yahoo_remove_active_transfer(sfd); +	}  } +/* End File Transfer */  enum yahoo_status yahoo_current_status(int id)  {  	struct yahoo_data *yd = find_conn_by_id(id); -	if(!yd) +	if (!yd)  		return YAHOO_STATUS_OFFLINE;  	return yd->current_status;  } -const YList * yahoo_get_buddylist(int id) +const YList *yahoo_get_buddylist(int id)  {  	struct yahoo_data *yd = find_conn_by_id(id); -	if(!yd) +	if (!yd)  		return NULL;  	return yd->buddies;  } -const YList * yahoo_get_ignorelist(int id) +const YList *yahoo_get_ignorelist(int id)  {  	struct yahoo_data *yd = find_conn_by_id(id); -	if(!yd) +	if (!yd)  		return NULL;  	return yd->ignore;  } -const YList * yahoo_get_identities(int id) +const YList *yahoo_get_identities(int id)  {  	struct yahoo_data *yd = find_conn_by_id(id); -	if(!yd) +	if (!yd)  		return NULL;  	return yd->identities;  } -const char * yahoo_get_cookie(int id, const char *which) +const char *yahoo_get_cookie(int id, const char *which)  {  	struct yahoo_data *yd = find_conn_by_id(id); -	if(!yd) +	if (!yd)  		return NULL; -	if(!strncasecmp(which, "y", 1)) +	if (!strncasecmp(which, "y", 1))  		return yd->cookie_y; -	if(!strncasecmp(which, "t", 1)) +	if (!strncasecmp(which, "b", 1)) +		return yd->cookie_b; +	if (!strncasecmp(which, "t", 1))  		return yd->cookie_t; -	if(!strncasecmp(which, "c", 1)) +	if (!strncasecmp(which, "c", 1))  		return yd->cookie_c; -	if(!strncasecmp(which, "login", 5)) +	if (!strncasecmp(which, "login", 5))  		return yd->login_cookie;  	return NULL;  } -void yahoo_get_url_handle(int id, const char *url,  -		yahoo_get_url_handle_callback callback, void *data) -{ -	struct yahoo_data *yd = find_conn_by_id(id); -	if(!yd) -		return; - -	yahoo_get_url_fd(id, url, yd, callback, data); -} - -const char * yahoo_get_profile_url( void ) +const char *yahoo_get_profile_url(void)  {  	return profile_url;  } - diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index e4d541d5..dfd2e70f 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -1,7 +1,7 @@  /*   * libyahoo2 wrapper to BitlBee   * - * Mostly Copyright 2004 Wilmer van der Gaast <wilmer@gaast.net> + * Mostly Copyright 2004-2010 Wilmer van der Gaast <wilmer@gaast.net>   *   * 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 @@ -157,7 +157,7 @@ static void byahoo_logout( struct im_connection *ic )  	GSList *l;  	while( ic->groupchats ) -		imcb_chat_free( ic->groupchats ); +		imcb_chat_free( ic->groupchats->data );  	for( l = yd->buddygroups; l; l = l->next )  	{ @@ -270,8 +270,32 @@ static void byahoo_keepalive( struct im_connection *ic )  static void byahoo_add_buddy( struct im_connection *ic, char *who, char *group )  {  	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; +	bee_user_t *bu; -	yahoo_add_buddy( yd->y2_id, who, group ? group : BYAHOO_DEFAULT_GROUP, NULL ); +	if( group && ( bu = bee_user_by_handle( ic->bee, ic, who ) ) && bu->group ) +	{ +		GSList *bgl; +		 +		/* If the person is in our list already, this is a group change. */ +		yahoo_change_buddy_group( yd->y2_id, who, bu->group->name, group ); +		 +		/* No idea how often people have people in multiple groups and +		   BitlBee doesn't currently support this anyway .. but keep +		   this struct up-to-date for now. */ +		for( bgl = yd->buddygroups; bgl; bgl = bgl->next ) +		{ +			struct byahoo_buddygroups *bg = bgl->data; +			 +			if( g_strcasecmp( bg->buddy, who ) == 0 && +			    g_strcasecmp( bg->group, bu->group->name ) == 0 ) +			{ +				g_free( bg->group ); +				bg->group = g_strdup( group ); +			} +		} +	} +	else +		yahoo_add_buddy( yd->y2_id, who, group ? group : BYAHOO_DEFAULT_GROUP, NULL );  }  static void byahoo_remove_buddy( struct im_connection *ic, char *who, char *group ) @@ -340,14 +364,14 @@ static void byahoo_auth_allow( struct im_connection *ic, const char *who )  {  	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; -	yahoo_accept_buddy_ymsg13( yd->y2_id, NULL, who ); +	yahoo_confirm_buddy( yd->y2_id, who, 0, "" );  }  static void byahoo_auth_deny( struct im_connection *ic, const char *who )  {  	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; -	yahoo_reject_buddy_ymsg13( yd->y2_id, NULL, who, NULL ); +	yahoo_confirm_buddy( yd->y2_id, who, 1, "" );  }  void byahoo_initmodule( ) @@ -420,7 +444,7 @@ void byahoo_connect_callback( gpointer data, gint source, b_input_condition cond  		return;  	} -	d->callback( d->fd, 0, d->data ); +	d->callback( NULL + d->fd, 0, d->data );  	g_free( d );  } @@ -440,7 +464,7 @@ gboolean byahoo_read_ready_callback( gpointer data, gint source, b_input_conditi  		/* WTF doesn't libyahoo clean this up? */  		return FALSE; -	yahoo_read_ready( d->id, d->fd, d->data ); +	yahoo_read_ready( d->id, NULL + d->fd, d->data );  	return TRUE;  } @@ -457,7 +481,7 @@ gboolean byahoo_write_ready_callback( gpointer data, gint source, b_input_condit  {  	struct byahoo_write_ready_data *d = data; -	return yahoo_write_ready( d->id, d->fd, d->data ); +	return yahoo_write_ready( d->id, NULL + d->fd, d->data );  }  void ext_yahoo_login_response( int id, int succ, const char *url ) @@ -486,7 +510,7 @@ void ext_yahoo_login_response( int id, int succ, const char *url )  	else  	{  		char *errstr; -		int allow_reconnect = TRUE; +		int allow_reconnect = FALSE;  		yd->logged_in = FALSE; @@ -496,13 +520,15 @@ void ext_yahoo_login_response( int id, int succ, const char *url )  			errstr = "Incorrect Yahoo! password";  		else if( succ == YAHOO_LOGIN_LOCK )  			errstr = "Yahoo! account locked"; +		else if( succ == 1236 ) +			errstr = "Yahoo! account locked or machine temporarily banned";  		else if( succ == YAHOO_LOGIN_DUPL ) -		{  			errstr = "Logged in on a different machine or device"; -			allow_reconnect = FALSE; -		}  		else if( succ == YAHOO_LOGIN_SOCK ) +		{  			errstr = "Socket problem"; +			allow_reconnect = TRUE; +		}  		else  			errstr = "Unknown error"; @@ -605,17 +631,16 @@ void ext_yahoo_status_changed( int id, const char *who, int stat, const char *ms  		state_string = "Offline";  		flags = 0;  		break; -	case YAHOO_STATUS_NOTIFY: -		state_string = "Notify"; -		break;  	}  	imcb_buddy_status( ic, who, flags, state_string, msg ); -	/* Not implemented yet...  	if( stat == YAHOO_STATUS_IDLE ) -		imcb_buddy_times( ic, who, 0, away ); -	*/ +		imcb_buddy_times( ic, who, 0, idle ); +} + +void ext_yahoo_got_buzz( int id, const char *me, const char *who, long tm ) +{  }  void ext_yahoo_got_im( int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8 ) @@ -631,15 +656,22 @@ void ext_yahoo_got_im( int id, const char *me, const char *who, const char *msg,  	}  } -void ext_yahoo_got_file( int id, -                         const char *ignored, -                         const char *who, const char *url, long expires, const char *msg, const char *fname, unsigned long fesize ) +void ext_yahoo_got_file( int id, const char *ignored, const char *who, const char *msg, +                         const char *fname, unsigned long fesize, char *trid )  {  	struct im_connection *ic = byahoo_get_ic_by_id( id );  	imcb_log( ic, "Got a file transfer (file = %s) from %s. Ignoring for now due to lack of support.", fname, who );  } +void ext_yahoo_got_ft_data( int id, const unsigned char *in, int len, void *data ) +{ +} + +void ext_yahoo_file_transfer_done( int id, int result, void *data ) +{ +} +  void ext_yahoo_typing_notify( int id, const char *ignored, const char *who, int stat )  {  	struct im_connection *ic = byahoo_get_ic_by_id( id ); @@ -650,7 +682,7 @@ void ext_yahoo_typing_notify( int id, const char *ignored, const char *who, int  		imcb_buddy_typing( ic, (char*) who, 0 );  } -void ext_yahoo_system_message( int id, const char *msg ) +void ext_yahoo_system_message( int id, const char *me, const char *who, const char *msg )  {  	struct im_connection *ic = byahoo_get_ic_by_id( id ); @@ -672,9 +704,10 @@ void ext_yahoo_error( int id, const char *err, int fatal, int num )  }  /* TODO: Clear up the mess of inp and d structures */ -int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *data ) +int ext_yahoo_add_handler( int id, void *fd_, yahoo_input_condition cond, void *data )  {  	struct byahoo_input_data *inp = g_new0( struct byahoo_input_data, 1 ); +	int fd = (int) fd_;  	if( cond == YAHOO_INPUT_READ )  	{ @@ -685,7 +718,7 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat  		d->data = data;  		inp->d = d; -		d->tag = inp->h = b_input_add( fd, GAIM_INPUT_READ, (b_event_handler) byahoo_read_ready_callback, (gpointer) d ); +		d->tag = inp->h = b_input_add( fd, B_EV_IO_READ, (b_event_handler) byahoo_read_ready_callback, (gpointer) d );  	}  	else if( cond == YAHOO_INPUT_WRITE )  	{ @@ -696,17 +729,17 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat  		d->data = data;  		inp->d = d; -		d->tag = inp->h = b_input_add( fd, GAIM_INPUT_WRITE, (b_event_handler) byahoo_write_ready_callback, (gpointer) d ); +		d->tag = inp->h = b_input_add( fd, B_EV_IO_WRITE, (b_event_handler) byahoo_write_ready_callback, (gpointer) d );  	}  	else  	{  		g_free( inp ); -		return( -1 ); +		return -1;  		/* Panic... */  	}  	byahoo_inputs = g_slist_append( byahoo_inputs, inp ); -	return( inp->h ); +	return inp->h;  }  void ext_yahoo_remove_handler( int id, int tag ) @@ -730,7 +763,7 @@ void ext_yahoo_remove_handler( int id, int tag )  	b_event_remove( tag );  } -int ext_yahoo_connect_async( int id, const char *host, int port, yahoo_connect_callback callback, void *data ) +int ext_yahoo_connect_async( int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl )  {  	struct byahoo_connect_callback_data *d;  	int fd; @@ -746,48 +779,41 @@ int ext_yahoo_connect_async( int id, const char *host, int port, yahoo_connect_c  	d->data = data;  	d->id = id; -	return( fd ); +	return fd;  } -/* Because we don't want asynchronous connects in BitlBee, and because -   libyahoo doesn't seem to use this one anyway, this one is now defunct. */ -int ext_yahoo_connect(const char *host, int port) +char *ext_yahoo_get_ip_addr( const char *domain )  { -#if 0 -	struct sockaddr_in serv_addr; -	static struct hostent *server; -	static char last_host[256]; -	int servfd; -	char **p; +	return NULL; +} -	if(last_host[0] || g_strcasecmp(last_host, host)!=0) { -		if(!(server = gethostbyname(host))) { -			return -1; -		} -		strncpy(last_host, host, 255); -	} +int ext_yahoo_write( void *fd, char *buf, int len ) +{ +	return write( (int) fd, buf, len ); +} -	if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { -		return -1; -	} +int ext_yahoo_read( void *fd, char *buf, int len ) +{ +	return read( (int) fd, buf, len ); +} -	for (p = server->h_addr_list; *p; p++) -	{ -		memset(&serv_addr, 0, sizeof(serv_addr)); -		serv_addr.sin_family = AF_INET; -		memcpy(&serv_addr.sin_addr.s_addr, *p, server->h_length); -		serv_addr.sin_port = htons(port); - -		if(connect(servfd, (struct sockaddr *) &serv_addr,  -					sizeof(serv_addr)) == -1) { -			return -1; -		} else { -			return servfd; -		} -	} +void ext_yahoo_close( void *fd ) +{ +	close( (int) fd ); +} -	closesocket(servfd); -#endif +void ext_yahoo_got_buddy_change_group( int id, const char *me, const char *who, +                                       const char *old_group, const char *new_group ) +{ +	struct im_connection *ic = byahoo_get_ic_by_id( id ); +	 +	imcb_add_buddy( ic, who, new_group ); +} + +/* Because we don't want asynchronous connects in BitlBee, and because +   libyahoo doesn't seem to use this one anyway, this one is now defunct. */ +int ext_yahoo_connect(const char *host, int port) +{  	return -1;  } @@ -795,10 +821,14 @@ static void byahoo_accept_conf( void *data )  {  	struct byahoo_conf_invitation *inv = data;  	struct groupchat *b; +	GSList *l; -	for( b = inv->ic->groupchats; b; b = b->next ) +	for( l = inv->ic->groupchats; l; l = l->next ) +	{ +		b = l->data;  		if( b == inv->c )  			break; +	}  	if( b != NULL )  	{ @@ -864,9 +894,7 @@ void ext_yahoo_conf_userdecline( int id, const char *ignored, const char *who, c  void ext_yahoo_conf_userjoin( int id, const char *ignored, const char *who, const char *room )  {  	struct im_connection *ic = byahoo_get_ic_by_id( id ); -	struct groupchat *c; -	 -	for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next ); +	struct groupchat *c = bee_chat_by_title( ic->bee, ic, room );  	if( c )  		imcb_chat_add_buddy( c, (char*) who ); @@ -876,9 +904,7 @@ void ext_yahoo_conf_userleave( int id, const char *ignored, const char *who, con  {  	struct im_connection *ic = byahoo_get_ic_by_id( id ); -	struct groupchat *c; -	 -	for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next ); +	struct groupchat *c = bee_chat_by_title( ic->bee, ic, room );  	if( c )  		imcb_chat_remove_buddy( c, (char*) who, "" ); @@ -888,9 +914,7 @@ void ext_yahoo_conf_message( int id, const char *ignored, const char *who, const  {  	struct im_connection *ic = byahoo_get_ic_by_id( id );  	char *m = byahoo_strip( msg ); -	struct groupchat *c; -	 -	for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next ); +	struct groupchat *c = bee_chat_by_title( ic->bee, ic, room );  	if( c )  		imcb_chat_msg( c, (char*) who, (char*) m, 0, 0 ); @@ -901,7 +925,7 @@ void ext_yahoo_chat_cat_xml( int id, const char *xml )  {  } -void ext_yahoo_chat_join( int id, const char *who, const char *room, const char *topic, YList *members, int fd ) +void ext_yahoo_chat_join( int id, const char *who, const char *room, const char *topic, YList *members, void *fd )  {  } @@ -929,25 +953,18 @@ void ext_yahoo_chat_yahooerror( int id, const char *me )  {  } -void ext_yahoo_contact_auth_request( int id, const char *myid, const char *who, const char *msg ) -{ -	struct im_connection *ic = byahoo_get_ic_by_id( id ); -	 -	imcb_ask_auth( ic, who, NULL ); -} -  void ext_yahoo_contact_added( int id, const char *myid, const char *who, const char *msg )  {  	struct im_connection *ic = byahoo_get_ic_by_id( id ); -	imcb_add_buddy( ic, (char*) who, NULL ); +	imcb_ask_auth( ic, who, msg );  }  void ext_yahoo_rejected( int id, const char *who, const char *msg )  {  } -void ext_yahoo_game_notify( int id, const char *me, const char *who, int stat ) +void ext_yahoo_game_notify( int id, const char *me, const char *who, int stat, const char *msg )  {  } @@ -992,7 +1009,7 @@ void ext_yahoo_got_webcam_image( int id, const char * who, const unsigned char *  {  } -void ext_yahoo_got_ping( int id, const char *msg) +void ext_yahoo_got_ping( int id, const char *msg )  {  } diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h index 2184a321..589aaa5a 100644 --- a/protocols/yahoo/yahoo2.h +++ b/protocols/yahoo/yahoo2.h @@ -50,19 +50,18 @@ extern "C" {  #include "yahoo2_types.h" -/* returns the socket descriptor for a given pager connection. shouldn't be needed */ -int  yahoo_get_fd(int id); +/* returns the socket descriptor object for a given pager connection. shouldn't be needed */ +	void *yahoo_get_fd(int id);  /* says how much logging to do */  /* see yahoo2_types.h for the different values */ -int  yahoo_set_log_level(enum yahoo_log_level level); -enum yahoo_log_level  yahoo_get_log_level( void ); +	int yahoo_set_log_level(enum yahoo_log_level level); +	enum yahoo_log_level yahoo_get_log_level(void);  /* these functions should be self explanatory */  /* who always means the buddy you're acting on */  /* id is the successful value returned by yahoo_init */ -  /* init returns a connection id used to identify the connection hereon */  /* or 0 on failure */  /* you must call init before calling any other function */ @@ -87,101 +86,129 @@ enum yahoo_log_level  yahoo_get_log_level( void );   *   * You should set at least local_host if you intend to use webcams   */ -int  yahoo_init_with_attributes(const char *username, const char *password, ...); +	int yahoo_init_with_attributes(const char *username, +		const char *password, ...);  /* yahoo_init does the same as yahoo_init_with_attributes, assuming defaults   * for all attributes */ -int  yahoo_init(const char *username, const char *password); - - +	int yahoo_init(const char *username, const char *password);  /* release all resources held by this session */  /* you need to call yahoo_close for a session only if   * yahoo_logoff is never called for it (ie, it was never logged in) */ -void yahoo_close(int id); +	void yahoo_close(int id);  /* login logs in to the server */  /* initial is of type enum yahoo_status.  see yahoo2_types.h */ -void yahoo_login(int id, int initial); -void yahoo_logoff(int id); +	void yahoo_login(int id, int initial); +	void yahoo_logoff(int id);  /* reloads status of all buddies */ -void yahoo_refresh(int id); +	void yahoo_refresh(int id);  /* activates/deactivates an identity */ -void yahoo_set_identity_status(int id, const char * identity, int active); +	void yahoo_set_identity_status(int id, const char *identity, +		int active);  /* regets the entire buddy list from the server */ -void yahoo_get_list(int id); +	void yahoo_get_list(int id);  /* download buddy contact information from your yahoo addressbook */ -void yahoo_get_yab(int id); +	void yahoo_get_yab(int id);  /* add/modify an address book entry.  if yab->dbid is set, it will */  /* modify that entry else it creates a new entry */ -void yahoo_set_yab(int id, struct yab * yab); -void yahoo_keepalive(int id); -void yahoo_chat_keepalive(int id); +	void yahoo_set_yab(int id, struct yab *yab); +	void yahoo_keepalive(int id); +	void yahoo_chat_keepalive(int id);  /* from is the identity you're sending from.  if NULL, the default is used */  /* utf8 is whether msg is a utf8 string or not. */ -void yahoo_send_im(int id, const char *from, const char *who, const char *msg, int utf8, int picture); +	void yahoo_send_im(int id, const char *from, const char *who, +		const char *msg, int utf8, int picture); +	void yahoo_send_buzz(int id, const char *from, const char *who);  /* if type is true, send typing notice, else send stopped typing notice */ -void yahoo_send_typing(int id, const char *from, const char *who, int typ); +	void yahoo_send_typing(int id, const char *from, const char *who, +		int typ);  /* used to set away/back status. */  /* away says whether the custom message is an away message or a sig */ -void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away); - -void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg); -void yahoo_remove_buddy(int id, const char *who, const char *group); -void yahoo_reject_buddy(int id, const char *who, const char *msg); -void yahoo_stealth_buddy(int id, const char *who, int unstealth); +	void yahoo_set_away(int id, enum yahoo_status state, const char *msg, +		int away); + +	void yahoo_add_buddy(int id, const char *who, const char *group, +		const char *msg); +	void yahoo_remove_buddy(int id, const char *who, const char *group); +	void yahoo_confirm_buddy(int id, const char *who, int reject, +		const char *msg); +	void yahoo_stealth_buddy(int id, const char *who, int unstealth);  /* if unignore is true, unignore, else ignore */ -void yahoo_ignore_buddy(int id, const char *who, int unignore); -void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group); -void yahoo_group_rename(int id, const char *old_group, const char *new_group); - -void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg); -void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg); -void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg); -void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8); -void yahoo_conference_logon(int id, const char * from, YList *who, const char *room); -void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room); +	void yahoo_ignore_buddy(int id, const char *who, int unignore); +	void yahoo_change_buddy_group(int id, const char *who, +		const char *old_group, const char *new_group); +	void yahoo_group_rename(int id, const char *old_group, +		const char *new_group); + +	void yahoo_conference_invite(int id, const char *from, YList *who, +		const char *room, const char *msg); +	void yahoo_conference_addinvite(int id, const char *from, +		const char *who, const char *room, const YList *members, +		const char *msg); +	void yahoo_conference_decline(int id, const char *from, YList *who, +		const char *room, const char *msg); +	void yahoo_conference_message(int id, const char *from, YList *who, +		const char *room, const char *msg, int utf8); +	void yahoo_conference_logon(int id, const char *from, YList *who, +		const char *room); +	void yahoo_conference_logoff(int id, const char *from, YList *who, +		const char *room);  /* Get a list of chatrooms */ -void yahoo_get_chatrooms(int id,int chatroomid); +	void yahoo_get_chatrooms(int id, int chatroomid);  /* join room with specified roomname and roomid */ -void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid); +	void yahoo_chat_logon(int id, const char *from, const char *room, +		const char *roomid);  /* Send message "msg" to room with specified roomname, msgtype is 1-normal message or 2-/me mesage */ -void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8); +	void yahoo_chat_message(int id, const char *from, const char *room, +		const char *msg, const int msgtype, const int utf8);  /* Log off chat */ -void yahoo_chat_logoff(int id, const char *from); +	void yahoo_chat_logoff(int id, const char *from);  /* requests a webcam feed */  /* who is the person who's webcam you would like to view */  /* if who is null, then you're the broadcaster */ -void yahoo_webcam_get_feed(int id, const char *who); -void yahoo_webcam_close_feed(int id, const char *who); +	void yahoo_webcam_get_feed(int id, const char *who); +	void yahoo_webcam_close_feed(int id, const char *who);  /* sends an image when uploading */  /* image points to a JPEG-2000 image, length is the length of the image */  /* in bytes. The timestamp is the time in milliseconds since we started the */  /* webcam. */ -void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp); +	void yahoo_webcam_send_image(int id, unsigned char *image, +		unsigned int length, unsigned int timestamp);  /* this function should be called if we want to allow a user to watch the */  /* webcam. Who is the user we want to accept. */  /* Accept user (accept = 1), decline user (accept = 0) */ -void yahoo_webcam_accept_viewer(int id, const char* who, int accept); +	void yahoo_webcam_accept_viewer(int id, const char *who, int accept);  /* send an invitation to a user to view your webcam */ -void yahoo_webcam_invite(int id, const char *who); +	void yahoo_webcam_invite(int id, const char *who);  /* will set up a connection and initiate file transfer.   * callback will be called with the fd that you should write   * the file data to   */ -void yahoo_send_file(int id, const char *who, const char *msg, const char *name, unsigned long size, +	void yahoo_send_file(int id, const char *who, const char *msg, +		const char *name, unsigned long size,  		yahoo_get_fd_callback callback, void *data); +/* + * Respond to a file transfer request. Be sure to provide the callback data + * since that is your only chance to recognize future callbacks + */ +	void yahoo_send_file_transfer_response(int client_id, int response, +		char *id, void *data); + +  /* send a search request   */ -void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, +	void yahoo_search(int id, enum yahoo_search_type t, const char *text, +		enum yahoo_search_gender g, enum yahoo_search_agerange ar,  		int photo, int yahoo_only);  /* continue last search @@ -189,40 +216,32 @@ void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo   *   * where the above three are passed to ext_yahoo_got_search_result   */ -void yahoo_search_again(int id, int start); - -/* returns a socket fd to a url for downloading a file. */ -void yahoo_get_url_handle(int id, const char *url,  -		yahoo_get_url_handle_callback callback, void *data); +	void yahoo_search_again(int id, int start);  /* these should be called when input is available on a fd */  /* registered by ext_yahoo_add_handler */  /* if these return negative values, errno may be set */ -int  yahoo_read_ready(int id, int fd, void *data); -int  yahoo_write_ready(int id, int fd, void *data); +	int yahoo_read_ready(int id, void *fd, void *data); +	int yahoo_write_ready(int id, void *fd, void *data);  /* utility functions. these do not hit the server */ -enum yahoo_status yahoo_current_status(int id); -const YList * yahoo_get_buddylist(int id); -const YList * yahoo_get_ignorelist(int id); -const YList * yahoo_get_identities(int id); +	enum yahoo_status yahoo_current_status(int id); +	const YList *yahoo_get_buddylist(int id); +	const YList *yahoo_get_ignorelist(int id); +	const YList *yahoo_get_identities(int id);  /* 'which' could be y, t, c or login.  This may change in later versions. */ -const char  * yahoo_get_cookie(int id, const char *which); +	const char *yahoo_get_cookie(int id, const char *which);  /* returns the url used to get user profiles - you must append the user id */  /* as of now this is http://profiles.yahoo.com/ */  /* You'll have to do urlencoding yourself, but see yahoo_httplib.h first */ -const char  * yahoo_get_profile_url( void ); - -void yahoo_buddyicon_request(int id, const char *who); +	const char *yahoo_get_profile_url(void); -void yahoo_accept_buddy_ymsg13(int,const char*,const char*); -void yahoo_reject_buddy_ymsg13(int,const char*,const char*,const char*); +	void yahoo_buddyicon_request(int id, const char *who);  #include "yahoo_httplib.h"  #ifdef __cplusplus  }  #endif -  #endif diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h index e2c8ea42..0dccf188 100644 --- a/protocols/yahoo/yahoo2_callbacks.h +++ b/protocols/yahoo/yahoo2_callbacks.h @@ -29,7 +29,6 @@   * declared in this file and defined in libyahoo2.c   */ -  #ifndef YAHOO2_CALLBACKS_H  #define YAHOO2_CALLBACKS_H @@ -45,25 +44,26 @@ extern "C" {   * Callback interface for libyahoo2   */ -typedef enum { -	YAHOO_INPUT_READ = 1 << 0, -	YAHOO_INPUT_WRITE = 1 << 1, -	YAHOO_INPUT_EXCEPTION = 1 << 2 -} yahoo_input_condition; +	typedef enum { +		YAHOO_INPUT_READ = 1 << 0, +		YAHOO_INPUT_WRITE = 1 << 1, +		YAHOO_INPUT_EXCEPTION = 1 << 2 +	} yahoo_input_condition;  /*   * A callback function called when an asynchronous connect completes.   *    * Params: - *     fd    - The file descriptor that has been connected, or -1 on error + *     fd    - The file descriptor object that has been connected, or NULL on  + *             error   *     error - The value of errno set by the call to connect or 0 if no error   *	       Set both fd and error to 0 if the connect was cancelled by the   *	       user   *     callback_data - the callback_data passed to the ext_yahoo_connect_async   *	       function   */ -typedef void (*yahoo_connect_callback)(int fd, int error, void *callback_data); - +	typedef void (*yahoo_connect_callback) (void *fd, int error, +		void *callback_data);  /*   * The following functions need to be implemented in the client @@ -93,8 +93,8 @@ struct yahoo_callbacks {   * 	succ - enum yahoo_login_status   * 	url  - url to reactivate account if locked   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, const char *url); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response) (int id, int succ, +		const char *url);  /*   * Name: ext_yahoo_got_buddies @@ -103,8 +103,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, const char   * 	id   - the id that identifies the server connection   * 	buds - the buddy list   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies) (int id, YList *buds);  /*   * Name: ext_yahoo_got_ignore @@ -113,8 +112,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds);   * 	id   - the id that identifies the server connection   * 	igns - the ignore list   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore) (int id, YList *igns);  /*   * Name: ext_yahoo_got_identities @@ -123,8 +121,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns);   * 	id   - the id that identifies the server connection   * 	ids  - the identity list   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities) (int id, YList *ids);  /*   * Name: ext_yahoo_got_cookies @@ -132,8 +129,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids);   * Params:   * 	id   - the id that identifies the server connection   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies) (int id);  /*   * Name: ext_yahoo_got_ping @@ -142,8 +138,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id);   * 	id   - the id that identifies the server connection   *  errormsg - optional error message   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ping)(int id, const char *errormsg); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ping) (int id, +		const char *errormsg);  /*   * Name: ext_yahoo_status_changed @@ -158,8 +154,21 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ping)(int id, const char *errormsg);   *	mobile - this is set for mobile users/buddies   *	TODO: add support for pager, chat, and game states   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, const char *who, int stat, const char *msg, int away, int idle, int mobile); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed) (int id, +		const char *who, int stat, const char *msg, int away, int idle, +		int mobile); +/* + * Name: ext_yahoo_got_buzz + * 	Called when remote user sends you a buzz. + * Params: + * 	id   - the id that identifies the server connection + * 	me   - the identity the message was sent to + * 	who  - the handle of the remote user + * 	tm   - timestamp of message if offline + */ +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buzz) (int id, const char *me, +		const char *who, long tm);  /*   * Name: ext_yahoo_got_im @@ -176,8 +185,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, const char *who, int   * 				5   * 	utf8 - whether the message is encoded as utf8 or not   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im) (int id, const char *me, +		const char *who, const char *msg, long tm, int stat, int utf8);  /*   * Name: ext_yahoo_got_conf_invite @@ -190,8 +199,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, const char *me, const char *w   * 	msg  - the message   *	members - the initial members of the conference (null terminated list)   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, const char *me, const char *who, const char *room, const char *msg, YList *members); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite) (int id, +		const char *me, const char *who, const char *room, +		const char *msg, YList *members);  /*   * Name: ext_yahoo_conf_userdecline @@ -203,8 +213,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, const char *me, cons   * 	room - the room   * 	msg  - the declining message   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, const char *me, const char *who, const char *room, const char *msg); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline) (int id, +		const char *me, const char *who, const char *room, +		const char *msg);  /*   * Name: ext_yahoo_conf_userjoin @@ -215,8 +226,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, const char *me, con   * 	who  - the user who has joined   * 	room - the room joined   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, const char *me, const char *who, const char *room); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin) (int id, +		const char *me, const char *who, const char *room);  /*   * Name: ext_yahoo_conf_userleave @@ -227,8 +238,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, const char *me, const   * 	who  - the user who has left   * 	room - the room left   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, const char *me, const char *who, const char *room); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave) (int id, +		const char *me, const char *who, const char *room);  /*   * Name: ext_yahoo_chat_cat_xml @@ -237,8 +248,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, const char *me, const   * 	id      - the id that identifies the server connection   * 	xml     - ?   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, const char *xml); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml) (int id, +		const char *xml);  /*   * Name: ext_yahoo_chat_join @@ -251,10 +262,10 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, const char *xml);   * 	topic   - the topic of the room, freed by library after call   *	members - the initial members of the chatroom (null terminated YList    *	          of yahoo_chat_member's) Must be freed by the client - *	fd	- the socket where the connection is coming from (for tracking) + *	fd	- the object where the connection is coming from (for tracking)   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, const char *me, const char *room, const char *topic, YList *members, int fd); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join) (int id, const char *me, +		const char *room, const char *topic, YList *members, void *fd);  /*   * Name: ext_yahoo_chat_userjoin @@ -265,8 +276,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, const char *me, const char   * 	room - the room joined   * 	who  - the user who has joined, Must be freed by the client   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, const char *me, const char *room, struct yahoo_chat_member *who); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin) (int id, +		const char *me, const char *room, +		struct yahoo_chat_member *who);  /*   * Name: ext_yahoo_chat_userleave @@ -277,8 +289,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, const char *me, const   * 	room - the room left   * 	who  - the user who has left (Just the User ID)   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, const char *me, const char *room, const char *who); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave) (int id, +		const char *me, const char *room, const char *who);  /*   * Name: ext_yahoo_chat_message @@ -293,8 +305,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, const char *me, const   * 		   2 = /me type message   * 	utf8 - whether the message is utf8 encoded or not   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, const char *me, const char *who, const char *room, const char *msg, int msgtype, int utf8); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message) (int id, +		const char *me, const char *who, const char *room, +		const char *msg, int msgtype, int utf8);  /*   * @@ -309,8 +322,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, const char *me, const c   * Returns:   *	nothing.   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id, const char *me); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout) (int id, +		const char *me);  /*   * @@ -326,8 +339,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id, const char *me);   * Returns:   *	nothing.   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id, const char *me); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror) (int id, +		const char *me);  /*   * Name: ext_yahoo_conf_message @@ -340,8 +353,9 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id, const char *me);   * 	msg  - the message   * 	utf8 - whether the message is utf8 encoded or not   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message)(int id, const char *me, const char *who, const char *room, const char *msg, int utf8); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message) (int id, +		const char *me, const char *who, const char *room, +		const char *msg, int utf8);  /*   * Name: ext_yahoo_got_file @@ -350,26 +364,42 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message)(int id, const char *me, const c   * 	id   - the id that identifies the server connection   * 	me   - the identity the file was sent to   * 	who  - the user who sent the file - * 	url  - the file url - * 	expires  - the expiry date of the file on the server (timestamp)   * 	msg  - the message   * 	fname- the file name if direct transfer   * 	fsize- the file size if direct transfer + * 	trid - transfer id. Unique for this transfer + * + * NOTE: Subsequent callbacks for file transfer do not send all of this + * information again since it is wasteful. Implementations are expected to + * save this information and supply it as callback data when the file or + * confirmation is sent   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, const char *me, const char *who, const char *url, long expires, const char *msg, const char *fname, unsigned long fesize); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file) (int id, const char *me, +		const char *who, const char *msg, const char *fname, +		unsigned long fesize, char *trid);  /* - * Name: ext_yahoo_contact_auth_request - * 	Called when a contact wants to add you to his/her contact list + * Name: ext_yahoo_got_ft_data + * 	Called multiple times when parts of the file are received   * Params:   * 	id   - the id that identifies the server connection - * 	myid - the identity s/he added - * 	who  - who did it - * 	msg  - any message sent + * 	in   - The data + * 	len  - Length of the data + * 	data - callback data   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myid, const char *who, const char *msg); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ft_data) (int id, +		const unsigned char *in, int len, void *data); +/* + * Name: ext_yahoo_file_transfer_done + * 	File transfer is done + * Params: + * 	id     - the id that identifies the server connection + * 	result - To notify if it finished successfully or with a failure + * 	data   - callback data + */ +	void YAHOO_CALLBACK_TYPE(ext_yahoo_file_transfer_done) (int id, +		int result, void *data);  /*   * Name: ext_yahoo_contact_added @@ -380,8 +410,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myi   * 	who  - who was added   * 	msg  - any message sent   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, const char *myid, const char *who, const char *msg); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added) (int id, +		const char *myid, const char *who, const char *msg);  /*   * Name: ext_yahoo_rejected @@ -391,8 +421,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, const char *myid, cons   * 	who  - who rejected you   * 	msg  - any message sent   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, const char *who, const char *msg); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected) (int id, const char *who, +		const char *msg);  /*   * Name: ext_yahoo_typing_notify @@ -403,8 +433,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, const char *who, const char   * 	who  - the handle of the remote user   * 	stat - 1 if typing, 0 if stopped typing   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify)(int id, const char *me, const char *who, int stat); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify) (int id, +		const char *me, const char *who, int stat);  /*   * Name: ext_yahoo_game_notify @@ -414,9 +444,10 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify)(int id, const char *me, const   * 	me   - the handle of the identity the notification is sent to   * 	who  - the handle of the remote user   * 	stat - 1 if game, 0 if stopped gaming + * 	msg  - game description and/or other text   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, const char *me, const char *who, int stat); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify) (int id, const char *me, +		const char *who, int stat, const char *msg);  /*   * Name: ext_yahoo_mail_notify @@ -427,17 +458,20 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, const char *me, const ch   * 	subj - the subject of the mail - NULL if only mail count   * 	cnt  - mail count - 0 if new mail notification   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify)(int id, const char *from, const char *subj, int cnt); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify) (int id, +		const char *from, const char *subj, int cnt);  /*   * Name: ext_yahoo_system_message   * 	System message   * Params:   * 	id   - the id that identifies the server connection + * 	me   - the handle of the identity the notification is sent to + * 	who  - the source of the system message (there are different types)   * 	msg  - the message   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, const char *msg); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message) (int id, +		const char *me, const char *who, const char *msg);  /*   * Name: ext_yahoo_got_buddyicon @@ -449,7 +483,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, const char *msg);   * 	url - the url to use to load the icon   * 	checksum - the checksum of the icon content   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon)(int id, const char *me, const char *who, const char *url, int checksum); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon) (int id, +		const char *me, const char *who, const char *url, int checksum);  /*   * Name: ext_yahoo_got_buddyicon_checksum @@ -460,7 +495,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon)(int id, const char *me, const   * 	who - the yahoo id of the buddy icon checksum is for   * 	checksum - the checksum of the icon content   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_checksum)(int id, const char *me,const char *who, int checksum); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_checksum) (int id, +		const char *me, const char *who, int checksum);  /*   * Name: ext_yahoo_got_buddyicon_request @@ -470,7 +506,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_checksum)(int id, const char *m   * 	me - the handle of the identity the notification is sent to   * 	who - the yahoo id of the buddy that requested the buddy icon   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_request)(int id, const char *me, const char *who); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_request) (int id, +		const char *me, const char *who);  /*   * Name: ext_yahoo_got_buddyicon_request @@ -479,7 +516,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddyicon_request)(int id, const char *me   * 	id - the id that identifies the server connection   * 	url - remote url, the uploaded buddy icon can be fetched from   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_buddyicon_uploaded)(int id, const char *url); +	void YAHOO_CALLBACK_TYPE(ext_yahoo_buddyicon_uploaded) (int id, +		const char *url);  /*   * Name: ext_yahoo_got_webcam_image @@ -504,11 +542,11 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_buddyicon_uploaded)(int id, const char *url);   *	to transport then others. When image_size is 0 we can still receive   *	a timestamp to stay in sync   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who, -		const unsigned char *image, unsigned int image_size, unsigned int real_size, +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image) (int id, +		const char *who, const unsigned char *image, +		unsigned int image_size, unsigned int real_size,  		unsigned int timestamp); -  /*   * Name: ext_yahoo_webcam_invite   * 	Called when you get a webcam invitation @@ -517,8 +555,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who,   * 	me   - identity the invitation is to   * 	from - who the invitation is from   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, const char *me, const char *from); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite) (int id, +		const char *me, const char *from);  /*   * Name: ext_yahoo_webcam_invite_reply @@ -529,8 +567,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, const char *me, const   * 	from - who the invitation response is from   *	accept - 0 (decline), 1 (accept)   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, const char *me, const char *from, int accept); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply) (int id, +		const char *me, const char *from, int accept);  /*   * Name: ext_yahoo_webcam_closed @@ -544,8 +582,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, const char *me,   *	         3 = user declines permission   *	         4 = user does not have webcam online   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, const char *who, int reason); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed) (int id, +		const char *who, int reason);  /*   * Name: ext_yahoo_got_search_result @@ -559,8 +597,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, const char *who, int r   * 		   these will be freed after this function returns, so   * 		   if you need to use the information, make a copy   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int start, int total, YList *contacts); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result) (int id, +		int found, int start, int total, YList *contacts);  /*   * Name: ext_yahoo_error @@ -571,8 +609,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int sta   * 	fatal- whether this error is fatal to the connection or not   * 	num  - Which error is this   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, const char *err, int fatal, int num); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_error) (int id, const char *err, +		int fatal, int num);  /*   * Name: ext_yahoo_webcam_viewer @@ -582,8 +620,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, const char *err, int fatal, in   *	who - the viewer   *	connect - 0=disconnect 1=connect 2=request   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, const char *who, int connect); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer) (int id, +		const char *who, int connect);  /*   * Name: ext_yahoo_webcam_data_request @@ -592,8 +630,8 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, const char *who, int c   *	id   - the id that identifies the server connection   *	send - whether to send images or not   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request) (int id, +		int send);  /*   * Name: ext_yahoo_log @@ -603,8 +641,7 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send);   * Returns:   * 	0   */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(const char *fmt, ...); - +	int YAHOO_CALLBACK_TYPE(ext_yahoo_log) (const char *fmt, ...);  /*   * Name: ext_yahoo_add_handler @@ -613,14 +650,14 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(const char *fmt, ...);   * 	when a YAHOO_INPUT_WRITE fd is ready.   * Params:   * 	id   - the id that identifies the server connection - * 	fd   - the fd on which to listen + * 	fd   - the fd object on which to listen   * 	cond - the condition on which to call the callback   * 	data - callback data to pass to yahoo_*_ready   * 	   * Returns: a tag to be used when removing the handler   */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condition cond, void *data); - +	int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler) (int id, void *fd, +		yahoo_input_condition cond, void *data);  /*   * Name: ext_yahoo_remove_handler @@ -629,8 +666,7 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condi   * 	id   - the id that identifies the connection   * 	tag  - the handler tag to remove   */ -void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag); - +	void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler) (int id, int tag);  /*   * Name: ext_yahoo_connect @@ -641,12 +677,11 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag);   * Returns:   * 	a unix file descriptor to the socket   */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(const char *host, int port); - +	int YAHOO_CALLBACK_TYPE(ext_yahoo_connect) (const char *host, int port);  /*   * Name: ext_yahoo_connect_async - * 	Connect to a host:port asynchronously.  This function should return + * 	Connect to a host:port asynchronously. This function should return   * 	immediately returing a tag used to identify the connection handler,   * 	or a pre-connect error (eg: host name lookup failure).   * 	Once the connect completes (successfully or unsuccessfully), callback @@ -659,11 +694,74 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(const char *host, int port);   * 	port - the port to connect on   * 	callback - function to call when connect completes   * 	callback_data - data to pass to the callback function + * 	use_ssl - Whether we need an SSL connection   * Returns: - * 	a unix file descriptor to the socket + * 	a tag signifying the connection attempt + */ +	int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async) (int id, +		const char *host, int port, yahoo_connect_callback callback, +		void *callback_data, int use_ssl); + +/* + * Name: ext_yahoo_get_ip_addr + * 	get IP Address for a domain name + * Params: + * 	domain - Domain name + * Returns: + * 	Newly allocated string containing the IP Address in IPv4 notation   */ -int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, const char *host, int port,  -		yahoo_connect_callback callback, void *callback_data); +	char *YAHOO_CALLBACK_TYPE(ext_yahoo_get_ip_addr) (const char *domain); + +/* + * Name: ext_yahoo_write + * 	Write data from the buffer into the socket for the specified connection + * Params: + * 	fd  - the file descriptor object that identifies this connection + * 	buf - Buffer to write the data from + * 	len - Length of the data + * Returns: + * 	Number of bytes written or -1 for error + */ +	int YAHOO_CALLBACK_TYPE(ext_yahoo_write) (void *fd, char *buf, int len); + +/* + * Name: ext_yahoo_read + * 	Read data into a buffer from socket for the specified connection + * Params: + * 	fd  - the file descriptor object that identifies this connection + * 	buf - Buffer to read the data into + * 	len - Max length to read + * Returns: + * 	Number of bytes read or -1 for error + */ +	int YAHOO_CALLBACK_TYPE(ext_yahoo_read) (void *fd, char *buf, int len); + +/* + * Name: ext_yahoo_close + * 	Close the file descriptor object and free its resources. Libyahoo2 will not + * 	use this object again. + * Params: + * 	fd  - the file descriptor object that identifies this connection + * Returns: + * 	Nothing + */ +	void YAHOO_CALLBACK_TYPE(ext_yahoo_close) (void *fd); + +/* + * Name: ext_yahoo_got_buddy_change_group + * 	Acknowledgement of buddy changing group + * Params: + * 	id: client id + * 	me: The user + * 	who: Buddy name + * 	old_group: Old group name + * 	new_group: New group name + * Returns: + * 	Nothing + */ +	void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddy_change_group) (int id, +		const char *me, const char *who, const char *old_group, +		const char *new_group);  #ifdef USE_STRUCT_CALLBACKS  }; @@ -672,7 +770,7 @@ int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, const char *host, int p   * if using a callback structure, call yahoo_register_callbacks   * before doing anything else   */ -void yahoo_register_callbacks(struct yahoo_callbacks * tyc); +void yahoo_register_callbacks(struct yahoo_callbacks *tyc);  #undef YAHOO_CALLBACK_TYPE @@ -683,4 +781,3 @@ void yahoo_register_callbacks(struct yahoo_callbacks * tyc);  #endif  #endif - diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h index f05acb3c..bbade5d8 100644 --- a/protocols/yahoo/yahoo2_types.h +++ b/protocols/yahoo/yahoo2_types.h @@ -28,74 +28,175 @@  extern "C" {  #endif -enum yahoo_status { -	YAHOO_STATUS_DISCONNECTED = -1, -	YAHOO_STATUS_AVAILABLE = 0, -	YAHOO_STATUS_BRB, -	YAHOO_STATUS_BUSY, -	YAHOO_STATUS_NOTATHOME, -	YAHOO_STATUS_NOTATDESK, -	YAHOO_STATUS_NOTINOFFICE, -	YAHOO_STATUS_ONPHONE, -	YAHOO_STATUS_ONVACATION, -	YAHOO_STATUS_OUTTOLUNCH, -	YAHOO_STATUS_STEPPEDOUT, -	YAHOO_STATUS_INVISIBLE = 12, -	YAHOO_STATUS_CUSTOM = 99, -	YAHOO_STATUS_IDLE = 999, -	YAHOO_STATUS_WEBLOGIN = 0x5a55aa55, -	YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */ -	YAHOO_STATUS_NOTIFY = 0x16 /* TYPING */ -}; -#define YAHOO_STATUS_GAME	0x2 		/* Games don't fit into the regular status model */ - -enum yahoo_login_status { -	YAHOO_LOGIN_OK = 0, -	YAHOO_LOGIN_LOGOFF = 2, -	YAHOO_LOGIN_UNAME = 3, -	YAHOO_LOGIN_PASSWD = 13, -	YAHOO_LOGIN_LOCK = 14, -	YAHOO_LOGIN_DUPL = 99, -	YAHOO_LOGIN_SOCK = -1, -}; - -enum ypacket_status { -	YPACKET_STATUS_DISCONNECTED = -1, -	YPACKET_STATUS_DEFAULT = 0, -	YPACKET_STATUS_SERVERACK = 1, -	YPACKET_STATUS_GAME     = 0x2, -	YPACKET_STATUS_AWAY     = 0x4, -	YPACKET_STATUS_CONTINUED = 0x5, -	YPACKET_STATUS_INVISIBLE = 12, -	YPACKET_STATUS_NOTIFY = 0x16, /* TYPING */ -	YPACKET_STATUS_WEBLOGIN = 0x5a55aa55, -	YPACKET_STATUS_OFFLINE = 0x5a55aa56 -}; - -enum yahoo_error { -	E_UNKNOWN = -1, -	E_CONNECTION = -2, -	E_SYSTEM = -3, -	E_CUSTOM = 0, - -	/* responses from ignore buddy */ -	E_IGNOREDUP = 2, -	E_IGNORENONE = 3, -	E_IGNORECONF = 12, -	 -	/* conference */ -	E_CONFNOTAVAIL = 20 -}; - -enum yahoo_log_level { -	YAHOO_LOG_NONE = 0, -	YAHOO_LOG_FATAL, -	YAHOO_LOG_ERR, -	YAHOO_LOG_WARNING, -	YAHOO_LOG_NOTICE, -	YAHOO_LOG_INFO, -	YAHOO_LOG_DEBUG -}; +	enum yahoo_service {	/* these are easier to see in hex */ +		YAHOO_SERVICE_LOGON = 1, +		YAHOO_SERVICE_LOGOFF, +		YAHOO_SERVICE_ISAWAY, +		YAHOO_SERVICE_ISBACK, +		YAHOO_SERVICE_IDLE,	/* 5 (placemarker) */ +		YAHOO_SERVICE_MESSAGE, +		YAHOO_SERVICE_IDACT, +		YAHOO_SERVICE_IDDEACT, +		YAHOO_SERVICE_MAILSTAT, +		YAHOO_SERVICE_USERSTAT,	/* 0xa */ +		YAHOO_SERVICE_NEWMAIL, +		YAHOO_SERVICE_CHATINVITE, +		YAHOO_SERVICE_CALENDAR, +		YAHOO_SERVICE_NEWPERSONALMAIL, +		YAHOO_SERVICE_NEWCONTACT, +		YAHOO_SERVICE_ADDIDENT,	/* 0x10 */ +		YAHOO_SERVICE_ADDIGNORE, +		YAHOO_SERVICE_PING, +		YAHOO_SERVICE_GOTGROUPRENAME,	/* < 1, 36(old), 37(new) */ +		YAHOO_SERVICE_SYSMESSAGE = 0x14, +		YAHOO_SERVICE_SKINNAME = 0x15, +		YAHOO_SERVICE_PASSTHROUGH2 = 0x16, +		YAHOO_SERVICE_CONFINVITE = 0x18, +		YAHOO_SERVICE_CONFLOGON, +		YAHOO_SERVICE_CONFDECLINE, +		YAHOO_SERVICE_CONFLOGOFF, +		YAHOO_SERVICE_CONFADDINVITE, +		YAHOO_SERVICE_CONFMSG, +		YAHOO_SERVICE_CHATLOGON, +		YAHOO_SERVICE_CHATLOGOFF, +		YAHOO_SERVICE_CHATMSG = 0x20, +		YAHOO_SERVICE_GAMELOGON = 0x28, +		YAHOO_SERVICE_GAMELOGOFF, +		YAHOO_SERVICE_GAMEMSG = 0x2a, +		YAHOO_SERVICE_FILETRANSFER = 0x46, +		YAHOO_SERVICE_VOICECHAT = 0x4A, +		YAHOO_SERVICE_NOTIFY, +		YAHOO_SERVICE_VERIFY, +		YAHOO_SERVICE_P2PFILEXFER, +		YAHOO_SERVICE_PEERTOPEER = 0x4F,	/* Checks if P2P possible */ +		YAHOO_SERVICE_WEBCAM, +		YAHOO_SERVICE_AUTHRESP = 0x54, +		YAHOO_SERVICE_LIST, +		YAHOO_SERVICE_AUTH = 0x57, +		YAHOO_SERVICE_AUTHBUDDY = 0x6d, +		YAHOO_SERVICE_ADDBUDDY = 0x83, +		YAHOO_SERVICE_REMBUDDY, +		YAHOO_SERVICE_IGNORECONTACT,	/* > 1, 7, 13 < 1, 66, 13, 0 */ +		YAHOO_SERVICE_REJECTCONTACT, +		YAHOO_SERVICE_GROUPRENAME = 0x89,	/* > 1, 65(new), 66(0), 67(old) */ +		YAHOO_SERVICE_Y7_PING = 0x8A, +		YAHOO_SERVICE_CHATONLINE = 0x96,	/* > 109(id), 1, 6(abcde) < 0,1 */ +		YAHOO_SERVICE_CHATGOTO, +		YAHOO_SERVICE_CHATJOIN,	/* > 1 104-room 129-1600326591 62-2 */ +		YAHOO_SERVICE_CHATLEAVE, +		YAHOO_SERVICE_CHATEXIT = 0x9b, +		YAHOO_SERVICE_CHATADDINVITE = 0x9d, +		YAHOO_SERVICE_CHATLOGOUT = 0xa0, +		YAHOO_SERVICE_CHATPING, +		YAHOO_SERVICE_COMMENT = 0xa8, +		YAHOO_SERVICE_GAME_INVITE = 0xb7, +		YAHOO_SERVICE_STEALTH_PERM = 0xb9, +		YAHOO_SERVICE_STEALTH_SESSION = 0xba, +		YAHOO_SERVICE_AVATAR = 0xbc, +		YAHOO_SERVICE_PICTURE_CHECKSUM = 0xbd, +		YAHOO_SERVICE_PICTURE = 0xbe, +		YAHOO_SERVICE_PICTURE_UPDATE = 0xc1, +		YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2, +		YAHOO_SERVICE_YAB_UPDATE = 0xc4, +		YAHOO_SERVICE_Y6_VISIBLE_TOGGLE = 0xc5,	/* YMSG13, key 13: 2 = invisible, 1 = visible */ +		YAHOO_SERVICE_Y6_STATUS_UPDATE = 0xc6,	/* YMSG13 */ +		YAHOO_SERVICE_PICTURE_STATUS = 0xc7,	/* YMSG13, key 213: 0 = none, 1 = avatar, 2 = picture */ +		YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8, +		YAHOO_SERVICE_AUDIBLE = 0xd0, +		YAHOO_SERVICE_Y7_PHOTO_SHARING = 0xd2, +		YAHOO_SERVICE_Y7_CONTACT_DETAILS = 0xd3,	/* YMSG13 */ +		YAHOO_SERVICE_Y7_CHAT_SESSION = 0xd4, +		YAHOO_SERVICE_Y7_AUTHORIZATION = 0xd6,	/* YMSG13 */ +		YAHOO_SERVICE_Y7_FILETRANSFER = 0xdc,	/* YMSG13 */ +		YAHOO_SERVICE_Y7_FILETRANSFERINFO,	/* YMSG13 */ +		YAHOO_SERVICE_Y7_FILETRANSFERACCEPT,	/* YMSG13 */ +		YAHOO_SERVICE_Y7_MINGLE = 0xe1,	/* YMSG13 */ +		YAHOO_SERVICE_Y7_CHANGE_GROUP = 0xe7,	/* YMSG13 */ +		YAHOO_SERVICE_MYSTERY = 0xef,	/* Don't know what this is for */ +		YAHOO_SERVICE_Y8_STATUS = 0xf0,	/* YMSG15 */ +		YAHOO_SERVICE_Y8_LIST = 0Xf1,	/* YMSG15 */ +		YAHOO_SERVICE_MESSAGE_CONFIRM = 0xfb, +		YAHOO_SERVICE_WEBLOGIN = 0x0226, +		YAHOO_SERVICE_SMS_MSG = 0x02ea +	}; + +	enum yahoo_status { +		YAHOO_STATUS_AVAILABLE = 0, +		YAHOO_STATUS_BRB, +		YAHOO_STATUS_BUSY, +		YAHOO_STATUS_NOTATHOME, +		YAHOO_STATUS_NOTATDESK, +		YAHOO_STATUS_NOTINOFFICE, +		YAHOO_STATUS_ONPHONE, +		YAHOO_STATUS_ONVACATION, +		YAHOO_STATUS_OUTTOLUNCH, +		YAHOO_STATUS_STEPPEDOUT, +		YAHOO_STATUS_INVISIBLE = 12, +		YAHOO_STATUS_CUSTOM = 99, +		YAHOO_STATUS_IDLE = 999, +		YAHOO_STATUS_OFFLINE = 0x5a55aa56	/* don't ask */ +	}; + +	enum ypacket_status { +		YPACKET_STATUS_DISCONNECTED = -1, +		YPACKET_STATUS_DEFAULT = 0, +		YPACKET_STATUS_SERVERACK = 1, +		YPACKET_STATUS_GAME = 0x2, +		YPACKET_STATUS_AWAY = 0x4, +		YPACKET_STATUS_CONTINUED = 0x5, +		YPACKET_STATUS_INVISIBLE = 12, +		YPACKET_STATUS_NOTIFY = 0x16,	/* TYPING */ +		YPACKET_STATUS_WEBLOGIN = 0x5a55aa55, +		YPACKET_STATUS_OFFLINE = 0x5a55aa56 +	}; + +#define YAHOO_STATUS_GAME	0x2	/* Games don't fit into the regular status model */ + +	enum yahoo_login_status { +		YAHOO_LOGIN_OK = 0, +		YAHOO_LOGIN_LOGOFF = 1, +		YAHOO_LOGIN_UNAME = 3, +		YAHOO_LOGIN_PASSWD = 13, +		YAHOO_LOGIN_LOCK = 14, +		YAHOO_LOGIN_DUPL = 99, +		YAHOO_LOGIN_SOCK = -1, +		YAHOO_LOGIN_UNKNOWN = 999 +	}; + +	enum yahoo_error { +		E_UNKNOWN = -1, +		E_CONNECTION = -2, +		E_SYSTEM = -3, +		E_CUSTOM = 0, + +		/* responses from ignore buddy */ +		E_IGNOREDUP = 2, +		E_IGNORENONE = 3, +		E_IGNORECONF = 12, + +		/* conference */ +		E_CONFNOTAVAIL = 20 +	}; + +	enum yahoo_log_level { +		YAHOO_LOG_NONE = 0, +		YAHOO_LOG_FATAL, +		YAHOO_LOG_ERR, +		YAHOO_LOG_WARNING, +		YAHOO_LOG_NOTICE, +		YAHOO_LOG_INFO, +		YAHOO_LOG_DEBUG +	}; + +	enum yahoo_file_transfer { +		YAHOO_FILE_TRANSFER_INIT = 1, +		YAHOO_FILE_TRANSFER_ACCEPT = 3, +		YAHOO_FILE_TRANSFER_REJECT = 4, +		YAHOO_FILE_TRANSFER_DONE = 5, +		YAHOO_FILE_TRANSFER_RELAY, +		YAHOO_FILE_TRANSFER_FAILED, +		YAHOO_FILE_TRANSFER_UNKNOWN +	};  #define YAHOO_PROTO_VER 0x0010 @@ -120,171 +221,176 @@ enum yahoo_log_level {  #define YAHOO_STYLE_URLON "\033[lm"  #define YAHOO_STYLE_URLOFF "\033[xlm" -enum yahoo_connection_type { -	YAHOO_CONNECTION_PAGER=0, -	YAHOO_CONNECTION_FT, -	YAHOO_CONNECTION_YAB, -	YAHOO_CONNECTION_WEBCAM_MASTER, -	YAHOO_CONNECTION_WEBCAM, -	YAHOO_CONNECTION_CHATCAT, -	YAHOO_CONNECTION_SEARCH, -	YAHOO_CONNECTION_AUTH, -}; - -enum yahoo_webcam_direction_type { -        YAHOO_WEBCAM_DOWNLOAD=0, -        YAHOO_WEBCAM_UPLOAD -}; - -enum yahoo_stealth_visibility_type { -	YAHOO_STEALTH_DEFAULT = 0, -  	YAHOO_STEALTH_ONLINE, -  	YAHOO_STEALTH_PERM_OFFLINE -}; +	enum yahoo_connection_type { +		YAHOO_CONNECTION_PAGER = 0, +		YAHOO_CONNECTION_FT, +		YAHOO_CONNECTION_YAB, +		YAHOO_CONNECTION_WEBCAM_MASTER, +		YAHOO_CONNECTION_WEBCAM, +		YAHOO_CONNECTION_CHATCAT, +		YAHOO_CONNECTION_SEARCH, +		YAHOO_CONNECTION_AUTH +	}; + +	enum yahoo_webcam_direction_type { +		YAHOO_WEBCAM_DOWNLOAD = 0, +		YAHOO_WEBCAM_UPLOAD +	}; + +	enum yahoo_stealth_visibility_type { +		YAHOO_STEALTH_DEFAULT = 0, +		YAHOO_STEALTH_ONLINE, +		YAHOO_STEALTH_PERM_OFFLINE +	};  /* chat member attribs */  #define YAHOO_CHAT_MALE 0x8000  #define YAHOO_CHAT_FEMALE 0x10000 +#define YAHOO_CHAT_FEMALE 0x10000  #define YAHOO_CHAT_DUNNO 0x400  #define YAHOO_CHAT_WEBCAM 0x10 -enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 }; - -struct yahoo_webcam { -	int direction;     /* Uploading or downloading */ -	int conn_type;     /* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */ - -	char *user;        /* user we are viewing */ -	char *server;      /* webcam server to connect to */ -	int   port;        /* webcam port to connect on */ -	char *key;         /* key to connect to the server with */ -	char *description; /* webcam description */ -	char *my_ip;       /* own ip number */ -}; - -struct yahoo_webcam_data { -	unsigned int data_size; -	unsigned int to_read; -	unsigned int timestamp; -	unsigned char packet_type; -}; - -struct yahoo_data { -	char  *user; -	char  *password; - -	char  *cookie_y; -	char  *cookie_t; -	char  *cookie_c; -	char  *login_cookie; - -	YList *buddies; -	YList *ignore; -	YList *identities; -	char  *login_id; - -	int   current_status; -	int   initial_status; -	int   logged_in; - -	int   session_id; - -	int   client_id; - -	char  *rawbuddylist; -	char  *ignorelist; - -	void  *server_settings; -	 -	struct yahoo_process_status_entry *half_user; -}; - -struct yab { -	char *id; -	char *fname; -	char *lname; -	char *nname; -	char *email; -	char *hphone; -	char *wphone; -	char *mphone; -	int  dbid; -}; - -struct yahoo_buddy { -	char *group; -	char *id; -	char *real_name; -	struct yab *yab_entry; -}; - -enum yahoo_search_type { -	YAHOO_SEARCH_KEYWORD = 0, -	YAHOO_SEARCH_YID, -	YAHOO_SEARCH_NAME -}; - -enum yahoo_search_gender { -	YAHOO_GENDER_NONE = 0, -	YAHOO_GENDER_MALE, -	YAHOO_GENDER_FEMALE -}; - -enum yahoo_search_agerange { -	YAHOO_AGERANGE_NONE = 0 -}; - -struct yahoo_found_contact { -	char *id; -	char *gender; -	char *location; -	int age; -	int online; -}; +	enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 }; + +	struct yahoo_webcam { +		int direction;	/* Uploading or downloading */ +		int conn_type;	/* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */ + +		char *user;	/* user we are viewing */ +		char *server;	/* webcam server to connect to */ +		int port;	/* webcam port to connect on */ +		char *key;	/* key to connect to the server with */ +		char *description;	/* webcam description */ +		char *my_ip;	/* own ip number */ +	}; + +	struct yahoo_webcam_data { +		unsigned int data_size; +		unsigned int to_read; +		unsigned int timestamp; +		unsigned char packet_type; +	}; + +	struct yahoo_data { +		char *user; +		char *password; + +		char *cookie_y; +		char *cookie_t; +		char *cookie_c; +		char *cookie_b; +		char *login_cookie; +		char *crumb; +		char *seed; + +		YList *buddies; +		YList *ignore; +		YList *identities; +		char *login_id; + +		int current_status; +		int initial_status; +		int logged_in; + +		int session_id; + +		int client_id; + +		char *rawbuddylist; +		char *ignorelist; + +		void *server_settings; + +		struct yahoo_process_status_entry *half_user; +	}; + +	struct yab { +		int yid; +		char *id; +		char *fname; +		char *lname; +		char *nname; +		char *email; +		char *hphone; +		char *wphone; +		char *mphone; +		int dbid; +	}; + +	struct yahoo_buddy { +		char *group; +		char *id; +		char *real_name; +		struct yab *yab_entry; +	}; + +	enum yahoo_search_type { +		YAHOO_SEARCH_KEYWORD = 0, +		YAHOO_SEARCH_YID, +		YAHOO_SEARCH_NAME +	}; + +	enum yahoo_search_gender { +		YAHOO_GENDER_NONE = 0, +		YAHOO_GENDER_MALE, +		YAHOO_GENDER_FEMALE +	}; + +	enum yahoo_search_agerange { +		YAHOO_AGERANGE_NONE = 0 +	}; + +	struct yahoo_found_contact { +		char *id; +		char *gender; +		char *location; +		int age; +		int online; +	};  /*   * Function pointer to be passed to http get/post and send file   */ -typedef void (*yahoo_get_fd_callback)(int id, int fd, int error, void *data); +	typedef void (*yahoo_get_fd_callback) (int id, void *fd, int error, +		void *data);  /*   * Function pointer to be passed to yahoo_get_url_handle   */ -typedef void (*yahoo_get_url_handle_callback)(int id, int fd, int error, -		const char *filename, unsigned long size, void *data); - - -struct yahoo_chat_member { -	char *id; -	int  age; -	int  attribs; -	char *alias; -	char *location; -}; - -struct yahoo_process_status_entry { -	char *name;	/* 7      name */ -	int state;	/* 10     state */ -	int flags;	/* 13     flags, bit 0 = pager, bit 1 = chat, bit 2 = game */ -	int mobile;	/* 60     mobile */ -	char *msg;	/* 19     custom status message */ -	int away;	/* 47     away (or invisible) */ -	int buddy_session; /* 11  state */ -	int f17;	/* 17     in chat? then what about flags? */ -	int idle;	/* 137    seconds idle */ -	int f138;	/* 138    state */ -	char *f184;	/* 184    state */ -	int f192;	/* 192    state */ -	int f10001;	/* 10001  state */ -	int f10002;	/* 10002  state */ -	int f198;	/* 198    state */ -	char *f197;	/* 197    state */ -	char *f205;	/* 205    state */ -	int f213;	/* 213    state */ -}; +	typedef void (*yahoo_get_url_handle_callback) (int id, void *fd, +		int error, const char *filename, unsigned long size, +		void *data); + +	struct yahoo_chat_member { +		char *id; +		int age; +		int attribs; +		char *alias; +		char *location; +	}; + +	struct yahoo_process_status_entry {  +		char *name;     /* 7      name */  +		int state;      /* 10     state */  +		int flags;      /* 13     flags, bit 0 = pager, bit 1 = chat, bit 2 = game */  +		int mobile;     /* 60     mobile */  +		char *msg;      /* 19     custom status message */  +		int away;       /* 47     away (or invisible) */  +		int buddy_session; /* 11  state */  +		int f17;        /* 17     in chat? then what about flags? */  +		int idle;       /* 137    seconds idle */  +		int f138;       /* 138    state */  +		char *f184;     /* 184    state */  +		int f192;       /* 192    state */  +		int f10001;     /* 10001  state */  +		int f10002;     /* 10002  state */  +		int f198;       /* 198    state */  +		char *f197;     /* 197    state */  +		char *f205;     /* 205    state */  +		int f213;       /* 213    state */  +	};  #ifdef __cplusplus  }  #endif -  #endif diff --git a/protocols/yahoo/yahoo_fn.h b/protocols/yahoo/yahoo_fn.h index 3f79f524..5400e5d0 100644 --- a/protocols/yahoo/yahoo_fn.h +++ b/protocols/yahoo/yahoo_fn.h @@ -18,16 +18,15 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   */ -#define IDENT  1 /* identify function */ -#define XOR    2 /* xor with arg1 */ -#define MULADD 3 /* multipy by arg1 then add arg2 */ -#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */ -#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */ +#define IDENT  1		/* identify function */ +#define XOR    2		/* xor with arg1 */ +#define MULADD 3		/* multipy by arg1 then add arg2 */ +#define LOOKUP 4		/* lookup each byte in the table pointed to by arg1 */ +#define BITFLD 5		/* reorder bits according to table pointed to by arg1 */ -struct yahoo_fn -{ -	int type;  +struct yahoo_fn { +	int type;  	long arg1, arg2;  }; -int yahoo_xfrm( int table, int depth, int seed ); +int yahoo_xfrm(int table, int depth, int seed); diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c index 1b084992..6bb8923d 100644 --- a/protocols/yahoo/yahoo_httplib.c +++ b/protocols/yahoo/yahoo_httplib.c @@ -29,14 +29,13 @@  #  define strchr index  #  define strrchr rindex  # endif -char *strchr (), *strrchr (); +char *strchr(), *strrchr();  # if !HAVE_MEMCPY  #  define memcpy(d, s, n) bcopy ((s), (d), (n))  #  define memmove(d, s, n) bcopy ((s), (d), (n))  # endif  #endif -  #include <errno.h>  #ifndef _WIN32  #include <unistd.h> @@ -62,7 +61,7 @@ extern struct yahoo_callbacks *yc;  extern enum yahoo_log_level log_level; -int yahoo_tcp_readline(char *ptr, int maxlen, int fd) +int yahoo_tcp_readline(char *ptr, int maxlen, void *fd)  {  	int n, rc;  	char c; @@ -70,11 +69,11 @@ int yahoo_tcp_readline(char *ptr, int maxlen, int fd)  	for (n = 1; n < maxlen; n++) {  		do { -			rc = read(fd, &c, 1); -		} while(rc == -1 && (errno == EINTR || errno == EAGAIN)); /* this is bad - it should be done asynchronously */ +			rc = YAHOO_CALLBACK(ext_yahoo_read) (fd, &c, 1); +		} while (rc == -1 && (errno == EINTR || errno == EAGAIN));	/* this is bad - it should be done asynchronously */  		if (rc == 1) { -			if(c == '\r')			/* get rid of \r */ +			if (c == '\r')	/* get rid of \r */  				continue;  			*ptr = c;  			if (c == '\n') @@ -82,9 +81,9 @@ int yahoo_tcp_readline(char *ptr, int maxlen, int fd)  			ptr++;  		} else if (rc == 0) {  			if (n == 1) -				return (0);		/* EOF, no data */ +				return (0);	/* EOF, no data */  			else -				break;			/* EOF, w/ data */ +				break;	/* EOF, w/ data */  		} else {  			return -1;  		} @@ -95,12 +94,12 @@ int yahoo_tcp_readline(char *ptr, int maxlen, int fd)  }  static int url_to_host_port_path(const char *url, -		char *host, int *port, char *path) +	char *host, int *port, char *path, int *ssl)  { -	char *urlcopy=NULL; -	char *slash=NULL; -	char *colon=NULL; -	 +	char *urlcopy = NULL; +	char *slash = NULL; +	char *colon = NULL; +  	/*  	 * http://hostname  	 * http://hostname/ @@ -110,10 +109,14 @@ static int url_to_host_port_path(const char *url,  	 * http://hostname:port/  	 * http://hostname:port/path  	 * http://hostname:port/path:foo +	 * and https:// variants of the above  	 */ -	if(strstr(url, "http://") == url) { -		urlcopy = strdup(url+7); +	if (strstr(url, "http://") == url) { +		urlcopy = strdup(url + 7); +	} else if (strstr(url, "https://") == url) { +		urlcopy = strdup(url + 8); +		*ssl = 1;  	} else {  		WARNING(("Weird url - unknown protocol: %s", url));  		return 0; @@ -122,14 +125,17 @@ static int url_to_host_port_path(const char *url,  	slash = strchr(urlcopy, '/');  	colon = strchr(urlcopy, ':'); -	if(!colon || (slash && slash < colon)) { -		*port = 80; +	if (!colon || (slash && slash < colon)) { +		if (*ssl) +			*port = 443; +		else +			*port = 80;  	} else {  		*colon = 0; -		*port = atoi(colon+1); +		*port = atoi(colon + 1);  	} -	if(!slash) { +	if (!slash) {  		strcpy(path, "/");  	} else {  		strcpy(path, slash); @@ -137,7 +143,7 @@ static int url_to_host_port_path(const char *url,  	}  	strcpy(host, urlcopy); -	 +  	FREE(urlcopy);  	return 1; @@ -145,135 +151,135 @@ static int url_to_host_port_path(const char *url,  static int isurlchar(unsigned char c)  { -	return (isalnum(c) || '-' == c || '_' == c); +	return (isalnum(c));  }  char *yahoo_urlencode(const char *instr)  { -	int ipos=0, bpos=0; +	int ipos = 0, bpos = 0;  	char *str = NULL;  	int len = strlen(instr); -	if(!(str = y_new(char, 3*len + 1) )) -		return ""; +	if (!(str = y_new(char, 3 *len + 1))) +		 return ""; -	while(instr[ipos]) { -		while(isurlchar(instr[ipos])) +	while (instr[ipos]) { +		while (isurlchar(instr[ipos]))  			str[bpos++] = instr[ipos++]; -		if(!instr[ipos]) +		if (!instr[ipos])  			break; -		 -		snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]); -		bpos+=3; + +		snprintf(&str[bpos], 4, "%%%02x", instr[ipos] & 0xff); +		bpos += 3;  		ipos++;  	} -	str[bpos]='\0'; +	str[bpos] = '\0';  	/* free extra alloc'ed mem. */  	len = strlen(str); -	str = y_renew(char, str, len+1); +	str = y_renew(char, str, len + 1);  	return (str);  }  char *yahoo_urldecode(const char *instr)  { -	int ipos=0, bpos=0; +	int ipos = 0, bpos = 0;  	char *str = NULL; -	char entity[3]={0,0,0}; +	char entity[3] = { 0, 0, 0 };  	unsigned dec;  	int len = strlen(instr); -	if(!(str = y_new(char, len+1) )) -		return ""; +	if (!(str = y_new(char, len + 1))) +		 return ""; -	while(instr[ipos]) { -		while(instr[ipos] && instr[ipos]!='%') -			if(instr[ipos]=='+') { -				str[bpos++]=' '; +	while (instr[ipos]) { +		while (instr[ipos] && instr[ipos] != '%') +			if (instr[ipos] == '+') { +				str[bpos++] = ' ';  				ipos++;  			} else  				str[bpos++] = instr[ipos++]; -		if(!instr[ipos]) +		if (!instr[ipos])  			break; -		 -		if(instr[ipos+1] && instr[ipos+2]) { + +		if (instr[ipos + 1] && instr[ipos + 2]) {  			ipos++; -			entity[0]=instr[ipos++]; -			entity[1]=instr[ipos++]; +			entity[0] = instr[ipos++]; +			entity[1] = instr[ipos++];  			sscanf(entity, "%2x", &dec);  			str[bpos++] = (char)dec;  		} else {  			str[bpos++] = instr[ipos++];  		}  	} -	str[bpos]='\0'; +	str[bpos] = '\0';  	/* free extra alloc'ed mem. */  	len = strlen(str); -	str = y_renew(char, str, len+1); +	str = y_renew(char, str, len + 1);  	return (str);  }  char *yahoo_xmldecode(const char *instr)  { -	int ipos=0, bpos=0, epos=0; +	int ipos = 0, bpos = 0, epos = 0;  	char *str = NULL; -	char entity[4]={0,0,0,0}; -	char *entitymap[5][2]={ -		{"amp;",  "&"},  +	char entity[4] = { 0, 0, 0, 0 }; +	char *entitymap[5][2] = { +		{"amp;", "&"},  		{"quot;", "\""}, -		{"lt;",   "<"},  -		{"gt;",   "<"},  +		{"lt;", "<"}, +		{"gt;", "<"},  		{"nbsp;", " "}  	};  	unsigned dec;  	int len = strlen(instr); -	if(!(str = y_new(char, len+1) )) -		return ""; +	if (!(str = y_new(char, len + 1))) +		 return ""; -	while(instr[ipos]) { -		while(instr[ipos] && instr[ipos]!='&') -			if(instr[ipos]=='+') { -				str[bpos++]=' '; +	while (instr[ipos]) { +		while (instr[ipos] && instr[ipos] != '&') +			if (instr[ipos] == '+') { +				str[bpos++] = ' ';  				ipos++;  			} else  				str[bpos++] = instr[ipos++]; -		if(!instr[ipos] || !instr[ipos+1]) +		if (!instr[ipos] || !instr[ipos + 1])  			break;  		ipos++; -		if(instr[ipos] == '#') { +		if (instr[ipos] == '#') {  			ipos++; -			epos=0; -			while(instr[ipos] != ';') -				entity[epos++]=instr[ipos++]; +			epos = 0; +			while (instr[ipos] != ';') +				entity[epos++] = instr[ipos++];  			sscanf(entity, "%u", &dec);  			str[bpos++] = (char)dec;  			ipos++;  		} else {  			int i; -			for (i=0; i<5; i++)  -				if(!strncmp(instr+ipos, entitymap[i][0],  -					       strlen(entitymap[i][0]))) { -				       	str[bpos++] = entitymap[i][1][0]; +			for (i = 0; i < 5; i++) +				if (!strncmp(instr + ipos, entitymap[i][0], +						strlen(entitymap[i][0]))) { +					str[bpos++] = entitymap[i][1][0];  					ipos += strlen(entitymap[i][0]);  					break;  				}  		}  	} -	str[bpos]='\0'; +	str[bpos] = '\0';  	/* free extra alloc'ed mem. */  	len = strlen(str); -	str = y_renew(char, str, len+1); +	str = y_renew(char, str, len + 1);  	return (str);  } -typedef void (*http_connected)(int id, int fd, int error); +typedef void (*http_connected) (int id, void *fd, int error);  struct callback_data {  	int id; @@ -282,150 +288,117 @@ struct callback_data {  	void *user_data;  }; -static void connect_complete(int fd, int error, void *data) +static void connect_complete(void *fd, int error, void *data)  {  	struct callback_data *ccd = data; -	if(error == 0 && fd > 0) -		write(fd, ccd->request, strlen(ccd->request)); -	FREE(ccd->request); +	if (error == 0) +		YAHOO_CALLBACK(ext_yahoo_write) (fd, ccd->request, +			strlen(ccd->request)); +	free(ccd->request);  	ccd->callback(ccd->id, fd, error, ccd->user_data);  	FREE(ccd);  } -static void yahoo_send_http_request(int id, char *host, int port, char *request,  -		yahoo_get_fd_callback callback, void *data) +static void yahoo_send_http_request(int id, char *host, int port, char *request, +	yahoo_get_fd_callback callback, void *data, int use_ssl)  { -	struct callback_data *ccd=y_new0(struct callback_data, 1); +	struct callback_data *ccd = y_new0(struct callback_data, 1);  	ccd->callback = callback;  	ccd->id = id;  	ccd->request = strdup(request);  	ccd->user_data = data; -	 -	YAHOO_CALLBACK(ext_yahoo_connect_async)(id, host, port, connect_complete, ccd); + +	YAHOO_CALLBACK(ext_yahoo_connect_async) (id, host, port, +		connect_complete, ccd, use_ssl);  } -void yahoo_http_post(int id, const char *url, const char *cookies, long content_length, -		yahoo_get_fd_callback callback, void *data) +void yahoo_http_post(int id, const char *url, const char *cookies, +	long content_length, yahoo_get_fd_callback callback, void *data)  {  	char host[255];  	int port = 80;  	char path[255];  	char buff[1024]; -	 -	if(!url_to_host_port_path(url, host, &port, path)) -		return; +	int ssl = 0; -	snprintf(buff, sizeof(buff),  -			"POST %s HTTP/1.0\r\n" -			"Content-length: %ld\r\n" -			"User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" -			"Host: %s:%d\r\n" -			"Cookie: %s\r\n" -			"\r\n", -			path, content_length,  -			host, port, -			cookies); +	if (!url_to_host_port_path(url, host, &port, path, &ssl)) +		return; -	yahoo_send_http_request(id, host, port, buff, callback, data); +	/* thanks to kopete dumpcap */ +	snprintf(buff, sizeof(buff), +		"POST %s HTTP/1.1\r\n" +		"Cookie: %s\r\n" +		"User-Agent: Mozilla/5.0\r\n" +		"Host: %s\r\n" +		"Content-Length: %ld\r\n" +		"Cache-Control: no-cache\r\n" +		"\r\n", path, cookies, host, content_length); + +	yahoo_send_http_request(id, host, port, buff, callback, data, ssl);  } -void yahoo_http_get(int id, const char *url, const char *cookies, -		yahoo_get_fd_callback callback, void *data) +void yahoo_http_get(int id, const char *url, const char *cookies, int http11, +	int keepalive, yahoo_get_fd_callback callback, void *data)  {  	char host[255];  	int port = 80;  	char path[255]; -	char buff[1024]; -	 -	if(!url_to_host_port_path(url, host, &port, path)) -		return; +	char buff[2048]; +	char cookiebuff[1024]; +	int ssl = 0; -	snprintf(buff, sizeof(buff),  -			"GET %s HTTP/1.0\r\n" -			"Host: %s:%d\r\n" -			"User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" -			"Cookie: %s\r\n" -			"\r\n", -			path, host, port, cookies); +	if (!url_to_host_port_path(url, host, &port, path, &ssl)) +		return; -	yahoo_send_http_request(id, host, port, buff, callback, data); +	/* Allow cases when we don't need to send a cookie */ +	if (cookies) +		snprintf(cookiebuff, sizeof(cookiebuff), "Cookie: %s\r\n", +			cookies); +	else +		cookiebuff[0] = '\0'; + +	snprintf(buff, sizeof(buff), +		"GET %s HTTP/1.%s\r\n" +		"%sHost: %s\r\n" +		"User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" +		"Accept: */*\r\n" +		"%s" "\r\n", path, http11?"1":"0", cookiebuff, host, +		keepalive? "Connection: Keep-Alive\r\n":"Connection: close\r\n"); + +	yahoo_send_http_request(id, host, port, buff, callback, data, ssl);  } -struct url_data { -	yahoo_get_url_handle_callback callback; -	void *user_data; -}; - -static void yahoo_got_url_fd(int id, int fd, int error, void *data) +void yahoo_http_head(int id, const char *url, const char *cookies, int len, +	char *payload, yahoo_get_fd_callback callback, void *data)  { -	char *tmp=NULL; -	char buff[1024]; -	unsigned long filesize=0; -	char *filename=NULL; -	int n; - -	struct url_data *ud = data; +	char host[255]; +	int port = 80; +	char path[255]; +	char buff[2048]; +	char cookiebuff[1024]; +	int ssl = 0; -	if(error || fd < 0) { -		ud->callback(id, fd, error, filename, filesize, ud->user_data); -		FREE(ud); +	if (!url_to_host_port_path(url, host, &port, path, &ssl))  		return; -	} - -	while((n=yahoo_tcp_readline(buff, sizeof(buff), fd)) > 0) { -		LOG(("Read:%s:\n", buff)); -		if(!strcmp(buff, "")) -			break; - -		if( !strncasecmp(buff, "Content-length:",  -				strlen("Content-length:")) ) { -			tmp = strrchr(buff, ' '); -			if(tmp) -				filesize = atol(tmp); -		} - -		if( !strncasecmp(buff, "Content-disposition:",  -				strlen("Content-disposition:")) ) { -			tmp = strstr(buff, "name="); -			if(tmp) { -				tmp+=strlen("name="); -				if(tmp[0] == '"') { -					char *tmp2; -					tmp++; -					tmp2 = strchr(tmp, '"'); -					if(tmp2) -						*tmp2 = '\0'; -				} else { -					char *tmp2; -					tmp2 = strchr(tmp, ';'); -					if(!tmp2) -						tmp2 = strchr(tmp, '\r'); -					if(!tmp2) -						tmp2 = strchr(tmp, '\n'); -					if(tmp2) -						*tmp2 = '\0'; -				} -				filename = strdup(tmp); -			} -		} -	} - -	LOG(("n == %d\n", n)); -	LOG(("Calling callback, filename:%s, size: %ld\n", filename, filesize)); -	ud->callback(id, fd, error, filename, filesize, ud->user_data); -	FREE(ud); -	FREE(filename); -} - -void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd, -		yahoo_get_url_handle_callback callback, void *data) -{ -	char buff[1024]; -	struct url_data *ud = y_new0(struct url_data, 1); -	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); -	ud->callback = callback; -	ud->user_data = data; -	yahoo_http_get(id, url, buff, yahoo_got_url_fd, ud); +	/* Allow cases when we don't need to send a cookie */ +	if (cookies) +		snprintf(cookiebuff, sizeof(cookiebuff), "Cookie: %s\r\n", +			cookies); +	else +		cookiebuff[0] = '\0'; + +	snprintf(buff, sizeof(buff), +		"HEAD %s HTTP/1.0\r\n" +		"Accept: */*\r\n" +		"Host: %s:%d\r\n" +		"User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" +		"%s" +		"Content-Length: %d\r\n" +		"Cache-Control: no-cache\r\n" +		"\r\n%s", path, host, port, cookiebuff, len, +		payload?payload:""); + +	yahoo_send_http_request(id, host, port, buff, callback, data, ssl);  } diff --git a/protocols/yahoo/yahoo_httplib.h b/protocols/yahoo/yahoo_httplib.h index fd28ad48..ab699b20 100644 --- a/protocols/yahoo/yahoo_httplib.h +++ b/protocols/yahoo/yahoo_httplib.h @@ -28,21 +28,21 @@ extern "C" {  #include "yahoo2_types.h" -char *yahoo_urlencode(const char *instr); -char *yahoo_urldecode(const char *instr); -char *yahoo_xmldecode(const char *instr); - -int yahoo_tcp_readline(char *ptr, int maxlen, int fd); -void yahoo_http_post(int id, const char *url, const char *cookies, long size, -		yahoo_get_fd_callback callback, void *data); -void yahoo_http_get(int id, const char *url, const char *cookies, -		yahoo_get_fd_callback callback, void *data); -void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd, -		yahoo_get_url_handle_callback callback, void *data); - +	char *yahoo_urlencode(const char *instr); +	char *yahoo_urldecode(const char *instr); +	char *yahoo_xmldecode(const char *instr); + +	int yahoo_tcp_readline(char *ptr, int maxlen, void *fd); +	void yahoo_http_post(int id, const char *url, const char *cookies, +		long size, yahoo_get_fd_callback callback, void *data); +	void yahoo_http_get(int id, const char *url, const char *cookies, +		int http11, int keepalive, yahoo_get_fd_callback callback, +		void *data); +	void yahoo_http_head(int id, const char *url, const char *cookies, +		int size, char *payload, yahoo_get_fd_callback callback, +		void *data);  #ifdef __cplusplus  }  #endif -  #endif diff --git a/protocols/yahoo/yahoo_list.h b/protocols/yahoo/yahoo_list.h index 0d335acd..c2e5ad18 100644 --- a/protocols/yahoo/yahoo_list.h +++ b/protocols/yahoo/yahoo_list.h @@ -23,7 +23,7 @@  #ifndef __YLIST_H__  #define __YLIST_H__ -/* GLib has linked list already, so I don't see why libyahoo2 has to copy this... */ +/* BitlBee already uses GLib so use it. */  typedef GList YList; diff --git a/protocols/yahoo/yahoo_util.c b/protocols/yahoo/yahoo_util.c index 5375205f..33a12674 100644 --- a/protocols/yahoo/yahoo_util.c +++ b/protocols/yahoo/yahoo_util.c @@ -35,12 +35,12 @@ char *strchr (), *strrchr ();  #include "yahoo_util.h" -char * y_string_append(char * string, char * append) +char *y_string_append(char *string, char *append)  {  	int size = strlen(string) + strlen(append) + 1; -	char * new_string = y_renew(char, string, size); +	char *new_string = y_renew(char, string, size); -	if(new_string == NULL) { +	if (new_string == NULL) {  		new_string = y_new(char, size);  		strcpy(new_string, string);  		FREE(string); diff --git a/protocols/yahoo/yahoo_util.h b/protocols/yahoo/yahoo_util.h index 0046fe16..8cb721c1 100644 --- a/protocols/yahoo/yahoo_util.h +++ b/protocols/yahoo/yahoo_util.h @@ -60,17 +60,19 @@  # define y_new0(type, n)	(type *)calloc((n), sizeof(type))  # define y_renew(type, mem, n)	(type *)realloc(mem, n) -void * y_memdup(const void * addr, int n); -char ** y_strsplit(char * str, char * sep, int nelem); -void y_strfreev(char ** vector); +void *y_memdup(const void *addr, int n); +char **y_strsplit(char *str, char *sep, int nelem); +void y_strfreev(char **vector); -int strncasecmp(const char * s1, const char * s2, size_t n); -int strcasecmp(const char * s1, const char * s2); +#ifndef _WIN32 +int strncasecmp(const char *s1, const char *s2, size_t n); +int strcasecmp(const char *s1, const char *s2); -char * strdup(const char *s); +char *strdup(const char *s);  int snprintf(char *str, size_t size, const char *format, ...);  int vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif  #endif @@ -94,9 +96,9 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap);   * The following three functions return newly allocated memory.   * You must free it yourself   */ -char * y_string_append(char * str, char * append); -char * y_str_to_utf8(const char * in); -char * y_utf8_to_str(const char * in); +char *y_string_append(char *str, char *append); +char *y_str_to_utf8(const char *in); +char *y_utf8_to_str(const char *in);  #endif | 
