diff options
Diffstat (limited to 'protocols/msn')
| -rw-r--r-- | protocols/msn/Makefile | 6 | ||||
| -rw-r--r-- | protocols/msn/invitation.c | 622 | ||||
| -rw-r--r-- | protocols/msn/invitation.h | 82 | ||||
| -rw-r--r-- | protocols/msn/msn.c | 93 | ||||
| -rw-r--r-- | protocols/msn/msn.h | 25 | ||||
| -rw-r--r-- | protocols/msn/msn_util.c | 108 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 131 | ||||
| -rw-r--r-- | protocols/msn/passport.c | 4 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 201 | 
9 files changed, 1102 insertions, 170 deletions
| diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 911f47bd..b9c7ed28 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -7,11 +7,13 @@  ### DEFINITIONS  -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/msn/ +endif  # [SH] Program variables  objects = msn.o msn_util.o ns.o passport.o sb.o soap.o tables.o -CFLAGS += -Wall  LFLAGS += -r  # [SH] Phony targets @@ -32,7 +34,7 @@ distclean: clean  $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c  	@echo '*' Compiling $<  	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c new file mode 100644 index 00000000..9f8b9a6e --- /dev/null +++ b/protocols/msn/invitation.c @@ -0,0 +1,622 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway                     * +*                                                                    * +* Copyright 2008 Uli Meis					     * +* Copyright 2006 Marijn Kruisselbrink and others                     * +\********************************************************************/ + +/* MSN module - File transfer support             */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +  + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + GNU General Public License for more details. +  + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA  02111-1307  USA + */ + +#include "bitlbee.h" +#include "invitation.h" +#include "msn.h" +#include "lib/ftutil.h" + +#ifdef debug +#undef debug +#endif +#define debug(msg...) log_message( LOGLVL_INFO, msg ) + +static void msn_ftp_free( file_transfer_t *file ); +static void msn_ftpr_accept( file_transfer_t *file ); +static void msn_ftp_finished( file_transfer_t *file ); +static void msn_ftp_canceled( file_transfer_t *file, char *reason ); +static gboolean msn_ftpr_write_request( file_transfer_t *file ); + +static gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ); +static gboolean msn_ftp_read( gpointer data, gint fd, b_input_condition cond ); +gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ); + +/* + * Vararg wrapper for imcb_file_canceled(). + */ +gboolean msn_ftp_abort( file_transfer_t *file, char *format, ... ) +{ +        va_list params; +        va_start( params, format ); +        char error[128]; + +        if( vsnprintf( error, 128, format, params ) < 0 ) +                sprintf( error, "internal error parsing error string (BUG)" ); +        va_end( params ); +	imcb_file_canceled( file, error ); +	return FALSE; +} + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ +	if( (op) == -1 ) \ +		return msn_ftp_abort( file , msg ": %s", strerror( errno ) ); + +void msn_ftp_invitation_cmd( struct im_connection *ic, char *who, int cookie, char *icmd, +			     char *trailer ) +{ +	struct msn_message *m = g_new0( struct msn_message, 1 ); +	 +	m->text = g_strdup_printf( "%s" +		    "Invitation-Command: %s\r\n" +		    "Invitation-Cookie: %u\r\n" +		    "%s", +		    MSN_INVITE_HEADERS, +		    icmd, +		    cookie, +		    trailer); +	 +	m->who = g_strdup( who ); + +	msn_sb_write_msg( ic, m ); +} + +void msn_ftp_cancel_invite( struct im_connection *ic, char *who,  int cookie, char *code ) +{ +	char buf[64]; + +	g_snprintf( buf, sizeof( buf ), "Cancel-Code: %s\r\n", code ); +	msn_ftp_invitation_cmd( ic, who, cookie, "CANCEL", buf ); +} + +void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *file, char *who ) +{ +	unsigned int cookie = time( NULL ); /* TODO: randomize */ +	char buf[2048]; + +	msn_filetransfer_t *msn_file = g_new0( msn_filetransfer_t, 1 ); +	file->data = msn_file; +	file->free = msn_ftp_free; +	file->canceled = msn_ftp_canceled; +	file->write = msn_ftps_write; +	msn_file->md = ic->proto_data; +	msn_file->invite_cookie = cookie; +	msn_file->handle = g_strdup( who ); +	msn_file->dcc = file; +	msn_file->md->filetransfers = g_slist_prepend( msn_file->md->filetransfers, msn_file->dcc ); +	msn_file->fd = -1; +	msn_file->sbufpos = 3; + +	g_snprintf( buf, sizeof( buf ),  +		"Application-Name: File Transfer\r\n" +		"Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n" +		"Application-File: %s\r\n" +		"Application-FileSize: %zd\r\n", +		file->file_name, +		file->file_size); + +	msn_ftp_invitation_cmd( msn_file->md->ic, msn_file->handle, cookie, "INVITE", buf ); + +	imcb_file_recv_start( file ); +} + +void msn_invitation_invite( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	char *itype = msn_findheader( body, "Application-GUID:", blen ); +	char *name, *size, *invitecookie, *reject = NULL; +	user_t *u; +	size_t isize; +	file_transfer_t *file; +	 +	if( !itype || strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) != 0 ) { +		/* Don't know what that is - don't care */ +		char *iname = msn_findheader( body, "Application-Name:", blen ); +		imcb_log( sb->ic, "Received unknown MSN invitation %s (%s) from %s", +			  itype ? : "with no GUID", iname ? iname : "no application name", handle ); +		g_free( iname ); +		reject = "REJECT_NOT_INSTALLED"; +	} else if (  +		!( name = msn_findheader( body, "Application-File:", blen )) ||  +		!( size = msn_findheader( body, "Application-FileSize:", blen )) ||  +		!( invitecookie = msn_findheader( body, "Invitation-Cookie:", blen)) || +		!( isize = atoll( size ) ) ) {  +		imcb_log( sb->ic, "Received corrupted transfer request from %s" +			  "(name=%s, size=%s, invitecookie=%s)", +			  handle, name, size, invitecookie ); +		reject = "REJECT"; +	} else if ( !( u = user_findhandle( sb->ic, handle ) ) ) { +		imcb_log( sb->ic, "Error in parsing transfer request, User '%s'" +			  "is not in contact list", handle ); +		reject = "REJECT"; +	} else if ( !( file = imcb_file_send_start( sb->ic, handle, name, isize ) ) ) { +		imcb_log( sb->ic, "Error initiating transfer for request from %s for %s", +			  handle, name ); +		reject = "REJECT"; +	} else { +		msn_filetransfer_t *msn_file = g_new0( msn_filetransfer_t, 1 ); +		file->data = msn_file; +		file->accept = msn_ftpr_accept; +		file->free = msn_ftp_free; +		file->finished = msn_ftp_finished; +		file->canceled = msn_ftp_canceled; +		file->write_request = msn_ftpr_write_request; +		msn_file->md = sb->ic->proto_data; +		msn_file->invite_cookie = cookie; +		msn_file->handle = g_strdup( handle ); +		msn_file->dcc = file; +		msn_file->md->filetransfers = g_slist_prepend( msn_file->md->filetransfers, msn_file->dcc ); +		msn_file->fd = -1; +	} + +	if( reject ) +		msn_ftp_cancel_invite( sb->ic, sb->who, cookie, reject ); + +	g_free( name ); +	g_free( size ); +	g_free( invitecookie ); +	g_free( itype ); +} + +msn_filetransfer_t* msn_find_filetransfer( struct msn_data *md, unsigned int cookie, char *handle ) +{ +	GSList *l; +	 +	for( l = md->filetransfers; l; l = l->next ) { +		msn_filetransfer_t *file = ( (file_transfer_t*) l->data )->data; +		if( file->invite_cookie == cookie && strcmp( handle, file->handle ) == 0 ) { +			return file; +		} +	} +	return NULL; +} + +gboolean msn_ftps_connected( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; +	struct sockaddr_storage clt_addr; +	socklen_t ssize = sizeof( clt_addr ); +	 +	debug( "Connected to MSNFTP client" ); +	 +	ASSERTSOCKOP( msn_file->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + +	closesocket( fd ); +	fd = msn_file->fd; +	sock_make_nonblocking( fd ); + +	msn_file->r_event_id = b_input_add( fd, B_EV_IO_READ, msn_ftp_read, file ); + +	return FALSE; +} + +void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	file_transfer_t *file = msn_file->dcc; +	char buf[1024]; +	unsigned int acookie = time ( NULL ); +	char host[HOST_NAME_MAX+1]; +	char port[6]; +	char *errmsg; + +	msn_file->auth_cookie = acookie; + +	if( ( msn_file->fd = ft_listen( NULL, host, port, FALSE, &errmsg ) ) == -1 ) { +		msn_ftp_abort( file, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); +		return; +	} + +	msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftps_connected, file ); + +	g_snprintf( buf, sizeof( buf ), +		    "IP-Address: %s\r\n" +		    "Port: %s\r\n" +		    "AuthCookie: %d\r\n" +		    "Launch-Application: FALSE\r\n" +		    "Request-Data: IP-Address:\r\n\r\n", +		    host, +		    port, +		    msn_file->auth_cookie ); + +	msn_ftp_invitation_cmd( msn_file->md->ic, handle, msn_file->invite_cookie, "ACCEPT", buf ); +} + +void msn_invitationr_accept( msn_filetransfer_t *msn_file, struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) { +	file_transfer_t *file = msn_file->dcc; +	char *authcookie, *ip, *port; + +	if( !( authcookie = msn_findheader( body, "AuthCookie:", blen ) ) || +	    !( ip = msn_findheader( body, "IP-Address:", blen ) ) || +	    !( port = msn_findheader( body, "Port:", blen ) ) ) { +		msn_ftp_abort( file, "Received invalid accept reply" ); +	} else if(  +		( msn_file->fd = proxy_connect( ip, atoi( port ), msn_ftp_connected, file ) ) +		< 0 ) { +			msn_ftp_abort( file, "Error connecting to MSN client" ); +	} else +		msn_file->auth_cookie = strtoul( authcookie, NULL, 10 ); + +	g_free( authcookie ); +	g_free( ip ); +	g_free( port ); +} + +void msn_invitation_accept( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	msn_filetransfer_t *msn_file = msn_find_filetransfer( sb->ic->proto_data, cookie, handle ); +	file_transfer_t *file = msn_file ? msn_file->dcc : NULL; +	 +	if( !msn_file  ) +		imcb_log( sb->ic, "Received invitation ACCEPT message for unknown invitation (already aborted?)" ); +	else if( file->sending )  +		msn_invitations_accept( msn_file, sb, handle, cookie, body, blen ); +	else +		msn_invitationr_accept( msn_file, sb, handle, cookie, body, blen ); +} + +void msn_invitation_cancel( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ) +{ +	msn_filetransfer_t *msn_file = msn_find_filetransfer( sb->ic->proto_data, cookie, handle ); +	 +	if( !msn_file ) +		imcb_log( sb->ic, "Received invitation CANCEL message for unknown invitation (already aborted?)" ); +	else +		msn_ftp_abort( msn_file->dcc, msn_findheader( body, "Cancel-Code:", blen ) ); +} + +int msn_ftp_write( file_transfer_t *file, char *format, ... ) +{ +	msn_filetransfer_t *msn_file = file->data; +	va_list params; +	int st; +	char *s; +	 +	va_start( params, format ); +	s = g_strdup_vprintf( format, params ); +	va_end( params ); +	 +	st = write( msn_file->fd, s, strlen( s ) ); +	if( st != strlen( s ) ) +		return msn_ftp_abort( file, "Error sending data over MSNFTP connection: %s", +				strerror( errno ) ); +	 +	g_free( s ); +	return 1; +} + +gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; +	 +	debug( "Connected to MSNFTP server, starting authentication" ); +	if( !msn_ftp_write( file, "VER MSNFTP\r\n" ) )  +		return FALSE; +	 +	sock_make_nonblocking( msn_file->fd ); +	msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); +	 +	return FALSE; +} + +gboolean msn_ftp_handle_command( file_transfer_t *file, char* line ) +{ +	msn_filetransfer_t *msn_file = file->data; +	char **cmd = msn_linesplit( line ); +	int count = 0; +	if( cmd[0] ) while( cmd[++count] ); +	 +	if( count < 1 ) +		return msn_ftp_abort( file, "Missing command in MSNFTP communication" ); +	 +	if( strcmp( cmd[0], "VER" ) == 0 ) { +		if( strcmp( cmd[1], "MSNFTP" ) != 0 ) +			return msn_ftp_abort( file, "Unsupported filetransfer protocol: %s", cmd[1] ); +		if( file->sending ) +			msn_ftp_write( file, "VER MSNFTP\r\n" ); +		else  +			msn_ftp_write( file, "USR %s %u\r\n", msn_file->md->ic->acc->user, msn_file->auth_cookie ); +	} else if( strcmp( cmd[0], "FIL" ) == 0 ) { +		if( strtoul( cmd[1], NULL, 10 ) != file->file_size ) +			return msn_ftp_abort( file, "FIL reply contains a different file size than the size in the invitation" ); +		msn_ftp_write( file, "TFR\r\n" ); +		msn_file->status |= MSN_TRANSFER_RECEIVING; +	} else if( strcmp( cmd[0], "USR" ) == 0 ) { +		if( ( strcmp( cmd[1], msn_file->handle ) != 0 ) || +		    ( strtoul( cmd[2], NULL, 10 ) != msn_file->auth_cookie ) ) +			msn_ftp_abort( file, "Authentication failed. " +				"Expected handle: %s (got %s), cookie: %u (got %s)", +				msn_file->handle, cmd[1], +				msn_file->auth_cookie, cmd[2] ); +		msn_ftp_write( file, "FIL %zu\r\n", file->file_size); +	} else if( strcmp( cmd[0], "TFR" ) == 0 ) { +		file->write_request( file ); +	} else if( strcmp( cmd[0], "BYE" ) == 0 ) { +		unsigned int retcode = count > 1 ? atoi(cmd[1]) : 1; + +		if( ( retcode==16777989 ) || ( retcode==16777987 ) ) +			imcb_file_finished( file ); +		else if( retcode==2147942405 ) +			imcb_file_canceled( file, "Failure: receiver is out of disk space" ); +		else if( retcode==2164261682 ) +			imcb_file_canceled( file, "Failure: receiver cancelled the transfer" ); +		else if( retcode==2164261683 ) +			imcb_file_canceled( file, "Failure: sender has cancelled the transfer" ); +		else if( retcode==2164261694 ) +			imcb_file_canceled( file, "Failure: connection is blocked" ); +		else { +			char buf[128]; + +			sprintf( buf, "Failure: unknown BYE code: %d", retcode); +			imcb_file_canceled( file, buf ); +		} +	} else if( strcmp( cmd[0], "CCL" ) == 0 ) { +		imcb_file_canceled( file, "Failure: receiver cancelled the transfer" ); +	} else { +		msn_ftp_abort( file, "Received invalid command %s from msn client", cmd[0] ); +	} +	return TRUE; +} + +gboolean msn_ftp_send( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; + +	msn_file->w_event_id = 0; + +	file->write_request( file ); + +	return FALSE; +} + +/* + * This should only be called if we can write, so just do it. + * Add a write watch so we can write more during the next cycle (if possible). + * This got a bit complicated because (at least) amsn expects packets of size 2045. + */ +gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) +{ +	msn_filetransfer_t *msn_file = file->data; +        int ret, overflow; + +	/* what we can't send now */ +	overflow = msn_file->sbufpos + len - MSNFTP_PSIZE; + +	/* append what we can do the send buffer */ +	memcpy( msn_file->sbuf + msn_file->sbufpos, buffer, MIN( len, MSNFTP_PSIZE - msn_file->sbufpos ) ); +	msn_file->sbufpos += MIN( len, MSNFTP_PSIZE - msn_file->sbufpos ); + +	/* if we don't have enough for a full packet and there's more wait for it */ +	if( ( msn_file->sbufpos < MSNFTP_PSIZE ) &&  +	    ( msn_file->data_sent + msn_file->sbufpos - 3 < file->file_size ) ) { +		if( !msn_file->w_event_id ) +			msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); +		return TRUE; +	} + +	/* Accumulated enough data, lets send something out */ + +	msn_file->sbuf[0] = 0; +	msn_file->sbuf[1] = ( msn_file->sbufpos - 3 ) & 0xff; +	msn_file->sbuf[2] = ( ( msn_file->sbufpos - 3 ) >> 8 ) & 0xff; + +        ASSERTSOCKOP( ret = send( msn_file->fd, msn_file->sbuf, msn_file->sbufpos, 0 ), "Sending" ); + +        msn_file->data_sent += ret - 3; + +        /* TODO: this should really not be fatal */ +        if( ret < msn_file->sbufpos ) +                return msn_ftp_abort( file, "send() sent %d instead of %d (send buffer full!)", ret, msn_file->sbufpos ); + +	msn_file->sbufpos = 3; + +	if( overflow > 0 ) { +		while( overflow > ( MSNFTP_PSIZE - 3 ) ) { +			if( !msn_ftps_write( file, buffer + len - overflow, MSNFTP_PSIZE - 3 ) ) +				return FALSE; +			overflow -= MSNFTP_PSIZE - 3; +		} +		return msn_ftps_write( file, buffer + len - overflow, overflow ); +	} + +	if( msn_file->data_sent == file->file_size ) { +		if( msn_file->w_event_id ) { +			b_event_remove( msn_file->w_event_id ); +			msn_file->w_event_id = 0; +		} +	} else { +		/* we might already be listening if this is data from an overflow */ +		if( !msn_file->w_event_id ) +			msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); +	} + +        return TRUE; +} + +/* Binary part of the file transfer protocol */ +gboolean msn_ftpr_read( file_transfer_t *file )  +{ +	msn_filetransfer_t *msn_file = file->data; +	int st; +	unsigned char buf[3]; + +	if( msn_file->data_remaining ) { +		msn_file->r_event_id = 0; + +		ASSERTSOCKOP( st = read( msn_file->fd, file->buffer, MIN( sizeof( file->buffer ), msn_file->data_remaining ) ), "Receiving" ); + +		if( st == 0 ) +			return msn_ftp_abort( file, "Remote end closed connection"); + +		msn_file->data_sent += st; + +		msn_file->data_remaining -= st; + +		file->write( file, file->buffer, st ); + +		if( msn_file->data_sent >= file->file_size ) +			imcb_file_finished( file ); + +		return FALSE; +	} else { +		ASSERTSOCKOP( st = read( msn_file->fd, buf, 1 ), "Receiving" ); +		if( st == 0 ) { +			return msn_ftp_abort( file, "read returned EOF while reading data header from msn client" ); +		} else if( buf[0] == '\r' || buf[0] == '\n' ) { +			debug( "Discarding extraneous newline" ); +		} else if( buf[0] != 0 ) { +			msn_ftp_abort( file, "Remote end canceled the transfer"); +			/* don't really care about these last 2 (should be 0,0) */ +			read( msn_file->fd, buf, 2 ); +			return FALSE; +		} else { +			unsigned int size; +			ASSERTSOCKOP( st = read( msn_file->fd, buf, 2 ), "Receiving" ); +			if( st < 2 ) +				return msn_ftp_abort( file, "read returned EOF while reading data header from msn client" ); + +			size = buf[0] + ((unsigned int) buf[1] << 8); +			msn_file->data_remaining = size; +		} +	} +	return TRUE; +} + +/* Text mode part of the file transfer protocol */ +gboolean msn_ftp_txtproto( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; +	int i = msn_file->tbufpos, st; +	char *tbuf = msn_file->tbuf; + +	ASSERTSOCKOP( st = read( msn_file->fd,  +				 tbuf + msn_file->tbufpos,  +				 sizeof( msn_file->tbuf ) - msn_file->tbufpos ), +				 "Receiving" ); + +	if( st == 0 ) +		return msn_ftp_abort( file, "read returned EOF while reading text from msn client" ); + +	msn_file->tbufpos += st; + +	do { +		for( ;i < msn_file->tbufpos; i++ ) { +			if( tbuf[i] == '\n' || tbuf[i] == '\r' ) { +				tbuf[i] = '\0'; +				if( i > 0 ) +					msn_ftp_handle_command( file, tbuf ); +				else +					while( tbuf[i] == '\n' || tbuf[i] == '\r' ) i++; +				memmove( tbuf, tbuf + i + 1, msn_file->tbufpos - i - 1 ); +				msn_file->tbufpos -= i + 1; +				i = 0; +				break; +			} +		} +	} while ( i < msn_file->tbufpos ); + +	if( msn_file->tbufpos == sizeof( msn_file->tbuf ) ) +		return msn_ftp_abort( file,  +				      "Line exceeded %d bytes in text protocol",  +				      sizeof( msn_file->tbuf ) ); +	return TRUE; +} + +gboolean msn_ftp_read( gpointer data, gint fd, b_input_condition cond ) +{ +	file_transfer_t *file = data; +	msn_filetransfer_t *msn_file = file->data; +	 +	if( msn_file->status & MSN_TRANSFER_RECEIVING ) +		return msn_ftpr_read( file ); +	else +		return msn_ftp_txtproto( file ); +} + +void msn_ftp_free( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; +	 +	if( msn_file->r_event_id ) +		b_event_remove( msn_file->r_event_id ); + +	if( msn_file->w_event_id ) +		b_event_remove( msn_file->w_event_id ); + +	if( msn_file->fd != -1 ) +		closesocket( msn_file->fd ); + +	msn_file->md->filetransfers = g_slist_remove( msn_file->md->filetransfers, msn_file->dcc ); +	 +	g_free( msn_file->handle ); +	 +	g_free( msn_file ); +} + +void msn_ftpr_accept( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; + +	msn_ftp_invitation_cmd( msn_file->md->ic, msn_file->handle, msn_file->invite_cookie, "ACCEPT",  +				"Launch-Application: FALSE\r\n"  +				"Request-Data: IP-Address:\r\n"); +} + +void msn_ftp_finished( file_transfer_t *file ) +{ +	msn_ftp_write( file, "BYE 16777989\r\n" ); +} + +void msn_ftp_canceled( file_transfer_t *file, char *reason ) +{ +	msn_filetransfer_t *msn_file = file->data; + +	msn_ftp_cancel_invite( msn_file->md->ic, msn_file->handle,  +			       msn_file->invite_cookie,  +			       file->status & FT_STATUS_TRANSFERRING ?  +					"FTTIMEOUT" :  +					"FAIL" ); + +	imcb_log( msn_file->md->ic, "File transfer aborted: %s", reason ); +} + +gboolean msn_ftpr_write_request( file_transfer_t *file ) +{ +	msn_filetransfer_t *msn_file = file->data; +	if( msn_file->r_event_id != 0 ) { +		msn_ftp_abort( file,  +					"BUG in MSN file transfer:" +					"write_request called when" +					"already watching for input" ); +		return FALSE; +	} + +	msn_file->r_event_id =  +		b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); + +	return TRUE; +} diff --git a/protocols/msn/invitation.h b/protocols/msn/invitation.h new file mode 100644 index 00000000..289efd7b --- /dev/null +++ b/protocols/msn/invitation.h @@ -0,0 +1,82 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway                     * +*                                                                    * +* Copyright 2006 Marijn Kruisselbrink and others                     * +\********************************************************************/ + +/* MSN module - File transfer support             */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +  + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + GNU General Public License for more details. +  + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA  02111-1307  USA + */ + +#ifndef _MSN_INVITATION_H +#define _MSN_INVITATION_H + +#include "msn.h" + +#define MSN_INVITE_HEADERS	"MIME-Version: 1.0\r\n" \ +				"Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" \ +				"\r\n" + +#define MSNFTP_PSIZE 2048 + +typedef enum { +	MSN_TRANSFER_RECEIVING	= 1, +	MSN_TRANSFER_SENDING	= 2 +} msn_filetransfer_status_t; + +typedef struct msn_filetransfer +{ +/* Generic invitation data */	 +	/* msn_data instance this invitation was received with. */ +	struct msn_data *md; +	/* Cookie specifying this invitation. */ +	unsigned int invite_cookie; +	/* Handle of user that started this invitation. */ +	char *handle; + +/* File transfer specific data */ +	/* Current status of the file transfer. */ +	msn_filetransfer_status_t status; +	/* Pointer to the dcc structure for this transfer. */ +	file_transfer_t *dcc; +	/* Socket the transfer is taking place over. */ +	int fd; +	/* Cookie received in the original invitation, this must be sent as soon as +	   a connection has been established. */ +	unsigned int auth_cookie; +	/* Data remaining to be received in the current packet. */ +	unsigned int data_remaining; +	/* Buffer containing received, but unprocessed text. */ +	char tbuf[256]; +	unsigned int tbufpos; +	 +	unsigned int data_sent; + +	gint r_event_id; +	gint w_event_id; +	 +	unsigned char sbuf[2048]; +	int sbufpos; + +} msn_filetransfer_t; + +void msn_invitation_invite( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); +void msn_invitation_accept( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); +void msn_invitation_cancel( struct msn_switchboard *sb, char *handle, unsigned int cookie, char *body, int blen ); + +#endif diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 4d859346..6222e1b6 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -30,16 +30,14 @@ int msn_chat_id;  GSList *msn_connections;  GSList *msn_switchboards; -static char *msn_set_display_name( set_t *set, char *value ); +static char *set_eval_display_name( set_t *set, char *value );  static void msn_init( account_t *acc )  { -	set_t *s; -	 -	s = set_add( &acc->set, "display_name", NULL, msn_set_display_name, acc ); -	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; - -	s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); +	set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); +	set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc ); +	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); +	set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc );  }  static void msn_login( account_t *acc ) @@ -80,6 +78,12 @@ static void msn_logout( struct im_connection *ic )  	if( md )  	{ +		/** Disabling MSN ft support for now. +		while( md->filetransfers ) { +			imcb_file_canceled( md->filetransfers->data, "Closing connection" ); +		} +		*/ +		  		if( md->fd >= 0 )  			closesocket( md->fd ); @@ -98,10 +102,18 @@ static void msn_logout( struct im_connection *ic )  		while( md->groupcount > 0 )  			g_free( md->grouplist[--md->groupcount] );  		g_free( md->grouplist ); -		  		g_free( md->passport_token );  		g_free( md->lock_key ); +		while( md->grpq ) +		{ +			struct msn_groupadd *ga = md->grpq->data; +			g_free( ga->group ); +			g_free( ga->who ); +			g_free( ga ); +			md->grpq = g_slist_remove( md->grpq, ga ); +		} +		  		g_free( md );  	} @@ -120,6 +132,14 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in  {  	struct msn_switchboard *sb; +#ifdef DEBUG +	if( strcmp( who, "raw" ) == 0 ) +	{ +		msn_write( ic, message, strlen( message ) ); +		msn_write( ic, "\r\n", 2 ); +	} +	else +#endif  	if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{  		return( msn_sb_sendmessage( sb, message ) ); @@ -157,11 +177,10 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message )  	char buf[1024];  	struct msn_data *md = ic->proto_data; -	if( state ) -		md->away_state = msn_away_state_by_name( state ) ? : -		                 msn_away_state_list + 1; -	else +	if( state == NULL )  		md->away_state = msn_away_state_list; +	else if( ( md->away_state = msn_away_state_by_name( state ) ) == NULL ) +		md->away_state = msn_away_state_list + 1;  	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code );  	msn_write( ic, buf, strlen( buf ) ); @@ -169,7 +188,7 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message )  static void msn_set_my_name( struct im_connection *ic, char *info )  { -	msn_set_display_name( set_find( &ic->acc->set, "display_name" ), info ); +	msn_set_display_name( ic, info );  }  static void msn_get_info(struct im_connection *ic, char *who)  @@ -180,12 +199,16 @@ static void msn_get_info(struct im_connection *ic, char *who)  static void msn_add_buddy( struct im_connection *ic, char *who, char *group )  { -	msn_buddy_list_add( ic, "FL", who, who ); +	struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who ); +	 +	msn_buddy_list_add( ic, "FL", who, who, group ); +	if( bu && bu->group ) +		msn_buddy_list_remove( ic, "FL", who, bu->group->name );  }  static void msn_remove_buddy( struct im_connection *ic, char *who, char *group )  { -	msn_buddy_list_remove( ic, "FL", who ); +	msn_buddy_list_remove( ic, "FL", who, NULL );  }  static void msn_chat_msg( struct groupchat *c, char *message, int flags ) @@ -221,6 +244,7 @@ static void msn_chat_leave( struct groupchat *c )  static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )  {  	struct msn_switchboard *sb; +	struct groupchat *c = imcb_chat_new( ic, who );  	if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{ @@ -238,10 +262,8 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )  		msn_sb_write_msg( ic, m ); -		return NULL; +		return c;  	} -	 -	return NULL;  }  static void msn_keepalive( struct im_connection *ic ) @@ -251,19 +273,19 @@ static void msn_keepalive( struct im_connection *ic )  static void msn_add_permit( struct im_connection *ic, char *who )  { -	msn_buddy_list_add( ic, "AL", who, who ); +	msn_buddy_list_add( ic, "AL", who, who, NULL );  }  static void msn_rem_permit( struct im_connection *ic, char *who )  { -	msn_buddy_list_remove( ic, "AL", who ); +	msn_buddy_list_remove( ic, "AL", who, NULL );  }  static void msn_add_deny( struct im_connection *ic, char *who )  {  	struct msn_switchboard *sb; -	msn_buddy_list_add( ic, "BL", who, who ); +	msn_buddy_list_add( ic, "BL", who, who, NULL );  	/* If there's still a conversation with this person, close it. */  	if( ( sb = msn_sb_by_handle( ic, who ) ) ) @@ -274,29 +296,29 @@ static void msn_add_deny( struct im_connection *ic, char *who )  static void msn_rem_deny( struct im_connection *ic, char *who )  { -	msn_buddy_list_remove( ic, "BL", who ); +	msn_buddy_list_remove( ic, "BL", who, NULL );  }  static int msn_send_typing( struct im_connection *ic, char *who, int typing )  { -	if( typing & OPT_TYPING ) +	struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who ); +	 +	if( !( bu->flags & BEE_USER_ONLINE ) ) +		return 0; +	else if( typing & OPT_TYPING )  		return( msn_buddy_msg( ic, who, TYPING_NOTIFICATION_MESSAGE, 0 ) );  	else -		return( 1 ); +		return 1;  } -static char *msn_set_display_name( set_t *set, char *value ) +static char *set_eval_display_name( set_t *set, char *value )  {  	account_t *acc = set->data;  	struct im_connection *ic = acc->ic; -	struct msn_data *md; -	char buf[1024], *fn; -	/* Double-check. */ +	/* Allow any name if we're offline. */  	if( ic == NULL ) -		return NULL; -	 -	md = ic->proto_data; +		return value;  	if( strlen( value ) > 129 )  	{ @@ -304,16 +326,10 @@ static char *msn_set_display_name( set_t *set, char *value )  		return NULL;  	} -	fn = msn_http_encode( value ); -	 -	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); -	msn_write( ic, buf, strlen( buf ) ); -	g_free( fn ); -	  	/* Returning NULL would be better, because the server still has to  	   confirm the name change. However, it looks a bit confusing to the  	   user. */ -	return value; +	return msn_set_display_name( ic, value ) ? value : NULL;  }  void msn_initmodule() @@ -342,6 +358,7 @@ void msn_initmodule()  	ret->rem_deny = msn_rem_deny;  	ret->send_typing = msn_send_typing;  	ret->handle_cmp = g_strcasecmp; +	//ret->transfer_request = msn_ftp_transfer_request;  	register_protocol(ret);  } diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index d57ff796..f060000a 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -30,6 +30,7 @@   */  #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"  #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r" +#define SB_KEEPALIVE_MESSAGE "\r\r\rDONT HANG UP ON ME!\r\r\r"  #ifdef DEBUG_MSN  #define debug( text... ) imcb_log( ic, text ); @@ -60,6 +61,10 @@                             "TypingUser: %s\r\n" \                             "\r\n\r\n" +#define SB_KEEPALIVE_HEADERS "MIME-Version: 1.0\r\n" \ +                             "Content-Type: text/x-ping\r\n" \ +                             "\r\n\r\n" +  #define PROFILE_URL "http://members.msn.com/"  struct msn_data @@ -73,10 +78,11 @@ struct msn_data  	char *passport_token;  	char *lock_key; -	GSList *msgq; +	GSList *msgq, *grpq;  	GSList *switchboards;  	int sb_failures;  	time_t first_sb_failure; +	GSList *filetransfers;  	const struct msn_away_state *away_state;  	int buddycount; @@ -91,6 +97,7 @@ struct msn_switchboard  	int fd;  	gint inp;  	struct msn_handler_data *handler; +	gint keepalive;  	int trId;  	int ready; @@ -122,6 +129,12 @@ struct msn_message  	char *text;  }; +struct msn_groupadd +{ +	char *who; +	char *group; +}; +  struct msn_handler_data  {  	int fd; @@ -161,14 +174,15 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond );  /* msn_util.c */  int msn_write( struct im_connection *ic, char *s, int len );  int msn_logged_in( struct im_connection *ic ); -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname ); -int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who ); +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ); +int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group );  void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname );  char *msn_findheader( char *text, char *header, int len );  char **msn_linesplit( char *line );  int msn_handler( struct msn_handler_data *h );  char *msn_http_encode( const char *input );  void msn_msgq_purge( struct im_connection *ic, GSList **list ); +gboolean msn_set_display_name( struct im_connection *ic, const char *rawname );  char *msn_p11_challenge( char *challenge );  /* tables.c */ @@ -188,5 +202,10 @@ 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 ); +void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial ); +void msn_sb_stop_keepalives( struct msn_switchboard *sb ); + +/* invitation.c */ +void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who );  #endif //_MSN_H diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 59363439..954ee716 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -38,10 +38,10 @@ int msn_write( struct im_connection *ic, char *s, int len )  	{  		imcb_error( ic, "Short write() to main server" );  		imc_logout( ic, TRUE ); -		return( 0 ); +		return 0;  	} -	return( 1 ); +	return 1;  }  int msn_logged_in( struct im_connection *ic ) @@ -51,32 +51,82 @@ int msn_logged_in( struct im_connection *ic )  	return( 0 );  } -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname_ ) +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group )  {  	struct msn_data *md = ic->proto_data; -	char buf[1024], *realname; -	 -	realname = msn_http_encode( realname_ ); +	char buf[1024], *realname, groupid[8]; -	g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname ); -	if( msn_write( ic, buf, strlen( buf ) ) ) +	*groupid = '\0'; +	if( group )  	{ -		g_free( realname ); +		int i; +		for( i = 0; i < md->groupcount; i ++ ) +			if( g_strcasecmp( md->grouplist[i], group ) == 0 ) +			{ +				g_snprintf( groupid, sizeof( groupid ), " %d", i ); +				break; +			} -		return( 1 ); +		if( *groupid == '\0' ) +		{ +			/* Have to create this group, it doesn't exist yet. */ +			struct msn_groupadd *ga; +			GSList *l; +			 +			for( l = md->grpq; l; l = l->next ) +			{ +				ga = l->data; +				if( g_strcasecmp( ga->group, group ) == 0 ) +					break; +			} +			 +			ga = g_new0( struct msn_groupadd, 1 ); +			ga->who = g_strdup( who ); +			ga->group = g_strdup( group ); +			md->grpq = g_slist_prepend( md->grpq, ga ); +			 +			if( l == NULL ) +			{ +				char *groupname = msn_http_encode( group ); +				g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 ); +				g_free( groupname ); +				return msn_write( ic, buf, strlen( buf ) ); +			} +			else +			{ +				/* This can happen if the user's doing lots of adds to a +				   new group at once; we're still waiting for the server +				   to confirm group creation. */ +				return 1; +			} +		}  	} +	realname = msn_http_encode( realname_ ); +	g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid );  	g_free( realname ); -	return( 0 ); +	return msn_write( ic, buf, strlen( buf ) );  } -int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who ) +int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group )  {  	struct msn_data *md = ic->proto_data; -	char buf[1024]; +	char buf[1024], groupid[8]; +	 +	*groupid = '\0'; +	if( group ) +	{ +		int i; +		for( i = 0; i < md->groupcount; i ++ ) +			if( g_strcasecmp( md->grouplist[i], group ) == 0 ) +			{ +				g_snprintf( groupid, sizeof( groupid ), " %d", i ); +				break; +			} +	} -	g_snprintf( buf, sizeof( buf ), "REM %d %s %s\r\n", ++md->trId, list, who ); +	g_snprintf( buf, sizeof( buf ), "REM %d %s %s%s\r\n", ++md->trId, list, who, groupid );  	if( msn_write( ic, buf, strlen( buf ) ) )  		return( 1 ); @@ -94,10 +144,9 @@ static void msn_buddy_ask_yes( void *data )  {  	struct msn_buddy_ask_data *bla = data; -	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname ); +	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL ); -	if( imcb_find_buddy( bla->ic, bla->handle ) == NULL ) -		imcb_ask_add( bla->ic, bla->handle, NULL ); +	imcb_ask_add( bla->ic, bla->handle, NULL );  	g_free( bla->handle );  	g_free( bla->realname ); @@ -108,7 +157,7 @@ static void msn_buddy_ask_no( void *data )  {  	struct msn_buddy_ask_data *bla = data; -	msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname ); +	msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL );  	g_free( bla->handle );  	g_free( bla->realname ); @@ -349,6 +398,7 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list )  	struct msn_message *m;  	GString *ret;  	GSList *l; +	int n = 0;  	l = *list;  	if( l == NULL ) @@ -363,7 +413,11 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list )  	{  		m = l->data; -		g_string_append_printf( ret, "\n%s", m->text ); +		if( strncmp( m->text, "\r\r\r", 3 ) != 0 ) +		{ +			g_string_append_printf( ret, "\n%s", m->text ); +			n ++; +		}  		g_free( m->who );  		g_free( m->text ); @@ -374,10 +428,23 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list )  	g_slist_free( *list );  	*list = NULL; -	imcb_log( ic, "%s", ret->str ); +	if( n > 0 ) +		imcb_log( ic, "%s", ret->str );  	g_string_free( ret, TRUE );  } +gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ) +{ +	char *fn = msn_http_encode( rawname ); +	struct msn_data *md = ic->proto_data; +	char buf[1024]; +	 +	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); +	g_free( fn ); +	 +	return msn_write( ic, buf, strlen( buf ) ) != 0; +} +  unsigned int little_endian( unsigned int dw )  {  #if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN @@ -400,7 +467,6 @@ unsigned int little_endian( unsigned int dw )  }  /* Copied and heavily modified from http://tmsnc.sourceforge.net/chl.c */ -  char *msn_p11_challenge( char *challenge )  {  	char *output, buf[256]; diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index f9c8ab29..40c4cdec 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -34,6 +34,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts );  static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );  static void msn_auth_got_passport_token( struct msn_auth_data *mad ); +static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name );  gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )  { @@ -74,7 +75,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )  	g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );  	if( msn_write( ic, s, strlen( s ) ) )  	{ -		ic->inpa = b_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, ic ); +		ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic );  		imcb_log( ic, "Connected to server, waiting for reply" );  	} @@ -230,25 +231,10 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		}  		else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 )  		{ -			set_t *s; -			  			if( num_parts == 7 ) -			{ -				http_decode( cmd[4] ); -				 -				strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) ); -				ic->displayname[sizeof(ic->displayname)-1] = 0; -				 -				if( ( s = set_find( &ic->acc->set, "display_name" ) ) ) -				{ -					g_free( s->value ); -					s->value = g_strdup( cmd[4] ); -				} -			} +				msn_ns_got_display_name( ic, cmd[4] );  			else -			{  				imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); -			}  			imcb_log( ic, "Authenticated, getting buddy list" ); @@ -435,8 +421,12 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "FLN" ) == 0 )  	{ -		if( cmd[1] ) -			imcb_buddy_status( ic, cmd[1], 0, NULL, NULL ); +		if( cmd[1] == NULL ) +			return 1; +		 +		imcb_buddy_status( ic, cmd[1], 0, NULL, NULL ); +		 +		msn_sb_start_keepalives( msn_sb_by_handle( ic, cmd[1] ), TRUE );  	}  	else if( strcmp( cmd[0], "NLN" ) == 0 )  	{ @@ -462,6 +452,8 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |   		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),  		                   st->name, NULL ); +		 +		msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) );  	}  	else if( strcmp( cmd[0], "RNG" ) == 0 )  	{ @@ -540,8 +532,14 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		}  		else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 )  		{ +			const char *group = NULL; +			int num; +			 +			if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount ) +				group = md->grouplist[num]; +			  			http_decode( cmd[5] ); -			imcb_add_buddy( ic, cmd[4], NULL ); +			imcb_add_buddy( ic, cmd[4], group );  			imcb_rename_buddy( ic, cmd[4], cmd[5] );  		}  	} @@ -566,6 +564,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		imc_logout( ic, allow_reconnect );  		return( 0 );  	} +#if 0 +	/* Discard this one completely for now since I don't care about the ack +	   and since MSN servers can apparently screw up the formatting. */  	else if( strcmp( cmd[0], "REA" ) == 0 )  	{  		if( num_parts != 5 ) @@ -596,6 +597,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			imcb_rename_buddy( ic, cmd[3], cmd[4] );  		}  	} +#endif  	else if( strcmp( cmd[0], "IPG" ) == 0 )  	{  		imcb_error( ic, "Received IPG command, we don't handle them yet." ); @@ -609,6 +611,50 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			return( 0 );  		}  	} +	else if( strcmp( cmd[0], "ADG" ) == 0 ) +	{ +		char *group = g_strdup( cmd[3] ); +		int groupnum, i; +		GSList *l, *next; +		 +		http_decode( group ); +		if( sscanf( cmd[4], "%d", &groupnum ) == 1 ) +		{ +			if( groupnum >= md->groupcount ) +			{ +				md->grouplist = g_renew( char *, md->grouplist, groupnum + 1 ); +				for( i = md->groupcount; i <= groupnum; i ++ ) +					md->grouplist[i] = NULL; +				md->groupcount = groupnum + 1; +			} +			g_free( md->grouplist[groupnum] ); +			md->grouplist[groupnum] = group; +		} +		else +		{ +			/* Shouldn't happen, but if it does, give up on the group. */ +			g_free( group ); +			imcb_error( ic, "Syntax error" ); +			imc_logout( ic, TRUE ); +			return 0; +		} +		 +		for( l = md->grpq; l; l = next ) +		{ +			struct msn_groupadd *ga = l->data; +			next = l->next; +			if( g_strcasecmp( ga->group, group ) == 0 ) +			{ +				if( !msn_buddy_list_add( ic, "FL", ga->who, ga->who, group ) ) +					return 0; +				 +				g_free( ga->group ); +				g_free( ga->who ); +				g_free( ga ); +				md->grpq = g_slist_remove( md->grpq, ga ); +			} +		} +	}  	else if( isdigit( cmd[0][0] ) )  	{  		int num = atoi( cmd[0] ); @@ -747,3 +793,48 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad )  		imc_logout( ic, TRUE );  	}  } + +static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ) +{ +	set_t *s; +	 +	if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL ) +		return FALSE; /* Shouldn't happen.. */ +	 +	http_decode( name ); +	 +	if( s->value && strcmp( s->value, name ) == 0 ) +	{ +		return TRUE; +		/* The names match, nothing to worry about. */ +	} +	else if( s->value != NULL && +	         ( strcmp( name, ic->acc->user ) == 0 || +	           set_getbool( &ic->acc->set, "local_display_name" ) ) ) +	{ +		/* The server thinks our display name is our e-mail address +		   which is probably wrong, or the user *wants* us to do this: +		   Always use the locally set display_name. */ +		return msn_set_display_name( ic, s->value ); +	} +	else +	{ +		if( s->value && *s->value ) +			imcb_log( ic, "BitlBee thinks your display name is `%s' but " +			              "the MSN server says it's `%s'. Using the MSN " +			              "server's name. Set local_display_name to true " +			              "to use the local name.", s->value, name ); +		 +		if( g_utf8_validate( name, -1, NULL ) ) +		{ +			g_free( s->value ); +			s->value = g_strdup( name ); +		} +		else +		{ +			imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); +		} +		 +		return TRUE; +	} +} diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c index 565d15f3..7c896db1 100644 --- a/protocols/msn/passport.c +++ b/protocols/msn/passport.c @@ -144,7 +144,9 @@ static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data  	struct msn_auth_data *mad = data;  	char *s; -	if( ( s = xt_find_attr( node, "Id" ) ) && strcmp( s, "PPToken1" ) == 0 ) +	if( ( s = xt_find_attr( node, "Id" ) ) && +	    ( strncmp( s, "Compact", 7 ) == 0 || +	      strncmp( s, "PPToken", 7 ) == 0 ) )  		mad->token = g_memdup( node->text, node->text_len + 1 );  	return XT_HANDLED; diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 461bd483..10425708 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -168,7 +168,23 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  		int i, j;  		/* Build the message. Convert LF to CR-LF for normal messages. */ -		if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 ) +		if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) == 0 ) +		{ +			i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); +			buf = g_new0( char, i ); +			i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); +		} +		else if( strcmp( text, SB_KEEPALIVE_MESSAGE ) == 0 ) +		{ +			buf = g_strdup( SB_KEEPALIVE_HEADERS ); +			i = strlen( buf ); +		} +		else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 )  +		{ +			buf = g_strdup( text ); +			i = strlen( buf ); +		} +		else  		{  			buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 );  			i = strlen( MSN_MESSAGE_HEADERS ); @@ -182,12 +198,6 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  				buf[i++] = text[j];  			}  		} -		else -		{ -			i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); -			buf = g_new0( char, i ); -			i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); -		}  		/* Build the final packet (MSG command + the message). */  		packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf ); @@ -222,11 +232,17 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb )  {  	struct im_connection *ic = sb->ic; +	struct groupchat *c = NULL;  	char buf[1024];  	/* Create the groupchat structure. */  	g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session ); -	sb->chat = imcb_chat_new( ic, buf ); +	if( sb->who ) +		c = bee_chat_by_title( ic->bee, ic, sb->who ); +	if( c && !msn_sb_by_chat( c ) ) +		sb->chat = c; +	else +		sb->chat = imcb_chat_new( ic, buf );  	/* Populate the channel. */  	if( sb->who ) imcb_chat_add_buddy( sb->chat, sb->who ); @@ -250,6 +266,7 @@ void msn_sb_destroy( struct msn_switchboard *sb )  	debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" );  	msn_msgq_purge( ic, &sb->msgq ); +	msn_sb_stop_keepalives( sb );  	if( sb->key ) g_free( sb->key );  	if( sb->who ) g_free( sb->who ); @@ -309,7 +326,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  		g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session );  	if( msn_sb_write( sb, buf, strlen( buf ) ) ) -		sb->inp = b_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb ); +		sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb );  	else  		debug( "Error %d while connecting to switchboard server", 2 ); @@ -322,9 +339,13 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c  	struct im_connection *ic = sb->ic;  	struct msn_data *md = ic->proto_data; -	if( msn_handler( sb->handler ) == -1 ) +	if( msn_handler( sb->handler ) != -1 ) +		return TRUE; +	 +	if( sb->msgq != NULL )  	{  		time_t now = time( NULL ); +		char buf[1024];  		if( now - md->first_sb_failure > 600 )  		{ @@ -341,37 +362,28 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c  			imcb_log( ic, "Warning: Many switchboard failures on MSN connection. "  			              "There might be problems delivering your messages." ); -		if( sb->msgq != NULL ) +		if( md->msgq == NULL )  		{ -			char buf[1024]; -			 -			if( md->msgq == NULL ) -			{ -				md->msgq = sb->msgq; -			} -			else -			{ -				GSList *l; -				 -				for( l = md->msgq; l->next; l = l->next ); -				l->next = sb->msgq; -			} -			sb->msgq = NULL; +			md->msgq = sb->msgq; +		} +		else +		{ +			GSList *l; -			debug( "Moved queued messages back to the main queue, creating a new switchboard to retry." ); -			g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); -			if( !msn_write( ic, buf, strlen( buf ) ) ) -				return FALSE; +			for( l = md->msgq; l->next; l = l->next ); +			l->next = sb->msgq;  		} +		sb->msgq = NULL; -		msn_sb_destroy( sb ); -		 -		return FALSE; -	} -	else -	{ -		return TRUE; +		debug( "Moved queued messages back to the main queue, " +		       "creating a new switchboard to retry." ); +		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); +		if( !msn_write( ic, buf, strlen( buf ) ) ) +			return FALSE;  	} +	 +	msn_sb_destroy( sb ); +	return FALSE;  }  static int msn_sb_command( gpointer data, char **cmd, int num_parts ) @@ -471,6 +483,8 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  		}  		sb->ready = 1; +		 +		msn_sb_start_keepalives( sb, FALSE );  	}  	else if( strcmp( cmd[0], "CAL" ) == 0 )  	{ @@ -520,6 +534,8 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  				sb->msgq = g_slist_remove( sb->msgq, m );  			} +			msn_sb_start_keepalives( sb, FALSE ); +			  			return( st );  		}  		else if( sb->who ) @@ -581,6 +597,8 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  		if( sb->who )  		{ +			msn_sb_stop_keepalives( sb ); +			  			/* This is a single-person chat, and the other person is leaving. */  			g_free( sb->who );  			sb->who = NULL; @@ -690,64 +708,46 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int  				/* PANIC! */  			}  		} +#if 0 +		// Disable MSN ft support for now.  		else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 )  		{ -			char *itype = msn_findheader( body, "Application-GUID:", blen ); -			char buf[1024]; +			char *command = msn_findheader( body, "Invitation-Command:", blen ); +			char *cookie = msn_findheader( body, "Invitation-Cookie:", blen ); +			unsigned int icookie;  			g_free( ct ); -			*buf = 0; -			 -			if( !itype ) -				return( 1 ); -			 -			/* File transfer. */ -			if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 ) -			{ -				char *name = msn_findheader( body, "Application-File:", blen ); -				char *size = msn_findheader( body, "Application-FileSize:", blen ); -				 -				if( name && size ) -				{ -					g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n" -					            "Filetransfers are not supported by BitlBee for now...", name, size ); -				} -				else -				{ -					strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" ); -				} -				 -				if( name ) g_free( name ); -				if( size ) g_free( size ); +			/* Every invite should have both a Command and Cookie header */ +			if( !command || !cookie ) { +				g_free( command ); +				g_free( cookie ); +				imcb_log( ic, "Warning: No command or cookie from %s", sb->who ); +				return 1;  			} -			else -			{ -				char *iname = msn_findheader( body, "Application-Name:", blen ); -				 -				g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>", -				                                itype, iname ? iname : "no name" ); -				 -				if( iname ) g_free( iname ); -			} -			 -			g_free( itype ); -			if( !*buf ) -				return( 1 ); +			icookie = strtoul( cookie, NULL, 10 ); +			g_free( cookie ); -			if( sb->who ) -			{ -				imcb_buddy_msg( ic, cmd[1], buf, 0, 0 ); -			} -			else if( sb->chat ) -			{ -				imcb_chat_msg( sb->chat, cmd[1], buf, 0, 0 ); -			} -			else -			{ -				/* PANIC! */ +			if( g_strncasecmp( command, "INVITE", 6 ) == 0 ) { +				msn_invitation_invite( sb, cmd[1], icookie, body, blen ); +			} else if( g_strncasecmp( command, "ACCEPT", 6 ) == 0 ) { +				msn_invitation_accept( sb, cmd[1], icookie, body, blen ); +			} else if( g_strncasecmp( command, "CANCEL", 6 ) == 0 ) { +				msn_invitation_cancel( sb, cmd[1], icookie, body, blen ); +			} else { +				imcb_log( ic, "Warning: Received invalid invitation with " +						"command %s from %s", command, sb->who );  			} +			 +			g_free( command ); +		} +#endif +		else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 )  +		{ +			imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not " +					"support msnmsgrp2p yet.", sb->who ); +			g_free( ct );  		}  		else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 )  		{ @@ -769,3 +769,34 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int  	return( 1 );  } + +static gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond ) +{ +	struct msn_switchboard *sb = data; +	return sb->ready && msn_sb_sendmessage( sb, SB_KEEPALIVE_MESSAGE ); +} + +void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial ) +{ +	bee_user_t *bu; +	 +	if( sb && sb->who && sb->keepalive == 0 && +	    ( bu = bee_user_by_handle( sb->ic->bee, sb->ic, sb->who ) ) && +	    !( bu->flags & BEE_USER_ONLINE ) && +	    set_getbool( &sb->ic->acc->set, "switchboard_keepalives" ) ) +	{ +		if( initial ) +			msn_sb_keepalive( sb, 0, 0 ); +		 +		sb->keepalive = b_timeout_add( 20000, msn_sb_keepalive, sb ); +	} +} + +void msn_sb_stop_keepalives( struct msn_switchboard *sb ) +{ +	if( sb && sb->keepalive > 0 ) +	{ +		b_event_remove( sb->keepalive ); +		sb->keepalive = 0; +	} +} | 
