Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
+++ protocols/msn/msn.c	(revision a6b2f13e38e75e7bca00a1e6c1963783f244c0b2)
@@ -31,14 +31,12 @@
 GSList *msn_switchboards;
 
-static char *msn_set_display_name( set_t *set, char *value );
+static char *set_eval_display_name( set_t *set, char *value );
 
 static void msn_init( account_t *acc )
 {
-	set_t *s;
-	
-	s = set_add( &acc->set, "display_name", NULL, msn_set_display_name, acc );
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
-
-	s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
+	set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
+	set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc );
+	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
+	set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc );
 }
 
@@ -167,5 +165,5 @@
 static void msn_set_my_name( struct im_connection *ic, char *info )
 {
-	msn_set_display_name( set_find( &ic->acc->set, "display_name" ), info );
+	msn_set_display_name( ic, info );
 }
 
@@ -283,16 +281,12 @@
 }
 
-static char *msn_set_display_name( set_t *set, char *value )
+static char *set_eval_display_name( set_t *set, char *value )
 {
 	account_t *acc = set->data;
 	struct im_connection *ic = acc->ic;
-	struct msn_data *md;
-	char buf[1024], *fn;
-	
-	/* Double-check. */
+	
+	/* Allow any name if we're offline. */
 	if( ic == NULL )
-		return NULL;
-	
-	md = ic->proto_data;
+		return value;
 	
 	if( strlen( value ) > 129 )
@@ -301,15 +295,9 @@
 		return NULL;
 	}
-	
-	fn = msn_http_encode( value );
-	
-	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn );
-	msn_write( ic, buf, strlen( buf ) );
-	g_free( fn );
 	
 	/* Returning NULL would be better, because the server still has to
 	   confirm the name change. However, it looks a bit confusing to the
 	   user. */
-	return value;
+	return msn_set_display_name( ic, value ) ? value : NULL;
 }
 
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
+++ protocols/msn/msn.h	(revision a6b2f13e38e75e7bca00a1e6c1963783f244c0b2)
@@ -31,4 +31,5 @@
 #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"
 #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
+#define SB_KEEPALIVE_MESSAGE "\r\r\rDONT HANG UP ON ME!\r\r\r"
 
 #ifdef DEBUG_MSN
@@ -53,4 +54,8 @@
                            "TypingUser: %s\r\n" \
                            "\r\n\r\n"
+
+#define SB_KEEPALIVE_HEADERS "MIME-Version: 1.0\r\n" \
+                             "Content-Type: text/x-ping\r\n" \
+                             "\r\n\r\n"
 
 #define PROFILE_URL "http://members.msn.com/"
@@ -83,4 +88,5 @@
 	gint inp;
 	struct msn_handler_data *handler;
+	gint keepalive;
 	
 	int trId;
@@ -161,4 +167,5 @@
 char *msn_http_encode( const char *input );
 void msn_msgq_purge( struct im_connection *ic, GSList **list );
+gboolean msn_set_display_name( struct im_connection *ic, const char *rawname );
 
 /* tables.c */
@@ -179,4 +186,6 @@
 gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond );
 int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m );
+void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial );
+void msn_sb_stop_keepalives( struct msn_switchboard *sb );
 
 #endif //_MSN_H
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
+++ protocols/msn/msn_util.c	(revision a6b2f13e38e75e7bca00a1e6c1963783f244c0b2)
@@ -38,8 +38,8 @@
 		imcb_error( ic, "Short write() to main server" );
 		imc_logout( ic, TRUE );
-		return( 0 );
-	}
-	
-	return( 1 );
+		return 0;
+	}
+	
+	return 1;
 }
 
@@ -377,2 +377,14 @@
 	g_string_free( ret, TRUE );
 }
+
+gboolean msn_set_display_name( struct im_connection *ic, const char *rawname )
+{
+	char *fn = msn_http_encode( rawname );
+	struct msn_data *md = ic->proto_data;
+	char buf[1024];
+	
+	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn );
+	g_free( fn );
+	
+	return msn_write( ic, buf, strlen( buf ) ) != 0;
+}
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
+++ protocols/msn/ns.c	(revision a6b2f13e38e75e7bca00a1e6c1963783f244c0b2)
@@ -35,4 +35,5 @@
 
 static void msn_auth_got_passport_token( struct msn_auth_data *mad );
+static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name );
 
 gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
@@ -231,23 +232,8 @@
 		else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 )
 		{
-			set_t *s;
-			
 			if( num_parts == 7 )
-			{
-				http_decode( cmd[4] );
-				
-				strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
-				ic->displayname[sizeof(ic->displayname)-1] = 0;
-				
-				if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
-				{
-					g_free( s->value );
-					s->value = g_strdup( cmd[4] );
-				}
-			}
+				msn_ns_got_display_name( ic, cmd[4] );
 			else
-			{
 				imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
-			}
 			
 			imcb_log( ic, "Authenticated, getting buddy list" );
@@ -422,6 +408,10 @@
 	else if( strcmp( cmd[0], "FLN" ) == 0 )
 	{
-		if( cmd[1] )
-			imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
+		if( cmd[1] == NULL )
+			return 1;
+		
+		imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
+		
+		msn_sb_start_keepalives( msn_sb_by_handle( ic, cmd[1] ), TRUE );
 	}
 	else if( strcmp( cmd[0], "NLN" ) == 0 )
@@ -449,4 +439,6 @@
 		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
 		                   st->name, NULL );
+		
+		msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) );
 	}
 	else if( strcmp( cmd[0], "RNG" ) == 0 )
@@ -553,4 +545,7 @@
 		return( 0 );
 	}
+#if 0
+	/* Discard this one completely for now since I don't care about the ack
+	   and since MSN servers can apparently screw up the formatting. */
 	else if( strcmp( cmd[0], "REA" ) == 0 )
 	{
@@ -583,4 +578,5 @@
 		}
 	}
+#endif
 	else if( strcmp( cmd[0], "IPG" ) == 0 )
 	{
@@ -732,2 +728,47 @@
 	}
 }
+
+static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name )
+{
+	set_t *s;
+	
+	if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL )
+		return FALSE; /* Shouldn't happen.. */
+	
+	http_decode( name );
+	
+	if( s->value && strcmp( s->value, name ) == 0 )
+	{
+		return TRUE;
+		/* The names match, nothing to worry about. */
+	}
+	else if( s->value != NULL &&
+	         ( strcmp( name, ic->acc->user ) == 0 ||
+	           set_getbool( &ic->acc->set, "local_display_name" ) ) )
+	{
+		/* The server thinks our display name is our e-mail address
+		   which is probably wrong, or the user *wants* us to do this:
+		   Always use the locally set display_name. */
+		return msn_set_display_name( ic, s->value );
+	}
+	else
+	{
+		if( s->value && *s->value )
+			imcb_log( ic, "BitlBee thinks your display name is `%s' but "
+			              "the MSN server says it's `%s'. Using the MSN "
+			              "server's name. Set local_display_name to true "
+			              "to use the local name.", s->value, name );
+		
+		if( g_utf8_validate( name, -1, NULL ) )
+		{
+			g_free( s->value );
+			s->value = g_strdup( name );
+		}
+		else
+		{
+			imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
+		}
+		
+		return TRUE;
+	}
+}
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
+++ protocols/msn/sb.c	(revision a6b2f13e38e75e7bca00a1e6c1963783f244c0b2)
@@ -168,5 +168,16 @@
 		
 		/* Build the message. Convert LF to CR-LF for normal messages. */
-		if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 )
+		if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) == 0 )
+		{
+			i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user );
+			buf = g_new0( char, i );
+			i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user );
+		}
+		else if( strcmp( text, SB_KEEPALIVE_MESSAGE ) == 0 )
+		{
+			buf = g_strdup( SB_KEEPALIVE_HEADERS );
+			i = strlen( buf );
+		}
+		else
 		{
 			buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 );
@@ -182,10 +193,4 @@
 			}
 		}
-		else
-		{
-			i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user );
-			buf = g_new0( char, i );
-			i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user );
-		}
 		
 		/* Build the final packet (MSG command + the message). */
@@ -250,4 +255,5 @@
 	
 	msn_msgq_purge( ic, &sb->msgq );
+	msn_sb_stop_keepalives( sb );
 	
 	if( sb->key ) g_free( sb->key );
@@ -471,4 +477,6 @@
 		
 		sb->ready = 1;
+		
+		msn_sb_start_keepalives( sb, FALSE );
 	}
 	else if( strcmp( cmd[0], "CAL" ) == 0 )
@@ -520,4 +528,6 @@
 			}
 			
+			msn_sb_start_keepalives( sb, FALSE );
+			
 			return( st );
 		}
@@ -581,4 +591,6 @@
 		if( sb->who )
 		{
+			msn_sb_stop_keepalives( sb );
+			
 			/* This is a single-person chat, and the other person is leaving. */
 			g_free( sb->who );
@@ -764,2 +776,32 @@
 	return( 1 );
 }
+
+static gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond )
+{
+	struct msn_switchboard *sb = data;
+	return sb->ready && msn_sb_sendmessage( sb, SB_KEEPALIVE_MESSAGE );
+}
+
+void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial )
+{
+	struct buddy *b;
+	
+	if( sb && sb->who && sb->keepalive == 0 &&
+	    ( b = imcb_find_buddy( sb->ic, sb->who ) ) && !b->present &&
+	    set_getbool( &sb->ic->acc->set, "switchboard_keepalives" ) )
+	{
+		if( initial )
+			msn_sb_keepalive( sb, 0, 0 );
+		
+		sb->keepalive = b_timeout_add( 20000, msn_sb_keepalive, sb );
+	}
+}
+
+void msn_sb_stop_keepalives( struct msn_switchboard *sb )
+{
+	if( sb && sb->keepalive > 0 )
+	{
+		b_event_remove( sb->keepalive );
+		sb->keepalive = 0;
+	}
+}
