changeset 19562:0a055f917c85

merge of '1247a733923713e275ecb722adf9abc726896c3f' and '26743756292ed5c798a0e97c6983736a95f5b0d2'
author Ethan Blanton <elb@pidgin.im>
date Sat, 01 Sep 2007 02:36:51 +0000
parents f1740f23c896 (current diff) 11849a5c3951 (diff)
children 3715a9411806
files ChangeLog.API
diffstat 14 files changed, 438 insertions(+), 263 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Sat Sep 01 01:49:38 2007 +0000
+++ b/ChangeLog.API	Sat Sep 01 02:36:51 2007 +0000
@@ -17,6 +17,9 @@
 		Added:
 		* pidgin_set_accessible_relations, sets up label-for and labelled-by
 		  ATK relations (broken out from pidgin_set_accessible_label)
+		* pidgin_conv_attach_to_conversation, to reattach the Pidgin UI to a
+		  conversation
+		* conversation-hiding and conversation-displayed signals.
 
 	Finch:
 		Added:
--- a/doc/gtkconv-signals.dox	Sat Sep 01 01:49:38 2007 +0000
+++ b/doc/gtkconv-signals.dox	Sat Sep 01 02:36:51 2007 +0000
@@ -8,6 +8,8 @@
   @signal displaying-chat-msg
   @signal displayed-chat-msg
   @signal conversation-switched
+  @signal conversation-hiding
+  @signal conversation-displayed
  @endsignals
 
  <hr>
@@ -116,5 +118,23 @@
   @param new_conv The now active conversation.
  @endsignaldef
 
+ @signaldef conversation-hiding
+  @signalproto
+void (*conversation_hiding)(PidginConversation *gtkconv);
+  @endsignalproto
+  @signaldesc
+   Emitted immediately before an existing conversation is hidden.
+  @param gtkconv  The PidginConversation
+ @endsignaldef
+
+ @signaldef conversation-displayed
+  @signalproto
+void (*conversation_displayed)(PidginConversation *gtkconv);
+  @endsignalproto
+  @signaldesc
+   Emitted right after the Pidgin UI is reattached to a conversation.
+  @param gtkconv  The PidginConversation
+ @endsignaldef
+
 */
 // vim: syntax=c tw=75 et
--- a/libpurple/certificate.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/libpurple/certificate.c	Sat Sep 01 02:36:51 2007 +0000
@@ -1205,7 +1205,14 @@
 	/* Load up the cached certificate */
 	cached_crt = purple_certificate_pool_retrieve(
 		tls_peers, vrq->subject_name);
-	g_assert(cached_crt);
+	if ( !cached_crt ) {
+		purple_debug_error("certificate/x509/tls_cached",
+				   "Lookup failed on cached certificate!\n"
+				   "It was here just a second ago. Forwarding "
+				   "to cert_changed.\n");
+		/* vrq now becomes the problem of cert_changed */
+		x509_tls_cached_peer_cert_changed(vrq);
+	}
 
 	/* Now get SHA1 sums for both and compare them */
 	/* TODO: This is not an elegant way to compare certs */
@@ -1338,7 +1345,14 @@
 
 	ca_crt = purple_certificate_pool_retrieve(ca, ca_id);
 	g_free(ca_id);
-	g_assert(ca_crt);
+	if (!ca_crt) {
+		purple_debug_error("certificate/x509/tls_cached",
+				   "Certificate authority disappeared out "
+				   "underneath me!\n");
+		purple_certificate_verify_complete(vrq,
+						   PURPLE_CERTIFICATE_INVALID);
+		return;
+	}
 	
 	/* Check the signature */
 	if ( !purple_certificate_signed_by(end_crt, ca_crt) ) {
@@ -1375,9 +1389,11 @@
 						 "tls_peers");
 
 	if (tls_peers) {
-		g_assert(purple_certificate_pool_store(tls_peers,
-						       vrq->subject_name,
-						       peer_crt) );
+		if (!purple_certificate_pool_store(tls_peers,vrq->subject_name,
+						   peer_crt) ) {
+			purple_debug_error("certificate/x509/tls_cached",
+					   "FAILED to cache peer certificate\n");
+		}
 	} else {
 		purple_debug_error("certificate/x509/tls_cached",
 				   "Unable to locate tls_peers certificate "
@@ -1790,7 +1806,6 @@
 	GByteArray *sha_bin;
 	gchar *cn;
 	time_t activation, expiration;
-	/* Length of these buffers is dictated by 'man ctime_r' */
 	gchar *activ_str, *expir_str;
 	gchar *secondary;
 
@@ -1807,7 +1822,11 @@
 	/* Get the certificate times */
 	/* TODO: Check the times against localtime */
 	/* TODO: errorcheck? */
-	g_assert(purple_certificate_get_times(crt, &activation, &expiration));
+	if (!purple_certificate_get_times(crt, &activation, &expiration)) {
+		purple_debug_error("certificate",
+				   "Failed to get certificate times!\n");
+		activation = expiration = 0;
+	}
 	activ_str = g_strdup(ctime(&activation));
 	expir_str = g_strdup(ctime(&expiration));
 
--- a/libpurple/example/nullclient.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/libpurple/example/nullclient.c	Sat Sep 01 02:36:51 2007 +0000
@@ -197,7 +197,7 @@
 	purple_util_set_user_dir(CUSTOM_USER_DIRECTORY);
 
 	/* We do not want any debugging for now to keep the noise to a minimum. */
-	purple_debug_set_enabled(TRUE);
+	purple_debug_set_enabled(FALSE);
 
 	/* Set the core-uiops, which is used to
 	 * 	- initialize the ui specific preferences.
@@ -257,24 +257,6 @@
 				PURPLE_CALLBACK(signed_on), NULL);
 }
 
-
-
-
-void signedOn( PurpleConnection *gc, gpointer dummy ) { 
-
-    
-    if( gc ) {
-
-        PurpleAccount* a = purple_connection_get_account( gc );
-
-        if( a ) {
-            
-            purple_presence_set_idle( purple_account_get_presence( a ), TRUE, time( NULL ) );
-        }
-    }    
-}
-
-
 int main()
 {
 	GList *iter;
@@ -300,26 +282,30 @@
 			names = g_list_append(names, info->id);
 		}
 	}
+	printf("Select the protocol [0-%d]: ", i-1);
+	fgets(name, sizeof(name), stdin);
+	sscanf(name, "%d", &num);
+	prpl = g_list_nth_data(names, num);
+
+	printf("Username: ");
+	fgets(name, sizeof(name), stdin);
+	name[strlen(name) - 1] = 0;  /* strip the \n at the end */
 
 	/* Create the account */
-	account = purple_account_new("msimprpl@xyzzy.cjb.net", "prpl-myspace" );
-	purple_account_set_password(account, "4224jc" );
+	account = purple_account_new(name, prpl);
+
+	/* Get the password for the account */
+	password = getpass("Password: ");
+	purple_account_set_password(account, password);
 
 	/* It's necessary to enable the account first. */
 	purple_account_set_enabled(account, UI_ID, TRUE);
 
-#if 0 
-	static int handle;    
-	purple_signal_connect( purple_connections_get_handle(), 
-                           "signed-on", &handle,
-                           PURPLE_CALLBACK( signedOn ), 
-                           NULL );
-    
 	/* Now, to connect the account(s), create a status and activate it. */
-	purple_savedstatus_activate( purple_savedstatus_get_default() );
+	status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
+	purple_savedstatus_activate(status);
 
 	connect_to_signals_for_demonstration_purposes_only();
-#endif
 
 	g_main_loop_run(loop);
 
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Sat Sep 01 02:36:51 2007 +0000
@@ -60,7 +60,7 @@
 		(gnutls_realloc_function) g_realloc, /* realloc */
 		(gnutls_free_function)    g_free     /* free */
 		);
-	
+
 	gnutls_global_init();
 
 	gnutls_certificate_allocate_credentials(&xcred);
@@ -73,7 +73,7 @@
 static gboolean
 ssl_gnutls_init(void)
 {
-   return TRUE;
+	return TRUE;
 }
 
 static void
@@ -130,17 +130,18 @@
 
 		purple_ssl_close(gsc);
 	} else {
-		purple_debug_info("gnutls", "Handshake complete\n");
-
-		/* TODO: Remove all this debugging babble */
 		/* Now we are cooking with gas! */
 		PurpleSslOps *ops = purple_ssl_get_ops();
 		GList * peers = ops->get_peer_certificates(gsc);
-		
+
 		PurpleCertificateScheme *x509 =
 			purple_certificate_find_scheme("x509");
 
 		GList * l;
+
+		/* TODO: Remove all this debugging babble */
+		purple_debug_info("gnutls", "Handshake complete\n");
+
 		for (l=peers; l; l = l->next) {
 			PurpleCertificate *crt = l->data;
 			GByteArray *z =
@@ -155,72 +156,71 @@
 
 			/* Kill the cert! */
 			x509->destroy_certificate(crt);
-			
+
 			g_free(fpr);
 			g_byte_array_free(z, TRUE);
 		}
 		g_list_free(peers);
-		
+
 		{
-		  const gnutls_datum_t *cert_list;
-		  unsigned int cert_list_size = 0;
-		  gnutls_session_t session=gnutls_data->session;
-		  
-		  cert_list =
-		    gnutls_certificate_get_peers(session, &cert_list_size);
-		  
-		  purple_debug_info("gnutls",
-				    "Peer provided %d certs\n",
-				    cert_list_size);
-		  int i;
-		  for (i=0; i<cert_list_size; i++)
-		    {
-		      gchar fpr_bin[256];
-		      gsize fpr_bin_sz = sizeof(fpr_bin);
-		      gchar * fpr_asc = NULL;
-		      gchar tbuf[256];
-		      gsize tsz=sizeof(tbuf);
-		      gchar * tasc = NULL;
-		      gnutls_x509_crt_t cert;
-		      
-		      gnutls_x509_crt_init(&cert);
-		      gnutls_x509_crt_import (cert, &cert_list[i],
-					      GNUTLS_X509_FMT_DER);
-		      
-		      gnutls_x509_crt_get_fingerprint(cert, GNUTLS_MAC_SHA,
-						      fpr_bin, &fpr_bin_sz);
-		      
-		      fpr_asc =
-			purple_base16_encode_chunked(fpr_bin,fpr_bin_sz);
-		      
-		      purple_debug_info("gnutls", 
-					"Lvl %d SHA1 fingerprint: %s\n",
-					i, fpr_asc);
-		      
-		      tsz=sizeof(tbuf);
-		      gnutls_x509_crt_get_serial(cert,tbuf,&tsz);
-		      tasc=
-			purple_base16_encode_chunked(tbuf, tsz);
-		      purple_debug_info("gnutls",
-					"Serial: %s\n",
-					tasc);
-		      g_free(tasc);
+			const gnutls_datum_t *cert_list;
+			unsigned int cert_list_size = 0;
+			gnutls_session_t session=gnutls_data->session;
+			int i;
+
+			cert_list =
+				gnutls_certificate_get_peers(session, &cert_list_size);
+
+			purple_debug_info("gnutls",
+					    "Peer provided %d certs\n",
+					    cert_list_size);
+			for (i=0; i<cert_list_size; i++)
+			{
+				gchar fpr_bin[256];
+				gsize fpr_bin_sz = sizeof(fpr_bin);
+				gchar * fpr_asc = NULL;
+				gchar tbuf[256];
+				gsize tsz=sizeof(tbuf);
+				gchar * tasc = NULL;
+				gnutls_x509_crt_t cert;
+
+				gnutls_x509_crt_init(&cert);
+				gnutls_x509_crt_import (cert, &cert_list[i],
+						GNUTLS_X509_FMT_DER);
+
+				gnutls_x509_crt_get_fingerprint(cert, GNUTLS_MAC_SHA,
+						fpr_bin, &fpr_bin_sz);
 
-		      tsz=sizeof(tbuf);
-		      gnutls_x509_crt_get_dn (cert, tbuf, &tsz);
-		      purple_debug_info("gnutls",
-					"Cert DN: %s\n",
-					tbuf);
-		      tsz=sizeof(tbuf);
-		      gnutls_x509_crt_get_issuer_dn (cert, tbuf, &tsz);
-		      purple_debug_info("gnutls",
-					"Cert Issuer DN: %s\n",
-					tbuf);
+				fpr_asc =
+						purple_base16_encode_chunked((const guchar *)fpr_bin, fpr_bin_sz);
+
+				purple_debug_info("gnutls",
+						"Lvl %d SHA1 fingerprint: %s\n",
+						i, fpr_asc);
+
+				tsz=sizeof(tbuf);
+				gnutls_x509_crt_get_serial(cert,tbuf,&tsz);
+				tasc=purple_base16_encode_chunked((const guchar *)tbuf, tsz);
+				purple_debug_info("gnutls",
+						"Serial: %s\n",
+						tasc);
+				g_free(tasc);
 
-		      g_free(fpr_asc); fpr_asc = NULL;
-		      gnutls_x509_crt_deinit(cert);
-		    }
-		  
+				tsz=sizeof(tbuf);
+				gnutls_x509_crt_get_dn (cert, tbuf, &tsz);
+				purple_debug_info("gnutls",
+						"Cert DN: %s\n",
+						tbuf);
+				tsz=sizeof(tbuf);
+				gnutls_x509_crt_get_issuer_dn (cert, tbuf, &tsz);
+				purple_debug_info("gnutls",
+						"Cert Issuer DN: %s\n",
+						tbuf);
+
+				g_free(fpr_asc);
+				fpr_asc = NULL;
+				gnutls_x509_crt_deinit(cert);
+			}
 		}
 
 		/* TODO: The following logic should really be in libpurple */
@@ -378,7 +378,7 @@
 	unsigned int cert_list_size = 0;
 
 	unsigned int i;
-	
+
 	/* This should never, ever happen. */
 	g_return_val_if_fail( gnutls_certificate_type_get (gnutls_data->session) == GNUTLS_CRT_X509, NULL);
 
@@ -426,12 +426,14 @@
 static void
 x509_crtdata_delref(x509_crtdata_t *cd)
 {
-	g_assert(cd->refcount > 0);
-	
 	(cd->refcount)--;
 
+	if (cd->refcount < 0)
+		g_critical("Refcount of x509_crtdata_t is %d, which is less "
+				"than zero!\n", cd->refcount);
+
 	/* If the refcount reaches zero, kill the structure */
-	if (cd->refcount == 0) {
+	if (cd->refcount <= 0) {
 		purple_debug_info("gnutls/x509",
 				  "Freeing unused cert data at %p\n",
 				  cd);
@@ -466,11 +468,11 @@
 	certdat = g_new0(x509_crtdata_t, 1);
 	gnutls_x509_crt_init(&(certdat->crt));
 	certdat->refcount = 0;
-	
+
 	/* Perform the actual certificate parse */
 	/* Yes, certdat->crt should be passed as-is */
 	gnutls_x509_crt_import(certdat->crt, &dt, mode);
-	
+
 	/* Allocate the certificate and load it with data */
 	crt = g_new0(PurpleCertificate, 1);
 	crt->scheme = &x509_gnutls;
@@ -495,7 +497,7 @@
 	purple_debug_info("gnutls",
 			  "Attempting to load X.509 certificate from %s\n",
 			  filename);
-	
+
 	/* Next, we'll simply yank the entire contents of the file
 	   into memory */
 	/* TODO: Should I worry about very large files here? */
@@ -506,7 +508,7 @@
 			    NULL      /* No error checking for now */
 		),
 		NULL);
-	
+
 	/* Load the datum struct */
 	dt.data = (unsigned char *) buf;
 	dt.size = buf_sz;
@@ -514,7 +516,7 @@
 	/* Perform the conversion */
 	crt = x509_import_from_datum(dt,
 				     GNUTLS_X509_FMT_PEM); // files should be in PEM format
-	
+
 	/* Cleanup */
 	g_free(buf);
 
@@ -571,7 +573,6 @@
 	success = purple_util_write_data_to_file_absolute(filename,
 							  out_buf, out_size);
 
-	
 	g_free(out_buf);
 	g_return_val_if_fail(success, FALSE);
 	return success;
@@ -596,10 +597,10 @@
 }
 /** Frees a Certificate
  *
- *  Destroys a Certificate's internal data structures and frees the pointer
- *  given.
- *  @param crt  Certificate instance to be destroyed. It WILL NOT be destroyed
- *              if it is not of the correct CertificateScheme. Can be NULL
+ * Destroys a Certificate's internal data structures and frees the pointer
+ * given.
+ * @param crt Certificate instance to be destroyed. It WILL NOT be destroyed
+ *            if it is not of the correct CertificateScheme. Can be NULL
  *
  */
 static void
@@ -622,7 +623,7 @@
 	/* Use the reference counting system to free (or not) the
 	   underlying data */
 	x509_crtdata_delref((x509_crtdata_t *)crt->data);
-	
+
 	/* Kill the structure itself */
 	g_free(crt);
 }
@@ -643,7 +644,7 @@
 	gnutls_x509_crt_t issuer_dat;
 	unsigned int verify; /* used to store result from GnuTLS verifier */
 	int ret;
-	
+
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(issuer, FALSE);
 
@@ -685,7 +686,7 @@
 		/* The issuer is not correct, or there were errors */
 		return FALSE;
 	}
-	
+
 	/* Now, check the signature */
 	/* The second argument is a ptr to an array of "trusted" issuer certs,
 	   but we're only using one trusted one */
@@ -696,7 +697,7 @@
 					current standard) */
 				     GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
 				     &verify);
-	
+
 	if (ret != 0) {
 		purple_debug_error("gnutls/x509",
 				   "Attempted certificate verification caused a GnuTLS error code %d. I will just say the signature is bad, but you should look into this.\n", ret);
@@ -713,7 +714,7 @@
 				  issuer_id, crt_id);
 		g_free(crt_id);
 		g_free(issuer_id);
-		
+
 		return FALSE;
 	} /* if (ret, etc.) */
 
@@ -742,7 +743,7 @@
 
 	/* This shouldn't happen */
 	g_return_val_if_fail(tmpsz == hashlen, NULL);
-	
+
 	/* Okay, now create and fill hash array */
 	hash = g_byte_array_new();
 	g_byte_array_append(hash, hashbuf, hashlen);
@@ -776,7 +777,7 @@
 		g_free(dn);
 		return NULL;
 	}
-	
+
 	return dn;
 }
 
@@ -807,7 +808,7 @@
 		g_free(dn);
 		return NULL;
 	}
-	
+
 	return dn;
 }
 
@@ -848,7 +849,6 @@
 		return NULL;
 	}
 
-	
 	return cn;
 }
 
@@ -893,7 +893,7 @@
 	if (*activation == errval || *expiration == errval) {
 		return FALSE;
 	}
-	
+
 	return TRUE;
 }
 
--- a/libpurple/protocols/yahoo/yahoo.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sat Sep 01 02:36:51 2007 +0000
@@ -3030,6 +3030,11 @@
 	if (yd->ycht)
 		ycht_connection_close(yd->ycht);
 
+	g_free(yd->pending_chat_room);
+	g_free(yd->pending_chat_id);
+	g_free(yd->pending_chat_topic);
+	g_free(yd->pending_chat_goto);
+
 	g_free(yd);
 	gc->proto_data = NULL;
 }
--- a/libpurple/protocols/yahoo/yahoo.h	Sat Sep 01 01:49:38 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Sat Sep 01 02:36:51 2007 +0000
@@ -130,6 +130,10 @@
 	gboolean chat_online;
 	gboolean in_chat;
 	char *chat_name;
+	char *pending_chat_room;
+	char *pending_chat_id;
+	char *pending_chat_topic;
+	char *pending_chat_goto;
 	char *auth;
 	gsize auth_written;
 	char *cookie_y;
--- a/libpurple/protocols/yahoo/yahoochat.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Sat Sep 01 02:36:51 2007 +0000
@@ -55,16 +55,24 @@
 {
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_packet *pkt;
+	const char *rll;
 
 	if (yd->wm) {
 		ycht_connection_open(gc);
 		return;
 	}
 
+	rll = purple_account_get_string(purple_connection_get_account(gc),
+								  "room_list_locale", YAHOO_ROOMLIST_LOCALE);
+
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE,0);
-	yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc),
-	                  109, purple_connection_get_display_name(gc), 6, "abcde",
-	                  135, "ym8.1.0.415");
+	yahoo_packet_hash(pkt, "sssss",
+					  109, purple_connection_get_display_name(gc),
+					  1, purple_connection_get_display_name(gc),
+					  6, "abcde",
+					/* I'm not sure this is the correct way to set this. */
+					  98, rll,
+					  135, "ym8.1.0.415");
 	yahoo_packet_send_and_free(pkt, yd);
 }
 
@@ -125,6 +133,7 @@
 		case 1: /* us, but we already know who we are */
 			break;
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 50: /* inviter */
@@ -136,6 +145,7 @@
 			g_string_append_printf(members, "%s\n", pair->value);
 			break;
 		case 58:
+			g_free(msg);
 			msg = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 13: /* ? */
@@ -145,6 +155,17 @@
 
 	if (!room) {
 		g_string_free(members, TRUE);
+		g_free(msg);
+		return;
+	}
+
+	if (!yahoo_privacy_check(gc, who) ||
+			(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
+		purple_debug_info("yahoo",
+		    "Invite to conference %s from %s has been dropped.\n", room, who);
+		g_free(room);
+		g_free(msg);
+		g_string_free(members, TRUE);
 		return;
 	}
 
@@ -153,19 +174,9 @@
 	if (msg)
 		g_hash_table_replace(components, g_strdup("topic"), msg);
 	g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
-	if (members) {
-		g_hash_table_replace(components, g_strdup("members"), g_strdup(members->str));
-	}
-	if (!yahoo_privacy_check(gc, who) ||
-		(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
-		purple_debug_info("yahoo",
-		    "Invite to conference %s from %s has been dropped.\n", room, who);
-		g_string_free(members, TRUE);
-		return;
-	}
+	g_hash_table_replace(components, g_strdup("members"), g_string_free(members, FALSE));
 	serv_got_chat_invite(gc, room, who, msg, components);
 
-	g_string_free(members, TRUE);
 }
 
 void yahoo_process_conference_decline(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -180,20 +191,21 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 54:
 			who = pair->value;
 			break;
 		case 14:
+			g_free(msg);
 			msg = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		}
 	}
 	if (!yahoo_privacy_check(gc, who)) {
 		g_free(room);
-		if (msg != NULL)
-			g_free(msg);
+		g_free(msg);
 		return;
 	}
 
@@ -209,8 +221,7 @@
 		}
 
 		g_free(room);
-		if (msg)
-			g_free(msg);
+		g_free(msg);
 	}
 }
 
@@ -226,6 +237,7 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 53:
@@ -254,6 +266,7 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 56:
@@ -276,7 +289,6 @@
 	char *room = NULL;
 	char *who = NULL;
 	char *msg = NULL;
-	char *msg2;
 	int utf8 = 0;
 	PurpleConversation *c;
 
@@ -285,6 +297,7 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 3:
@@ -299,28 +312,82 @@
 		}
 	}
 
-		if (room && who && msg) {
-			msg2 = yahoo_string_decode(gc, msg, utf8);
-			c = yahoo_find_conference(gc, room);
-			if (!c)
-				return;
-			msg = yahoo_codes_to_html(msg2);
-			serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL));
-			g_free(msg);
-			g_free(msg2);
+	if (room && who && msg) {
+		char *msg2;
+
+		c = yahoo_find_conference(gc, room);
+		if (!c) {
+			g_free(room);
+			return;
 		}
-		if (room)
-			g_free(room);
+
+		msg2 = yahoo_string_decode(gc, msg, utf8);
+		msg = yahoo_codes_to_html(msg2);
+		serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL));
+		g_free(msg);
+		g_free(msg2);
+	}
+
+	g_free(room);
 }
 
+static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic, const char *id)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_packet *pkt;
+	char *room2;
+	gboolean utf8 = TRUE;
+
+	if (yd->wm) {
+		g_return_if_fail(yd->ycht != NULL);
+		ycht_chat_join(yd->ycht, room);
+		return;
+	}
+
+	/* apparently room names are always utf8, or else always not utf8,
+	 * so we don't have to actually pass the flag in the packet. Or something. */
+	room2 = yahoo_string_encode(gc, room, &utf8);
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "ssss",
+						1, purple_connection_get_display_name(gc),
+						104, room2,
+						62, "2",
+						129, id ? id : "0");
+	yahoo_packet_send_and_free(pkt, yd);
+	g_free(room2);
+}
 
 /* this is a confirmation of yahoo_chat_online(); */
 void yahoo_process_chat_online(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data;
 
-	if (pkt->status == 1)
+	if (pkt->status == 1) {
 		yd->chat_online = 1;
+
+		/* We need to goto a user in chat */
+		if (yd->pending_chat_goto) {
+			struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0);
+			yahoo_packet_hash(pkt, "sss",
+				109, yd->pending_chat_goto,
+				1, purple_connection_get_display_name(gc),
+				62, "2");
+			yahoo_packet_send_and_free(pkt, yd);
+		} else if (yd->pending_chat_room) {
+			yahoo_chat_join(gc, purple_connection_get_display_name(gc), yd->pending_chat_room,
+				yd->pending_chat_topic, yd->pending_chat_id);
+		}
+
+		g_free(yd->pending_chat_room);
+		yd->pending_chat_room = NULL;
+		g_free(yd->pending_chat_id);
+		yd->pending_chat_id = NULL;
+		g_free(yd->pending_chat_topic);
+		yd->pending_chat_topic = NULL;
+		g_free(yd->pending_chat_goto);
+		yd->pending_chat_goto = NULL;
+	}
 }
 
 /* this is basicly the opposite of chat_online */
@@ -340,6 +407,14 @@
 
 	if (pkt->status == 1) {
 		yd->chat_online = 0;
+		g_free(yd->pending_chat_room);
+		yd->pending_chat_room = NULL;
+		g_free(yd->pending_chat_id);
+		yd->pending_chat_id = NULL;
+		g_free(yd->pending_chat_topic);
+		yd->pending_chat_topic = NULL;
+		g_free(yd->pending_chat_goto);
+		yd->pending_chat_goto = NULL;
 		if (yd->in_chat)
 			yahoo_c_leave(gc, YAHOO_CHAT_ID);
 	}
@@ -384,9 +459,11 @@
 		switch (pair->key) {
 
 		case 104:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 105:
+			g_free(topic);
 			topic = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 128:
@@ -445,8 +522,11 @@
 			purple_conversation_set_name(c, room);
 
 			c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
-			if (topic)
+			if (topic) {
 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+				/* Also print the topic to the backlog so that the captcha link is clickable */
+				purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL));
+			}
 			yd->in_chat = 1;
 			yd->chat_name = g_strdup(room);
 			purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE);
@@ -456,14 +536,22 @@
 			g_free(tmpmsg);
 		} else {
 			c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
-			if (topic)
+			if (topic) {
 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+				/* Also print the topic to the backlog so that the captcha link is clickable */
+				purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL));
+			}
 			yd->in_chat = 1;
 			yd->chat_name = g_strdup(room);
 			purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE);
 		}
 		g_list_free(flags);
 	} else if (c) {
+		if (topic) {
+			const char *cur_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(c));
+			if (cur_topic == NULL || strcmp(cur_topic, topic) != 0)
+				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+		}
 		yahoo_chat_add_users(PURPLE_CONV_CHAT(c), members);
 	}
 
@@ -497,8 +585,10 @@
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
 
-		if (pair->key == 104)
+		if (pair->key == 104) {
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
+		}
 		if (pair->key == 109)
 			who = pair->value;
 	}
@@ -529,6 +619,7 @@
 			utf8 = strtol(pair->value, NULL, 10);
 			break;
 		case 104:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 109:
@@ -583,6 +674,7 @@
 
 		switch (pair->key) {
 		case 104:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 129: /* room id? */
@@ -590,6 +682,7 @@
 		case 126: /* ??? */
 			break;
 		case 117:
+			g_free(msg);
 			msg = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 119:
@@ -603,24 +696,21 @@
 	if (room && who) {
 		GHashTable *components;
 
+		if (!yahoo_privacy_check(gc, who) ||
+				(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
+			purple_debug_info("yahoo", "Invite to room %s from %s has been dropped.\n", room, who);
+			g_free(room);
+			g_free(msg);
+			return;
+		}
+
 		components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 		g_hash_table_replace(components, g_strdup("room"), g_strdup(room));
-		if (!yahoo_privacy_check(gc, who) ||
-			(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
-			purple_debug_info("yahoo",
-			"Invite to room %s from %s has been dropped.\n", room, who);
-			if (room != NULL)
-				g_free(room);
-			if (msg != NULL)
-				g_free(msg);
-			return;
-		}
 		serv_got_chat_invite(gc, room, who, msg, components);
 	}
-	if (room)
-		g_free(room);
-	if (msg)
-		g_free(msg);
+
+	g_free(room);
+	g_free(msg);
 }
 
 void yahoo_process_chat_goto(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -783,6 +873,14 @@
 	yahoo_packet_send_and_free(pkt, yd);
 
 	yd->chat_online = 0;
+	g_free(yd->pending_chat_room);
+	yd->pending_chat_room = NULL;
+	g_free(yd->pending_chat_id);
+	yd->pending_chat_id = NULL;
+	g_free(yd->pending_chat_topic);
+	yd->pending_chat_topic = NULL;
+	g_free(yd->pending_chat_goto);
+	yd->pending_chat_goto = NULL;
 	g_free(eroom);
 }
 
@@ -829,29 +927,6 @@
 	return 0;
 }
 
-static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	struct yahoo_packet *pkt;
-	char *room2;
-	gboolean utf8 = TRUE;
-
-	if (yd->wm) {
-		g_return_if_fail(yd->ycht != NULL);
-		ycht_chat_join(yd->ycht, room);
-		return;
-	}
-
-	/* apparently room names are always utf8, or else always not utf8,
-	 * so we don't have to actually pass the flag in the packet. Or something. */
-	room2 = yahoo_string_encode(gc, room, &utf8);
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc),
-	                  62, "2", 104, room2, 129, "0");
-	yahoo_packet_send_and_free(pkt, yd);
-	g_free(room2);
-}
 
 static void yahoo_chat_invite(PurpleConnection *gc, const char *dn, const char *buddy,
 							const char *room, const char *msg)
@@ -892,8 +967,18 @@
 		return;
 	}
 
-	if (!yd->chat_online)
+	if (!yd->chat_online) {
 		yahoo_chat_online(gc);
+		g_free(yd->pending_chat_room);
+		yd->pending_chat_room = NULL;
+		g_free(yd->pending_chat_id);
+		yd->pending_chat_id = NULL;
+		g_free(yd->pending_chat_topic);
+		yd->pending_chat_topic = NULL;
+		g_free(yd->pending_chat_goto);
+		yd->pending_chat_goto = g_strdup(name);
+		return;
+	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0);
 	yahoo_packet_hash(pkt, "sss", 109, name, 1, purple_connection_get_display_name(gc), 62, "2");
@@ -988,8 +1073,7 @@
 void yahoo_c_join(PurpleConnection *gc, GHashTable *data)
 {
 	struct yahoo_data *yd;
-	char *room, *topic, *members, *type;
-	int id;
+	char *room, *topic, *type;
 	PurpleConversation *c;
 
 	yd = (struct yahoo_data *) gc->proto_data;
@@ -1004,9 +1088,9 @@
 	if (!topic)
 		topic = "";
 
-	members = g_hash_table_lookup(data, "members");
-
 	if ((type = g_hash_table_lookup(data, "type")) && !strcmp(type, "Conference")) {
+		int id;
+		const char *members = g_hash_table_lookup(data, "members");
 		id = yd->conf_id++;
 		c = serv_got_joined_chat(gc, id, room);
 		yd->confs = g_slist_prepend(yd->confs, c);
@@ -1014,13 +1098,27 @@
 		yahoo_conf_join(yd, c, purple_connection_get_display_name(gc), room, topic, members);
 		return;
 	} else {
-		if (yd->in_chat)
+		const char *id;
+		/*if (yd->in_chat)
 			yahoo_chat_leave(gc, room,
 					purple_connection_get_display_name(gc),
-					FALSE);
-		if (!yd->chat_online)
+					FALSE);*/
+
+		id = g_hash_table_lookup(data, "id");
+
+		if (!yd->chat_online) {
 			yahoo_chat_online(gc);
-		yahoo_chat_join(gc, purple_connection_get_display_name(gc), room, topic);
+			g_free(yd->pending_chat_room);
+			yd->pending_chat_room = g_strdup(room);
+			g_free(yd->pending_chat_id);
+			yd->pending_chat_id = g_strdup(id);
+			g_free(yd->pending_chat_topic);
+			yd->pending_chat_topic = g_strdup(topic);
+			g_free(yd->pending_chat_goto);
+			yd->pending_chat_goto = NULL;
+		} else {
+			yahoo_chat_join(gc, purple_connection_get_display_name(gc), room, topic, id);
+		}
 		return;
 	}
 }
@@ -1148,16 +1246,13 @@
 
 		for (i = 0; anames[i]; i++) {
 			if (!strcmp(anames[i], "id")) {
-				if (s->room.id)
-					g_free(s->room.id);
+				g_free(s->room.id);
 				s->room.id = g_strdup(avalues[i]);
 			} else if (!strcmp(anames[i], "name")) {
-				if (s->room.name)
-					g_free(s->room.name);
+				g_free(s->room.name);
 				s->room.name = g_strdup(avalues[i]);
 			} else if (!strcmp(anames[i], "topic")) {
-				if (s->room.topic)
-					g_free(s->room.topic);
+				g_free(s->room.topic);
 				s->room.topic = g_strdup(avalues[i]);
 			} else if (!strcmp(anames[i], "type")) {
 				if (!strcmp("yahoo", avalues[i]))
--- a/pidgin/gtkblist.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/pidgin/gtkblist.c	Sat Sep 01 02:36:51 2007 +0000
@@ -3209,6 +3209,7 @@
 {
 	GdkPixbuf *ret;
 	const char *protoname = NULL;
+	const char *icon = NULL;
 	struct _pidgin_blist_node *gtknode = node->ui_data;
 	struct _pidgin_blist_node *gtkbuddynode = NULL;
 	PurpleBuddy *buddy = NULL;
@@ -3257,62 +3258,54 @@
 									     purple_buddy_get_name(buddy),
 									     purple_buddy_get_account(buddy));
 		PurplePresence *p;
+		gboolean trans;
+
 		if(conv != NULL) {
 			PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
-			if(gtkconv != NULL && pidgin_conv_is_hidden(gtkconv) && size == PIDGIN_STATUS_ICON_SMALL) {
+			if((gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) && size == PIDGIN_STATUS_ICON_SMALL) {
 				return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE,
 							       icon_size, "GtkTreeView");
 			}
 		}
+
 		p = purple_buddy_get_presence(buddy);
+		trans = (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL);
 
 		if (PURPLE_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff)
-			ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_LOGIN,
-					icon_size, "GtkTreeView");
+			icon = PIDGIN_STOCK_STATUS_LOGIN;
 		else if (gtkbuddynode && gtkbuddynode->recent_signonoff)
-			ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_LOGOUT,
-					icon_size, "GtkTreeView");
+			icon = PIDGIN_STOCK_STATUS_LOGOUT;
 		else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE))
-			if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL)
-				ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_BUSY_I,
-						icon_size, "GtkTreeView");
+			if (trans)
+				icon = PIDGIN_STOCK_STATUS_BUSY_I;
 			else
-				ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_BUSY,
-						icon_size, "GtkTreeView");
+				icon = PIDGIN_STOCK_STATUS_BUSY;
 		else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY))
-		        if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL)
-		                ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AWAY_I,
-		                                icon_size, "GtkTreeView");
+			if (trans)
+				icon = PIDGIN_STOCK_STATUS_AWAY_I;
 		 	else
-				ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AWAY,
-						icon_size, "GtkTreeView");
+				icon = PIDGIN_STOCK_STATUS_AWAY;
 		else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY))
-			if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL)
-		        	ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_XA_I,
-						icon_size, "GtkTreeView");
+			if (trans)
+				icon = PIDGIN_STOCK_STATUS_XA_I;
 			else
-				ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_XA,
-						icon_size, "GtkTreeView");
+				icon = PIDGIN_STOCK_STATUS_XA;
 		else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE))
-			ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_OFFLINE,
-					icon_size, "GtkTreeView");
-		else if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL)
-			ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AVAILABLE_I,
-					icon_size, "GtkTreeView");
+			icon = PIDGIN_STOCK_STATUS_OFFLINE;
+		else if (trans)
+			icon = PIDGIN_STOCK_STATUS_AVAILABLE_I;
 		else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE))
-			ret = gtk_widget_render_icon(GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_INVISIBLE,
-					icon_size, "GtkTreeView");
+			icon = PIDGIN_STOCK_STATUS_INVISIBLE;
 		else
-			ret = gtk_widget_render_icon(GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AVAILABLE,
-					icon_size, "GtkTreeView");
+			icon = PIDGIN_STOCK_STATUS_AVAILABLE;
 	} else if (chat) {
-		ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_CHAT,
-				icon_size, "GtkTreeView");
+		icon = PIDGIN_STOCK_STATUS_CHAT;
 	} else {
-		ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_PERSON,
-				icon_size, "GtkTreeView");
-	}
-
+		icon = PIDGIN_STOCK_STATUS_PERSON;
+	}
+
+	ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), icon,
+			icon_size, "GtkTreeView");
 	return ret;
 }
 
@@ -3335,7 +3328,7 @@
 
 	if(conv != NULL) {
 		gtkconv = PIDGIN_CONVERSATION(conv);
-		if(gtkconv != NULL && pidgin_conv_is_hidden(gtkconv)) {
+		if(gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) {
 			hidden_conv = TRUE;
 		}
 	}
@@ -5213,6 +5206,10 @@
 		GdkPixbuf *emblem;
 		char *mark;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
+		const char *name = purple_chat_get_name(chat);
+		PurpleConversation *conv =
+				purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account);
+		gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv));
 
 		if(!insert_node(list, node, &iter))
 			return;
@@ -5228,15 +5225,20 @@
 			avatar = NULL;
 
 		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);
+		if (hidden) {
+			char *bold = g_strdup_printf("<b>%s</b>", mark);
+			g_free(mark);
+			mark = bold;
+		}
 
 		gtk_tree_store_set(gtkblist->treemodel, &iter,
 				STATUS_ICON_COLUMN, status,
 				STATUS_ICON_VISIBLE_COLUMN, TRUE,
 				BUDDY_ICON_COLUMN, avatar ? avatar : gtkblist->empty_avatar,
 				BUDDY_ICON_VISIBLE_COLUMN,  purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"),
-			        EMBLEM_COLUMN, emblem,
+				EMBLEM_COLUMN, emblem,
 				EMBLEM_VISIBLE_COLUMN, emblem != NULL,
-		 	        PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL),
+				PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL),
 				PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
 				NAME_COLUMN, mark,
 				GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
--- a/pidgin/gtkconv.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/pidgin/gtkconv.c	Sat Sep 01 02:36:51 2007 +0000
@@ -1303,7 +1303,10 @@
 menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget)
 {
 	PidginWindow *win = data;
+	PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
 	PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+	purple_signal_emit(pidgin_conversations_get_handle(),
+			"conversation-hiding", gtkconv);
 	purple_conversation_set_ui_ops(conv, NULL);
 }
 
@@ -5622,6 +5625,7 @@
 		account, name, displaying, conv, flags);
 	g_free(displaying);
 }
+
 static void
 pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
 {
@@ -7206,6 +7210,8 @@
 	if (gtkconv->attach.current)
 		return TRUE;
 
+	purple_signal_emit(pidgin_conversations_get_handle(),
+			"conversation-displayed", gtkconv);
 	g_source_remove(gtkconv->attach.timer);
 	gtkconv->attach.timer = 0;
 	return FALSE;
@@ -7228,12 +7234,15 @@
 		list = g_list_last(list);
 		gtkconv->attach.current = list;
 		gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
-	}
-
-	/* XXX: If this is a chat:
-	 * 	- populate the userlist
-	 * 	- set the topic
-	 */
+	} else {
+		purple_signal_emit(pidgin_conversations_get_handle(),
+				"conversation-displayed", gtkconv);
+	}
+
+	if (conv->type == PURPLE_CONV_TYPE_CHAT) {
+		pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
+		pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE);
+	}
 
 	return TRUE;
 }
@@ -7415,6 +7424,16 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 										PURPLE_SUBTYPE_CONVERSATION));
 
+	purple_signal_register(handle, "conversation-hiding",
+						 purple_marshal_VOID__POINTER_POINTER, NULL, 1,
+						 purple_value_new(PURPLE_TYPE_BOXED,
+										"PidginConversation *"));
+
+	purple_signal_register(handle, "conversation-displayed",
+						 purple_marshal_VOID__POINTER_POINTER, NULL, 1,
+						 purple_value_new(PURPLE_TYPE_BOXED,
+										"PidginConversation *"));
+
 	/**********************************************************************
 	 * Register commands
 	 **********************************************************************/
--- a/pidgin/gtkconv.h	Sat Sep 01 01:49:38 2007 +0000
+++ b/pidgin/gtkconv.h	Sat Sep 01 02:36:51 2007 +0000
@@ -245,6 +245,13 @@
  */
 void pidgin_conv_present_conversation(PurpleConversation *conv);
 
+/**
+ * Reattach Pidgin UI to a conversation.
+ *
+ * @param conv  The conversation.
+ *
+ * @return  Wheter Pidgin UI was successfully attached.
+ */
 gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv);
 
 PidginWindow *pidgin_conv_get_window(PidginConversation *gtkconv);
--- a/pidgin/gtkimhtmltoolbar.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sat Sep 01 02:36:51 2007 +0000
@@ -786,6 +786,7 @@
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD);
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->italic), buttons & GTK_IMHTML_ITALIC);
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->underline), buttons & GTK_IMHTML_UNDERLINE);
+	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->strikethrough), buttons & GTK_IMHTML_STRIKE);
 
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->larger_size), buttons & GTK_IMHTML_GROW);
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smaller_size), buttons & GTK_IMHTML_SHRINK);
@@ -798,6 +799,7 @@
 							 (buttons & GTK_IMHTML_BOLD ||
 							  buttons & GTK_IMHTML_ITALIC ||
 							  buttons & GTK_IMHTML_UNDERLINE ||
+							  buttons & GTK_IMHTML_STRIKE ||
 							  buttons & GTK_IMHTML_GROW ||
 							  buttons & GTK_IMHTML_SHRINK ||
 							  buttons & GTK_IMHTML_FACE ||
@@ -831,7 +833,7 @@
 
 static void update_buttons(GtkIMHtmlToolbar *toolbar)
 {
-	gboolean bold, italic, underline;
+	gboolean bold, italic, underline, strike;
 	char *tmp;
 	char *tmp2;
 	GtkLabel *label = g_object_get_data(G_OBJECT(toolbar), "font_label");
@@ -840,6 +842,7 @@
 
 	gtk_imhtml_get_current_format(GTK_IMHTML(toolbar->imhtml),
 								  &bold, &italic, &underline);
+	strike = GTK_IMHTML(toolbar->imhtml)->edit.strike;
 
 	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->bold)) != bold)
 		toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), bold,
@@ -847,10 +850,12 @@
 	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)) != italic)
 		toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic,
 									   toolbar);
-
 	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)) != underline)
 		toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline),
 									   underline, toolbar);
+	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->strikethrough)) != strike)
+		toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->strikethrough),
+									   strike, toolbar);
 
 	/* These buttons aren't ever "active". */
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smaller_size), FALSE);
@@ -874,6 +879,12 @@
 		gtk_label_set_markup_with_mnemonic(label, markup);
 		g_free(markup);
 	}
+	if (strike) {
+		gchar *markup = g_strdup_printf("<s>%s</s>",
+				gtk_label_get_label(label));
+		gtk_label_set_markup_with_mnemonic(label, markup);
+		g_free(markup);
+	}
 
 	tmp = gtk_imhtml_get_current_fontface(GTK_IMHTML(toolbar->imhtml));
 	toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->font),
--- a/pidgin/gtksourceundomanager.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/pidgin/gtksourceundomanager.c	Sat Sep 01 02:36:51 2007 +0000
@@ -677,8 +677,6 @@
 	if (um->priv->running_not_undoable_actions > 0)
 		return;
 
-	g_return_if_fail (strlen (text) >= (guint)length);
-	
 	undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
 
 	undo_action.action.insert.pos    = gtk_text_iter_get_offset (pos);
@@ -774,7 +772,7 @@
 		*action = *undo_action;
 
 		if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
-			action->action.insert.text = g_strdup (undo_action->action.insert.text);
+			action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length);
 		else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
 			action->action.delete.text = g_strdup (undo_action->action.delete.text); 
 		else
--- a/pidgin/plugins/notify.c	Sat Sep 01 01:49:38 2007 +0000
+++ b/pidgin/plugins/notify.c	Sat Sep 01 02:36:51 2007 +0000
@@ -167,7 +167,7 @@
 	gboolean has_focus;
 	PidginWindow *purplewin = NULL;
 
-	if (conv == NULL)
+	if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
 		return 0;
 
 	/* We want to remove the notifications, but not reset the counter */
@@ -224,6 +224,8 @@
 	PidginWindow *purplewin = NULL;
 
 	g_return_if_fail(conv != NULL);
+	if (PIDGIN_CONVERSATION(conv) == NULL)
+		return;
 
 	purplewin = PIDGIN_CONVERSATION(conv)->win;
 	active_conv = pidgin_conv_window_get_active_conversation(purplewin);
@@ -417,10 +419,14 @@
 deleting_conv(PurpleConversation *conv)
 {
 	PidginWindow *purplewin = NULL;
+	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+
+	if (gtkconv == NULL)
+		return;
 
 	detach_signals(conv);
 
-	purplewin = PIDGIN_CONVERSATION(conv)->win;
+	purplewin = gtkconv->win;
 
 	handle_urgent(purplewin, FALSE);
 	purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));