summaryrefslogtreecommitdiffstats
path: root/chat-irc/bitlbee/msn.patch
diff options
context:
space:
mode:
Diffstat (limited to 'chat-irc/bitlbee/msn.patch')
-rw-r--r--chat-irc/bitlbee/msn.patch2408
1 files changed, 2408 insertions, 0 deletions
diff --git a/chat-irc/bitlbee/msn.patch b/chat-irc/bitlbee/msn.patch
new file mode 100644
index 0000000000..7f429b4f8a
--- /dev/null
+++ b/chat-irc/bitlbee/msn.patch
@@ -0,0 +1,2408 @@
+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 <ctype.h>
++#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: <msnmsgr:%s>\r\n"
++ "From: <msnmsgr:%s>\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: <msnmsgr:%s>\r\n"
++ "From: <msnmsgr:%s>\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 ),
++ "<<bitlbee>> %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: <msnmsgr:%s>\r\n"
++ "From: <msnmsgr:%s>\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: <msnmsgr:" );
++ char *branch = p2p_findheader( packet, "branch={" );
++ char *call_id = p2p_findheader( packet, "Call-ID: {" );
++ char *cseq = p2p_findheader( packet, "CSeq:" );
++ char *content_type = p2p_findheader( packet, "Content-Type:" );
++
++ if( session_id && dest && branch && call_id && cseq && content_type )
++ {
++ dest[strlen( dest )-1]=0;
++ branch[strlen( branch )-1]=0;
++ call_id[strlen( call_id )-1]=0;
++
++ if( !strncmp( content_type, "application/x-msnmsgr-sessionreqbody", strlen( "application/x-msnmsgr-sessionreqbody" ) ) )
++ {
++ char *eufguid = p2p_findheader( packet, "EUF-GUID: {" );
++ char *appid = p2p_findheader( packet, "AppID:" );
++ char *context = p2p_findheader( packet, "Context:" );
++
++ if( eufguid && appid && context )
++ {
++ eufguid[strlen( eufguid )-1]=0;
++
++ /* Check eufguid, A4268EEC-FEC5-49E5-95C3-F126696BDBF6 = emoticon or buddy image */
++ if( !strncmp( eufguid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", strlen( "A4268EEC-FEC5-49E5-95C3-F126696BDBF6" ) ) )
++ {
++ /* buddy image or emoticon */
++ packet->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(<http-decoded msnobject>+<\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 <ctype.h>
+ #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 ),
++ "<<bitlbee>> *** %s has opened a conversation window. ***",
++ u->nick);
++ }
++ else
++ {
++ len = g_snprintf( buffer, sizeof( buffer ),
++ "<<bitlbee>> *** %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 ),
++ "<<bitlbee>> *** 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 ),
++ "<<bitlbee>> *** %s has closed the conversation window. ***",
++ u->nick);
++ }
++ else
++ {
++ len = g_snprintf( buffer, sizeof( buffer ),
++ "<<bitlbee>> *** %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 );