diff options
Diffstat (limited to 'protocols')
| -rw-r--r-- | protocols/jabber/conference.c | 25 | ||||
| -rw-r--r-- | protocols/jabber/io.c | 38 | ||||
| -rw-r--r-- | protocols/jabber/iq.c | 36 | ||||
| -rw-r--r-- | protocols/jabber/jabber.c | 24 | ||||
| -rw-r--r-- | protocols/jabber/jabber.h | 11 | ||||
| -rw-r--r-- | protocols/jabber/jabber_util.c | 13 | ||||
| -rw-r--r-- | protocols/jabber/presence.c | 27 | ||||
| -rw-r--r-- | protocols/msn/msn.c | 79 | ||||
| -rw-r--r-- | protocols/msn/msn.h | 7 | ||||
| -rw-r--r-- | protocols/msn/msn_util.c | 8 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 49 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 42 | ||||
| -rw-r--r-- | protocols/msn/tables.c | 37 | ||||
| -rw-r--r-- | protocols/nogaim.c | 219 | ||||
| -rw-r--r-- | protocols/nogaim.h | 28 | ||||
| -rw-r--r-- | protocols/oscar/aim.h | 13 | ||||
| -rw-r--r-- | protocols/oscar/oscar.c | 109 | ||||
| -rw-r--r-- | protocols/yahoo/libyahoo2.c | 725 | ||||
| -rw-r--r-- | protocols/yahoo/yahoo.c | 121 | ||||
| -rw-r--r-- | protocols/yahoo/yahoo2.h | 3 | ||||
| -rw-r--r-- | protocols/yahoo/yahoo2_callbacks.h | 12 | ||||
| -rw-r--r-- | protocols/yahoo/yahoo2_types.h | 44 | 
22 files changed, 1088 insertions, 582 deletions
| diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 79fdd053..f434c58a 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -25,7 +25,7 @@  static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); -struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ) +struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password )  {  	struct jabber_chat *jc;  	struct xt_node *node; @@ -35,9 +35,9 @@ struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *  	roomjid = g_strdup_printf( "%s/%s", room, nick );  	node = xt_new_node( "x", NULL, NULL );  	xt_add_attr( node, "xmlns", XMLNS_MUC ); -	node = jabber_make_packet( "presence", NULL, roomjid, node );  	if( password )  		xt_add_child( node, xt_new_node( "password", password, NULL ) ); +	node = jabber_make_packet( "presence", NULL, roomjid, node );  	jabber_cache_add( ic, node, jabber_chat_join_failed );  	if( !jabber_write_packet( ic, node ) ) @@ -233,8 +233,10 @@ void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bu  			if( ( s = xt_find_attr( c, "xmlns" ) ) &&  			    ( strcmp( s, XMLNS_MUC_USER ) == 0 ) )  			{ -				c = xt_find_node( c->children, "item" ); -				if( ( s = xt_find_attr( c, "jid" ) ) ) +				struct xt_node *item; +				 +				item = xt_find_node( c->children, "item" ); +				if( ( s = xt_find_attr( item, "jid" ) ) )  				{  					/* Yay, found what we need. :-) */  					bud->ext_jid = jabber_normalize( s ); @@ -282,12 +284,15 @@ void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bu  	}  	else if( type ) /* type can only be NULL or "unavailable" in this function */  	{ -		s = strchr( bud->ext_jid, '/' ); -		if( s ) *s = 0; -		imcb_chat_remove_buddy( chat, bud->ext_jid, NULL ); -		if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) -			imcb_remove_buddy( ic, bud->ext_jid, NULL ); -		if( s ) *s = '/'; +		if( ( bud->flags & JBFLAG_IS_CHATROOM ) && bud->ext_jid ) +		{ +			s = strchr( bud->ext_jid, '/' ); +			if( s ) *s = 0; +			imcb_chat_remove_buddy( chat, bud->ext_jid, NULL ); +			if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) +				imcb_remove_buddy( ic, bud->ext_jid, NULL ); +			if( s ) *s = '/'; +		}  		if( bud == jc->me )  			jabber_chat_free( chat ); diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 10efad37..4a790f27 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -374,39 +374,23 @@ static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )  	}  	if( ( c = xt_find_node( node->children, "bind" ) ) ) -	{ -		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) ); -		xt_add_attr( reply, "xmlns", XMLNS_BIND ); -		reply = jabber_make_packet( "iq", "set", NULL, reply ); -		jabber_cache_add( ic, reply, jabber_pkt_bind_sess ); -		 -		if( !jabber_write_packet( ic, reply ) ) -			return XT_ABORT; -		 -		jd->flags |= JFLAG_WAIT_BIND; -	} +		jd->flags |= JFLAG_WANT_BIND;  	if( ( c = xt_find_node( node->children, "session" ) ) ) -	{ -		reply = xt_new_node( "session", NULL, NULL ); -		xt_add_attr( reply, "xmlns", XMLNS_SESSION ); -		reply = jabber_make_packet( "iq", "set", NULL, reply ); -		jabber_cache_add( ic, reply, jabber_pkt_bind_sess ); -		 -		if( !jabber_write_packet( ic, reply ) ) -			return XT_ABORT; -		 -		jd->flags |= JFLAG_WAIT_SESSION; -	} +		jd->flags |= JFLAG_WANT_SESSION;  	/* This flag is already set if we authenticated via SASL, so now  	   we can resume the session in the new stream, if we don't have  	   to bind/initialize the session. */ -	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 ) +	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )  	{  		if( !jabber_get_roster( ic ) )  			return XT_ABORT;  	} +	else if( jd->flags & JFLAG_AUTHENTICATED ) +	{ +		return jabber_pkt_bind_sess( ic, NULL, NULL ); +	}  	return XT_HANDLED;  } @@ -440,6 +424,7 @@ static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )  	imcb_log( ic, "Converting stream to TLS" ); +	jd->flags |= JFLAG_STARTTLS_DONE;  	jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic );  	return XT_HANDLED; @@ -530,9 +515,10 @@ gboolean jabber_start_stream( struct im_connection *ic )  	if( jd->r_inpa <= 0 )  		jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic ); -	greet = g_strdup_printf( "<?xml version='1.0' ?>" -	                         "<stream:stream to=\"%s\" xmlns=\"jabber:client\" " -	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server ); +	greet = g_strdup_printf( "%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" " +	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">",  +	                          ( jd->flags & JFLAG_STARTTLS_DONE ) ? "" : "<?xml version='1.0' ?>", +	                          jd->server );  	st = jabber_write( ic, greet, strlen( greet ) ); diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 38c5a5a9..21e52da6 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -50,10 +50,11 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )  	else if( strcmp( type, "get" ) == 0 )  	{  		if( !( ( c = xt_find_node( node->children, "query" ) ) || -		       ( c = xt_find_node( node->children, "ping" ) ) ) || /* O_o WHAT is wrong with just <query/> ????? */ +		       ( c = xt_find_node( node->children, "ping" ) ) ) ||  		    !( s = xt_find_attr( c, "xmlns" ) ) )  		{ -			imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type ); +			/* Sigh. Who decided to suddenly invent new elements +			   instead of just sticking with <query/>? */  			return XT_HANDLED;  		} @@ -296,24 +297,43 @@ static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node  xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )  {  	struct jabber_data *jd = ic->proto_data; -	struct xt_node *c; +	struct xt_node *c, *reply = NULL;  	char *s; -	if( ( c = xt_find_node( node->children, "bind" ) ) ) +	if( node && ( c = xt_find_node( node->children, "bind" ) ) )  	{  		c = xt_find_node( c->children, "jid" );  		if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&  		    strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )  			imcb_log( ic, "Server changed session resource string to `%s'", s + 1 ); -		jd->flags &= ~JFLAG_WAIT_BIND; +		jd->flags &= ~JFLAG_WANT_BIND;  	} -	else +	else if( node && ( c = xt_find_node( node->children, "session" ) ) )  	{ -		jd->flags &= ~JFLAG_WAIT_SESSION; +		jd->flags &= ~JFLAG_WANT_SESSION;  	} -	if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 ) +	if( jd->flags & JFLAG_WANT_BIND ) +	{ +		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) ); +		xt_add_attr( reply, "xmlns", XMLNS_BIND ); +	} +	else if( jd->flags & JFLAG_WANT_SESSION ) +	{ +		reply = xt_new_node( "session", NULL, NULL ); +		xt_add_attr( reply, "xmlns", XMLNS_SESSION ); +	} +	 +	if( reply != NULL ) +	{ +		reply = jabber_make_packet( "iq", "set", NULL, reply ); +		jabber_cache_add( ic, reply, jabber_pkt_bind_sess ); +		 +		if( !jabber_write_packet( ic, reply ) ) +			return XT_ABORT; +	} +	else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )  	{  		if( !jabber_get_roster( ic ) )  			return XT_ABORT; diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index c9c1d0a0..eca7d2d3 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -69,7 +69,7 @@ static void jabber_init( account_t *acc )  	s = set_add( &acc->set, "resource_select", "priority", NULL, acc );  	s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); -	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; +	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;  	s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY; @@ -79,6 +79,8 @@ static void jabber_init( account_t *acc )  	s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY; +	 +	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;  }  static void jabber_generate_id_hash( struct jabber_data *jd ); @@ -363,10 +365,11 @@ static void jabber_get_info( struct im_connection *ic, char *who )  	while( bud )  	{ -		imcb_log( ic, "Buddy %s (%d) information:\nAway state: %s\nAway message: %s", -		                   bud->full_jid, bud->priority, -		                   bud->away_state ? bud->away_state->full_name : "(none)", -		                   bud->away_message ? : "(none)" ); +		imcb_log( ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority ); +		if( bud->away_state ) +			imcb_log( ic, "Away state: %s", bud->away_state->full_name ); +		imcb_log( ic, "Status message: %s", bud->away_message ? : "(none)" ); +		  		bud = bud->next;  	} @@ -376,11 +379,12 @@ static void jabber_get_info( struct im_connection *ic, char *who )  static void jabber_set_away( struct im_connection *ic, char *state_txt, char *message )  {  	struct jabber_data *jd = ic->proto_data; -	struct jabber_away_state *state; -	/* Save all this info. We need it, for example, when changing the priority setting. */ -	state = (void *) jabber_away_state_by_name( state_txt ); -	jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */ +	/* state_txt == NULL -> Not away. +	   Unknown state -> fall back to the first defined away state. */ +	jd->away_state = state_txt ? jabber_away_state_by_name( state_txt ) +	                 ? : jabber_away_state_list : NULL; +	  	g_free( jd->away_message );  	jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL; @@ -424,7 +428,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, char *room, char *nick, char *password ) +static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password )  {  	if( strchr( room, '@' ) == NULL )  		imcb_error( ic, "Invalid room name: %s", room ); diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 61238a30..8e3bf036 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -26,9 +26,9 @@  #include <glib.h> -#include "xmltree.h"  #include "bitlbee.h"  #include "md5.h" +#include "xmltree.h"  extern GSList *jabber_connections; @@ -39,12 +39,13 @@ typedef enum  	JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */  	JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after  	                                   SASL or TLS). */ -	JFLAG_WAIT_SESSION = 8,	        /* Set if we sent a <session> tag and need a reply +	JFLAG_WANT_SESSION = 8,	        /* Set if the server wants a <session/> tag  	                                   before we continue. */ -	JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */ +	JFLAG_WANT_BIND = 16,           /* ... for <bind> tag. */  	JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this  	                                   activates all XEP-85 related code. */  	JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */ +	JFLAG_STARTTLS_DONE = 128,      /* If a plaintext session was converted to TLS. */  } jabber_flags_t;  typedef enum @@ -83,7 +84,7 @@ struct jabber_data  	/* After changing one of these two (or the priority setting), call  	   presence_send_update() to inform the server about the changes. */ -	struct jabber_away_state *away_state; +	const struct jabber_away_state *away_state;  	char *away_message;  	md5_state_t cached_id_prefix; @@ -240,7 +241,7 @@ xt_status sasl_pkt_result( struct xt_node *node, gpointer data );  gboolean sasl_supported( struct im_connection *ic );  /* conference.c */ -struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ); +struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password );  struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name );  void jabber_chat_free( struct groupchat *c );  int jabber_chat_msg( struct groupchat *ic, char *message, int flags ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 1bee5009..185d3878 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -36,10 +36,10 @@ char *set_eval_priority( set_t *set, char *value )  	{  		/* Priority is a signed 8-bit integer, according to RFC 3921. */  		if( i < -128 || i > 127 ) -			return NULL; +			return SET_INVALID;  	}  	else -		return NULL; +		return SET_INVALID;  	/* Only run this stuff if the account is online ATM,  	   and if the setting seems to be acceptable. */ @@ -227,10 +227,9 @@ xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *  const struct jabber_away_state jabber_away_state_list[] =  {  	{ "away",  "Away" }, -	{ "chat",  "Free for Chat" }, +	{ "chat",  "Free for Chat" },   /* WTF actually uses this? */  	{ "dnd",   "Do not Disturb" },  	{ "xa",    "Extended Away" }, -	{ "",      "Online" },  	{ "",      NULL }  }; @@ -238,6 +237,9 @@ const struct jabber_away_state *jabber_away_state_by_code( char *code )  {  	int i; +	if( code == NULL ) +		return NULL; +	  	for( i = 0; jabber_away_state_list[i].full_name; i ++ )  		if( g_strcasecmp( jabber_away_state_list[i].code, code ) == 0 )  			return jabber_away_state_list + i; @@ -249,6 +251,9 @@ const struct jabber_away_state *jabber_away_state_by_name( char *name )  {  	int i; +	if( name == NULL ) +		return NULL; +	  	for( i = 0; jabber_away_state_list[i].full_name; i ++ )  		if( g_strcasecmp( jabber_away_state_list[i].full_name, name ) == 0 )  			return jabber_away_state_list + i; diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 6fc360b7..28aaea1b 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -48,8 +48,9 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  	{  		if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) )  		{ -			if( set_getbool( &ic->irc->set, "debug" ) ) -				imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from ); +			/* +			imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from ); +			*/  			return XT_HANDLED;  		} @@ -105,8 +106,9 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  	{  		if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )  		{ -			if( set_getbool( &ic->irc->set, "debug" ) ) -				imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from ); +			/* +			imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from ); +			*/  			return XT_HANDLED;  		} @@ -187,13 +189,12 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  	{  		int is_away = 0; -		if( send_presence->away_state && !( *send_presence->away_state->code == 0 || -		    strcmp( send_presence->away_state->code, "chat" ) == 0 ) ) +		if( send_presence->away_state && +		    strcmp( send_presence->away_state->code, "chat" ) != 0 )  			is_away = OPT_AWAY;  		imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away, -		                   ( is_away && send_presence->away_state ) ? -		                   send_presence->away_state->full_name : NULL, +		                   is_away ? send_presence->away_state->full_name : NULL,  		                   send_presence->away_message );  	} @@ -206,17 +207,15 @@ int presence_send_update( struct im_connection *ic )  {  	struct jabber_data *jd = ic->proto_data;  	struct xt_node *node, *cap; -	char *show = jd->away_state->code; -	char *status = jd->away_message;  	struct groupchat *c;  	int st;  	node = jabber_make_packet( "presence", NULL, NULL, NULL );  	xt_add_child( node, xt_new_node( "priority", set_getstr( &ic->acc->set, "priority" ), NULL ) ); -	if( show && *show ) -		xt_add_child( node, xt_new_node( "show", show, NULL ) ); -	if( status ) -		xt_add_child( node, xt_new_node( "status", status, NULL ) ); +	if( jd->away_state ) +		xt_add_child( node, xt_new_node( "show", jd->away_state->code, NULL ) ); +	if( jd->away_message ) +		xt_add_child( node, xt_new_node( "status", jd->away_message, NULL ) );  	/* This makes the packet slightly bigger, but clients interested in  	   capabilities can now cache the discovery info. This reduces the diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 09670dfa..8930847d 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -116,7 +116,6 @@ static void msn_logout( struct im_connection *ic )  static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away )  {  	struct msn_switchboard *sb; -	struct msn_data *md = ic->proto_data;  	if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{ @@ -125,47 +124,13 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in  	else  	{  		struct msn_message *m; -		char buf[1024];  		/* Create a message. We have to arrange a usable switchboard, and send the message later. */  		m = g_new0( struct msn_message, 1 );  		m->who = g_strdup( who );  		m->text = g_strdup( message ); -		/* FIXME: *CHECK* the reliability of using spare sb's! */ -		if( ( sb = msn_sb_spare( ic ) ) ) -		{ -			debug( "Trying to use a spare switchboard to message %s", who ); -			 -			sb->who = g_strdup( who ); -			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); -			if( msn_sb_write( sb, buf, strlen( buf ) ) ) -			{ -				/* He/She should join the switchboard soon, let's queue the message. */ -				sb->msgq = g_slist_append( sb->msgq, m ); -				return( 1 ); -			} -		} -		 -		debug( "Creating a new switchboard to message %s", who ); -		 -		/* If we reach this line, there was no spare switchboard, so let's make one. */ -		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); -		if( !msn_write( ic, buf, strlen( buf ) ) ) -		{ -			g_free( m->who ); -			g_free( m->text ); -			g_free( m ); -			 -			return( 0 ); -		} -		 -		/* And queue the message to md. We'll pick it up when the switchboard comes up. */ -		md->msgq = g_slist_append( md->msgq, m ); -		 -		/* FIXME: If the switchboard creation fails, the message will not be sent. */ -		 -		return( 1 ); +		return msn_sb_write_msg( ic, m );  	}  	return( 0 ); @@ -177,8 +142,9 @@ static GList *msn_away_states( struct im_connection *ic )  	int i;  	if( l == NULL ) -		for( i = 0; msn_away_state_list[i].number > -1; i ++ ) -			l = g_list_append( l, (void*) msn_away_state_list[i].name ); +		for( i = 0; *msn_away_state_list[i].code; i ++ ) +			if( *msn_away_state_list[i].name ) +				l = g_list_append( l, (void*) msn_away_state_list[i].name );  	return l;  } @@ -187,17 +153,14 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message )  {  	char buf[1024];  	struct msn_data *md = ic->proto_data; -	const struct msn_away_state *st; -	if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 ) -		st = msn_away_state_by_name( "Away" ); +	if( state ) +		md->away_state = msn_away_state_by_name( state ) ? : +		                 msn_away_state_list + 1;  	else -		st = msn_away_state_by_name( state ); +		md->away_state = msn_away_state_list; -	if( !st ) st = msn_away_state_list; -	md->away_state = st; -	 -	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code ); +	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code );  	msn_write( ic, buf, strlen( buf ) );  } @@ -255,8 +218,6 @@ 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 msn_data *md = ic->proto_data; -	char buf[1024];  	if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{ @@ -267,31 +228,13 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )  	{  		struct msn_message *m; -		if( ( sb = msn_sb_spare( ic ) ) ) -		{ -			debug( "Trying to reuse an existing switchboard as a groupchat with %s", who ); -			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); -			if( msn_sb_write( sb, buf, strlen( buf ) ) ) -				return msn_sb_to_chat( sb ); -		} -		 -		/* If the stuff above failed for some reason: */ -		debug( "Creating a new switchboard to groupchat with %s", who ); -		 -		/* Request a new switchboard. */ -		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); -		if( !msn_write( ic, buf, strlen( buf ) ) ) -			return( 0 ); -		  		/* Create a magic message. This is quite hackish, but who cares? :-P */  		m = g_new0( struct msn_message, 1 );  		m->who = g_strdup( who );  		m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE ); -		/* Queue the magic message and cross your fingers. */ -		md->msgq = g_slist_append( md->msgq, m ); -		 -		/* FIXME: Can I try to return something here already? */ +		msn_sb_write_msg( ic, m ); +  		return NULL;  	} diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 16e5ab52..84914bc3 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -23,6 +23,9 @@    Suite 330, Boston, MA  02111-1307  USA  */ +#ifndef _MSN_H +#define _MSN_H +  /* Some hackish magicstrings to make special-purpose messages/switchboards.   */  #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r" @@ -93,7 +96,6 @@ struct msn_switchboard  struct msn_away_state  { -	int number;  	char code[4];  	char name[16];  }; @@ -175,3 +177,6 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text );  struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb );  void msn_sb_destroy( struct msn_switchboard *sb );  gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ); +int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); + +#endif //_MSN_H diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 58ad22f8..668a8b8a 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -170,9 +170,9 @@ char *msn_findheader( char *text, char *header, int len )  		while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;  		/* End of headers? */ -		if( strncmp( text + i - 2, "\n\n", 2 ) == 0 || -		    strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 || -		    strncmp( text + i - 2, "\r\r", 2 ) == 0 ) +		if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) || +		    ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||    +		                  strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )  		{  			break;  		} @@ -373,6 +373,6 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list )  	g_slist_free( *list );  	*list = NULL; -	imcb_log( ic, ret->str ); +	imcb_log( ic, "%s", ret->str );  	g_string_free( ret, TRUE );  } diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index fe48f96d..d05d8e0d 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -419,11 +419,12 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		if( !st )  		{  			/* FIXME: Warn/Bomb about unknown away state? */ -			st = msn_away_state_list; +			st = msn_away_state_list + 1;  		} -		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN | -		                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL ); +		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN |  +		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ), +		                   st->name, NULL );  	}  	else if( strcmp( cmd[0], "FLN" ) == 0 )  	{ @@ -448,11 +449,12 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		if( !st )  		{  			/* FIXME: Warn/Bomb about unknown away state? */ -			st = msn_away_state_list; +			st = msn_away_state_list + 1;  		} -		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | -		                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL ); +		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |  +		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ), +		                   st->name, NULL );  	}  	else if( strcmp( cmd[0], "RNG" ) == 0 )  	{ @@ -662,8 +664,8 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int  						imcb_log( ic, "The server is going down for maintenance in %s minutes.", arg1 );  				} -				if( arg1 ) g_free( arg1 ); -				if( mtype ) g_free( mtype ); +				g_free( arg1 ); +				g_free( mtype );  			}  			else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )  			{ @@ -671,25 +673,30 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int  			}  			else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )  			{ -				char *inbox = msn_findheader( body, "Inbox-Unread:", blen ); -				char *folders = msn_findheader( body, "Folders-Unread:", blen ); -				 -				if( inbox && folders && set_getbool( &ic->acc->set, "mail_notifications" ) ) +				if( set_getbool( &ic->acc->set, "mail_notifications" ) )  				{ -					imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders ); +					char *inbox = msn_findheader( body, "Inbox-Unread:", blen ); +					char *folders = msn_findheader( body, "Folders-Unread:", blen ); + +					if( inbox && folders ) +						imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders ); +					 +					g_free( inbox ); +					g_free( folders );  				} -				 -				g_free( inbox ); -				g_free( folders );  			}  			else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )  			{ -				char *from = msn_findheader( body, "From-Addr:", blen ); -				char *fromname = msn_findheader( body, "From:", blen ); -				 -				if( from && fromname && set_getbool( &ic->acc->set, "mail_notifications" ) ) +				if( set_getbool( &ic->acc->set, "mail_notifications" ) )  				{ -					imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from ); +					char *from = msn_findheader( body, "From-Addr:", blen ); +					char *fromname = msn_findheader( body, "From:", blen ); +					 +					if( from && fromname ) +						imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from ); + +					g_free( from ); +					g_free( fromname );  				}  			}  			else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 18c41ef5..e9526234 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -47,6 +47,48 @@ int msn_sb_write( struct msn_switchboard *sb, char *s, int len )  	return( 1 );  } +int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ) +{ +	struct msn_data *md = ic->proto_data; +	struct msn_switchboard *sb; +	char buf[1024]; + +	/* FIXME: *CHECK* the reliability of using spare sb's! */ +	if( ( sb = msn_sb_spare( ic ) ) ) +	{ +		debug( "Trying to use a spare switchboard to message %s", m->who ); +		 +		sb->who = g_strdup( m->who ); +		g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who ); +		if( msn_sb_write( sb, buf, strlen( buf ) ) ) +		{ +			/* He/She should join the switchboard soon, let's queue the message. */ +			sb->msgq = g_slist_append( sb->msgq, m ); +			return( 1 ); +		} +	} +	 +	debug( "Creating a new switchboard to message %s", m->who ); +	 +	/* If we reach this line, there was no spare switchboard, so let's make one. */ +	g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); +	if( !msn_write( ic, buf, strlen( buf ) ) ) +	{ +		g_free( m->who ); +		g_free( m->text ); +		g_free( m ); +		 +		return( 0 ); +	} +	 +	/* And queue the message to md. We'll pick it up when the switchboard comes up. */ +	md->msgq = g_slist_append( md->msgq, m ); +	 +	/* FIXME: If the switchboard creation fails, the message will not be sent. */ +	 +	return( 1 ); +} +  struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session )  {  	struct msn_data *md = ic->proto_data; diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c index 5ba9ea73..42b12aa9 100644 --- a/protocols/msn/tables.c +++ b/protocols/msn/tables.c @@ -28,48 +28,37 @@  const struct msn_away_state msn_away_state_list[] =  { -	{  0, "NLN", "Available" }, -	{  1, "BSY", "Busy" }, -	{  3, "IDL", "Idle" }, -	{  5, "BRB", "Be Right Back" }, -	{  7, "AWY", "Away" }, -	{  9, "PHN", "On the Phone" }, -	{ 11, "LUN", "Out to Lunch" }, -	{ 13, "HDN", "Hidden" }, -	{ -1, "",    "" } +	{ "NLN", "" }, +	{ "AWY", "Away" }, +	{ "BSY", "Busy" }, +	{ "IDL", "Idle" }, +	{ "BRB", "Be Right Back" }, +	{ "PHN", "On the Phone" }, +	{ "LUN", "Out to Lunch" }, +	{ "HDN", "Hidden" }, +	{ "",    "" }  }; -const struct msn_away_state *msn_away_state_by_number( int number ) -{ -	int i; -	 -	for( i = 0; msn_away_state_list[i].number > -1; i ++ ) -		if( msn_away_state_list[i].number == number ) -			return( msn_away_state_list + i ); -	 -	return( NULL ); -} -  const struct msn_away_state *msn_away_state_by_code( char *code )  {  	int i; -	for( i = 0; msn_away_state_list[i].number > -1; i ++ ) +	for( i = 0; *msn_away_state_list[i].code; i ++ )  		if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 )  			return( msn_away_state_list + i ); -	return( NULL ); +	return NULL;  }  const struct msn_away_state *msn_away_state_by_name( char *name )  {  	int i; -	for( i = 0; msn_away_state_list[i].number > -1; i ++ ) +	for( i = 0; *msn_away_state_list[i].code; i ++ )  		if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 )  			return( msn_away_state_list + i ); -	return( NULL ); +	return NULL;  }  const struct msn_status_code msn_status_code_list[] = diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 62669aaf..75c2139b 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -1,7 +1,7 @@    /********************************************************************\    * BitlBee -- An IRC to other IM-networks gateway                     *    *                                                                    * -  * Copyright 2002-2006 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* @@ -32,9 +32,11 @@  */  #define BITLBEE_CORE -#include "nogaim.h"  #include <ctype.h> +#include "nogaim.h" +#include "chat.h" +  static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );  GSList *connections; @@ -248,6 +250,8 @@ 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 @@ -263,9 +267,21 @@ void imcb_connected( struct im_connection *ic )  	ic->keepalive = b_timeout_add( 60000, send_keepalive, ic );  	ic->flags |= OPT_LOGGED_IN; -	/* Also necessary when we're not away, at least for some of the -	   protocols. */ -	imc_set_away( ic, u->away ); +	/* Necessary to send initial presence status, even if we're not away. */ +	imc_away_send_update( ic ); +	 +	/* Apparently we're connected successfully, so reset the +	   exponential backoff timer. */ +	ic->acc->auto_reconnect_delay = 0; +	 +	for( c = irc->chatrooms; c; c = c->next ) +	{ +		if( c->acc != ic->acc ) +			continue; +		 +		if( set_getbool( &c->set, "auto_join" ) ) +			chat_join( irc, c, NULL ); +	}  }  gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ) @@ -289,6 +305,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	irc_t *irc = ic->irc;  	user_t *t, *u;  	account_t *a; +	int delay;  	/* Nested calls might happen sometimes, this is probably the best  	   place to catch them. */ @@ -304,6 +321,9 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	ic->acc->prpl->logout( ic );  	b_event_remove( ic->inpa ); +	g_free( ic->away ); +	ic->away = NULL; +	  	u = irc->users;  	while( u )  	{ @@ -328,10 +348,9 @@ 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" ) && -	         set_getbool( &a->set, "auto_reconnect" ) ) +	         set_getbool( &a->set, "auto_reconnect" ) && +	         ( delay = account_reconnect_delay( a ) ) > 0 )  	{ -		int delay = set_getint( &irc->set, "auto_reconnect_delay" ); -		  		imcb_log( ic, "Reconnecting in %d seconds..", delay );  		a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a );  	} @@ -428,6 +447,7 @@ struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )  void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname )  {  	user_t *u = user_findhandle( ic, handle ); +	char *set;  	if( !u || !realname ) return; @@ -440,6 +460,23 @@ void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char  		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 ); +	}  }  void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group ) @@ -452,7 +489,7 @@ void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *grou  /* 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, char *handle, char *nick ) +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; @@ -487,33 +524,70 @@ void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )  	}  } -/* prpl.c */ -struct show_got_added_data +struct imcb_ask_cb_data  {  	struct im_connection *ic;  	char *handle;  }; -void show_got_added_no( void *data ) +static void imcb_ask_auth_cb_no( void *data ) +{ +	struct imcb_ask_cb_data *cbd = data; +	 +	cbd->ic->acc->prpl->auth_deny( cbd->ic, cbd->handle ); +	 +	g_free( cbd->handle ); +	g_free( cbd ); +} + +static void imcb_ask_auth_cb_yes( void *data ) +{ +	struct imcb_ask_cb_data *cbd = data; +	 +	cbd->ic->acc->prpl->auth_allow( cbd->ic, cbd->handle ); +	 +	g_free( cbd->handle ); +	g_free( cbd ); +} + +void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname ) +{ +	struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 ); +	char *s, *realname_ = NULL; +	 +	if( realname != NULL ) +		realname_ = g_strdup_printf( " (%s)", realname ); +	 +	s = g_strdup_printf( "The user %s%s wants to add you to his/her buddy list.", +	                     handle, realname_ ?: "" ); +	 +	g_free( realname_ ); +	 +	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 ); +} + + +static void imcb_ask_add_cb_no( void *data )  { -	g_free( ((struct show_got_added_data*)data)->handle ); +	g_free( ((struct imcb_ask_cb_data*)data)->handle );  	g_free( data );  } -void show_got_added_yes( void *data ) +static void imcb_ask_add_cb_yes( void *data )  { -	struct show_got_added_data *sga = data; +	struct imcb_ask_cb_data *cbd = data; -	sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL ); -	/* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */ +	cbd->ic->acc->prpl->add_buddy( cbd->ic, cbd->handle, NULL ); -	return show_got_added_no( data ); +	return imcb_ask_add_cb_no( data );  } -void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname ) +void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname )  { -	struct show_got_added_data *data = g_new0( struct show_got_added_data, 1 ); +	struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );  	char *s;  	/* TODO: Make a setting for this! */ @@ -524,7 +598,7 @@ void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname  	data->ic = ic;  	data->handle = g_strdup( handle ); -	query_add( ic->irc, ic, s, show_got_added_yes, show_got_added_no, data ); +	query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data );  } @@ -923,14 +997,10 @@ char *set_eval_away_devoice( set_t *set, char *value )  	irc_t *irc = set->data;  	int st; -	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) -		st = 1; -	else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) -		st = 0; -	else if( sscanf( value, "%d", &st ) != 1 ) -		return( NULL ); +	if( !is_bool( value ) ) +		return SET_INVALID; -	st = st != 0; +	st = bool2int( value );  	/* Horror.... */ @@ -974,7 +1044,7 @@ char *set_eval_away_devoice( set_t *set, char *value )  		                                            irc->channel, pm, v, list );  	} -	return( set_eval_bool( set, value ) ); +	return value;  } @@ -1016,51 +1086,30 @@ int imc_chat_msg( struct groupchat *c, char *msg, int flags )  	return 1;  } -static char *imc_away_alias_find( GList *gcm, char *away ); +static char *imc_away_state_find( GList *gcm, char *away, char **message ); -int imc_set_away( struct im_connection *ic, char *away ) +int imc_away_send_update( struct im_connection *ic )  { -	GList *m, *ms; -	char *s; -	 -	if( !away ) away = ""; -	ms = m = ic->acc->prpl->away_states( ic ); -	 -	while( m ) -	{ -		if( *away ) -		{ -			if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 ) -				break; -		} -		else -		{ -			if( g_strcasecmp( m->data, "Available" ) == 0 ) -				break; -			if( g_strcasecmp( m->data, "Online" ) == 0 ) -				break; -		} -		m = m->next; -	} +	char *away, *msg = NULL; -	if( m ) +	away = set_getstr( &ic->acc->set, "away" ) ? +	     : set_getstr( &ic->irc->set, "away" ); +	if( away && *away )  	{ -		ic->acc->prpl->set_away( ic, m->data, *away ? away : NULL ); +		GList *m = ic->acc->prpl->away_states( ic ); +		msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL; +		away = imc_away_state_find( m, away, &msg ) ? : m->data;  	} -	else +	else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )  	{ -		s = imc_away_alias_find( ms, away ); -		if( s ) -		{ -			ic->acc->prpl->set_away( ic, s, away ); -			if( set_getbool( &ic->irc->set, "debug" ) ) -				imcb_log( ic, "Setting away state to %s", s ); -		} -		else -			ic->acc->prpl->set_away( ic, GAIM_AWAY_CUSTOM, away ); +		away = NULL; +		msg = set_getstr( &ic->acc->set, "status" ) ? +		    : set_getstr( &ic->irc->set, "status" );  	} -	return( 1 ); +	ic->acc->prpl->set_away( ic, away, msg ); +	 +	return 1;  }  static char *imc_away_alias_list[8][5] = @@ -1075,16 +1124,33 @@ static char *imc_away_alias_list[8][5] =  	{ NULL }  }; -static char *imc_away_alias_find( GList *gcm, char *away ) +static char *imc_away_state_find( GList *gcm, char *away, char **message )  {  	GList *m;  	int i, j; +	for( m = gcm; m; m = m->next ) +		if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 ) +		{ +			/* At least the Yahoo! module works better if message +			   contains no data unless it adds something to what +			   we have in state already. */ +			if( strlen( m->data ) == strlen( away ) ) +				*message = NULL; +			 +			return m->data; +		} +	  	for( i = 0; *imc_away_alias_list[i]; i ++ )  	{ +		int keep_message; +		  		for( j = 0; imc_away_alias_list[i][j]; j ++ )  			if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 ) +			{ +				keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );  				break; +			}  		if( !imc_away_alias_list[i][j] )	/* If we reach the end, this row */  			continue;			/* is not what we want. Next!    */ @@ -1092,17 +1158,22 @@ static char *imc_away_alias_find( GList *gcm, char *away )  		/* Now find an entry in this row which exists in gcm */  		for( j = 0; imc_away_alias_list[i][j]; j ++ )  		{ -			m = gcm; -			while( m ) -			{ +			for( m = gcm; m; m = m->next )  				if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 ) -					return( imc_away_alias_list[i][j] ); -				m = m->next; -			} +				{ +					if( !keep_message ) +						*message = NULL; +					 +					return imc_away_alias_list[i][j]; +				}  		} +		 +		/* No need to look further, apparently this state doesn't +		   have any good alias for this protocol. */ +		break;  	} -	return( NULL ); +	return NULL;  }  void imc_add_allow( struct im_connection *ic, char *handle ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index ee5d7a30..a523a3a5 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -38,6 +38,8 @@  #ifndef _NOGAIM_H  #define _NOGAIM_H +#include <stdint.h> +  #include "bitlbee.h"  #include "account.h"  #include "proxy.h" @@ -46,7 +48,6 @@  #define BUDDY_ALIAS_MAXLEN 388   /* because MSN names can be 387 characters */  #define WEBSITE "http://www.bitlbee.org/" -#define GAIM_AWAY_CUSTOM "Custom"  /* Sharing flags between all kinds of things. I just hope I won't hit any     limits before 32-bit machines become extinct. ;-) */ @@ -207,7 +208,7 @@ struct prpl {  	 * your protocol does not support publicly named group chats, then do  	 * not implement this. */  	struct groupchat * -	     (* chat_join)	(struct im_connection *, char *room, char *nick, char *password); +	     (* chat_join)	(struct im_connection *, const char *room, const char *nick, const char *password);  	/* 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 @@ -215,13 +216,17 @@ struct prpl {  	void (* chat_topic)	(struct groupchat *, char *topic);  	/* You can tell what away states your protocol supports, so that -	 * BitlBee will try to map the IRC away reasons to them, or use -	 * GAIM_AWAY_CUSTOM when calling skype_set_away(). */ +	 * BitlBee will try to map the IRC away reasons to them. If your +	 * protocol doesn't have any, just return one generic "Away". */  	GList *(* away_states)(struct im_connection *ic);  	/* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh*  	 * - Most protocols will just want to set this to g_strcasecmp().*/  	int (* handle_cmp) (const char *who1, const char *who2); + +	/* 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);  };  /* im_api core stuff. */ @@ -237,7 +242,7 @@ G_MODULE_EXPORT void register_protocol( struct prpl * );  /* You will need this function in prpl->login() to get an im_connection from   * the account_t parameter. */  G_MODULE_EXPORT struct im_connection *imcb_new( account_t *acc ); -G_MODULE_EXPORT void imcb_free( struct im_connection *ic ); +G_MODULE_EXPORT void imc_free( struct im_connection *ic );  /* Once you're connected, you should call this function, so that the user will   * see the success. */  G_MODULE_EXPORT void imcb_connected( struct im_connection *ic ); @@ -250,13 +255,20 @@ G_MODULE_EXPORT void imc_logout( struct im_connection *ic, int allow_reconnect )  G_MODULE_EXPORT void imcb_log( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 );  /* To tell the user an error, ie. before logging out when an error occurs. */  G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 ); +  /* To ask a your about something.   * - 'msg' is the question.   * - 'data' can be your custom struct - it will be passed to the callbacks.   * - '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_add( struct im_connection *ic, char *handle, const char *realname ); + +/* Two common questions you may want to ask: + * - X added you to his contact list, allow? + * - X is not in your contact list, want to add? + */ +G_MODULE_EXPORT void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname ); +G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname );  /* Buddy management */  /* This function should be called for each handle which are visible to the @@ -266,7 +278,7 @@ G_MODULE_EXPORT void imcb_add_buddy( struct im_connection *ic, const char *handl  G_MODULE_EXPORT void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group );  G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle );  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, char *handle, char *nick ); +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. @@ -301,7 +313,7 @@ G_MODULE_EXPORT void imcb_chat_topic( struct groupchat *c, char *who, char *topi  G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c );  /* Actions, or whatever. */ -int imc_set_away( struct im_connection *ic, char *away ); +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 ); diff --git a/protocols/oscar/aim.h b/protocols/oscar/aim.h index 9516996c..d1fc602a 100644 --- a/protocols/oscar/aim.h +++ b/protocols/oscar/aim.h @@ -143,6 +143,17 @@ struct client_info_s {  	  "en", \  } +#define AIM_CLIENTINFO_KNOWNGOOD_5_1_3036 { \ +	"AOL Instant Messenger, version 5.1.3036/WIN32", \ +	0x0109, \ +	0x0005, \ +	0x0001, \ +	0x0000, \ +	0x0bdc, \ +	"us", \ +	"en", \ +} +  /*   * I would make 4.1.2010 the default, but they seem to have found   * an alternate way of breaking that one.  @@ -151,7 +162,7 @@ struct client_info_s {   * memory test, which may require you to have a WinAIM binary laying    * around. (see login.c::memrequest())   */ -#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 +#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_5_1_3036  #ifndef TRUE  #define TRUE 1 diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 36e03166..f0e65f9a 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -90,7 +90,7 @@ struct oscar_data {  	GSList *oscar_chats; -	gboolean killme; +	gboolean killme, no_reconnect;  	gboolean icq;  	GSList *evilhack; @@ -180,6 +180,7 @@ static struct chat_connection *find_oscar_chat_by_conn(struct im_connection *ic,  static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);  static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_logout     (aim_session_t *, aim_frame_t *, ...);  static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);  static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);  static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...); @@ -293,7 +294,7 @@ static gboolean oscar_callback(gpointer data, gint source,  		if (aim_get_command(odata->sess, conn) >= 0) {  			aim_rxdispatch(odata->sess);                                 if (odata->killme) -                                       imc_logout(ic, TRUE); +                                       imc_logout(ic, !odata->no_reconnect);  		} else {  			if ((conn->type == AIM_CONN_TYPE_BOS) ||  				   !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) { @@ -378,6 +379,8 @@ static void oscar_init(account_t *acc)  		s = set_add( &acc->set, "web_aware", "false", set_eval_bool, acc );  		s->flags |= ACC_SET_OFFLINE_ONLY;  	} +	 +	acc->flags |= ACC_FLAG_AWAY_MESSAGE;  }  static void oscar_login(account_t *acc) { @@ -519,6 +522,7 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {  			break;  		case 0x18:  			/* connecting too frequently */ +			od->no_reconnect = TRUE;  			imcb_error(ic, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));  			break;  		case 0x1c: @@ -571,6 +575,7 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {  	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);  	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);  	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0); +	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_parse_logout, 0);  	((struct oscar_data *)ic->proto_data)->conn = bosconn;  	for (i = 0; i < (int)strlen(info->bosip); i++) { @@ -750,6 +755,30 @@ static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {  	return 1;  } +static int gaim_parse_logout(aim_session_t *sess, aim_frame_t *fr, ...) { +	struct im_connection *ic = sess->aux_data; +	struct oscar_data *odata = ic->proto_data; +	int code; +	va_list ap; + +	va_start(ap, fr); +	code = va_arg(ap, int); +	va_end(ap); +	 +	imcb_error( ic, "Connection aborted by server: %s", code == 1 ? +	                "someone else logged in with your account" : +	                "unknown reason" ); +	 +	/* Tell BitlBee to disable auto_reconnect if code == 1, since that +	   means a concurrent login somewhere else. */ +	odata->no_reconnect = code == 1; +	 +	/* DO NOT log out here! Just tell the callback to do it. */ +	odata->killme = TRUE; + +	return 1; +} +  static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {  	struct im_connection *ic = sess->aux_data;  	struct chat_connection *chatcon; @@ -1924,6 +1953,8 @@ static void oscar_get_away(struct im_connection *g, char *who) {  static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)  { +	if (state == NULL) +		state = "";  	if (!g_strcasecmp(state, _("Visible"))) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); @@ -1931,15 +1962,16 @@ static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od,  	} else if (!g_strcasecmp(state, _("Invisible"))) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);  		return; -	} /* else... */ +	} else if (message == NULL) { +		message = state; +	}  	if (od->rights.maxawaymsglen == 0)  		imcb_error(ic, "oscar_set_away_aim called before locate rights received");  	aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); -	if (ic->away) -		g_free(ic->away); +	g_free(ic->away);  	ic->away = NULL;  	if (!message) { @@ -1959,55 +1991,53 @@ static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od,  static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)  { -    const char *msg = NULL; +	const char *msg = NULL;  	gboolean no_message = FALSE;  	/* clean old states */ -    if (ic->away) { -		g_free(ic->away); -		ic->away = NULL; -    } +	g_free(ic->away); +	ic->away = NULL;  	od->sess->aim_icq_state = 0;  	/* if no message, then use an empty message */ -    if (message) { -        msg = message; -    } else { -        msg = ""; +	if (message) { +		msg = message; +	} else { +		msg = "";  		no_message = TRUE; -    } +	} -	if (!g_strcasecmp(state, "Online")) { +	if (state == NULL) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);  	} else if (!g_strcasecmp(state, "Away")) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); -        ic->away = g_strdup(msg); +		ic->away = g_strdup(msg);  		od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;  	} else if (!g_strcasecmp(state, "Do Not Disturb")) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY); -        ic->away = g_strdup(msg); +		ic->away = g_strdup(msg);  		od->sess->aim_icq_state = AIM_MTYPE_AUTODND;  	} else if (!g_strcasecmp(state, "Not Available")) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY); -        ic->away = g_strdup(msg); +		ic->away = g_strdup(msg);  		od->sess->aim_icq_state = AIM_MTYPE_AUTONA;  	} else if (!g_strcasecmp(state, "Occupied")) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY); -        ic->away = g_strdup(msg); +		ic->away = g_strdup(msg);  		od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;  	} else if (!g_strcasecmp(state, "Free For Chat")) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT); -        ic->away = g_strdup(msg); +		ic->away = g_strdup(msg);  		od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;  	} else if (!g_strcasecmp(state, "Invisible")) {  		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE); -        ic->away = g_strdup(msg); -	} else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) { +		ic->away = g_strdup(msg); +	} else {  	 	if (no_message) {  			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);  		} else {  			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); -            ic->away = g_strdup(msg); +			ic->away = g_strdup(msg);  			od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;  		}  	} @@ -2019,7 +2049,7 @@ static void oscar_set_away(struct im_connection *ic, char *state, char *message)  {  	struct oscar_data *od = (struct oscar_data *)ic->proto_data; -    oscar_set_away_aim(ic, od, state, message); +	oscar_set_away_aim(ic, od, state, message);  	if (od->icq)  		oscar_set_away_icq(ic, od, state, message); @@ -2251,20 +2281,21 @@ static void oscar_rem_deny(struct im_connection *ic, char *who) {  static GList *oscar_away_states(struct im_connection *ic)  {  	struct oscar_data *od = ic->proto_data; -	GList *m = NULL; -	if (!od->icq) -		return g_list_append(m, GAIM_AWAY_CUSTOM); - -	m = g_list_append(m, "Online"); -	m = g_list_append(m, "Away"); -	m = g_list_append(m, "Do Not Disturb"); -	m = g_list_append(m, "Not Available"); -	m = g_list_append(m, "Occupied"); -	m = g_list_append(m, "Free For Chat"); -	m = g_list_append(m, "Invisible"); - -	return m; +	if (od->icq) { +		static GList *m = NULL; +		m = g_list_append(m, "Away"); +		m = g_list_append(m, "Do Not Disturb"); +		m = g_list_append(m, "Not Available"); +		m = g_list_append(m, "Occupied"); +		m = g_list_append(m, "Free For Chat"); +		m = g_list_append(m, "Invisible"); +		return m; +	} else { +		static GList *m = NULL; +		m = g_list_append(m, "Away"); +		return m; +	}  }  static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...) @@ -2580,7 +2611,7 @@ void oscar_chat_leave(struct groupchat *c)  	oscar_chat_kill(c->ic, c->data);  } -struct groupchat *oscar_chat_join(struct im_connection * ic, char * room, char * nick, char * password ) +struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, const char * nick, const char * password )  {  	struct oscar_data * od = (struct oscar_data *)ic->proto_data;  	aim_conn_t * cur; diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index a61955c4..5b2ff44e 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -88,6 +88,7 @@ char *strchr (), *strrchr ();  #endif  #include "base64.h" +#include "http_client.h"  #ifdef USE_STRUCT_CALLBACKS  struct yahoo_callbacks *yc=NULL; @@ -168,6 +169,7 @@ enum yahoo_service { /* these are easier to see in hex */  	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, @@ -191,16 +193,19 @@ enum yahoo_service { /* these are easier to see in hex */  	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, @@ -208,7 +213,19 @@ enum yahoo_service { /* these are easier to see in hex */  	YAHOO_SERVICE_PICTURE_CHECKSUM = 0xbd,  	YAHOO_SERVICE_PICTURE = 0xbe,  	YAHOO_SERVICE_PICTURE_UPDATE = 0xc1, -	YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2 +	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 yahoo_pair { @@ -732,7 +749,7 @@ 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, 0x000c); +	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); @@ -746,7 +763,7 @@ static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet  	if( yid->type == YAHOO_CONNECTION_FT )  		yahoo_send_data(yid->fd, data, len);  	else -	yahoo_add_to_send_queue(yid, data, len); +		yahoo_add_to_send_queue(yid, data, len);  	FREE(data);  } @@ -837,55 +854,6 @@ static int is_same_bud(const void * a, const void * b) {  	return strcmp(subject->id, object->id);  } -static YList * bud_str2list(char *rawlist) -{ -	YList * l = NULL; - -	char **lines; -	char **split; -	char **buddies; -	char **tmp, **bud; - -	lines = y_strsplit(rawlist, "\n", -1); -	for (tmp = lines; *tmp; tmp++) { -		struct yahoo_buddy *newbud; - -		split = y_strsplit(*tmp, ":", 2); -		if (!split) -			continue; -		if (!split[0] || !split[1]) { -			y_strfreev(split); -			continue; -		} -		buddies = y_strsplit(split[1], ",", -1); - -		for (bud = buddies; bud && *bud; bud++) { -			newbud = y_new0(struct yahoo_buddy, 1); -			newbud->id = strdup(*bud); -			newbud->group = strdup(split[0]); - -			if(y_list_find_custom(l, newbud, is_same_bud)) { -				FREE(newbud->id); -				FREE(newbud->group); -				FREE(newbud); -				continue; -			} - -			newbud->real_name = NULL; - -			l = y_list_append(l, newbud); - -			NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group)); -		} - -		y_strfreev(buddies); -		y_strfreev(split); -	} -	y_strfreev(lines); - -	return l; -} -  static char * getcookie(char *rawcookie)  {  	char * cookie=NULL; @@ -1342,134 +1310,150 @@ static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_pac  	y_list_free(messages);  } - -static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_status(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	YList *l;  	struct yahoo_data *yd = yid->yd; -	struct user -	{ -		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 */ -	} *u; +	struct yahoo_process_status_entry *u;  	YList *users = 0; -	 +  	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL); +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +			YAHOO_LOGIN_DUPL, NULL);  		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). */ +	u = yd->half_user; +  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data;  		switch (pair->key) { -		case 0: /* we won't actually do anything with this */ +		case 300:	/* Begin buddy */ +			if (!strcmp(pair->value, "315") && !u) { +				u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1); +			} +			break; +		case 301:	/* End buddy */ +			if (!strcmp(pair->value, "315") && u) { +				users = y_list_prepend(users, u); +				u = yd->half_user = NULL; +			} +			break; +		case 0:	/* we won't actually do anything with this */  			NOTICE(("key %d:%s", pair->key, pair->value));  			break; -		case 1: /* we don't get the full buddy list here. */ +		case 1:	/* we don't get the full buddy list here. */  			if (!yd->logged_in) { -				yd->logged_in = TRUE; -				if(yd->current_status < 0) +				yd->logged_in = 1; +				if (yd->current_status < 0)  					yd->current_status = yd->initial_status; -				YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL); +				YAHOO_CALLBACK(ext_yahoo_login_response) (yd-> +					client_id, YAHOO_LOGIN_OK, NULL);  			}  			break; -		case 8: /* how many online buddies we have */ +		case 8:	/* how many online buddies we have */  			NOTICE(("key %d:%s", pair->key, pair->value));  			break; -		case 7: /* the current buddy */ -			u = y_new0(struct user, 1); +		case 7:	/* the current buddy */ +			if (!u) { +				/* This will only happen in case of a single level message */ +				u = y_new0(struct yahoo_process_status_entry, 1); +				users = y_list_prepend(users, u); +			}  			u->name = pair->value; -			users = y_list_prepend(users, u);  			break; -		case 10: /* state */ -			((struct user*)users->data)->state = strtol(pair->value, NULL, 10); +		case 10:	/* state */ +			u->state = strtol(pair->value, NULL, 10);  			break; -		case 19: /* custom status message */ -			((struct user*)users->data)->msg = pair->value; +		case 19:	/* custom status message */ +			u->msg = pair->value;  			break; -		case 47: /* is it an away message or not */ -			((struct user*)users->data)->away = atoi(pair->value); +		case 47:	/* is it an away message or not. Not applicable for YMSG16 anymore */ +			u->away = atoi(pair->value);  			break; -		case 137: /* seconds idle */ -			((struct user*)users->data)->idle = atoi(pair->value); +		case 137:	/* seconds idle */ +			u->idle = atoi(pair->value);  			break; -		case 11: /* this is the buddy's session id */ -			((struct user*)users->data)->buddy_session = atoi(pair->value); +		case 11:	/* this is the buddy's session id */ +			u->buddy_session = atoi(pair->value);  			break; -		case 17: /* in chat? */ -			((struct user*)users->data)->f17 = atoi(pair->value); +		case 17:	/* in chat? */ +			u->f17 = atoi(pair->value);  			break; -		case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */ -			((struct user*)users->data)->flags = atoi(pair->value); +		case 13:	/* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */ +			u->flags = atoi(pair->value);  			break; -		case 60: /* SMS -> 1 MOBILE USER */ +		case 60:	/* SMS -> 1 MOBILE USER */  			/* sometimes going offline makes this 2, but invisible never sends it */ -			((struct user*)users->data)->mobile = atoi(pair->value); +			u->mobile = atoi(pair->value);  			break;  		case 138: -			((struct user*)users->data)->f138 = atoi(pair->value); +			u->f138 = atoi(pair->value);  			break;  		case 184: -			((struct user*)users->data)->f184 = pair->value; +			u->f184 = pair->value;  			break;  		case 192: -			((struct user*)users->data)->f192 = atoi(pair->value); +			u->f192 = atoi(pair->value);  			break;  		case 10001: -			((struct user*)users->data)->f10001 = atoi(pair->value); +			u->f10001 = atoi(pair->value);  			break;  		case 10002: -			((struct user*)users->data)->f10002 = atoi(pair->value); +			u->f10002 = atoi(pair->value);  			break;  		case 198: -			((struct user*)users->data)->f198 = atoi(pair->value); +			u->f198 = atoi(pair->value);  			break;  		case 197: -			((struct user*)users->data)->f197 = pair->value; +			u->f197 = pair->value;  			break;  		case 205: -			((struct user*)users->data)->f205 = pair->value; +			u->f205 = pair->value;  			break;  		case 213: -			((struct user*)users->data)->f213 = atoi(pair->value); +			u->f213 = atoi(pair->value);  			break; -		case 16: /* Custom error message */ -			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0, E_CUSTOM); +		case 16:	/* Custom error message */ +			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, +				pair->value, 0, E_CUSTOM);  			break;  		default: -			WARNING(("unknown status key %d:%s", pair->key, pair->value)); +			WARNING(("unknown status key %d:%s", pair->key, +					pair->value));  			break;  		}  	} -	 +  	while (users) {  		YList *t = users; -		struct user *u = users->data; +		struct yahoo_process_status_entry *u = users->data;  		if (u->name != NULL) { -			if (pkt->service == YAHOO_SERVICE_LOGOFF || u->flags == 0) { -				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0); +			if (pkt->service == +				YAHOO_SERVICE_LOGOFF +				/*|| u->flags == 0 No flags for YMSG16 */ ) { +				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd-> +					client_id, u->name, +					YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);  			} else { -				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, u->state, u->msg, u->away, u->idle, u->mobile); +				/* Key 47 always seems to be 1 for YMSG16 */ +				if (!u->state) +					u->away = 0; +				else +					u->away = 1; + +				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd-> +					client_id, u->name, u->state, u->msg, +					u->away, u->idle, u->mobile);  			}  		} @@ -1479,88 +1463,130 @@ static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_pack  	}  } -static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +static void yahoo_process_buddy_list(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt)  {  	struct yahoo_data *yd = yid->yd;  	YList *l; +	int last_packet = 0; +	char *cur_group = NULL; +	struct yahoo_buddy *newbud = NULL; -	if (!yd->logged_in) { -		yd->logged_in = TRUE; -		if(yd->current_status < 0) -			yd->current_status = yd->initial_status; -		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL); -	} - +	/* we could be getting multiple packets here */  	for (l = pkt->hash; l; l = l->next) {  		struct yahoo_pair *pair = l->data; -		switch(pair->key) { -		case 87: /* buddies */ -			if(!yd->rawbuddylist) -				yd->rawbuddylist = strdup(pair->value); -			else { -				yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value); -			} +		switch (pair->key) { +		case 300: +		case 301: +		case 302: +			break;	/* Separators. Our logic does not need them */ +		case 303: +			if (318 == atoi(pair->value)) +				last_packet = 1;  			break; +		case 65: +			cur_group = strdup(pair->value); +			break; +		case 7: +			newbud = y_new0(struct yahoo_buddy, 1); +			newbud->id = strdup(pair->value); +			if (cur_group) +				newbud->group = strdup(cur_group); +			else if (yd->buddies) { +				struct yahoo_buddy *lastbud = +					(struct yahoo_buddy *)y_list_nth(yd-> +					buddies, +					y_list_length(yd->buddies) - 1)->data; +				newbud->group = strdup(lastbud->group); +			} else +				newbud->group = strdup("Buddies"); + +			yd->buddies = y_list_append(yd->buddies, newbud); -		case 88: /* ignore list */ -			if(!yd->ignorelist) -				yd->ignorelist = strdup("Ignore:"); -			yd->ignorelist = y_string_append(yd->ignorelist, pair->value);  			break; +		} +	} + +	/* we could be getting multiple packets here */ +	if (pkt->hash && !last_packet) +		return; + +	YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies); -		case 89: /* identities */ +	/* Logged in */ +	if (!yd->logged_in) { +		yd->logged_in = 1; +		if (yd->current_status < 0) +			yd->current_status = yd->initial_status; +		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, +			YAHOO_LOGIN_OK, NULL); + +		/* +		yahoo_set_away(yd->client_id, yd->initial_status, NULL, +			(yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1); + +		yahoo_get_yab(yd->client_id); +		*/ +	} + +} + +static void yahoo_process_list(struct yahoo_input_data *yid, +	struct yahoo_packet *pkt) +{ +	struct yahoo_data *yd = yid->yd; +	YList *l; + +	/* we could be getting multiple packets here */ +	for (l = pkt->hash; l; l = l->next) { +		struct yahoo_pair *pair = l->data; + +		switch (pair->key) { +		case 89:	/* identities */  			{ -			char **identities = y_strsplit(pair->value, ",", -1); -			int i; -			for(i=0; identities[i]; i++) -				yd->identities = y_list_append(yd->identities,  +				char **identities = +					y_strsplit(pair->value, ",", -1); +				int i; +				for (i = 0; identities[i]; i++) +					yd->identities = +						y_list_append(yd->identities,  						strdup(identities[i])); -			y_strfreev(identities); +				y_strfreev(identities);  			} -			YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities); +			YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id, +				yd->identities);  			break; -		case 59: /* cookies */ -			if(yd->ignorelist) { -				yd->ignore = bud_str2list(yd->ignorelist); -				FREE(yd->ignorelist); -				YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore); -			} -			if(yd->rawbuddylist) { -				yd->buddies = bud_str2list(yd->rawbuddylist); -				FREE(yd->rawbuddylist); -				YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies); -			} - -			if(pair->value[0]=='Y') { +		case 59:	/* cookies */ +			if (pair->value[0] == 'Y') {  				FREE(yd->cookie_y);  				FREE(yd->login_cookie);  				yd->cookie_y = getcookie(pair->value);  				yd->login_cookie = getlcookie(yd->cookie_y); -			} else if(pair->value[0]=='T') { +			} else if (pair->value[0] == 'T') {  				FREE(yd->cookie_t);  				yd->cookie_t = getcookie(pair->value); -			} else if(pair->value[0]=='C') { +			} else if (pair->value[0] == 'C') {  				FREE(yd->cookie_c);  				yd->cookie_c = getcookie(pair->value); -			}  - -			if(yd->cookie_y && yd->cookie_t && yd->cookie_c) -				YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id); +			}  			break; -		case 3: /* my id */ -		case 90: /* 1 */ -		case 100: /* 0 */ -		case 101: /* NULL */ -		case 102: /* NULL */ -		case 93: /* 86400/1440 */ +		case 3:	/* my id */ +		case 90:	/* 1 */ +		case 100:	/* 0 */ +		case 101:	/* NULL */ +		case 102:	/* NULL */ +		case 93:	/* 86400/1440 */  			break;  		}  	} + +	if (yd->cookie_y && yd->cookie_t)	/* We don't get cookie_c anymore */ +		YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);  }  static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) @@ -2225,6 +2251,204 @@ static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *se  	free(crypt_hash);  } +struct yahoo_https_auth_data +{ +	struct yahoo_input_data *yid; +	char *token; +	char *chal; +}; + +static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had); +static void yahoo_https_auth_token_finish(struct http_request *req); +static void yahoo_https_auth_init(struct yahoo_https_auth_data *had); +static void yahoo_https_auth_finish(struct http_request *req); + +/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks +   and FAIL miserably if they're not there... */ +static char *yahoo_ha_find_key(char *response, char *key) +{ +	char *s, *end; +	int len = strlen(key); +	 +	s = response; +	do { +		if (strncmp(s, key, len) == 0 && s[len] == '=') { +			s += len + 1; +			if ((end = strchr(s, '\r'))) +				return g_strndup(s, end - s); +			else +				return g_strdup(s); +		} +		 +		if ((s = strchr(s, '\n'))) +			s ++; +	} while (s && *s); +	 +	return NULL; +} + +static enum yahoo_status yahoo_https_status_parse(int code) +{ +	switch (code) +	{ +		case 1212: return YAHOO_LOGIN_PASSWD; +		case 1213: return YAHOO_LOGIN_LOCK; +		case 1235: return YAHOO_LOGIN_UNAME; +		default: return (enum yahoo_status) code; +	} +} + +static void yahoo_process_auth_0x10(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); +	 +	had->yid = yid; +	had->chal = g_strdup(seed); +	 +	yahoo_https_auth_token_init(had); +} + +static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had) +{ +	struct yahoo_input_data *yid = had->yid; +	struct yahoo_data *yd = yid->yd; +	struct http_request *req; +	char *login, *passwd, *chal; +	char *url; +	 +	login = g_strndup(yd->user, 3 * strlen(yd->user)); +	http_encode(login); +	passwd = g_strndup(yd->password, 3 * strlen(yd->password)); +	http_encode(passwd); +	chal = g_strndup(had->chal, 3 * strlen(had->chal)); +	http_encode(chal); +	 +	url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s", +	                       (int) time(NULL), login, passwd, chal); +	 +	req = http_dorequest_url(url, yahoo_https_auth_token_finish, had); +	 +	g_free(url); +	g_free(chal); +	g_free(passwd); +	g_free(login); +} + +static void yahoo_https_auth_token_finish(struct http_request *req) +{ +	struct yahoo_https_auth_data *had = req->data; +	struct yahoo_input_data *yid; +	struct yahoo_data *yd; +	int st; +	 +	if (y_list_find(inputs, had->yid) == NULL) +		return; +	 +	yid = had->yid; +	yd = yid->yd; +	 +	if (req->status_code != 200) { +		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); +		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); +		goto fail; +	} +	 +	return yahoo_https_auth_init(had); +	 +fail: +	g_free(had->token); +	g_free(had->chal); +	g_free(had); +} + +static void yahoo_https_auth_init(struct yahoo_https_auth_data *had) +{ +	struct http_request *req; +	char *url; +	 +	url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=%d&token=%s", +	                      (int) time(NULL), had->token); +	 +	req = http_dorequest_url(url, yahoo_https_auth_finish, had); +	 +	g_free(url); +} + +static void yahoo_https_auth_finish(struct http_request *req) +{ +	struct yahoo_https_auth_data *had = req->data; +	struct yahoo_input_data *yid; +	struct yahoo_data *yd; +	struct yahoo_packet *pack; +	char *crumb = NULL; +	int st; +	 +	if (y_list_find(inputs, had->yid) == NULL) +		return; +	 +	yid = had->yid; +	yd = yid->yd; +	 +	md5_byte_t result[16]; +	md5_state_t ctx; +	 +	unsigned char yhash[32]; + +	if (req->status_code != 200) { +		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); +		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); +		goto fail; +	} +	 +	md5_init(&ctx);   +	md5_append(&ctx, (unsigned char*) crumb, 11); +	md5_append(&ctx, (unsigned char*) had->chal, strlen(had->chal)); +	md5_finish(&ctx, result); +	to_y64(yhash, result, 16); + +	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); +	yahoo_packet_hash(pack, 1, yd->user); +	yahoo_packet_hash(pack, 0, yd->user); +	yahoo_packet_hash(pack, 277, yd->cookie_y); +	yahoo_packet_hash(pack, 278, yd->cookie_t); +	yahoo_packet_hash(pack, 307, (char*) yhash); +	yahoo_packet_hash(pack, 244, "524223"); +	yahoo_packet_hash(pack, 2, yd->user); +	yahoo_packet_hash(pack, 2, "1"); +	yahoo_packet_hash(pack, 98, "us"); +	yahoo_packet_hash(pack, 135, "7.5.0.647"); +	 +	yahoo_send_packet(yid, pack, 0); +		 +	yahoo_packet_free(pack); +	 +fail: +	g_free(crumb); +	g_free(had->token); +	g_free(had->chal); +	g_free(had); +} +  static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt)  {  	char *seed = NULL; @@ -2253,6 +2477,9 @@ static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet  		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)); @@ -2407,7 +2634,7 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa  		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); @@ -2416,6 +2643,26 @@ static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_pa  /*	YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */  } +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)); +	} + +	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)  {  	struct yahoo_data *yd = yid->yd; @@ -2627,7 +2874,7 @@ static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_  	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) @@ -2649,6 +2896,7 @@ static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_  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)  	{  	case YAHOO_SERVICE_USERSTAT: @@ -2660,6 +2908,8 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	case YAHOO_SERVICE_GAMELOGOFF:  	case YAHOO_SERVICE_IDACT:  	case YAHOO_SERVICE_IDDEACT: +	case YAHOO_SERVICE_Y6_STATUS_UPDATE: +	case YAHOO_SERVICE_YMSG15_STATUS:  		yahoo_process_status(yid, pkt);  		break;  	case YAHOO_SERVICE_NOTIFY: @@ -2673,6 +2923,7 @@ 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_NEWCONTACT:  		yahoo_process_contact(yid, pkt);  		break; @@ -2713,6 +2964,9 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	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; @@ -2741,7 +2995,6 @@ 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); @@ -2755,6 +3008,8 @@ static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_pack  	case YAHOO_SERVICE_PICTURE_UPLOAD:  		yahoo_process_picture_upload(yid, pkt);  		break;	 +	case YAHOO_SERVICE_YMSG15_BUDDY_LIST:	/* Buddy List */ +		yahoo_process_buddy_list(yid, pkt);  	default:  		WARNING(("unknown service 0x%02x", pkt->service));  		yahoo_dump_unhandled(pkt); @@ -3538,7 +3793,7 @@ static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) =  	yahoo_process_webcam_master_connection,  	yahoo_process_webcam_connection,  	yahoo_process_chatcat_connection, -	yahoo_process_search_connection +	yahoo_process_search_connection,  };  int yahoo_read_ready(int id, int fd, void *data) @@ -3556,7 +3811,7 @@ int yahoo_read_ready(int id, int fd, void *data)  		len = read(fd, buf, sizeof(buf));  	} while(len == -1 && errno == EINTR); -	if(len == -1 && errno == EAGAIN)	/* we'll try again later */ +	if(len == -1 && (errno == EAGAIN||errno == EINTR))	/* we'll try again later */  		return 1;  	if (len <= 0) { @@ -3759,7 +4014,7 @@ void yahoo_send_typing(int id, const char *from, const char *who, int typ)  	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id);  	yahoo_packet_hash(pkt, 5, who); -	yahoo_packet_hash(pkt, 4, 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"); @@ -3774,46 +4029,40 @@ 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_data *yd;  	struct yahoo_packet *pkt = NULL; -	int service; +	int old_status;  	char s[4];  	if(!yid)  		return;  	yd = yid->yd; +	old_status = yd->current_status; +	yd->current_status = state; -	if (msg) { -		yd->current_status = YAHOO_STATUS_CUSTOM; -	} else { -		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); +		yahoo_packet_hash(pkt, 13, "2"); +		yahoo_send_packet(yid, pkt, 0); +		yahoo_packet_free(pkt); -	if (yd->current_status == YAHOO_STATUS_AVAILABLE) -		service = YAHOO_SERVICE_ISBACK; -	else -		service = YAHOO_SERVICE_ISAWAY; -	  -	if ((away == 2) && (yd->current_status == YAHOO_STATUS_AVAILABLE)) { -		pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_BRB, yd->session_id); -		yahoo_packet_hash(pkt, 10, "999"); -		yahoo_packet_hash(pkt, 47, "2"); -	}else { -		pkt = yahoo_packet_new(service, YAHOO_STATUS_AVAILABLE, yd->session_id); -		snprintf(s, sizeof(s), "%d", yd->current_status); -		yahoo_packet_hash(pkt, 10, s); -		if (yd->current_status == YAHOO_STATUS_CUSTOM) { -			yahoo_packet_hash(pkt, 19, msg); -			yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0"); -		} else { -			yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0"); -		} -		 -		 -		 +		return;  	} +	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 : ""); +	yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");  	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); +		yahoo_packet_hash(pkt, 13, "1"); +		yahoo_send_packet(yid, pkt, 0); +		yahoo_packet_free(pkt); +	}  }  void yahoo_logoff(int id) @@ -3828,7 +4077,10 @@ void yahoo_logoff(int id)  	LOG(("yahoo_logoff: current status: %d", yd->current_status)); -	if(yd->current_status != -1) { +	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);  		yd->current_status = -1; @@ -4061,12 +4313,24 @@ void yahoo_add_buddy(int id, const char *who, const char *group, const char *msg  	if (!yd->logged_in)  		return; -	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); -	yahoo_packet_hash(pkt, 1, yd->user); -	yahoo_packet_hash(pkt, 7, who); -	yahoo_packet_hash(pkt, 65, group); +	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, 65, group); +	yahoo_packet_hash(pkt, 97, "1"); +	yahoo_packet_hash(pkt, 1, yd->user); +	yahoo_packet_hash(pkt, 302, "319"); +	yahoo_packet_hash(pkt, 300, "319"); +	yahoo_packet_hash(pkt, 7, who); +	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);  } @@ -4090,6 +4354,49 @@ 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)  {  	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 197d76a1..a47de966 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -129,6 +129,8 @@ static char *byahoo_strip( const char *in )  static void byahoo_init( account_t *acc )  {  	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); +	 +	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;  }  static void byahoo_login( account_t *acc ) @@ -197,27 +199,11 @@ static void byahoo_set_away( struct im_connection *ic, char *state, char *msg )  {  	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data; -	ic->away = NULL; -	 -	if( state && msg && g_strcasecmp( state, msg ) != 0 ) -	{ -		yd->current_status = YAHOO_STATUS_CUSTOM; -		ic->away = ""; -	} -	else if( state ) +	if( state && msg == NULL )  	{ -		/* Set msg to NULL since (if it isn't NULL already) it's equal -		   to state. msg must be empty if we want to use an existing -		   away state. */ -		msg = NULL; -		 -		ic->away = ""; -		if( g_strcasecmp( state, "Available" ) == 0 ) -		{ -			yd->current_status = YAHOO_STATUS_AVAILABLE; -			ic->away = NULL; -		} -		else if( g_strcasecmp( state, "Be Right Back" ) == 0 ) +		/* Use these states only if msg doesn't contain additional +		   info since away messages are only supported with CUSTOM. */ +		if( g_strcasecmp( state, "Be Right Back" ) == 0 )  			yd->current_status = YAHOO_STATUS_BRB;  		else if( g_strcasecmp( state, "Busy" ) == 0 )  			yd->current_status = YAHOO_STATUS_BUSY; @@ -237,35 +223,34 @@ static void byahoo_set_away( struct im_connection *ic, char *state, char *msg )  			yd->current_status = YAHOO_STATUS_STEPPEDOUT;  		else if( g_strcasecmp( state, "Invisible" ) == 0 )  			yd->current_status = YAHOO_STATUS_INVISIBLE; -		else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 ) -		{ -			yd->current_status = YAHOO_STATUS_AVAILABLE; -			 -			ic->away = NULL; -		} +		else +			yd->current_status = YAHOO_STATUS_CUSTOM;  	} +	else if( state ) +		yd->current_status = YAHOO_STATUS_CUSTOM;  	else  		yd->current_status = YAHOO_STATUS_AVAILABLE; -	yahoo_set_away( yd->y2_id, yd->current_status, msg, ic->away != NULL ? 2 : 0 ); +	yahoo_set_away( yd->y2_id, yd->current_status, msg, state ? 2 : 0 );  }  static GList *byahoo_away_states( struct im_connection *ic )  { -	GList *m = NULL; - -	m = g_list_append( m, "Available" ); -	m = g_list_append( m, "Be Right Back" ); -	m = g_list_append( m, "Busy" ); -	m = g_list_append( m, "Not At Home" ); -	m = g_list_append( m, "Not At Desk" ); -	m = g_list_append( m, "Not In Office" ); -	m = g_list_append( m, "On Phone" ); -	m = g_list_append( m, "On Vacation" ); -	m = g_list_append( m, "Out To Lunch" ); -	m = g_list_append( m, "Stepped Out" ); -	m = g_list_append( m, "Invisible" ); -	m = g_list_append( m, GAIM_AWAY_CUSTOM ); +	static GList *m = NULL; + +	if( m == NULL ) +	{ +		m = g_list_append( m, "Be Right Back" ); +		m = g_list_append( m, "Busy" ); +		m = g_list_append( m, "Not At Home" ); +		m = g_list_append( m, "Not At Desk" ); +		m = g_list_append( m, "Not In Office" ); +		m = g_list_append( m, "On Phone" ); +		m = g_list_append( m, "On Vacation" ); +		m = g_list_append( m, "Out To Lunch" ); +		m = g_list_append( m, "Stepped Out" ); +		m = g_list_append( m, "Invisible" ); +	}  	return m;  } @@ -346,6 +331,20 @@ static struct groupchat *byahoo_chat_with( struct im_connection *ic, char *who )  	return c;  } +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 ); +} + +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 ); +} +  void byahoo_initmodule( )  {  	struct prpl *ret = g_new0(struct prpl, 1); @@ -371,6 +370,9 @@ void byahoo_initmodule( )  	ret->handle_cmp = g_strcasecmp; +	ret->auth_allow = byahoo_auth_allow; +	ret->auth_deny = byahoo_auth_deny; +	  	register_protocol(ret);  } @@ -450,9 +452,7 @@ gboolean byahoo_write_ready_callback( gpointer data, gint source, b_input_condit  {  	struct byahoo_write_ready_data *d = data; -	yahoo_write_ready( d->id, d->fd, d->data ); -	 -	return FALSE; +	return yahoo_write_ready( d->id, d->fd, d->data );  }  void ext_yahoo_login_response( int id, int succ, const char *url ) @@ -664,9 +664,6 @@ void ext_yahoo_error( int id, const char *err, int fatal, int num )  	struct im_connection *ic = byahoo_get_ic_by_id( id );  	imcb_error( ic, "%s", err ); -	 -	if( fatal ) -		imc_logout( ic, TRUE );  }  /* TODO: Clear up the mess of inp and d structures */ @@ -792,9 +789,22 @@ int ext_yahoo_connect(const char *host, int port)  static void byahoo_accept_conf( void *data )  {  	struct byahoo_conf_invitation *inv = data; +	struct groupchat *b; +	 +	for( b = inv->ic->groupchats; b; b = b->next ) +		if( b == inv->c ) +			break; +	 +	if( b != NULL ) +	{ +		yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name ); +		imcb_chat_add_buddy( inv->c, inv->ic->acc->user ); +	} +	else +	{ +		imcb_log( inv->ic, "Duplicate/corrupted invitation to `%s'.", inv->name ); +	} -	yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name ); -	imcb_chat_add_buddy( inv->c, inv->ic->acc->user );  	g_free( inv->name );  	g_free( inv );  } @@ -910,11 +920,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 )  { -	/* Groups schmoups. If I want to handle groups properly I can get the -	   buddy data from some internal libyahoo2 structure. */ -	imcb_add_buddy( byahoo_get_ic_by_id( id ), (char*) who, NULL ); +	struct im_connection *ic = byahoo_get_ic_by_id( id ); +	 +	imcb_add_buddy( ic, (char*) who, NULL );  }  void ext_yahoo_rejected( int id, const char *who, const char *msg ) diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h index e54e09fb..2184a321 100644 --- a/protocols/yahoo/yahoo2.h +++ b/protocols/yahoo/yahoo2.h @@ -216,6 +216,9 @@ const char  * yahoo_get_profile_url( void );  void yahoo_buddyicon_request(int id, const char *who); +void yahoo_accept_buddy_ymsg13(int,const char*,const char*); +void yahoo_reject_buddy_ymsg13(int,const char*,const char*,const char*); +  #include "yahoo_httplib.h"  #ifdef __cplusplus diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h index b7f4e99b..e2c8ea42 100644 --- a/protocols/yahoo/yahoo2_callbacks.h +++ b/protocols/yahoo/yahoo2_callbacks.h @@ -360,6 +360,18 @@ void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, const char *me, const char  /* + * Name: ext_yahoo_contact_auth_request + * 	Called when a contact wants to add you to his/her contact list + * Params: + * 	id   - the id that identifies the server connection + * 	myid - the identity s/he added + * 	who  - who did it + * 	msg  - any message sent + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myid, const char *who, const char *msg); + + +/*   * Name: ext_yahoo_contact_added   * 	Called when a contact is added to your list   * Params: diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h index df1756eb..f05acb3c 100644 --- a/protocols/yahoo/yahoo2_types.h +++ b/protocols/yahoo/yahoo2_types.h @@ -56,7 +56,20 @@ enum yahoo_login_status {  	YAHOO_LOGIN_PASSWD = 13,  	YAHOO_LOGIN_LOCK = 14,  	YAHOO_LOGIN_DUPL = 99, -	YAHOO_LOGIN_SOCK = -1 +	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 { @@ -84,7 +97,7 @@ enum yahoo_log_level {  	YAHOO_LOG_DEBUG  }; -#define YAHOO_PROTO_VER 0x000b +#define YAHOO_PROTO_VER 0x0010  /* Yahoo style/color directives */  #define YAHOO_COLOR_BLACK "\033[30m" @@ -114,7 +127,8 @@ enum yahoo_connection_type {  	YAHOO_CONNECTION_WEBCAM_MASTER,  	YAHOO_CONNECTION_WEBCAM,  	YAHOO_CONNECTION_CHATCAT, -	YAHOO_CONNECTION_SEARCH +	YAHOO_CONNECTION_SEARCH, +	YAHOO_CONNECTION_AUTH,  };  enum yahoo_webcam_direction_type { @@ -131,7 +145,6 @@ enum yahoo_stealth_visibility_type {  /* 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 @@ -182,6 +195,8 @@ struct yahoo_data {  	char  *ignorelist;  	void  *server_settings; +	 +	struct yahoo_process_status_entry *half_user;  };  struct yab { @@ -247,6 +262,27 @@ struct yahoo_chat_member {  	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 | 
