diff -urN bitlbee-0.92/commands.c bitlbee-0.92.akke/commands.c --- bitlbee-0.92/commands.c 2005-02-08 22:48:35.000000000 +0100 +++ bitlbee-0.92.akke/commands.c 2005-05-07 02:29:45.000000000 +0200 @@ -642,6 +642,33 @@ return( 0 ); } +int user_is_blocked( user_t *u ) +{ + char *buf; + struct gaim_connection *gc = u->gc; + GSList *l; + int length = strlen( u->user ) + 1 /* @ */ + strlen( u->host ) + 1 /* trailing \x00 */; + + buf = g_new0( char, length + 1 ); + if ( !buf ) + return( 0 ); + + g_snprintf( buf, length, "%s@%s", u->user, u->host ); + + for( l = gc->deny; l; l = l->next ) + if( g_strcasecmp( l->data, buf ) == 0 ) + return( 1 ); + + return( 0 ); +} + +char *blocked_string( user_t *u ) +{ + if ( user_is_blocked( u ) ) + return " (User is blocked!)"; + return ""; +} + int cmd_blist( irc_t *irc, char **cmd ) { int online = 0, away = 0, offline = 0; @@ -665,21 +692,21 @@ if( online == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && u->online && !u->away ) { g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, proto_name[u->gc->user->protocol] ); - irc_usermsg( irc, "%-16.16s %-40.40s %s", u->nick, s, "Online" ); + irc_usermsg( irc, "%-16.16s %-40.40s %s%s", u->nick, s, "Online", blocked_string( u ) ); n_online ++; } if( away == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && u->online && u->away ) { g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, proto_name[u->gc->user->protocol] ); - irc_usermsg( irc, "%-16.16s %-40.40s %s", u->nick, s, u->away ); + irc_usermsg( irc, "%-16.16s %-40.40s %s%s", u->nick, s, u->away, blocked_string( u ) ); n_away ++; } if( offline == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && !u->online ) { g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, proto_name[u->gc->user->protocol] ); - irc_usermsg( irc, "%-16.16s %-40.40s %s", u->nick, s, "Offline" ); + irc_usermsg( irc, "%-16.16s %-40.40s %s%s", u->nick, s, "Offline", blocked_string( u ) ); n_offline ++; } diff -urN bitlbee-0.92/irc.c bitlbee-0.92.akke/irc.c --- bitlbee-0.92/irc.c 2005-02-24 00:32:02.000000000 +0100 +++ bitlbee-0.92.akke/irc.c 2005-05-07 03:15:20.000000000 +0200 @@ -31,6 +31,43 @@ GSList *irc_connection_list = NULL; +char *set_update_mymsnbuddyimage( irc_t *irc, set_t *set, char *value ) +{ + GSList *c = get_connections(); + struct gaim_connection *conn; + + if( access( value, R_OK ) == 0 ) /* buddy-image is readable ... */ + { + if( set->value ) /* stupid hack, but required: */ + { /* We need the value before this function ends */ + g_free( set->value ); /* as we call set_getstr() from the msn module */ + } /* when status is changed. And we do that here! */ + /* I don't think there's an easier method to */ + set->value = g_strdup( value ); /* trigger a call to a function when a 'set' is */ + /* done on my_buddy_image */ + + while( c ) + { + conn = ( struct gaim_connection * ) c->data; /* We set the away state to the current away state */ + if( conn->protocol == PROTO_MSN ) /* This is how MSN resets it's msnobject so users know */ + { /* his buddy-image has changed... */ + if( conn->flags & OPT_LOGGED_IN ) /* Not a real 'hack' here.. it's just microsoft's */ + proto_away( conn, conn->away ); /* default... lol */ + } + + c = c->next; + } + } + else /* buddy-image is not readable ... */ + { + irc_usermsg( irc, "Could not read file '%s'. Keeping current buddy_image...", value); + return NULL; + } + + return( value ); +} + + irc_t *irc_new( int fd ) { irc_t *irc = g_new0( irc_t, 1 ); @@ -128,7 +165,27 @@ set_add( irc, "save_on_quit", "1", set_eval_bool ); set_add( irc, "to_char", ": ", set_eval_to_char ); set_add( irc, "typing_notice", "false", set_eval_bool ); - + + /* MSN buddy/emoticon images stuff */ + set_add( irc, "msn_images_path_buddy", "/tmp/msn", NULL ); + set_add( irc, "msn_images_path_emoticon", "/tmp/msn", NULL ); + set_add( irc, "msn_images_mybuddyimage", "/tmp/msn/myimage.png", set_update_mymsnbuddyimage ); + /* MSN buddy list stuff */ + set_add( irc, "msn_buddylist_checks", "true", set_eval_bool ); + /* MSN notify stuff */ + set_add( irc, "msn_notify_openwindow", "true", set_eval_bool ); + set_add( irc, "msn_notify_closewindow", "true", set_eval_bool ); + set_add( irc, "msn_notify_timeout", "true", set_eval_bool ); + /* MSN font stuff */ + set_add( irc, "msn_font_face", "MS Shell Dlg", NULL ); + set_add( irc, "msn_font_CS", "0", set_eval_int ); + set_add( irc, "msn_font_PF", "22", set_eval_int ); + set_add( irc, "msn_font_color", "000000", NULL ); + set_add( irc, "msn_font_bold", "false", set_eval_bool ); + set_add( irc, "msn_font_italic", "false", set_eval_bool ); + set_add( irc, "msn_font_overstrike", "false", set_eval_bool ); + set_add( irc, "msn_font_underline", "false", set_eval_bool ); + conf_loaddefaults( irc ); return( irc ); diff -urN bitlbee-0.92/protocols/msn/Makefile bitlbee-0.92.akke/protocols/msn/Makefile --- bitlbee-0.92/protocols/msn/Makefile 2004-04-08 18:34:11.000000000 +0200 +++ bitlbee-0.92.akke/protocols/msn/Makefile 2005-05-07 02:30:02.000000000 +0200 @@ -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 msnobject.o msnc1.o CFLAGS += -Wall LFLAGS += -r diff -urN bitlbee-0.92/protocols/msn/msn.c bitlbee-0.92.akke/protocols/msn/msn.c --- bitlbee-0.92/protocols/msn/msn.c 2005-02-23 18:31:49.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/msn.c 2005-05-07 03:15:12.000000000 +0200 @@ -25,6 +25,8 @@ #include "nogaim.h" #include "msn.h" +#include "msnc1.h" +#include "msnobject.h" static struct prpl *my_protocol = NULL; @@ -57,6 +59,9 @@ md->away_state = msn_away_state_list; msn_connections = g_slist_append( msn_connections, gc ); + + p2p_init_random(); + md->msnobject = NULL; } } @@ -77,6 +82,11 @@ while( md->switchboards ) msn_sb_destroy( md->switchboards->data ); + + msn_cleanup_userinfolist( md ); + if( md->msnobject ) + g_free( md->msnobject ); + if( md->msgq ) { @@ -200,7 +210,15 @@ if( !st ) st = msn_away_state_list; md->away_state = st; - g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code ); + msn_update_mybuddyimage(gc); + if (md->msnobject) + { + g_snprintf( buf, sizeof( buf ), "CHG %d %s %s %s\r\n", ++md->trId, st->code, CLIENT_ID, md->msnobject ); + } + else + { + g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, st->code, 0 ); + } msn_write( gc, buf, strlen( buf ) ); } @@ -241,7 +259,35 @@ static void msn_get_info(struct gaim_connection *gc, char *who) { /* Just make an URL and let the user fetch the info */ - serv_got_crap( gc, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who ); + struct msn_data *md = gc->proto_data; + struct msn_userinfo *userinfo = msn_find_userinfo( md, who ); + if ( userinfo ) + { + char *filename = msn_imagefullpath( gc, userinfo->who, userinfo->msnobject, TYPE_BUDDY_IMAGE, NULL, "png" ); + if( filename ) + { + if( access( filename, F_OK ) == 0 ) + { + serv_got_crap( gc, "%s\n%s: %s%s\n - buddy-image: %s", + _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who, filename); + } + else + { + serv_got_crap( gc, "%s\n%s: %s%s\n - buddy-image would be %s, but it's not downloaded yet! :(", + _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who, filename); + } + } + else + { + serv_got_crap( gc, "%s\n%s: %s%s\n - Could not converg msnobject to filename, msnobject: %s", + _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who, userinfo->msnobject); + } + } + else + { + serv_got_crap( gc, "%s\n%s: %s%s\n - He hasn't got a buddy-image!", + _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who ); + } } static void msn_add_buddy( struct gaim_connection *gc, char *who ) diff -urN bitlbee-0.92/protocols/msn/msn.h bitlbee-0.92.akke/protocols/msn/msn.h --- bitlbee-0.92/protocols/msn/msn.h 2004-12-03 22:59:08.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/msn.h 2005-05-07 02:38:45.000000000 +0200 @@ -28,6 +28,8 @@ #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 CLIENT_ID "268435500" + #ifdef _WIN32 #define debug #else @@ -44,7 +46,7 @@ #define MSN_MESSAGE_HEADERS "MIME-Version: 1.0\r\n" \ "Content-Type: text/plain; charset=UTF-8\r\n" \ "User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ - "X-MMS-IM-Format: FN=MS%20Shell%20Dlg; EF=; CO=0; CS=0; PF=0\r\n" \ + "X-MMS-IM-Format: FN=%s; EF=%s; CO=%s; CS=%d; PF=%d\r\n" \ "\r\n" #define MSN_TYPING_HEADERS "MIME-Version: 1.0\r\n" \ @@ -55,6 +57,13 @@ #define PROFILE_URL "http://members.msn.com/" +struct msn_userinfo +{ + char *who; + char *msnobject; +}; + + struct msn_data { struct gaim_connection *gc; @@ -68,6 +77,10 @@ GSList *switchboards; int buddycount; struct msn_away_state *away_state; + + /* P2P stuff */ + char *msnobject; + GSList *userinfolist; }; struct msn_switchboard @@ -87,6 +100,9 @@ GSList *msgq; char *who; struct conversation *chat; + + /* P2P stuff*/ + GSList *p2p_sessionlist; }; struct msn_away_state diff -urN bitlbee-0.92/protocols/msn/msn_util.c bitlbee-0.92.akke/protocols/msn/msn_util.c --- bitlbee-0.92/protocols/msn/msn_util.c 2004-05-03 22:02:52.000000000 +0200 +++ bitlbee-0.92.akke/protocols/msn/msn_util.c 2005-05-07 03:15:25.000000000 +0200 @@ -26,6 +26,8 @@ #include "nogaim.h" #include "msn.h" #include +#include "msnobject.h" +#include "msnc1.h" int msn_write( struct gaim_connection *gc, char *s, int len ) { @@ -54,7 +56,15 @@ execute this code if we're not away. */ if( md->away_state == msn_away_state_list ) { - g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 ); + msn_update_mybuddyimage(gc); + if ( md->msnobject ) + { + g_snprintf( buf, sizeof( buf ), "CHG %d %s %s %s\r\n", ++md->trId, md->away_state->code, CLIENT_ID, md->msnobject ); + } + else + { + g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 ); + } return( msn_write( gc, buf, strlen( buf ) ) ); } @@ -318,7 +328,13 @@ if( h->msglen > h->rxlen ) break; - msg = g_strndup( h->rxq, h->msglen ); + /* maybe some info is required, why did this buddy-image patch + * replace g_strndup() with g_memdup()? + * Well, easy: + * g_strndup() doesn't like binary data, g_memdup() does! */ + msg = g_memdup( h->rxq, h->msglen + 1 ); + msg[h->msglen]=0; + cmd = msn_linesplit( h->cmd_text ); for( count = 0; cmd[count]; count ++ ); diff -urN bitlbee-0.92/protocols/msn/msnc1.c bitlbee-0.92.akke/protocols/msn/msnc1.c --- bitlbee-0.92/protocols/msn/msnc1.c 1970-01-01 01:00:00.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/msnc1.c 2005-05-07 03:10:01.000000000 +0200 @@ -0,0 +1,1387 @@ +/* + * Implementation of the buddy/emoticon - images introduced in MSN6 + * ----------------------------------------------------------------- + * + * This is a rather quick-implementation and could probably be + * optimized/corrected in many ways. But I don't even like MSN! + * I just implemented this because ppl kept asking why I didn't + * configure a buddy-image (they don't know what bitlbee is!). + * + * So I decided to implement this stupid feature into bitlbee but + * I don't realy care about if the code is good or bad. The good + * thing is it's working and now ppl can see my face in their MSN + * window... And I can see theirs, but like I said, I don't care :p + * + * Enjoy! + * + * Andy Knuts (irc://akke && email://djfred@poeperkesdag.be) + * + */ + +#include "nogaim.h" +#include "msn.h" +#include "msnc1.h" +#include "msnobject.h" + +/* + * Sets your buddy image to the one specified in the my_buddy_image + * setting. + */ +void msn_update_mybuddyimage( struct gaim_connection *gc ) +{ + struct msn_data *md = gc->proto_data; + char *filename = set_getstr( gc->irc, "msn_images_mybuddyimage" ); + + if ( filename && access( filename, R_OK ) == 0 ) + { + if( md->msnobject ) + g_free( md->msnobject ); + + md->msnobject = msnobject_from_params( gc->username, TYPE_BUDDY_IMAGE, filename ); + } + else + { + do_error_dialog(gc, "Could not create msnobject for my buddy image. Please check your 'my_buddy_image' setting!", "MSN"); + } +} + +/* + * returns the userinfo ( containing msnobject etc... ) for a given user + */ +struct msn_userinfo *msn_find_userinfo( struct msn_data *md, char *who ) +{ + GSList *tmp = md->userinfolist; + + struct msn_userinfo *userinfo; + + while( tmp != NULL ) + { + userinfo = ( struct msn_userinfo * ) tmp->data; + if( !strcmp( userinfo->who, who ) ) + return userinfo; + + tmp = g_slist_next( tmp ); + } + + return NULL; +} + +/* + * deletes all data from a msn_userinfo struct + */ +void msn_cleanup_userinfo( struct msn_userinfo *userinfo ) +{ + if( userinfo->who ) + { + g_free( userinfo->who ); + userinfo->who = NULL; + } + + if( userinfo->msnobject ) + { + g_free( userinfo->msnobject ); + userinfo->msnobject = NULL; + } +} + +/* + * deleted all userinfo data from a msn_data struct + */ +void msn_cleanup_userinfolist( struct msn_data *md ) +{ + GSList *tmp = md->userinfolist; + if( tmp ) + { + while( tmp != NULL ) + { + msn_cleanup_userinfo(( struct msn_userinfo* )tmp->data ); + tmp = g_slist_next( tmp ); + } + + g_slist_free( md->userinfolist ); + md->userinfolist = NULL; + } +} + +/* + * add or update an existing userinfo in md->userinfolist GSList + */ +void msn_add_or_update_userinfolist( struct gaim_connection *gc, struct msn_userinfo *new_userinfo ) +{ + struct msn_userinfo *userinfo; + struct msn_data *md = gc->proto_data; + userinfo = msn_find_userinfo( md, new_userinfo->who ); + int user_buddyimage_changed = 0; + if( userinfo ) + { + if( userinfo->msnobject && new_userinfo->msnobject && + strcmp(userinfo->msnobject, new_userinfo->msnobject) != 0 ) + { + user_buddyimage_changed = 1; + } + msn_cleanup_userinfo( userinfo ); + md->userinfolist = g_slist_remove( md->userinfolist, userinfo ); + } + md->userinfolist = g_slist_append( md->userinfolist, new_userinfo ); + if( user_buddyimage_changed ) + { + struct msn_switchboard *sb = msn_sb_by_handle( gc, new_userinfo->who ); + if( sb ) + { + p2p_request_msnobject(sb, sb->who, NULL, NULL ); + } + } +} + +/* + * create a userinfo struct from the provided parameters + */ + +struct msn_userinfo *userinfo_from_params( char *who, char *msnobject ) +{ + struct msn_userinfo *userinfo; + userinfo = g_new( struct msn_userinfo, 1 ); + + if( !userinfo ) + return NULL; + + userinfo->who = g_strdup( who ); + userinfo->msnobject = g_strdup( msnobject ); + + return userinfo; +} + + +/* + * 2 stupid functions actually ... + */ +void p2p_init_random() +{ + srand( time( NULL ) ); +} + +DWORD p2p_randomnr() +{ + return rand(); +} + +/* + * generate a random UID + */ +char *p2p_rand_guid() +{ + return g_strdup_printf( "%4X%4X-%4X-%4X-%4X-%4X%4X%4X", + rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, rand() % 0xAAFF + 0x1111 ); +} + +/* + * create a P2PPacket and initialize it's members to the defauls... + */ +struct P2PPacket *p2p_packet_new() +{ + struct P2PPacket *packet = g_new0( struct P2PPacket, 1 ); + + if( !packet ) + return NULL; + +/* + packet->destination = NULL; + packet->filename = NULL; + packet->call_id = NULL; + packet->branch = NULL; +*/ + packet->fd = -1; + + return packet; +} + +/* + * convert a textbuffer containing a packet into a packet struct + * ... returns NULL on error + */ +struct P2PPacket *p2p_packet_from_buffer( char *buffer, int length ) +{ + int pos; + struct P2PPacket *packet; + + if( length <( sizeof( struct P2PHeader ) + sizeof( struct P2PFooter ) ) ) + return NULL; + + packet = p2p_packet_new(); + if( !packet ) + return NULL; + + pos = 0; + + memcpy( &( packet->header ), buffer, sizeof( struct P2PHeader ) ); + pos += sizeof( struct P2PHeader ); + + if( length < ( sizeof( struct P2PHeader ) + packet->header.length + sizeof( struct P2PFooter ) ) ) + { + g_free( packet ); + return NULL; + } + + if( packet->header.length > 0 ) + { + memcpy( packet->data, buffer+pos, packet->header.length ); + pos += packet->header.length; + } + + memcpy( &( packet->footer ), buffer+pos, sizeof( struct P2PFooter ) ); + + return packet; +} + +/* + * find p2p packet by call_id + */ +struct P2PPacket *p2p_find_packet_by_callid( struct msn_switchboard *sb, char *call_id ) +{ + if( !call_id ) + return NULL; + + GSList *tmp = sb->p2p_sessionlist; + + struct P2PPacket *packet; + + while( tmp != NULL ) + { + packet = ( struct P2PPacket * ) tmp->data; + if( packet->call_id && + !strcmp( packet->call_id, call_id ) ) + return packet; + + tmp = g_slist_next( tmp ); + } + + return NULL; + +} + +/* + * find a session by type/value in the current session list of this sb + */ +struct P2PPacket *p2p_find_packet( struct msn_switchboard *sb, DWORD value, int type ) +{ + GSList *tmp = sb->p2p_sessionlist; + struct P2PPacket *packet; + + if( value == 0 ) + return NULL; + + while( tmp != NULL ) + { + packet = ( struct P2PPacket * ) tmp->data; + switch( type ) + { + case FIND_BY_SESSION_ID: + if( packet->header.session_id == value ) + { + return packet; + } + break; + + case FIND_BY_ID: + if( packet->header.id == value ) + { + return packet; + } + break; + + default: + return NULL; + } + + tmp = g_slist_next( tmp ); + } + + return NULL; +} + +/* + * extracts the given header from the packet slp data by doing + * it's utmost best to find it! + */ +char *p2p_findheader( struct P2PPacket *packet, char *header ) +{ + char *p, *tmp; + + p = msn_findheader( packet->data, header, packet->header.length ); + + if( p ) + return p; + + tmp = strstr( packet->data, "\r\n\r\n" ); + while( tmp ) + { + tmp += 4; + + p = msn_findheader( tmp, header, packet->header.length - ( tmp - packet->data ) ); + + if( p ) + return p; + + tmp = strstr( tmp, "\r\n\r\n" ); + } + + tmp = strstr( packet->data, header ); + + if( tmp ) + { + char *tmp2; + tmp2 = strstr( tmp, "\r" ); + if( tmp2 ) + { + tmp += strlen( header ); + int length = ( tmp2-tmp ); + p = g_new0( char, length+1 ); + if( !p ) + return NULL; + strncpy( p, tmp, length ); + p[length]=0; + return p; + } + } + + return NULL; +} + +/* + * free all properties of a P2PPacket + */ +void p2p_cleanup_packet( struct P2PPacket *packet ) +{ + if( packet->destination ) + { + g_free( packet->destination ); + packet->destination = NULL; + } + + if( packet->branch ) + { + g_free( packet->branch ); + packet->branch = NULL; + } + + if( packet->call_id ) + { + g_free( packet->call_id ); + packet->call_id = NULL; + } + + if( packet->filename ) + { + g_free( packet->filename ); + packet->filename = NULL; + } + + if( packet->fd != -1 ) + { + close( packet->fd ); + packet->fd = -1; + } +} + +/* + * gets called from sb.c every time before the sb itself gets g_free()'s + */ +void p2p_cleanup_sb( struct msn_switchboard *sb ) +{ + GSList *tmp; + + tmp = sb->p2p_sessionlist; + + if( tmp ) + { + while( tmp != NULL ) + { + p2p_cleanup_packet(( struct P2PPacket * )tmp->data ); + tmp = g_slist_next( tmp ); + } + + g_slist_free( sb->p2p_sessionlist ); + sb->p2p_sessionlist = NULL; + } +} + +/* + * returns a fullpath for the requested image + */ +char *msn_imagefullpath( struct gaim_connection *gc, char *who, char *msnobject, int type, char *emoticon_shortcut, char *ext ) +{ + char *path = NULL, *fullpath = NULL, *sha1c, *sha1d, md5[60], filename[150]; + struct stat statinfo; + int buffersize; + md5_state_t state; + md5_byte_t digest[16]; + user_t *user; + int i; + + switch( type ) + { + case TYPE_BUDDY_IMAGE: + path = set_getstr( gc->irc, "msn_images_path_buddy" ); + break; + case TYPE_EMOTICON_IMAGE: + path = set_getstr( gc->irc, "msn_images_path_emoticon" ); + break; + } + + if( !path ) + return NULL; + + if(( stat( path, &statinfo ) ) != 0 ) + return NULL; + + if( !S_ISDIR( statinfo.st_mode ) ) + return NULL; + + sha1c = msnobject_get_field( "SHA1C", msnobject ); + if( !sha1c ) + { + do_error_dialog( gc, "Could not allocate memory for 'sha1c' in msn_imagefullpath()", "MSN" ); + return NULL; + } + + sha1d = msnobject_get_field( "SHA1D", msnobject ); + if( !sha1d ) + { + do_error_dialog( gc, "Could not allocate memory for 'sha1d' in msn_imagefullpath()", "MSN" ); + g_free( sha1c ); + return NULL; + } + + md5_init( &state ); + md5_append( &state,( const md5_byte_t * ) sha1c, strlen( sha1c ) ); + md5_append( &state,( const md5_byte_t * ) sha1d, strlen( sha1d ) ); + md5_finish( &state, digest ); + + g_free( sha1c ); + g_free( sha1d ); + + bzero( md5, sizeof( md5 ) ); + + for( i = 0; i < 16; i ++ ) + g_snprintf( md5+strlen( md5 ), 3, "%02x", digest[i] ); + + g_snprintf( filename, sizeof( filename ), "%s.%s", md5, ext ); + + user = user_findhandle( gc, who ); + if ( user ) + { + buffersize = strlen( path ) + 1 + strlen( who ) + 1 + strlen( filename ); + + if( emoticon_shortcut ) + buffersize += strlen( emoticon_shortcut ); + + fullpath = g_new0( char, buffersize ); + + if ( fullpath ) + { + switch( type ) + { + case TYPE_BUDDY_IMAGE: + g_snprintf( fullpath, buffersize, "%s/%s.%s", path, user->nick, filename ); + break; + case TYPE_EMOTICON_IMAGE: + g_snprintf( fullpath, buffersize, "%s/%s.emoticon.%s", path, user->nick, filename ); + break; + } + } + else + { + do_error_dialog( gc, "Could not allocate memory for 'fullpath' in msn_imagefullpath()", "MSN" ); + } + } + else + { + do_error_dialog( gc, "Could not find userhandle in msn_imagefullpath()", "MSN" ); + } + + return fullpath; +} + + +/* + * send packet 'packet' to the sb + */ +void p2p_sendpacket( struct msn_switchboard *sb, struct P2PPacket *packet ) +{ + + struct gaim_connection *gc = sb->gc; + char cmd[1024], *buffer, *p; + char *header; + int len; + + if( !packet->destination ) + { + do_error_dialog( gc, "Could not send packet in p2p_sendpacket(), no destination givven!", "MSN" ); + return; + } + + header = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: "; + + len = strlen( header ) + + strlen( packet->destination ) + + 4 /* \r\n\r\n */ + + sizeof( struct P2PHeader ) + + sizeof( struct P2PFooter ); + + if( packet->header.length > 0 ) + len += packet->header.length; /* length of packet->data */ + + buffer = g_new0(char, len + 1); + + if (!buffer) + { + do_error_dialog( gc, "Could not allocate memory for 'buffer' in p2p_sendpacket()", "MSN" ); + return; + } + + g_snprintf( buffer, len, "%s%s\r\n\r\n", header, packet->destination ); + p = buffer+strlen( buffer ); + + memcpy( p, &( packet->header ), sizeof( struct P2PHeader ) ); + p += sizeof( struct P2PHeader ); + + if( packet->header.length > 0 ) + { + memcpy( p, packet->data, packet->header.length ); + p += packet->header.length; + } + + memcpy( p, &( packet->footer ), sizeof( struct P2PFooter ) ); + p += sizeof( struct P2PFooter ); + + g_snprintf( cmd, sizeof( cmd ), "MSG %d D %d\r\n", ++sb->trId, len ); + + if( !( msn_sb_write( sb, cmd, strlen( cmd ) ) && msn_sb_write( sb, buffer, len ) ) ) + { + do_error_dialog( gc, "Error writing to switchboard in p2p_sendpacket()", "MSN" ); + } + + g_free( buffer ); + +} + + +/* + * Sends an ACK packet to the sb + */ +void p2p_sendack( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ) +{ + if( !packet ) + return; + + packet->header.id++; + packet->header.offset = 0; + packet->header.total_size = received_packet->header.total_size; + packet->header.length = 0; + packet->header.flags = 0x2; + packet->header.ack_sub_id = received_packet->header.ack_id; + packet->header.ack_id = received_packet->header.id; + packet->header.ack_size = received_packet->header.length; + packet->footer.value = 0; + + p2p_sendpacket( sb, packet ); +} + +/* + * Sends the base identifier packet to the sb + */ +void p2p_sendbaseidentifier( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ) +{ + if( !packet ) + return; + + packet->header.session_id = 0; + packet->header.offset = 0; + packet->header.total_size = received_packet->header.total_size; + packet->header.flags = 0x2; + packet->header.ack_sub_id = received_packet->header.ack_id; + packet->header.ack_id = received_packet->header.id; + packet->header.id = p2p_randomnr(); + packet->header.ack_size = received_packet->header.length; + packet->header.length = 0; + + packet->footer.value = 0; + + p2p_sendpacket( sb, packet ); + + packet->header.id -= 4; +} + +/* + * Send's the "200 OK" message to the sb + */ +void p2p_send200ok( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ) +{ + struct gaim_connection *gc = sb->gc; + char body[1024]; + + if( !packet ) + return; + + packet->header.session_id = 0; + packet->header.id++; + packet->header.offset = 0; + + g_snprintf( body, sizeof( body ), "SessionID: %d\r\n\r\n", packet->session_id ); + + bzero( packet->data, sizeof( packet->data ) ); + + packet->header.total_size = g_snprintf( packet->data, sizeof( packet->data ), + "MSNSLP/1.0 200 OK\r\n" + "To: \r\n" + "From: \r\n" + "Via: MSNSLP/1.0/TLP ;branch={%s}\r\n" + "CSeq: %d\r\n" + "Call-ID: {%s}\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionreqbody\r\n" + "Content-Length: %d\r\n\r\n" + "%s", + packet->destination, gc->username, packet->branch, ++packet->cseq, + packet->call_id, strlen( body )+1 /*adding up the \x00 in the content-length*/ , body ); + + packet->header.total_size++; /* we need the \x00 at the end */ + packet->header.length = packet->header.total_size; + packet->header.flags = 0; + packet->header.ack_id = received_packet->header.id; + packet->header.ack_sub_id = 0; + packet->header.ack_size = 0; + + packet->footer.value = 0; + + p2p_sendpacket( sb, packet ); +} + +/* + * Sends the DATA PREPARATION packet to the sb + */ +void p2p_senddataprep( struct msn_switchboard *sb, struct P2PPacket *packet ) +{ + if( !packet ) + return; + + packet->header.session_id = packet->session_id; + packet->header.id++; + packet->header.offset = 0; + packet->header.total_size = 4; + packet->header.length = 4; + packet->header.flags = 0; + packet->header.ack_id = p2p_randomnr(); + packet->header.ack_sub_id = 0; + packet->header.ack_size = 0; + + bzero( packet->data, sizeof( packet->data ) ); + + packet->footer.value = 0x01000000; + + p2p_sendpacket( sb, packet ); +} + +/* + * Sends data( picture data ) to the sb + * splitting it into multiple messages if required + */ +void p2p_senddata( struct msn_switchboard *sb, struct P2PPacket *packet ) +{ + int fd, bytes; + struct stat fileinfo; + + if( !packet ) + return; + + if( !packet->filename ) + return; + + if( ( fd=open( packet->filename, O_RDONLY ) ) == -1 ) + return; + + if( fstat( fd, &fileinfo ) != 0 ) + { + close( fd ); + return; + } + + packet->header.id++; + packet->header.total_size = fileinfo.st_size; + packet->header.flags = 0x20; + packet->header.ack_sub_id = 0; + packet->header.ack_size = 0; + packet->header.offset = 0; + + packet->footer.value = 0x01000000; + +#define MAX_CHUNK_SIZE 1200 + while(( bytes = read( fd, packet->data, MAX_CHUNK_SIZE ) ) > 0 ) { + packet->header.ack_id = p2p_randomnr(); + packet->header.length = bytes; + p2p_sendpacket( sb, packet ); + packet->header.offset += packet->header.length; + } + + close( fd ); +} + +/* + * sends an INVITE for context to the packet's destination... + */ +void p2p_sendinvite( struct msn_switchboard *sb, struct P2PPacket *packet, char *context ) +{ + struct gaim_connection *gc = sb->gc; + char body[1024]; + + packet->session_id = p2p_randomnr(); + + if( packet->fd == -1 ) + { + do_error_dialog( gc, "Couldn't send an INVITE because the current packet hasn't got an open file to write to! WTF?", "MSN" ); + return; + } + + if( packet->branch ) + { + g_free( packet->branch ); + } + packet->branch = p2p_rand_guid(); + + if( packet->call_id ) + { + g_free( packet->call_id ); + } + packet->call_id = p2p_rand_guid(); + + packet->header.session_id = 0; + packet->header.id = p2p_randomnr(); + packet->header.offset = 0; + + g_snprintf( body, sizeof( body ), + "\r\n" + "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n" + "SessionID: %d\r\n" + "AppID: 1\r\n" + "Context: %s\r\n", + packet->session_id, context ); + + bzero( packet->data, sizeof( packet->data ) ); + + packet->header.total_size = g_snprintf( packet->data, sizeof( packet->data ), + "INVITE MSNMSGR:%s MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: MSNSLP/1.0/TLP ;branch={%s}\r\n" + "CSeq: 0\r\n" + "Call-ID: {%s}\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionreqbody\r\n" + "Content-Length: %d\r\n" + "%s", + packet->destination, packet->destination, gc->username, packet->branch, packet->call_id, strlen( body )+1, body ); + + packet->header.total_size++; + + packet->header.length = packet->header.total_size; + packet->header.flags = 0; + packet->header.ack_id = p2p_randomnr(); + packet->header.ack_sub_id = 0; + packet->header.ack_size = 0; + + packet->footer.value = 0; + + sb->p2p_sessionlist = g_slist_append( sb->p2p_sessionlist, packet ); + + packet->next_step_on_ack = BASEIDENTIFIER; + + p2p_sendpacket( sb, packet ); +} + +/* + * request an msnobject from a user, it'll convert the msnobject to a context for p2p_sendinvite() + * makes a fullpath using msn_imagefullpath() and opens that file for writing, add the filedescriptor + * to the packet and then sends the invite... + * if emo_msnobject is NULL we'll request the user's buddy-image, else emo_msnobject + * is requested as an emoticon... + */ +void p2p_request_msnobject( struct msn_switchboard *sb, char *destination, char *emo_msnobject, char *emoticon_shortcut) +{ + struct gaim_connection *gc = sb->gc; + struct msn_data *md = gc->proto_data; + char *context = NULL, *msnobject = NULL; + struct P2PPacket *packet; + int type = -1; + + if( emo_msnobject == NULL ) /* we need to request the user's msnobject for buddy-image */ + { + struct msn_userinfo *userinfo = msn_find_userinfo( md, destination ); + if( userinfo ) + { + msnobject = userinfo->msnobject; + context = msnobject_to_context( userinfo->msnobject ); + type = TYPE_BUDDY_IMAGE; + } + else + { + /* Requesting a buddy-image from a user that doesn't have one ??? + * return! we shouldn't be doing anything then! + */ + return; + } + } + else /* We'll request an emoticon... */ + { + msnobject = emo_msnobject; + context = msnobject_to_context( emo_msnobject ); + type = TYPE_EMOTICON_IMAGE; + } + + if( context ) + { + packet = p2p_packet_new(); + + if ( packet ) + { + char *filename = NULL; + char *emoticon_info_filename = NULL; + + packet->destination = g_strdup( destination ); + switch( type ) + { + case TYPE_BUDDY_IMAGE: + filename = msn_imagefullpath( gc, packet->destination, msnobject, type, emoticon_shortcut, "png" ); + break; + case TYPE_EMOTICON_IMAGE: + filename = msn_imagefullpath( gc, packet->destination, msnobject, type, emoticon_shortcut, "png" ); + if( emoticon_shortcut ) + { + emoticon_info_filename = msn_imagefullpath( gc, packet->destination, msnobject, type, emoticon_shortcut, "emo" ); + if( emoticon_info_filename ) + { + FILE *fp; + fp = fopen( emoticon_info_filename, "w" ); + if( fp ) + { + fprintf( fp, "SHORTCUT=%s\n", emoticon_shortcut ); + fprintf( fp, "PNGFILE=%s\n", filename ); + fclose( fp ); + } + else + { + do_error_dialog( gc, "Could not open this emoticon's .emo file for writing!!!", "MSN" ); + g_free( filename ); + filename = NULL; + } + g_free( emoticon_info_filename ); + emoticon_info_filename = NULL; + } + else + { + g_free( filename ); + filename = NULL; + } + } + else + { + /* This should never be reached! */ + do_error_dialog( gc, "Requesting TYPE_EMOTICON_IMAGE without a valid emoticon_shortcut", "MSN" ); + } + break; + default: + /* In fact we should never reach this peace of code! */ + do_error_dialog( gc, "Requesting an unknown msnobject type? Not even trying that! ...", "MSN" ); + break; + } + + + if( filename ) + { + if( emoticon_shortcut ) + { + char buffer[1024]; + int len = g_snprintf( buffer, sizeof( buffer ), + "<> %s is an emoticon ( %s )", + emoticon_shortcut, filename ); + serv_got_im( gc, destination, buffer, 0, 0, len ); + } + + if( access( filename, F_OK ) == 0 ) + { + switch( type ) + { + case TYPE_BUDDY_IMAGE: + serv_got_crap( gc, "Not downloading buddy image from %s because we already have it cached!", + packet->destination ); + break; + case TYPE_EMOTICON_IMAGE: + serv_got_crap( gc, "Not downloading emoticon image from %s because we already have it cached!", + packet->destination ); + break; + } + } + else + { + packet->fd = open( filename, O_WRONLY|O_TRUNC|O_CREAT, 00777 ); + + if ( packet->fd != -1 ) + { + switch( type ) + { + case TYPE_BUDDY_IMAGE: + serv_got_crap( gc, "Saving %s's buddy image to: %s", + packet->destination, filename ); + break; + case TYPE_EMOTICON_IMAGE: + serv_got_crap( gc, "Saving emoticon '%s' from %s to: %s", + emoticon_shortcut, packet->destination, filename ); + break; + } + + if( packet->filename ) + { + g_free( packet->filename ); + } + packet->filename = g_strdup( filename ); + packet->type = type; + + /* p2p_sendinvite will add packet to the md->p2p_sessionlist so it'll + * be g_free()'ed at the right time! + */ + p2p_sendinvite( sb, packet, context ); + } + else + { + switch( type ) + { + case TYPE_BUDDY_IMAGE: + do_error_dialog( gc, "Couldn't write to the buddy images directory. Please check your 'msn_images_path_buddy' setting! Not requesting the msnobject!", "MSN" ); + break; + case TYPE_EMOTICON_IMAGE: + do_error_dialog( gc, "Couldn't write to the emoticon images directory. Please check your 'msn_images_path_emoticon' setting! Not requesting the msnobject!", "MSN" ); + break; + } + } + } + + g_free( filename ); + } + else + { + switch( type ) + { + case TYPE_BUDDY_IMAGE: + do_error_dialog( gc, "Please check your 'msn_images_path_buddy' setting. It doesn't seem to be a valid directory!", "MSN" ); + break; + case TYPE_EMOTICON_IMAGE: + do_error_dialog( gc, "Please check your 'msn_images_path_emoticon' setting. It doesn't seem to be a valid directory!", "MSN" ); + break; + } + } + } + else + { + do_error_dialog( gc, "Could not allocate memory for 'packet' in p2p_request_msnobject()", "MSN" ); + } + + g_free( context ); + } + else + { + do_error_dialog( gc, "Couldn't create context for msnobject in p2p_request_msnobject()", "MSN" ); + } + +} + +/* + * Send BYE packet to sb + */ +void p2p_sendbye( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ) +{ + struct gaim_connection *gc = sb->gc; + + if( !packet ) + return; + + packet->header.session_id = 0; + packet->header.id++; + packet->header.offset = 0; + + bzero( packet->data, sizeof( packet->data ) ); + + packet->header.total_size = g_snprintf( packet->data, sizeof( packet->data ), + "BYE MSNMSGR:%s MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: MSNSLP/1.0/TLP ;branch={%s}\r\n" + "CSeq: 0\r\n" + "Call-ID: {%s}\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionclosebody\r\n" + "Content-Length: 3\r\n\r\n", + packet->destination, packet->destination, gc->username, packet->branch, packet->call_id ); + + packet->header.total_size++; /* we need a \x00 at the end! */ + packet->header.length = packet->header.total_size; + + packet->header.flags = 0; + packet->header.ack_id = received_packet->header.id; + packet->header.ack_sub_id = 0; + packet->header.ack_size = 0; + + packet->footer.value = 0; + + p2p_sendpacket( sb, packet ); +} + +void log_packet(struct P2PPacket *packet, char *dir) +{ +/* +FILE *fp; +fp=fopen("/tmp/log.akke","a"); +fprintf(fp, "\r\n%s START-----------------------------------------------\r\n", dir); +fprintf(fp, "header:\n"); +fprintf(fp, " session_id: %d\n", packet->header.session_id); +fprintf(fp, " id: %d\n", packet->header.id); +fprintf(fp, " offset: %llu\n", packet->header.offset); +fprintf(fp, " len: %llu\n", packet->header.total_size); +fprintf(fp, " length: %d\n", packet->header.length); +fprintf(fp, " flags: %d\n", packet->header.flags); +fprintf(fp, " ack_id: %d\n", packet->header.ack_id); +fprintf(fp, " ack_sub_id: %d\n", packet->header.ack_sub_id); +fprintf(fp, " ack_size: %llu\n", packet->header.ack_size); +if (packet->header.length > 0) +{ + fprintf(fp, "data:\n"); + fprintf(fp, "%s\n", packet->data); +} +fprintf(fp, "footer:\n"); +fprintf(fp, " value: %d\n", packet->footer.value); +fprintf(fp, "\r\n%s STOP-----------------------------------------------\r\n", dir); +fclose(fp); +*/ +} + + +/* + * Main handler. Will parse all P2P/SLP messages and handle it appropriate.. + */ +void p2p_handler( struct msn_switchboard *sb, struct P2PPacket *packet ) +{ + struct gaim_connection *gc = sb->gc; + struct P2PPacket *tmp_packet; + + log_packet(packet, "IN"); + /* if this is an ACK packet */ + if( packet->header.flags == 0x2 ) + { + debug( "P2P: ACK received..." ); + + /* Check if any of our sessions is waiting for an ACK */ + tmp_packet = p2p_find_packet( sb, packet->header.ack_id, FIND_BY_ID ); + if( tmp_packet ) + { + debug( "session found by FIND_BY_ID ..." ); + switch( tmp_packet->next_step_on_ack ) + { + /* Sending image part */ + case DATA_PREP: + debug( "Ok! Now sending data preparation..." ); + + tmp_packet->next_step_on_ack = SEND_DATA; + p2p_senddataprep( sb, tmp_packet ); + + break; + + case SEND_DATA: + debug( "Ok! Now sending the data..."); + + tmp_packet->next_step_on_ack = DATA_SENT; + p2p_senddata( sb, tmp_packet ); + switch( tmp_packet->type ) + { + case TYPE_BUDDY_IMAGE: + serv_got_crap( gc, "%s downloaded my buddy image!", + tmp_packet->destination ); + break; + case TYPE_EMOTICON_IMAGE: + serv_got_crap( gc, "%s downloaded my emoticon image %s!", + tmp_packet->destination, tmp_packet->filename ); + break; + } + + break; + + case DATA_SENT: + debug( "Ok! This is the data sent ack. Nothing to be done..." ); + tmp_packet->next_step_on_ack = -1; + serv_got_crap( gc, "My buddy image successfully sent to %s", + tmp_packet->destination ); + break; + + /* Receiving image part */ + case BASEIDENTIFIER: + debug( "Ok! This is the base identifier. Nothing to be done..." ); + break; + + case BYEACK: + debug( "Ok! This is an ACK to my bye msg. Deleting session..." ); + p2p_cleanup_packet( tmp_packet ); + sb->p2p_sessionlist = g_slist_remove( sb->p2p_sessionlist, tmp_packet ); + + break; + + default: + debug( "huh? I don't expect any ACK!!! ERROR ERROR??" ); + break; + } + } + else + { + debug( "hmm, i couldn't find a session for this ack. Ignoring packet..."); + return; /* ignore ACK packet */ + } + } else + + /* Check if this is an INVITE packet */ + if( !strncmp( packet->data, "INVITE MSNMSGR", strlen( "INVITE MSNMSGR" ) ) ) + { + char *session_id = p2p_findheader( packet, "SessionID:" ); + char *dest = p2p_findheader( packet, "From: destination = g_strdup( dest ); + packet->session_id = atoi( session_id ); + packet->fd = -1; + packet->next_step_on_ack = -1; + + sb->p2p_sessionlist = g_slist_append( sb->p2p_sessionlist, packet ); + + p2p_sendbaseidentifier( sb, packet, packet ); + + debug( "Sent base identifier..."); + } + + + if( !strncmp( appid, "1", 1 ) ) + { + //todo: check context, if it's a buddy or emoticon and if it's valid.. + // then use msn_images_mybuddyimage and probably create a new + // option msn_images_myemoticonimages + // blabla.. you get the point.. + char *my_buddy_image = set_getstr( gc->irc, "msn_images_mybuddyimage" ); + if( access( my_buddy_image, R_OK ) == 0 ) + { + packet->next_step_on_ack = DATA_PREP; + packet->filename = g_strdup( my_buddy_image ); + packet->branch = g_strdup( branch ); + packet->call_id = g_strdup( call_id ); + packet->cseq = atoi( cseq ); + packet->type = TYPE_BUDDY_IMAGE; // we don't support sending emoticons so this is ok for now ... + p2p_send200ok( sb, packet, packet ); + debug( "Sent 200 OK message" ); + } + else + { + debug( "Buddy Image not sent!!" ); + do_error_dialog( gc, "Buddy image not sent!", "MSN"); + // todo: send an error back instead of leaving the session unterminated! + } + } else + + if( !strncmp( appid, "2", 1 ) ) + { + // todo: for filetransfers the INVITE message kan have a totalsize != length + // because the preview data is at the context field + // therefor we need to concatenate INVITE messages until offset+length = totalsize + // before parsing the invite message + // YOU NEED TO FIX THAT *BEFORE* starting to implement the filetransfer code + do_error_dialog( gc, "TODO: Complete filetransfer code!", "MSN" ); + } + } + + if( appid ) + g_free( appid ); + if( context ) + g_free( context ); + if( eufguid ) + g_free( eufguid ); + } + } + + if( session_id ) + g_free( session_id ); + if( dest ) + g_free( dest ); + if( branch ) + g_free( branch ); + if( call_id ) + g_free( call_id ); + if( cseq ) + g_free( cseq ); + if( content_type ) + g_free( content_type ); + } else + + /* If this is a 200 OK response */ + if( !strncmp( packet->data, "MSNSLP/1.0 200 OK", strlen( "MSNSLP/1.0 200 OK" ) ) ) + { + char *call_id = p2p_findheader( packet, "Call-ID: {" ); + if( call_id ) + { + call_id[strlen( call_id )-1]=0; + + tmp_packet = p2p_find_packet_by_callid( sb, call_id ); + if( tmp_packet ) + { + debug( "200 OK message received. Sending an ACK" ); + p2p_sendack( sb, tmp_packet, packet ); + tmp_packet->header.session_id = tmp_packet->session_id; + } + else + { + debug( "200 OK message received but i don't have a session for it... Ignoring!" ); + } + + g_free( call_id ); + } + } else + + /* If this is a BYE response */ + if( !strncmp( packet->data, "BYE MSNMSGR:", strlen( "BYE MSNMSGR:" ) ) ) + { + debug( "BYE message received." ); + char *call_id = p2p_findheader( packet, "Call-ID: {" ); + if( call_id ) + { + call_id[strlen( call_id )-1]=0; + + struct P2PPacket *tmp_packet = p2p_find_packet_by_callid( sb, call_id ); + if( tmp_packet ) + { + debug( "Sending an ACK + BYE message + deleting the session!" ); + tmp_packet->header.session_id = 0; + p2p_sendack( sb, tmp_packet, packet ); + + p2p_cleanup_packet( tmp_packet ); + sb->p2p_sessionlist = g_slist_remove( sb->p2p_sessionlist, tmp_packet ); + } + else + { + debug( "I don't expect a BYE message. Ignoring message" ); + } + + g_free( call_id ); + } + } else + + /* MSN Messenger 6.2 sends a message back with flag being 0x40 + * right after downloading our buddy image. I can't find any + * '3rd-party documentation' about that flag but everything seems + * to be working so i guess it's a normal thing? + * If you know more about that flag: I'm interested to know about it! + * (maybe there's an error in any of my packets and 0x40 means ERROR?) + */ + if( packet->header.flags == 0x40 ) + { + /* IGNORE */ + } else + + /* Check for data preparation / data messages packets */ + if( packet->header.session_id != 0 ) + { + tmp_packet = p2p_find_packet( sb, packet->header.session_id, FIND_BY_SESSION_ID ); + if( tmp_packet ) + { + if( packet->header.total_size == 4 && packet->header.length == 4 ) + { + /* Data preparation received */ + if( tmp_packet->filename ) + { + debug( "Data perparation received. Sending ACK..." ); + p2p_sendack( sb, tmp_packet, packet ); + } + else + { + debug( "Got a data preparation message but no filename to write to. THIS SHOULD'NT HAPPEN!" ); + } + } + else + { + /* Data message received */ + debug( "Data message ... Data is comming!!!" ); + + if( tmp_packet->fd != -1 ) + { + if( write( tmp_packet->fd, packet->data, packet->header.length ) == -1 ) + { + debug( "Error writing buddy/emoticon data! This shouldn't hapen :(" ); + } + else + { + debug( "buddy/emoticon data written successfully!" ); + } + } + else + { + debug( "Got a data packet but i don't have a file to write to. This should not happen!" ); + } + + if( packet->header.offset + packet->header.length >= packet->header.total_size ) + { + /* end of file */ + debug( "File receive completed!" ); + serv_got_crap( gc, "File %s received successfully!", + tmp_packet->filename ); + + if( tmp_packet->fd != -1 ) + { + close( tmp_packet->fd ); + tmp_packet->fd = -1; + + } + p2p_sendack( sb, tmp_packet, packet ); + tmp_packet->next_step_on_ack = BYEACK; + p2p_sendbye( sb, tmp_packet, packet ); + } + } + } + else + { + /* SHOULD NOT HAPPEN */ + debug( "Well this shouldn't happen, Reference: 239OJPA2" ); + } + } else + + /* something else */ + { + /* Unsupported? */ + debug( "received a p2p packet but don't know how to handle it (yet?)" ); + } +} diff -urN bitlbee-0.92/protocols/msn/msnc1.h bitlbee-0.92.akke/protocols/msn/msnc1.h --- bitlbee-0.92/protocols/msn/msnc1.h 1970-01-01 01:00:00.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/msnc1.h 2005-05-07 02:41:33.000000000 +0200 @@ -0,0 +1,82 @@ +#ifndef MSNC1 +#define MSNC1 + +#define DWORD unsigned int +#define QWORD unsigned long long + +struct P2PHeader +{ + DWORD session_id; + DWORD id; + QWORD offset; + QWORD total_size; + DWORD length; + DWORD flags; + DWORD ack_id; + DWORD ack_sub_id; + QWORD ack_size; +}; + +struct P2PFooter +{ + DWORD value; +}; + +struct P2PPacket +{ + /* msn p2p stuff */ + struct P2PHeader header; + char data[1352]; /* max. 1202 characters ( or 1352 in case of a Direct Connection ( not even supported by now! ) ) */ + struct P2PFooter footer; + + /* msn slp stuff */ + char *destination, *branch, *call_id, *filename; + int cseq, session_id; + int type, next_step_on_ack; + int fd; +}; + +enum { + FIND_BY_SESSION_ID = 1, + FIND_BY_ID, +}; + +enum { + IGNORE = 1, + DATA_PREP, + SEND_DATA, + DATA_SENT, + BASEIDENTIFIER, + BYEACK, +}; + +void msn_update_mybuddyimage( struct gaim_connection *gc ); +struct msn_userinfo *msn_find_userinfo( struct msn_data *md, char *who ); +void msn_cleanup_userinfo( struct msn_userinfo *userinfo ); +void msn_cleanup_userinfolist( struct msn_data *md ); +void msn_add_or_update_userinfolist( struct gaim_connection *gc, struct msn_userinfo *new_userinfo ); +struct msn_userinfo *userinfo_from_params( char *who, char *msnobject ); +void p2p_init_random(); +DWORD p2p_randomnr(); +char *p2p_rand_guid(); +struct P2PPacket *p2p_packet_new(); +struct P2PPacket *p2p_packet_from_buffer( char *buffer, int length ); +struct P2PPacket *p2p_find_packet_by_callid( struct msn_switchboard *sb, char *call_id ); +struct P2PPacket *p2p_find_packet( struct msn_switchboard *sb, DWORD value, int type ); +char *p2p_findheader( struct P2PPacket *packet, char *header ); +void p2p_cleanup_packet( struct P2PPacket *packet ); +void p2p_cleanup_sb( struct msn_switchboard *sb ); +char *msn_imagefullpath( struct gaim_connection *gc, char *who, char *msnobject, int type, char *emoticon_shortcut, char *ext ); +void p2p_sendpacket( struct msn_switchboard *sb, struct P2PPacket *packet ); +void p2p_sendack( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ); +void p2p_sendbaseidentifier( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ); +void p2p_send200ok( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ); +void p2p_senddataprep( struct msn_switchboard *sb, struct P2PPacket *packet ); +void p2p_senddata( struct msn_switchboard *sb, struct P2PPacket *packet ); +void p2p_sendinvite( struct msn_switchboard *sb, struct P2PPacket *packet, char *context ); +void p2p_request_msnobject( struct msn_switchboard *sb, char *destination, char *msnobject, char *emoticon_shortcut ); +void p2p_sendbye( struct msn_switchboard *sb, struct P2PPacket *packet, struct P2PPacket *received_packet ); +void p2p_handler( struct msn_switchboard *sb, struct P2PPacket *packet ); + +#endif + diff -urN bitlbee-0.92/protocols/msn/msnobject.c bitlbee-0.92.akke/protocols/msn/msnobject.c --- bitlbee-0.92/protocols/msn/msnobject.c 1970-01-01 01:00:00.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/msnobject.c 2005-05-07 02:30:12.000000000 +0200 @@ -0,0 +1,156 @@ +#include "msnobject.h" +#include "msn.h" + +/* + * BASE64 encoding, functions comes from yahoo protocol module + * just some little mods.. + */ +static char msn_base64digits[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/="; +void msn_tobase64( unsigned char *out, const unsigned char *in, int inlen ) +{ + for ( ; inlen >= 3; inlen -= 3 ) + { + *out++ = msn_base64digits[in[0] >> 2]; + *out++ = msn_base64digits[( ( in[0]<<4 ) & 0x30 ) | ( in[1]>>4 )]; + *out++ = msn_base64digits[( ( in[1]<<2 ) & 0x3c ) | ( in[2]>>6 )]; + *out++ = msn_base64digits[in[2] & 0x3f]; + in += 3; + } + if ( inlen > 0 ) + { + unsigned char fragment; + + *out++ = msn_base64digits[in[0] >> 2]; + fragment = ( in[0] << 4 ) & 0x30; + if ( inlen > 1 ) + fragment |= in[1] >> 4; + *out++ = msn_base64digits[fragment]; + *out++ = ( inlen < 2 ) ? '=' + : msn_base64digits[( in[1] << 2 ) & 0x3c]; + *out++ = '='; + } + *out = '\0'; +} + +/* + * convert an http-encoded msnobject to an msn context + * ( = base64(+<\x00>) ) + */ +char *msnobject_to_context( char *msnobject ) +{ + char *buf1, *buf2; + int length = strlen( msnobject ); + + buf1 = g_new0( char, length + 2 ); + if ( !buf1 ) + return NULL; + + strncpy( buf1, msnobject, length ); + http_decode( buf1 ); + + length = strlen( buf1 ); + + buf2 = g_new0( char, length * 3 ); + + if ( !buf2 ) + { + g_free( buf1 ); + return NULL; + } + + msn_tobase64( buf2, buf1, length + 1 ); + + g_free( buf1 ); + + return buf2; +} + +/* + * create a new msnobject from the specified parameters + */ +char *msnobject_from_params( char *creator, int type, char *filename ) +{ + SHA_CTX SHA_Context; + FILE *fd; + struct stat statinfo; + unsigned char digest[20]; + + char buffer[1024], sha1d[200], sha1c[200], *tmp, *location; + int len; + + if ( !filename ) + return NULL; + + if ( ( stat( filename, &statinfo ) ) != 0 ) + return NULL; + + if ( !( fd=fopen( filename, "r" ) ) ) + return NULL; + + tmp = g_strdup( filename ); + if ( !tmp ) + return NULL; + location = basename( tmp ); + g_free( tmp ); + + /* calculate SHA1D */ + shaInit( &SHA_Context ); + while ( ( len = fread( buffer, 1, sizeof( buffer ), fd ) ) > 0 ) + { + shaUpdate( &SHA_Context, buffer, len ); + } + shaFinal( &SHA_Context, digest ); + msn_tobase64( sha1d, digest, 20 ); + fclose( fd ); + http_encode( sha1d ); + + /* calculate SHA1C */ + shaInit( &SHA_Context ); + len = g_snprintf( buffer, sizeof( buffer ), + "Creator%sSize%dType%dLocation%sFriendlyAAA=SHA1D%s", + creator, ( int )statinfo.st_size, type, location, sha1d ); + shaUpdate( &SHA_Context, buffer, len ); + shaFinal( &SHA_Context, digest ); + msn_tobase64( sha1c, digest, 20 ); + http_encode( sha1c ); + + /* create msnobject */ + g_snprintf( buffer, sizeof( buffer ), + "%%3Cmsnobj%%20Creator%%3D%%22%s%%22%%20Size%%3D%%22%d%%22%%20Type%%3D%%22%d%%22%%20Location%%3D%%22%s%%22%%20Friendly%%3D%%22AAA%%3D%%22%%20SHA1D%%3D%%22%s%%22%%20SHA1C%%3D%%22%s%%22/%%3E", + creator, ( int )statinfo.st_size, type, location, sha1d, sha1c ); + + return g_strdup( buffer ); +} + +/* + * returns a field from an msnobject.. + */ +char *msnobject_get_field( char *field, char *msnobject ) +{ + char *tag = NULL, *start = NULL, *end = NULL, tmp[200]; + + char *decoded_msnobject = g_new0( char, strlen( msnobject ) * 3 ); + + if ( !decoded_msnobject ) + return NULL; + + strcpy( decoded_msnobject, msnobject ); + http_decode( decoded_msnobject ); + + g_snprintf( tmp, sizeof( tmp ), "%s=\"", field ); + + if ( ( start = strstr( decoded_msnobject, tmp ) ) != NULL ) + { + start += strlen( tmp ); + end = strchr( start, '"' ); + if ( end ) + tag = g_strndup( start, end-start ); + } + + g_free( decoded_msnobject ); + + return tag; +} + diff -urN bitlbee-0.92/protocols/msn/msnobject.h bitlbee-0.92.akke/protocols/msn/msnobject.h --- bitlbee-0.92/protocols/msn/msnobject.h 1970-01-01 01:00:00.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/msnobject.h 2005-05-07 02:41:29.000000000 +0200 @@ -0,0 +1,14 @@ +#ifndef MSN_OBJECT +#define MSN_OBJECT + +#include "nogaim.h" + +#define TYPE_EMOTICON_IMAGE 2 +#define TYPE_BUDDY_IMAGE 3 + +void msn_tobase64( unsigned char *out, const unsigned char *in, int inlen ); +char *msnobject_to_context( char *msnobject ); +char *msnobject_from_params( char *creator, int type, char *filename ); +char *msnobject_get_field(char *field, char *msnobject ); + +#endif diff -urN bitlbee-0.92/protocols/msn/ns.c bitlbee-0.92.akke/protocols/msn/ns.c --- bitlbee-0.92/protocols/msn/ns.c 2005-02-23 18:28:41.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/ns.c 2005-05-07 02:52:38.000000000 +0200 @@ -26,6 +26,7 @@ #include #include "nogaim.h" #include "msn.h" +#include "msnc1.h" #include "passport.h" #include "md5.h" @@ -71,7 +72,7 @@ md->handler->fd = md->fd; md->handler->rxq = g_new0( char, 1 ); - g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId ); + g_snprintf( s, sizeof( s ), "VER %d MSNP9 CVR0\r\n", ++md->trId ); if( msn_write( gc, s, strlen( s ) ) ) { gc->inpa = gaim_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, gc ); @@ -105,7 +106,7 @@ if( strcmp( cmd[0], "VER" ) == 0 ) { - if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 ) + if( cmd[2] && strncmp( cmd[2], "MSNP9", 5 ) != 0 ) { hide_login_progress( gc, "Unsupported protocol" ); signoff( gc ); @@ -297,6 +298,9 @@ if( list & 1 ) /* FL */ { + if( set_getint( gc->irc, "msn_buddylist_checks" ) == 1 && ( list & 8 ) == 0 ) + serv_got_crap( gc, "\0034Contact %s is in your buddy list but you don't appear in his/her one (yet?)!", cmd[1] ); + add_buddy( gc, NULL, cmd[1], cmd[2] ); } if( list & 2 ) /* AL */ @@ -309,6 +313,9 @@ } if( list & 8 ) /* RL */ { + if( set_getint( gc->irc, "msn_buddylist_checks" ) == 1 && ( list & 1 ) == 0 ) + serv_got_crap( gc, "\0034Contact %s has got you in his/her buddy list but you don't have him/her in yours!", cmd[1] ); + if( ( list & 6 ) == 0 ) msn_buddy_ask( gc, cmd[1], cmd[2] ); } @@ -367,7 +374,7 @@ { struct msn_away_state *st; - if( num_parts != 6 ) + if( num_parts != 6 && num_parts != 7 ) { hide_login_progress_error( gc, "Syntax error" ); signoff( gc ); @@ -376,6 +383,15 @@ http_decode( cmd[4] ); serv_buddy_rename( gc, cmd[3], cmd[4] ); + + if( num_parts == 7 ) /* there's an msnobject at the end! */ + { + struct msn_userinfo *userinfo = userinfo_from_params(cmd[3], cmd[6]); + if (userinfo) + { + msn_add_or_update_userinfolist( gc, userinfo ); + } + } st = msn_away_state_by_code( cmd[2] ); if( !st ) @@ -395,7 +411,7 @@ { struct msn_away_state *st; - if( num_parts != 5 ) + if( num_parts != 5 && num_parts != 6 ) { hide_login_progress_error( gc, "Syntax error" ); signoff( gc ); @@ -404,7 +420,16 @@ http_decode( cmd[3] ); serv_buddy_rename( gc, cmd[2], cmd[3] ); - + + if( num_parts == 6 ) /* there's an msnobject at the end! */ + { + struct msn_userinfo *userinfo = userinfo_from_params(cmd[2], cmd[5]); + if (userinfo) + { + msn_add_or_update_userinfolist( gc, userinfo ); + } + } + st = msn_away_state_by_code( cmd[1] ); if( !st ) { @@ -451,6 +476,29 @@ sb = msn_sb_create( gc, server, port, cmd[4], session ); sb->who = g_strdup( cmd[5] ); + + if( set_getint( gc->irc, "msn_notify_openwindow" ) == 1 ) + { + char buffer[1024]; + int len; + user_t *u; + u = user_findhandle( gc, sb->who ); + if( u ) + { + len = g_snprintf( buffer, sizeof( buffer ), + "<> *** %s has opened a conversation window. ***", + u->nick); + } + else + { + len = g_snprintf( buffer, sizeof( buffer ), + "<> *** %s has opened a conversation window. ***", + sb->who); + } + + serv_got_im( gc, sb->who, buffer, 0, 0, len ); + } + } else if( strcmp( cmd[0], "ADD" ) == 0 ) { @@ -470,17 +518,32 @@ /* We got added by someone. If we don't have this person in permit/deny yet, inform the user. */ for( l = gc->permit; l; l = l->next ) if( g_strcasecmp( l->data, cmd[4] ) == 0 ) + { + if( set_getint( gc->irc, "msn_buddylist_checks" ) == 1 ) + serv_got_crap( gc, "\0034Contact %s (which is in your allow-list) has just RE-added/allowed you to his/her buddy list!", + cmd[4] ); return( 1 ); - + } for( l = gc->deny; l; l = l->next ) if( g_strcasecmp( l->data, cmd[4] ) == 0 ) + { + if( set_getint( gc->irc, "msn_buddylist_checks" ) == 1 ) + serv_got_crap( gc, "\0034Contact %s (which is in your block-list) has just RE-added you to his/her buddy list!", + cmd[4] ); return( 1 ); + } msn_buddy_ask( gc, cmd[4], cmd[5] ); } } else if( strcmp( cmd[0], "REM" ) == 0 ) { + if( num_parts == 5 && strcmp( cmd[2], "RL" ) == 0 ) + { + if( set_getint( gc->irc, "msn_buddylist_checks" ) == 1 ) + serv_got_crap( gc, "\0034Contact %s has just removed you from his/her buddy list!", + cmd[4] ); + } } else if( strcmp( cmd[0], "OUT" ) == 0 ) { diff -urN bitlbee-0.92/protocols/msn/sb.c bitlbee-0.92.akke/protocols/msn/sb.c --- bitlbee-0.92/protocols/msn/sb.c 2005-02-23 16:50:24.000000000 +0100 +++ bitlbee-0.92.akke/protocols/msn/sb.c 2005-05-07 03:15:57.000000000 +0200 @@ -28,6 +28,7 @@ #include "msn.h" #include "passport.h" #include "md5.h" +#include "msnc1.h" static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond ); static int msn_sb_command( gpointer data, char **cmd, int num_parts ); @@ -55,6 +56,7 @@ sb->fd = proxy_connect( host, port, msn_sb_connected, sb ); if( sb->fd < 0 ) { + p2p_cleanup_sb ( sb ); g_free( sb ); return( NULL ); } @@ -126,10 +128,61 @@ if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 ) { - buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 ); - i = strlen( MSN_MESSAGE_HEADERS ); + char *tmp, *font_face, *font_color, font_colorbuffer[5], font_style[10]; + int font_charset, font_pitchandfamily, buffer_size; + + /* Font face stuff */ + tmp = set_getstr( sb->gc->irc, "msn_font_face" ); + if( !tmp ) + tmp = "MS Shell Dlg"; /* Default font face */ + + font_face = g_new0( char, strlen(tmp) * 3 ); /* We don't use g_strdup() because we need * + * a buffer 3 times as big because of http_encode() */ + strcpy( font_face, tmp ); + http_encode( font_face ); + + /* Font color stuff */ + tmp = set_getstr( sb->gc->irc, "msn_font_color" ); + if( !tmp ) + tmp = "000000"; /* Default font color (=black) */ + font_color = g_strdup( tmp ); + if( !font_color || strlen( font_color ) != 6 ) + { + do_error_dialog( sb->gc, "Please check your 'msn_font_color' setting. Message not sent!!!", "MSN" ); + g_free( font_face ); + return( 0 ); + } + /* font color fixup: msn server wants BGR instead of RGB style..*/ + strncpy( font_colorbuffer, font_color, 2 ); + font_color[0] = font_color[4]; + font_color[1] = font_color[5]; + font_color[4] = font_colorbuffer[0]; + font_color[5] = font_colorbuffer[1]; + + /* Font style styff */ + strcpy( font_style, "" ); + if( set_getint( sb->gc->irc, "msn_font_bold" ) == 1 ) + strcat( font_style, "B"); + if( set_getint( sb->gc->irc, "msn_font_italic" ) == 1 ) + strcat( font_style, "I"); + if( set_getint( sb->gc->irc, "msn_font_overstrike" ) == 1 ) + strcat( font_style, "S"); + if( set_getint( sb->gc->irc, "msn_font_underline" ) == 1 ) + strcat( font_style, "U"); + + /* Font charset */ + font_charset = set_getint( sb->gc->irc, "msn_font_CS" ); + + /* Font Pitch & Family */ + font_pitchandfamily = set_getint( sb->gc->irc, "msn_font_PF" ); + + buffer_size = strlen( MSN_MESSAGE_HEADERS ) + strlen( font_face ) + strlen( font_style ) + strlen( font_color ) + strlen( text ) * 2; + buf = g_new0( char, buffer_size + 1 ); + i = g_snprintf( buf, buffer_size, MSN_MESSAGE_HEADERS, font_face, font_style, font_color, font_charset, font_pitchandfamily ); + + g_free( font_color ); + g_free( font_face ); - strcpy( buf, MSN_MESSAGE_HEADERS ); for( j = 0; text[j]; j ++ ) { if( text[j] == '\n' ) @@ -137,6 +190,10 @@ buf[i++] = text[j]; } +FILE *fp; +fp = fopen("/tmp/tmp.log","a"); +fprintf(fp, "%s",buf); +fclose(fp); } else { @@ -238,6 +295,7 @@ msn_switchboards = g_slist_remove( msn_switchboards, sb ); md->switchboards = g_slist_remove( md->switchboards, sb ); + p2p_cleanup_sb ( sb ); g_free( sb ); } @@ -389,6 +447,11 @@ } sb->ready = 1; + + if( sb->who ) + { + p2p_request_msnobject(sb, sb->who, NULL, NULL ); + } } else if( strcmp( cmd[0], "CAL" ) == 0 ) { @@ -437,7 +500,10 @@ sb->msgq = g_slist_remove( sb->msgq, m ); } - + + if( sb->who ) + p2p_request_msnobject(sb, sb->who, NULL, NULL ); + return( st ); } else if( sb->who ) @@ -489,6 +555,34 @@ if( sb->who ) { /* This is a single-person chat, and the other person is leaving. */ + char buffer[1024]; + int len; + + if( cmd[2] && *cmd[2] && set_getint( gc->irc, "msn_notify_timeout" ) == 1 ) + { + len = g_snprintf( buffer, sizeof( buffer ), + "<> *** This conversation has timed out. ***" ); + serv_got_im( gc, sb->who, buffer, 0, 0, len ); + } + else if( !cmd[2] && set_getint( gc->irc, "msn_notify_closewindow" ) == 1 ) + { + user_t *u; + u = user_findhandle( gc, sb->who ); + if( u ) + { + len = g_snprintf( buffer, sizeof( buffer ), + "<> *** %s has closed the conversation window. ***", + u->nick); + } + else + { + len = g_snprintf( buffer, sizeof( buffer ), + "<> *** %s has closed the conversation window. ***", + sb->who); + } + serv_got_im( gc, sb->who, buffer, 0, 0, len ); + } + g_free( sb->who ); sb->who = NULL; sb->ready = 0; @@ -544,7 +638,7 @@ if( !num_parts ) return( 1 ); - + if( ( body = strstr( msg, "\r\n\r\n" ) ) ) { body += 4; @@ -649,6 +743,78 @@ g_free( ct ); } + + else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0) + { + char *p2p = msn_findheader( msg, "P2P-Dest:", msglen ); + + if ( p2p ) + { + struct P2PPacket *packet; + packet = p2p_packet_from_buffer( body, blen ); + if ( packet ) + { + p2p_handler( sb, packet ); + if( g_slist_find( sb->p2p_sessionlist, packet ) == NULL ) + { + /* The packet isn't used as a session specifier + * so we should g_free() it here as we don't need it + * anymore now... + * If it's the session sepcifier it'll be g_free()'d + * at the right time (when it's sb is cleaned up)! + */ + p2p_cleanup_packet( packet ); + g_free( packet ); + } + } + else + { + do_error_dialog( gc, "Corrupted application/x-msnmsgrp2p message received", "MSN" ); + } + g_free( p2p ); + } + + g_free( ct ); + } + else if( g_strncasecmp( ct, "text/x-mms-emoticon", 19 ) == 0) + { + char *p = strchr( body, '\t' ); + if ( p ) + { + char *shortcut, *msnobject; + + shortcut = g_new0( char, ( ( p-body ) + 1 ) ); + if ( !shortcut ) + { + do_error_dialog( gc, "Could not allocate memory for 'shortcut' in msn_sb_msg()", "MSN" ); + g_free( ct ); + return( 1 ); + } + strncpy( shortcut, body, ( p-body ) ); + + p++; + + msnobject = g_new0( char, strlen( p ) ); + if ( !msnobject ) + { + do_error_dialog( gc, "Could not allocate memory for 'msnobject' in msn_sb_msg()", "MSN" ); + g_free( shortcut ); + g_free( ct ); + return( 1 ); + } + strncpy( msnobject, p, ( strlen( p ) - 1 ) ); + + p2p_request_msnobject( sb, sb->who, msnobject, shortcut ); + + g_free( shortcut ); + g_free( msnobject ); + } + else + { + do_error_dialog( gc, "Corrupted text/x-mms-emoticon message received", "MSN" ); + } + g_free( ct ); + } else { g_free( ct );