From a2b99ec7a1a02c57b2ef44663e56bdfab6063a4f Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 11 Aug 2008 00:17:58 +0200 Subject: Added MSN file transfer of type MSNFTP. Transfer is direct and the sender can not be firewalled. --- protocols/msn/Makefile | 2 +- protocols/msn/invitation.c | 666 +++++++++++++++++++++++++++++++++++++++++++++ protocols/msn/invitation.h | 82 ++++++ protocols/msn/msn.c | 5 + protocols/msn/msn.h | 4 + protocols/msn/sb.c | 95 +++---- 6 files changed, 797 insertions(+), 57 deletions(-) create mode 100644 protocols/msn/invitation.c create mode 100644 protocols/msn/invitation.h (limited to 'protocols/msn') diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 6a588613..dd5d46e2 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = msn.o msn_util.o ns.o passport.o sb.o tables.o +objects = msn.o msn_util.o ns.o passport.o sb.o tables.o invitation.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c new file mode 100644 index 00000000..4e24d54c --- /dev/null +++ b/protocols/msn/invitation.c @@ -0,0 +1,666 @@ +/********************************************************************\ +* 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" + +#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 ) ); + +/* + * Creates a listening socket and returns its address in host, port. + */ +gboolean msn_ftp_listen( msn_filetransfer_t *msn_file, char *host, char *port ) +{ + file_transfer_t *file = msn_file->dcc; + int fd,gret; + char hostname[ HOST_NAME_MAX + 1 ]; + struct addrinfo hints, *rp; + struct sockaddr_storage saddrst, *saddr = &saddrst; + socklen_t ssize = sizeof( struct sockaddr_storage ); + + /* won't be long till someone asks for this to be configurable :) */ + + ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) + return msn_ftp_abort( file, "getaddrinfo() failed: %s", gai_strerror( gret ) ); + + memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); + + ASSERTSOCKOP( fd = msn_file->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr + , host, INET6_ADDRSTRLEN ) ) + return msn_ftp_abort( file, "inet_ntop failed on listening socket" ); + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + if( saddr->ss_family == AF_INET ) + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); + else + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); + + return TRUE; +} + +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, GAIM_INPUT_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[INET6_ADDRSTRLEN]; + char port[6]; + + msn_file->auth_cookie = acookie; + + if( !msn_ftp_listen( msn_file, host, port ) ) + return; + + msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_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 046b2772..590a1382 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -76,6 +76,10 @@ static void msn_logout( struct im_connection *ic ) if( md ) { + while( md->filetransfers ) { + imcb_file_canceled( md->filetransfers->data, "Closing msn connection" ); + } + if( md->fd >= 0 ) closesocket( md->fd ); @@ -337,6 +341,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 7c849acf..e2badbf9 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -68,6 +68,7 @@ struct msn_data GSList *switchboards; int sb_failures; time_t first_sb_failure; + GSList *filetransfers; const struct msn_away_state *away_state; int buddycount; @@ -180,4 +181,7 @@ 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 ); +/* 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/sb.c b/protocols/msn/sb.c index e9526234..b60a9a8b 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -28,6 +28,7 @@ #include "msn.h" #include "passport.h" #include "md5.h" +#include "invitation.h" static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond ); static int msn_sb_command( gpointer data, char **cmd, int num_parts ); @@ -167,7 +168,16 @@ 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( 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 ); @@ -181,12 +191,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 ); @@ -686,62 +690,41 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int } else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 ) { - char *itype = msn_findheader( body, "Application-GUID:", blen ); - char buf[1024]; + char *command = msn_findheader( body, "Invitation-Command:", blen ); + char *cookie = msn_findheader( body, "Invitation-Cookie:", blen ); + unsigned int icookie; g_free( ct ); - *buf = 0; - - if( !itype ) - return( 1 ); - - /* File transfer. */ - if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 ) - { - char *name = msn_findheader( body, "Application-File:", blen ); - char *size = msn_findheader( body, "Application-FileSize:", blen ); - - if( name && size ) - { - g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n" - "Filetransfers are not supported by BitlBee for now...", name, size ); - } - else - { - strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" ); - } - - if( name ) g_free( name ); - if( size ) g_free( size ); - } - else - { - char *iname = msn_findheader( body, "Application-Name:", blen ); - - g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>", - itype, iname ? iname : "no name" ); - - if( iname ) g_free( iname ); + /* Every invite should have both a Command and Cookie header */ + if( !command || !cookie ) { + g_free( command ); + g_free( cookie ); + imcb_log( ic, "Warning: No command or cookie from %s", sb->who ); + return 1; } - g_free( itype ); + icookie = strtoul( cookie, NULL, 10 ); + g_free( cookie ); - if( !*buf ) - return( 1 ); - - 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 ); + } + 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 ) { -- cgit v1.2.3 From 66be7849ef6b74c39bc8f1dc1d96bc4788eb50a0 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 11 Aug 2008 16:31:03 +0200 Subject: copied ulibc/BSD ifdefs over to invitation.c. I'll have to move these to a generic header file eventually. --- protocols/msn/invitation.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'protocols/msn') diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index 4e24d54c..021764d2 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -28,6 +28,20 @@ #include "invitation.h" #include "msn.h" +/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + #ifdef debug #undef debug #endif -- cgit v1.2.3 From a02f34fb047af728f991ced3688c5e804c130878 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 01:07:12 +0200 Subject: Added conf entries and lib/ftutil.[ch]. ft_listen = :;: to specify listening addresses for the bitlbee<->client connection and the bitlbee<->IM peer connection, respectively. ft_max_size should be obvious. ft_max_kbps should limit the kilobits per second per transfer (not implemented yet). --- protocols/msn/invitation.c | 70 ++++------------------------------------------ 1 file changed, 6 insertions(+), 64 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index 021764d2..f44155fa 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -27,20 +27,7 @@ #include "bitlbee.h" #include "invitation.h" #include "msn.h" - -/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif +#include "lib/ftutil.h" #ifdef debug #undef debug @@ -78,54 +65,6 @@ gboolean msn_ftp_abort( file_transfer_t *file, char *format, ... ) if( (op) == -1 ) \ return msn_ftp_abort( file , msg ": %s", strerror( errno ) ); -/* - * Creates a listening socket and returns its address in host, port. - */ -gboolean msn_ftp_listen( msn_filetransfer_t *msn_file, char *host, char *port ) -{ - file_transfer_t *file = msn_file->dcc; - int fd,gret; - char hostname[ HOST_NAME_MAX + 1 ]; - struct addrinfo hints, *rp; - struct sockaddr_storage saddrst, *saddr = &saddrst; - socklen_t ssize = sizeof( struct sockaddr_storage ); - - /* won't be long till someone asks for this to be configurable :) */ - - ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); - - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - - if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) - return msn_ftp_abort( file, "getaddrinfo() failed: %s", gai_strerror( gret ) ); - - memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); - - ASSERTSOCKOP( fd = msn_file->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); - - freeaddrinfo( rp ); - - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); - - if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? - ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr - , host, INET6_ADDRSTRLEN ) ) - return msn_ftp_abort( file, "inet_ntop failed on listening socket" ); - - ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); - - if( saddr->ss_family == AF_INET ) - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); - else - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); - - return TRUE; -} - void msn_ftp_invitation_cmd( struct im_connection *ic, char *who, int cookie, char *icmd, char *trailer ) { @@ -281,11 +220,14 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar unsigned int acookie = time ( NULL ); char host[INET6_ADDRSTRLEN]; char port[6]; + char *errmsg; msn_file->auth_cookie = acookie; - if( !msn_ftp_listen( msn_file, host, port ) ) - return; + 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, GAIM_INPUT_READ, msn_ftps_connected, file ); -- cgit v1.2.3 From e046390da36e369c94af607fdedfe7b9f99d9e47 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 00:25:54 +0100 Subject: Make purple use BitlBee's event handling API. Since the APIs never really diverged too much this is fairly transparent. I did rename and redefine GAIM_INPUT_* variables to really make it work without adding another stupid layer in between. One problem left, the new libpurple input API doesn't care about return values. Fixing that in the next CL. --- protocols/msn/ns.c | 2 +- protocols/msn/sb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index fe48f96d..6928186a 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -74,7 +74,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" ); } diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e9526234..b0f7a2c5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -308,7 +308,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 ); -- cgit v1.2.3 From 1c3008ac0b2b29f7e14ec9b874af3277c511c7a4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 13 Dec 2009 14:48:56 +0000 Subject: No functional changes, just some style "fixes". Although I admit I'm somewhat growing out of my own coding style, I do try to keep things consistent at least within files. Comments are now in reviewboard: http://code.bitlbee.org/rb/r/13/ --- protocols/msn/Makefile | 2 +- protocols/msn/msn.c | 2 +- protocols/msn/sb.c | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index dd5d46e2..5d199b9e 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = msn.o msn_util.o ns.o passport.o sb.o tables.o invitation.o +objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 590a1382..b17a0bfa 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -77,7 +77,7 @@ static void msn_logout( struct im_connection *ic ) if( md ) { while( md->filetransfers ) { - imcb_file_canceled( md->filetransfers->data, "Closing msn connection" ); + imcb_file_canceled( md->filetransfers->data, "Closing connection" ); } if( md->fd >= 0 ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index b60a9a8b..c3302e57 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -173,11 +173,13 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) 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( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 ) + } + else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 ) { buf = g_strdup( text ); i = strlen( buf ); - } else + } + else { buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 ); i = strlen( MSN_MESSAGE_HEADERS ); -- cgit v1.2.3 From 60e4df367e5c3af0eb1aada19f9c39ef7079e8e6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:23:27 +0000 Subject: Small cleanup. The max_packet_size variable doesn't seem to be read anywhere, and reworked string handling in ft_listen() a little bit. --- protocols/msn/invitation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/msn') diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index f44155fa..d2b2a5c8 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -218,7 +218,7 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar file_transfer_t *file = msn_file->dcc; char buf[1024]; unsigned int acookie = time ( NULL ); - char host[INET6_ADDRSTRLEN]; + char host[HOST_NAME_MAX+1]; char port[6]; char *errmsg; -- cgit v1.2.3 From 17a6ee93f4fbefe8b4356d884fdd95f4e72ce8cc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 16:37:06 +0200 Subject: Including DCC stuff again, with a wonderful extra layer of abstraction. Some hooks are missing so sending files doesn't work yet. Receiving also still seems to have some issues. On the plus side, at least the MSN/Jabber modules work again. --- protocols/msn/Makefile | 2 +- protocols/msn/msn.c | 4 +++- protocols/msn/msn_util.c | 3 +-- protocols/msn/sb.c | 3 +++ 4 files changed, 8 insertions(+), 4 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 5d199b9e..6a588613 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o +objects = msn.o msn_util.o ns.o passport.o sb.o tables.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 3a8b8f7b..8b73f103 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -80,9 +80,11 @@ 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 ); @@ -343,7 +345,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; + //ret->transfer_request = msn_ftp_transfer_request; register_protocol(ret); } diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 668a8b8a..f85981e5 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -95,8 +95,7 @@ static void msn_buddy_ask_yes( void *data ) msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname ); - 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 ); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index c3302e57..a935ce97 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -690,6 +690,8 @@ 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 *command = msn_findheader( body, "Invitation-Command:", blen ); @@ -722,6 +724,7 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int 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 " -- cgit v1.2.3 From 36577aa5efb2ef3daafd17f9ad179fedef28278e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 01:28:38 +0100 Subject: Create the struct groupchat early on in msn_chat_with() so the new chat setup method works properly. --- protocols/msn/msn.c | 5 ++--- protocols/msn/sb.c | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 85dd22ec..d6a4b158 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -222,6 +222,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 ) ) ) { @@ -239,10 +240,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 ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 641af5e7..626cc83e 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -236,7 +236,10 @@ struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb ) /* 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 ) + sb->chat = bee_chat_by_title( ic->bee, ic, sb->who ); + if( sb->chat == NULL ) + sb->chat = imcb_chat_new( ic, buf ); /* Populate the channel. */ if( sb->who ) imcb_chat_add_buddy( sb->chat, sb->who ); -- cgit v1.2.3 From e5abfd413a797b268db0b107d0748eb7e40da431 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 12:26:57 +0100 Subject: Safety check for yesterday's fixes: Double-check that a groupchat struct isn't claimed already. --- protocols/msn/sb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 626cc83e..bfdbfe64 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -232,13 +232,16 @@ 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 ); if( sb->who ) - sb->chat = bee_chat_by_title( ic->bee, ic, sb->who ); - if( sb->chat == NULL ) + 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. */ -- cgit v1.2.3 From d25ebea73aef7d6b2bbc17212af5109383277e6e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:04:55 +0100 Subject: GAIM_INPUT_* were renamed, at last. --- protocols/msn/invitation.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index d2b2a5c8..9f8b9a6e 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -208,7 +208,7 @@ gboolean msn_ftps_connected( gpointer data, gint fd, b_input_condition cond ) fd = msn_file->fd; sock_make_nonblocking( fd ); - msn_file->r_event_id = b_input_add( fd, GAIM_INPUT_READ, msn_ftp_read, file ); + msn_file->r_event_id = b_input_add( fd, B_EV_IO_READ, msn_ftp_read, file ); return FALSE; } @@ -229,7 +229,7 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar return; } - msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftps_connected, file ); + 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" @@ -317,7 +317,7 @@ gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ) return FALSE; sock_make_nonblocking( msn_file->fd ); - msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); return FALSE; } @@ -414,7 +414,7 @@ gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) 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, GAIM_INPUT_WRITE, msn_ftp_send, file ); + msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); return TRUE; } @@ -451,7 +451,7 @@ gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) } 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, GAIM_INPUT_WRITE, msn_ftp_send, file ); + msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); } return TRUE; @@ -616,7 +616,7 @@ gboolean msn_ftpr_write_request( file_transfer_t *file ) } msn_file->r_event_id = - b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); return TRUE; } -- cgit v1.2.3 From f60079b74053a53da3720b992c603fddf75ff1dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:26:54 +0100 Subject: Allow one to run the configure script from a different directory and put all build files in there. I need this to properly make Debian package variants (i.e. libpurple and native). --- protocols/msn/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'protocols/msn') diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 5d199b9e..1de755a8 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/msn/ +endif # [SH] Program variables objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ -- cgit v1.2.3 From 6acc0332708b8c844f721dbda9c7ecb94b8dd232 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 9 Jun 2010 01:35:13 +0100 Subject: Moving MSN contacts between groups is now possible, but no support yet for creating new groups. --- protocols/msn/msn.c | 6 +++--- protocols/msn/msn.h | 2 +- protocols/msn/msn_util.c | 22 +++++++++++++++++----- protocols/msn/ns.c | 8 +++++++- 4 files changed, 28 insertions(+), 10 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index d6a4b158..a5807318 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -181,7 +181,7 @@ static void msn_get_info(struct im_connection *ic, char *who) static void msn_add_buddy( struct im_connection *ic, char *who, char *group ) { - msn_buddy_list_add( ic, "FL", who, who ); + msn_buddy_list_add( ic, "FL", who, who, group ); } static void msn_remove_buddy( struct im_connection *ic, char *who, char *group ) @@ -251,7 +251,7 @@ static void msn_keepalive( struct im_connection *ic ) static void msn_add_permit( struct im_connection *ic, char *who ) { - msn_buddy_list_add( ic, "AL", who, who ); + msn_buddy_list_add( ic, "AL", who, who, NULL ); } static void msn_rem_permit( struct im_connection *ic, char *who ) @@ -263,7 +263,7 @@ static void msn_add_deny( struct im_connection *ic, char *who ) { struct msn_switchboard *sb; - msn_buddy_list_add( ic, "BL", who, who ); + msn_buddy_list_add( ic, "BL", who, who, NULL ); /* If there's still a conversation with this person, close it. */ if( ( sb = msn_sb_by_handle( ic, who ) ) ) diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index f3cb8635..e4abcb60 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -159,7 +159,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); /* msn_util.c */ int msn_write( struct im_connection *ic, char *s, int len ); int msn_logged_in( struct im_connection *ic ); -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname ); +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ); int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who ); void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ); char *msn_findheader( char *text, char *header, int len ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index a8d24b30..f9c10e72 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -50,14 +50,26 @@ int msn_logged_in( struct im_connection *ic ) return( 0 ); } -int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname_ ) +int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ) { struct msn_data *md = ic->proto_data; - char buf[1024], *realname; + char buf[1024], *realname, groupid[8]; realname = msn_http_encode( realname_ ); - g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname ); + *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 ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); if( msn_write( ic, buf, strlen( buf ) ) ) { g_free( realname ); @@ -93,7 +105,7 @@ 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 ); imcb_ask_add( bla->ic, bla->handle, NULL ); @@ -106,7 +118,7 @@ static void msn_buddy_ask_no( void *data ) { struct msn_buddy_ask_data *bla = data; - msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname ); + msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL ); g_free( bla->handle ); g_free( bla->realname ); diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 2b0600a3..8afcbe23 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -532,8 +532,14 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 ) { + const char *group = NULL; + int num; + + if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount ) + group = md->grouplist[num]; + http_decode( cmd[5] ); - imcb_add_buddy( ic, cmd[4], NULL ); + imcb_add_buddy( ic, cmd[4], group ); imcb_rename_buddy( ic, cmd[4], cmd[5] ); } } -- cgit v1.2.3 From 70ac4771ebf3358d7bec9bd6fe37cde4c23223bc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 11 Jun 2010 13:34:39 +0200 Subject: Create new MSN groups when necessary. --- protocols/msn/msn.c | 17 +++++++++++++++++ protocols/msn/msn.h | 8 +++++++- protocols/msn/msn_util.c | 46 ++++++++++++++++++++++++++++++++++++---------- protocols/msn/ns.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 11 deletions(-) (limited to 'protocols/msn') diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index a5807318..8b4f1a81 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -103,6 +103,15 @@ static void msn_logout( struct im_connection *ic ) g_free( md->grouplist[--md->groupcount] ); g_free( md->grouplist ); + while( md->grpq ) + { + struct msn_groupadd *ga = md->grpq->data; + g_free( ga->group ); + g_free( ga->who ); + g_free( ga ); + md->grpq = g_slist_remove( md->grpq, ga ); + } + g_free( md ); } @@ -121,6 +130,14 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in { struct msn_switchboard *sb; +#ifdef DEBUG + if( strcmp( who, "raw" ) == 0 ) + { + msn_write( ic, message, strlen( message ) ); + msn_write( ic, "\r\n", 2 ); + } + else +#endif if( ( sb = msn_sb_by_handle( ic, who ) ) ) { return( msn_sb_sendmessage( sb, message ) ); diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index e4abcb60..59036e37 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -69,7 +69,7 @@ struct msn_data int trId; - GSList *msgq; + GSList *msgq, *grpq; GSList *switchboards; int sb_failures; time_t first_sb_failure; @@ -120,6 +120,12 @@ struct msn_message char *text; }; +struct msn_groupadd +{ + char *who; + char *group; +}; + struct msn_handler_data { int fd; diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index f9c10e72..24f7e10e 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -55,8 +55,6 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * struct msn_data *md = ic->proto_data; char buf[1024], *realname, groupid[8]; - realname = msn_http_encode( realname_ ); - *groupid = '\0'; if( group ) { @@ -67,19 +65,47 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * g_snprintf( groupid, sizeof( groupid ), " %d", i ); break; } - } - - g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); - if( msn_write( ic, buf, strlen( buf ) ) ) - { - g_free( realname ); - 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 ) diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 8afcbe23..0be9e727 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -611,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] ); -- cgit v1.2.3