changeset 21746:82016f2ea290

merge of '36ed28f7212302e500d79866f33eacde299878b2' and 'ef615b4ae18609e0cbe2783aafc7a5114db4a06f'
author Luke Schierer <lschiere@pidgin.im>
date Mon, 03 Dec 2007 16:20:59 +0000
parents ea18c129edfb (current diff) 2c0ae5e29376 (diff)
children d376b1e191b5
files
diffstat 23 files changed, 301 insertions(+), 178 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Dec 03 14:46:54 2007 +0000
+++ b/ChangeLog	Mon Dec 03 16:20:59 2007 +0000
@@ -1,13 +1,17 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.1 (??/??/????):
+version 2.3.2 (??/??/????):
 	libpurple:
 	* Fixed various problems with loss of status messages when going
 	  or returning from idle on MySpaceIM.
 
 	Finch:
-	* Color is used in the buddylist to indicate status. Look at the sample
-	  gntrc file in the man-page for details.
+	* Color is used in the buddylist to indicate status, and the conversation
+	  window to indicate various message attributes. Look at the sample gntrc
+	  file in the man-page for details.
+	* The default keybinding for dump-screen is now M-D and uses a file
+	  request dialog. M-d will properly delete-forward-word, and M-f has been
+	  fixed to imitate readline's behavior.
 
 version 2.3.0 (11/24/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.3.0
--- a/ChangeLog.API	Mon Dec 03 14:46:54 2007 +0000
+++ b/ChangeLog.API	Mon Dec 03 16:20:59 2007 +0000
@@ -1,6 +1,6 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.1 (??/??/????):
+version 2.3.2 (??/??/????):
 	Finch:
 		libgnt:
 		* Added gnt_tree_set_row_color to set the color for a row in a tree.
@@ -8,6 +8,8 @@
 		* Added gnt_color_add_pair to define a new color.
 		* Added gnt_colors_get_color to get an ncurses color value from a
 		  string.
+		* Added gnt_style_get_color to get a color pair from an entry in
+		  ~/.gntrc
 
 version 2.3.0 (11/24/2007):
 	libpurple:
--- a/doc/finch.1.in	Mon Dec 03 14:46:54 2007 +0000
+++ b/doc/finch.1.in	Mon Dec 03 16:20:59 2007 +0000
@@ -145,8 +145,19 @@
 .br
 color-offline = red; black
 .br
+color-message-sent = cyan; default
+.br
+color-message-received = red; default
+.br
+color-message-highlight = black; green
+.br
+color-message-action = yellow; default
+.br
+color-timestamp = blue; default
+.br
 #See below for details on color
 .br
+
 [general]
 .br
 shadow = 0
--- a/finch/gntblist.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/gntblist.c	Mon Dec 03 16:20:59 2007 +0000
@@ -1768,37 +1768,18 @@
 	draw_tooltip(ggblist);
 }
 
-static int
-get_color(char *key)
-{
-#if GLIB_CHECK_VERSION(2,6,0)
-	int fg = 0, bg = 0;
-	gsize n;
-	char **vals;
-	vals = gnt_style_get_string_list(NULL, key, &n);
-	if (vals && n == 2) {
-		fg = gnt_colors_get_color(vals[0]);
-		bg = gnt_colors_get_color(vals[1]);
-		return gnt_color_add_pair(fg, bg);
-	}
-	return 0;
-#else
-	return 0;
-#endif
-}
-
 void finch_blist_init()
 {
-	color_available = get_color("color-available");
+	color_available = gnt_style_get_color(NULL, "color-available");
 	if (!color_available)
 		color_available = gnt_color_add_pair(COLOR_GREEN, -1);
-	color_away = get_color("color-away");
+	color_away = gnt_style_get_color(NULL, "color-away");
 	if (!color_away)
 		color_away = gnt_color_add_pair(COLOR_BLUE, -1);
-	color_idle = get_color("color-idle");
+	color_idle = gnt_style_get_color(NULL, "color-idle");
 	if (!color_idle)
 		color_idle = gnt_color_add_pair(COLOR_CYAN, -1);
-	color_offline = get_color("color-offline");
+	color_offline = gnt_style_get_color(NULL, "color-offline");
 	if (!color_offline)
 		color_offline = gnt_color_add_pair(COLOR_RED, -1);
 
--- a/finch/gntconv.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/gntconv.c	Mon Dec 03 16:20:59 2007 +0000
@@ -49,6 +49,7 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
+#include "gntstyle.h"
 #include "gnttextview.h"
 #include "gnttree.h"
 #include "gntutils.h"
@@ -64,6 +65,12 @@
 		const char *message, PurpleMessageFlags flags, time_t mtime);
 static void generate_send_to_menu(FinchConv *ggc);
 
+static int color_message_receive;
+static int color_message_send;
+static int color_message_highlight;
+static int color_message_action;
+static int color_timestamp;
+
 static PurpleBlistNode *
 get_conversation_blist_node(PurpleConversation *conv)
 {
@@ -753,7 +760,9 @@
 	/* Unnecessary to print the timestamp for delayed message */
 	if (purple_prefs_get_bool("/finch/conversations/timestamps"))
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
-					purple_utf8_strftime("(%H:%M:%S) ", localtime(&mtime)), GNT_TEXT_FLAG_DIM);
+					purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), gnt_color_pair(color_timestamp));
+
+	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), " ", GNT_TEXT_FLAG_NORMAL);
 
 	if (flags & PURPLE_MESSAGE_AUTO_RESP)
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
@@ -764,22 +773,31 @@
 	{
 		char * name = NULL;
 
-		if (purple_message_meify((char*)message, -1))
-			name = g_strdup_printf("*** %s ", who);
-		else
-			name =  g_strdup_printf("%s: ", who);
-
-		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
-				name, GNT_TEXT_FLAG_BOLD);
+		if (purple_message_meify((char*)message, -1)) {
+			name = g_strdup_printf("*** %s", who);
+			gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
+				name, gnt_color_pair(color_message_action));
+		} else {
+			name =  g_strdup_printf("%s", who);
+			if (flags & PURPLE_MESSAGE_SEND)
+				gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
+					name, gnt_color_pair(color_message_send));
+			else
+				if (flags & PURPLE_MESSAGE_NICK) {
+					gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
+						name, gnt_color_pair(color_message_highlight));
+				} else {
+					gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
+						name, gnt_color_pair(color_message_receive));
+				}
+		}
+		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), ": ", GNT_TEXT_FLAG_NORMAL);
 		g_free(name);
-	}
-	else
+	} else
 		fl = GNT_TEXT_FLAG_DIM;
 
 	if (flags & PURPLE_MESSAGE_ERROR)
 		fl |= GNT_TEXT_FLAG_BOLD;
-	if (flags & PURPLE_MESSAGE_NICK)
-		fl |= GNT_TEXT_FLAG_UNDERLINE;
 
 	/* XXX: Remove this workaround when textview can parse messages. */
 	newline = purple_strdup_withhtml(message);
@@ -1126,6 +1144,21 @@
 
 void finch_conversation_init()
 {
+	color_message_send = gnt_style_get_color(NULL, "color-message-sent");
+	if (!color_message_send)
+		color_message_send = gnt_color_add_pair(COLOR_CYAN, -1);
+	color_message_receive = gnt_style_get_color(NULL, "color-message-received");
+	if (!color_message_receive)
+		color_message_receive = gnt_color_add_pair(COLOR_RED, -1);
+	color_message_highlight = gnt_style_get_color(NULL, "color-message-highlight");
+	if (!color_message_highlight)
+		color_message_highlight = gnt_color_add_pair(COLOR_GREEN, -1);
+	color_timestamp = gnt_style_get_color(NULL, "color-timestamp");
+	if (!color_timestamp)
+		color_timestamp = gnt_color_add_pair(COLOR_BLUE, -1);
+	color_message_action = gnt_style_get_color(NULL, "color-message-action");
+	if (!color_message_action)
+		color_message_action = gnt_color_add_pair(COLOR_YELLOW, -1);
 	purple_prefs_add_none(PREF_ROOT);
 	purple_prefs_add_none(PREF_ROOT "/size");
 	purple_prefs_add_int(PREF_ROOT "/size/width", 70);
--- a/finch/libgnt/gntmain.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/libgnt/gntmain.c	Mon Dec 03 16:20:59 2007 +0000
@@ -679,8 +679,9 @@
 	g_free(cp);
 	clean_pid();
 	wm->mode = GNT_KP_MODE_NORMAL;
-	clear();
+	endwin();
 	setup_io();
+	refresh();
 	refresh_screen();
 }
 #endif
--- a/finch/libgnt/gntstyle.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Mon Dec 03 16:20:59 2007 +0000
@@ -59,6 +59,25 @@
 #endif
 }
 
+int
+gnt_style_get_color(char *group, char *key)
+{
+#if GLIB_CHECK_VERSION(2,6,0)
+	int fg = 0, bg = 0;
+	gsize n;
+	char **vals;
+	vals = gnt_style_get_string_list(group, key, &n);
+	if (vals && n == 2) {
+		fg = gnt_colors_get_color(vals[0]);
+		bg = gnt_colors_get_color(vals[1]);
+		return gnt_color_add_pair(fg, bg);
+	}
+	return 0;
+#else
+	return 0;
+#endif
+}
+
 char **gnt_style_get_string_list(const char *group, const char *key, gsize *length)
 {
 #if GLIB_CHECK_VERSION(2,6,0)
--- a/finch/libgnt/gntstyle.h	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Mon Dec 03 16:20:59 2007 +0000
@@ -74,11 +74,24 @@
  *
  * @return        NULL terminated string array. The array should be freed with g_strfreev().
  *
- * @since 2.3.1 (gnt), 2.3.1 (pidgin)
+ * @since 2.3.2
  */
 char **gnt_style_get_string_list(const char *group, const char *key, gsize *length);
 
 /**
+ * Get the value of a color pair in ~/.gntrc.
+ *
+ * @param group   The name of the group in the keyfile. If @c NULL, the prgname
+ *                will be used first, if available. Otherwise, "general" will be used.
+ * @param key     The key
+ *
+ * @return  The value of the color as an int, or 0 on error.
+ *
+ * @since 2.3.2
+ */
+int gnt_style_get_color(char *group, char *key);
+
+/**
  * Parse a boolean preference. For example, if 'value' is "false" (ignoring case)
  * or "0", the return value will be @c FALSE, otherwise @c TRUE.
  *
--- a/finch/libgnt/gntwm.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/libgnt/gntwm.c	Mon Dec 03 16:20:59 2007 +0000
@@ -692,6 +692,7 @@
 		{'\0', NULL}
 	};
 
+	gnt_widget_destroy(GNT_WIDGET(fs));
 
 	if ((file = g_fopen(path, "w+")) == NULL) {
 		return;
@@ -803,7 +804,6 @@
 	}
 	fprintf(file, "</pre>\n</body>");
 	fclose(file);
-	gnt_widget_destroy(GNT_WIDGET(fs));
 }
 
 static void
@@ -817,6 +817,11 @@
 {
 	GntWidget *window = gnt_file_sel_new();
 	GntFileSel *sel = GNT_FILE_SEL(window);
+
+	g_object_set(G_OBJECT(window), "vertical", TRUE, NULL);
+	gnt_box_add_widget(GNT_BOX(window), gnt_label_new("Please enter the filename to save the screenshot."));
+	gnt_box_set_title(GNT_BOX(window), "Save Screenshot...");
+
 	gnt_file_sel_set_suggested_filename(sel, "dump.html");
 	g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(dump_file_save), NULL);
 	g_signal_connect(G_OBJECT(sel->cancel), "activate", G_CALLBACK(dump_file_cancel), sel);
--- a/finch/libgnt/test/tv.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/finch/libgnt/test/tv.c	Mon Dec 03 16:20:59 2007 +0000
@@ -112,8 +112,8 @@
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 2nd line\n", GNT_TEXT_FLAG_NORMAL);
 
-	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
-	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL);
+	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
+	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
 
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 4th line\n", GNT_TEXT_FLAG_NORMAL);
--- a/libpurple/protocols/bonjour/jabber.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Dec 03 16:20:59 2007 +0000
@@ -62,6 +62,12 @@
 #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
 		"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
 
+enum sent_stream_start_types {
+	NOT_SENT       = 0,
+	PARTIALLY_SENT = 1,
+	FULLY_SENT     = 2
+};
+
 static void
 xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
 
@@ -100,6 +106,8 @@
 	bconv->rx_handler = 0;
 	bconv->pb = pb;
 
+	bonjour_parser_setup(bconv);
+
 	return bconv;
 }
 
@@ -289,7 +297,7 @@
 	/* If we're not ready to actually send, append it to the buffer */
 	if (bconv->tx_handler != 0
 			|| bconv->connect_data != NULL
-			|| !bconv->sent_stream_start
+			|| bconv->sent_stream_start != FULLY_SENT
 			|| !bconv->recv_stream_start
 			|| purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
 		ret = -1;
@@ -319,6 +327,7 @@
 	}
 
 	if (ret < len) {
+		/* Don't interfere with the stream starting */
 		if (bconv->tx_handler == 0)
 			bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
 				_send_data_write_cb, pb);
@@ -409,20 +418,6 @@
 	}
 }
 
-void bonjour_jabber_stream_started(PurpleBuddy *pb) {
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
-
-	/* If the stream has been completely started, we can start doing stuff */
-	if (bconv->sent_stream_start && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
-		/* Watch for when we can write the buffered messages */
-		bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
-			_send_data_write_cb, pb);
-		/* We can probably write the data right now. */
-		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
-	}
-
-}
 
 struct _stream_start_data {
 	char *msg;
@@ -478,14 +473,13 @@
 
 	/* Stream started; process the send buffer if there is one */
 	purple_input_remove(bconv->tx_handler);
-	bconv->tx_handler= 0;
-	bconv->sent_stream_start = TRUE;
+	bconv->tx_handler = 0;
+	bconv->sent_stream_start = FULLY_SENT;
 
 	bonjour_jabber_stream_started(pb);
-
 }
 
-static gboolean bonjour_jabber_stream_init(PurpleBuddy *pb, int client_socket)
+static gboolean bonjour_jabber_send_stream_init(PurpleBuddy *pb, int client_socket)
 {
 	int ret, len;
 	char *stream_start;
@@ -495,6 +489,8 @@
 								   purple_buddy_get_name(pb));
 	len = strlen(stream_start);
 
+	bb->conversation->sent_stream_start = PARTIALLY_SENT;
+
 	/* Start the stream */
 	ret = send(client_socket, stream_start, len, 0);
 
@@ -521,18 +517,55 @@
 		bb->conversation->tx_handler = purple_input_add(client_socket,
 			PURPLE_INPUT_WRITE, _start_stream, pb);
 	} else
-		bb->conversation->sent_stream_start = TRUE;
+		bb->conversation->sent_stream_start = FULLY_SENT;
 
 	g_free(stream_start);
 
-	/* setup the parser fresh for each stream */
-	bonjour_parser_setup(bb->conversation);
+	return TRUE;
+}
+
+static gboolean
+_async_bonjour_jabber_close_conversation(gpointer data) {
+	BonjourJabberConversation *bconv = data;
+	bonjour_jabber_close_conversation(bconv);
+	return FALSE;
+}
+
+void bonjour_jabber_stream_started(PurpleBuddy *pb) {
+	BonjourBuddy *bb = pb->proto_data;
+	BonjourJabberConversation *bconv = bb->conversation;
+
+	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(pb, bconv->socket)) {
+		const char *err = g_strerror(errno);
+		PurpleConversation *conv;
+
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
+				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
 
-	bb->conversation->socket = client_socket;
-	bb->conversation->rx_handler = purple_input_add(client_socket,
-		PURPLE_INPUT_READ, _client_socket_handler, pb);
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		if (conv != NULL)
+			purple_conversation_write(conv, NULL,
+				  _("Unable to send the message, the conversation couldn't be started."),
+				  PURPLE_MESSAGE_SYSTEM, time(NULL));
 
-	return TRUE;
+		close(bconv->socket);
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		purple_timeout_add(0, _async_bonjour_jabber_close_conversation, bb->conversation);
+		bb->conversation = NULL;
+		return;
+	}
+
+	/* If the stream has been completely started, we can start doing stuff */
+	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+		/* Watch for when we can write the buffered messages */
+		bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
+			_send_data_write_cb, pb);
+		/* We can probably write the data right now. */
+		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
+	}
+
 }
 
 static void
@@ -576,14 +609,15 @@
 	bb = pb->proto_data;
 
 	/* Check if the conversation has been previously started */
+	/* This really shouldn't ever happen unless something weird is going on */
 	if (bb->conversation == NULL)
 	{
 		bb->conversation = bonjour_jabber_conv_new(pb);
 
-		if (!bonjour_jabber_stream_init(pb, client_socket)) {
-			close(client_socket);
-			return;
-		}
+		/* We wait for the stream start before doing anything else */
+		bb->conversation->socket = client_socket;
+		bb->conversation->rx_handler = purple_input_add(client_socket,
+			PURPLE_INPUT_READ, _client_socket_handler, pb);
 
 	} else {
 		purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n");
@@ -696,7 +730,7 @@
 		return;
 	}
 
-	if (!bonjour_jabber_stream_init(pb, source)) {
+	if (!bonjour_jabber_send_stream_init(pb, source)) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
 
@@ -714,6 +748,11 @@
 		bb->conversation = NULL;
 		return;
 	}
+
+	/* Start listening for the stream acknowledgement */
+	bb->conversation->socket = source;
+	bb->conversation->rx_handler = purple_input_add(source,
+		PURPLE_INPUT_READ, _client_socket_handler, pb);
 }
 
 static PurpleBuddy *
@@ -843,7 +882,7 @@
 		/* Close the socket and remove the watcher */
 		if (bconv->socket >= 0) {
 			/* Send the end of the stream to the other end of the conversation */
-			if (bconv->sent_stream_start)
+			if (bconv->sent_stream_start == FULLY_SENT)
 				send(bconv->socket, STREAM_END, strlen(STREAM_END), 0);
 			/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
 			close(bconv->socket);
--- a/libpurple/protocols/bonjour/jabber.h	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Mon Dec 03 16:20:59 2007 +0000
@@ -47,7 +47,7 @@
 	guint rx_handler;
 	guint tx_handler;
 	PurpleCircBuffer *tx_buf;
-	gboolean sent_stream_start;
+	int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
 	gboolean recv_stream_start;
 	PurpleProxyConnectData *connect_data;
 	gpointer stream_data;
--- a/libpurple/protocols/msnp9/directconn.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/msnp9/directconn.c	Mon Dec 03 16:20:59 2007 +0000
@@ -81,6 +81,7 @@
 create_listener(int port)
 {
 	int fd;
+	int flags;
 	const int on = 1;
 
 #if 0
@@ -156,7 +157,8 @@
 		return -1;
 	}
 
-	fcntl(fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 
 	return fd;
 }
@@ -353,7 +355,7 @@
 }
 
 static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnDirectConn* directconn;
 	int fd;
@@ -405,6 +407,15 @@
 	}
 }
 
+static void
+directconn_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	if (error_message)
+		purple_debug_error("msn", "Error making direct connection: %s\n", error_message);
+
+	connect_cb(data, source, PURPLE_INPUT_READ);
+}
+
 gboolean
 msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
 {
@@ -424,14 +435,9 @@
 #endif
 
 	directconn->connect_data = purple_proxy_connect(NULL, session->account,
-			host, port, connect_cb, directconn);
+			host, port, directconn_connect_cb, directconn);
 
-	if (directconn->connect_data != NULL)
-	{
-		return TRUE;
-	}
-	else
-		return FALSE;
+	return (directconn->connect_data != NULL);
 }
 
 #if 0
--- a/libpurple/protocols/msnp9/servconn.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/msnp9/servconn.c	Mon Dec 03 16:20:59 2007 +0000
@@ -468,6 +468,7 @@
 create_listener(int port)
 {
 	int fd;
+	int flags;
 	const int on = 1;
 
 #if 0
@@ -543,7 +544,8 @@
 		return -1;
 	}
 
-	fcntl(fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 
 	return fd;
 }
--- a/libpurple/protocols/oscar/family_chatnav.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/oscar/family_chatnav.c	Mon Dec 03 16:20:59 2007 +0000
@@ -29,6 +29,49 @@
 
 #include "oscar.h"
 
+static int
+error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
+{
+	int ret = 0;
+	aim_snac_t *snac2;
+	guint16 error, chatnav_error;
+	GSList *tlvlist;
+
+	if (!(snac2 = aim_remsnac(od, snac->id))) {
+		purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08lx)\n", snac->id);
+		return 0;
+	}
+
+	if (snac2->family != 0x000d) {
+		purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family);
+		return 0;
+	}
+
+	/*
+	 * We now know what the original SNAC subtype was.
+	 */
+	if (snac2->type == 0x0008) /* create room */
+	{
+		error = byte_stream_get16(bs);
+		tlvlist = aim_tlvlist_read(bs);
+		chatnav_error = aim_tlv_get16(tlvlist, 0x0008, 1);
+
+		purple_debug_warning("oscar",
+				"Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n",
+				error, chatnav_error);
+		purple_notify_error(od->gc, NULL, _("Could not join chat room"),
+				chatnav_error == 0x0033 ? _("Invalid chat room name") : _("Unknown error"));
+
+		ret = 1;
+	}
+
+	if (snac2)
+		g_free(snac2->data);
+	g_free(snac2);
+
+	return ret;
+}
+
 /*
  * Subtype 0x0002
  *
@@ -451,7 +494,9 @@
 static int
 snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	if (snac->subtype == 0x0009)
+	if (snac->subtype == 0x0001)
+		return error(od, conn, mod, frame, snac, bs);
+	else if (snac->subtype == 0x0009)
 		return parseinfo(od, conn, mod, frame, snac, bs);
 
 	return 0;
--- a/libpurple/protocols/oscar/family_feedbag.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/oscar/family_feedbag.c	Mon Dec 03 16:20:59 2007 +0000
@@ -115,14 +115,10 @@
 	gboolean exists;
 	struct aim_ssi_item *cur, *new;
 
-	new = (struct aim_ssi_item *)g_malloc(sizeof(struct aim_ssi_item));
+	new = g_new(struct aim_ssi_item, 1);
 
 	/* Set the name */
-	if (name) {
-		new->name = (char *)g_malloc((strlen(name)+1)*sizeof(char));
-		strcpy(new->name, name);
-	} else
-		new->name = NULL;
+	new->name = g_strdup(name);
 
 	/* Set the group ID# and buddy ID# */
 	new->gid = gid;
@@ -510,7 +506,7 @@
 		for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) {
 			if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) {
 				n++;
-				new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp));
+				new = g_new(struct aim_ssi_tmp, 1);
 				new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
 				new->ack = 0xffff;
 				new->name = NULL;
@@ -530,7 +526,7 @@
 		for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
 			if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) {
 				n++;
-				new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp));
+				new = g_new(struct aim_ssi_tmp, 1);
 				new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
 				new->ack = 0xffff;
 				new->name = NULL;
@@ -551,7 +547,7 @@
 			cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid);
 			if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
 				n++;
-				new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp));
+				new = g_new(struct aim_ssi_tmp, 1);
 				new->action = SNAC_SUBTYPE_FEEDBAG_MOD;
 				new->ack = 0xffff;
 				new->name = NULL;
@@ -1028,8 +1024,7 @@
 		return -EINVAL;
 
 	g_free(group->name);
-	group->name = (char *)g_malloc((strlen(newgn)+1)*sizeof(char));
-	strcpy(group->name, newgn);
+	group->name = g_strdup(newgn);
 
 	/* Sync our local list with the server list */
 	return aim_ssi_sync(od);
@@ -1461,11 +1456,7 @@
 		if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) {
 			item->type = type;
 			g_free(item->name);
-			if (name) {
-				item->name = (char *)g_malloc((strlen(name)+1)*sizeof(char));
-				strcpy(item->name, name);
-			} else
-				item->name = NULL;
+			item->name = g_strdup(name);
 			aim_tlvlist_free(item->data);
 			item->data = aim_tlvlist_copy(data);
 		}
@@ -1473,11 +1464,7 @@
 		if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) {
 			item->type = type;
 			g_free(item->name);
-			if (name) {
-				item->name = (char *)g_malloc((strlen(name)+1)*sizeof(char));
-				strcpy(item->name, name);
-			} else
-				item->name = NULL;
+			item->name = g_strdup(name);
 			aim_tlvlist_free(item->data);
 			item->data = aim_tlvlist_copy(data);
 		}
@@ -1555,10 +1542,7 @@
 				/* Remove the item from the local list */
 				/* Make sure cur->item is still valid memory */
 				if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
-					if (cur->item->name) {
-						cur->name = (char *)g_malloc((strlen(cur->item->name)+1)*sizeof(char));
-						strcpy(cur->name, cur->item->name);
-					}
+					cur->name = g_strdup(cur->item->name);
 					aim_ssi_itemlist_del(&od->ssi.local, cur->item);
 				}
 				cur->item = NULL;
@@ -1569,11 +1553,7 @@
 					struct aim_ssi_item *cur1;
 					if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
 						g_free(cur->item->name);
-						if (cur1->name) {
-							cur->item->name = (char *)g_malloc((strlen(cur1->name)+1)*sizeof(char));
-							strcpy(cur->item->name, cur1->name);
-						} else
-							cur->item->name = NULL;
+						cur->item->name = g_strdup(cur1->name);
 						aim_tlvlist_free(cur->item->data);
 						cur->item->data = aim_tlvlist_copy(cur1->data);
 					}
@@ -1603,11 +1583,7 @@
 					struct aim_ssi_item *cur1;
 					if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
 						g_free(cur1->name);
-						if (cur->item->name) {
-							cur1->name = (char *)g_malloc((strlen(cur->item->name)+1)*sizeof(char));
-							strcpy(cur1->name, cur->item->name);
-						} else
-							cur1->name = NULL;
+						cur1->name = g_strdup(cur->item->name);
 						aim_tlvlist_free(cur1->data);
 						cur1->data = aim_tlvlist_copy(cur->item->data);
 					}
--- a/libpurple/protocols/oscar/family_icbm.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Mon Dec 03 16:20:59 2007 +0000
@@ -209,7 +209,6 @@
  */
 static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	aim_rxcallback_t userfunc;
 	struct aim_icbmparameters params;
 
 	params.maxchan = byte_stream_get16(bs);
@@ -219,8 +218,11 @@
 	params.maxrecverwarn = byte_stream_get16(bs);
 	params.minmsginterval = byte_stream_get32(bs);
 
-	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		return userfunc(od, conn, frame, &params);
+	params.flags = 0x0000000b;
+	params.maxmsglen = 8000;
+	params.minmsginterval = 0;
+
+	aim_im_setparams(od, &params);
 
 	return 0;
 }
@@ -288,7 +290,7 @@
 		if (!args->msg || (args->msglen <= 0))
 			return -EINVAL;
 
-		if (args->msglen >= MAXMSGLEN)
+		if (args->msglen > MAXMSGLEN)
 			return -E2BIG;
 	}
 
--- a/libpurple/protocols/oscar/oscar.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Dec 03 16:20:59 2007 +0000
@@ -176,7 +176,6 @@
 static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_locerr     (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_icbm_param_info  (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_memrequest       (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_selfinfo         (OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -1228,7 +1227,6 @@
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0);
-	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, 0x0005, purple_icbm_param_info, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
@@ -1833,9 +1831,6 @@
 		signon = info->onlinesince;
 	else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		signon = time(NULL) - info->sessionlen;
-	if (!aim_sncmp(purple_account_get_username(account), info->sn)) {
-		purple_connection_set_display_name(gc, info->sn);
-	}
 	purple_prpl_got_user_login_time(account, info->sn, signon);
 
 	/* Idle time stuff */
@@ -3437,6 +3432,8 @@
 	info = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
+	purple_connection_set_display_name(od->gc, info->sn);
+
 	/*
 	 * What's with the + 0.5?
 	 * The 0.5 is basically poor-man's rounding.  Normally
@@ -3498,32 +3495,6 @@
 	return 1;
 }
 
-static int purple_icbm_param_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
-	struct aim_icbmparameters *params;
-	va_list ap;
-
-	va_start(ap, fr);
-	params = va_arg(ap, struct aim_icbmparameters *);
-	va_end(ap);
-
-	/* XXX - evidently this crashes on solaris. i have no clue why
-	purple_debug_misc("oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, "
-			"max sender evil = %f, max receiver evil = %f, min msg interval = %u\n",
-			params->maxchan, params->flags, params->maxmsglen,
-			((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0,
-			params->minmsginterval);
-	*/
-
-	/* Maybe senderwarn and recverwarn should be user preferences... */
-	params->flags = 0x0000000b;
-	params->maxmsglen = 8000;
-	params->minmsginterval = 0;
-
-	aim_im_setparams(od, params);
-
-	return 1;
-}
-
 static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
 {
 	PurpleConnection *gc = od->gc;
@@ -5364,6 +5335,7 @@
 
 	if (chat_name != NULL)
 		g_hash_table_insert(defaults, "room", g_strdup(chat_name));
+	g_hash_table_insert(defaults, "exchange", g_strdup("4"));
 
 	return defaults;
 }
@@ -5380,26 +5352,29 @@
 	OscarData *od = (OscarData *)gc->proto_data;
 	FlapConnection *conn;
 	char *name, *exchange;
+	int exchange_int;
 
 	name = g_hash_table_lookup(data, "room");
 	exchange = g_hash_table_lookup(data, "exchange");
 
-	if ((name == NULL) || (*name == '\0')) {
-		purple_notify_error(gc, NULL, _("Invalid chat name specified."), NULL);
-		return;
-	}
+	g_return_if_fail(name != NULL && *name != '\0');
+	g_return_if_fail(exchange != NULL);
+
+	errno = 0;
+	exchange_int = strtol(exchange, NULL, 10);
+	g_return_if_fail(errno == 0);
 
 	purple_debug_info("oscar", "Attempting to join chat room %s.\n", name);
 
 	if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV)))
 	{
 		purple_debug_info("oscar", "chatnav exists, creating room\n");
-		aim_chatnav_createroom(od, conn, name, atoi(exchange));
+		aim_chatnav_createroom(od, conn, name, exchange_int);
 	} else {
 		/* this gets tricky */
 		struct create_room *cr = g_new0(struct create_room, 1);
 		purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
-		cr->exchange = atoi(exchange);
+		cr->exchange = exchange_int;
 		cr->name = g_strdup(name);
 		od->create_rooms = g_slist_prepend(od->create_rooms, cr);
 		aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
@@ -6725,7 +6700,7 @@
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
 
 	option = purple_account_option_bool_new(
-		_("Always use ICQ proxy server for file transfers\n(slower, but does not reveal your IP address)"), "always_use_rv_proxy",
+		_("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
 		OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
 
--- a/libpurple/protocols/oscar/oscar.h	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon Dec 03 16:20:59 2007 +0000
@@ -94,11 +94,8 @@
  * for WinAIM clients (up through the latest (4.0.1957)) to
  * send any more than 1kb.  Amaze all your windows friends
  * with utterly oversized instant messages!
- *
- * TODO: the real limit is the total SNAC size at 8192. Fix this.
- *
  */
-#define MAXMSGLEN 7987
+#define MAXMSGLEN 2544
 
 /*
  * Maximum size of a Buddy Icon.
--- a/libpurple/protocols/qq/header_info.h	Mon Dec 03 14:46:54 2007 +0000
+++ b/libpurple/protocols/qq/header_info.h	Mon Dec 03 16:20:59 2007 +0000
@@ -33,7 +33,7 @@
 #define QQ_PACKET_TAG           0x02	/* all QQ text packets starts with it */
 #define QQ_PACKET_TAIL          0x03	/* all QQ text packets end with it */
 
-#define QQ_CLIENT       0x0E1B
+#define QQ_CLIENT       0x0d55
 
 /* list of known QQ commands */
 enum {
--- a/pidgin/gtkconv.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/pidgin/gtkconv.c	Mon Dec 03 16:20:59 2007 +0000
@@ -6476,7 +6476,7 @@
 				markup = title;
 			}
 		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
-			const char *topic = gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text));
+			const char *topic = gtkconv->u.chat->topic_text ? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text)) : NULL;
 			char *esc = NULL;
 #if GTK_CHECK_VERSION(2,6,0)
 			esc = topic ? g_markup_escape_text(topic, -1) : NULL;
@@ -8674,7 +8674,7 @@
                                                  gtk_entry_get_text(entry));
 		}
 		serv_alias_buddy(buddy);
-	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {	        
+	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		gtk_entry_set_text(GTK_ENTRY(gtkconv->u.chat->topic_text), gtk_entry_get_text(entry));
 		topic_callback(NULL, gtkconv);
 	}
@@ -8685,7 +8685,7 @@
 infopane_entry_activate(PidginConversation *gtkconv)
 {
 	GtkWidget *entry = NULL;
-        PurpleConversation *conv = gtkconv->active_conv;
+	PurpleConversation *conv = gtkconv->active_conv;
 	const char *text = NULL;
 
 	if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) {
@@ -8701,9 +8701,21 @@
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
 		PurpleBuddy *buddy = purple_find_buddy(gtkconv->active_conv->account, gtkconv->active_conv->name);
 		if (!buddy)
+			/* This buddy isn't in your buddy list, so we can't alias him */
 			return FALSE;
+
 		text = purple_buddy_get_contact_alias(buddy);
 	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+		PurpleConnection *gc;
+		PurplePluginProtocolInfo *prpl_info = NULL;
+
+		gc = purple_conversation_get_gc(conv);
+		if (gc != NULL)
+			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		if (prpl_info && prpl_info->set_chat_topic == NULL)
+			/* This protocol doesn't support setting the chat room topic */
+			return FALSE;
+
 		text = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
 	}
 
@@ -8722,10 +8734,9 @@
 	g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(alias_cb), gtkconv);
 	g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(alias_focus_cb), gtkconv);
 	g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(alias_key_press_cb), gtkconv);
-	
-	
-
-	gtk_entry_set_text(GTK_ENTRY(entry), text);
+
+	if (text != NULL)
+		gtk_entry_set_text(GTK_ENTRY(entry), text);
 	gtk_widget_show(entry);
 	gtk_widget_hide(gtkconv->infopane);
 	gtk_widget_grab_focus(entry);
--- a/pidgin/gtkmain.c	Mon Dec 03 14:46:54 2007 +0000
+++ b/pidgin/gtkmain.c	Mon Dec 03 16:20:59 2007 +0000
@@ -609,7 +609,7 @@
 #ifndef _WIN32
 				  "c:dhmnl::s:v",
 #else
-				  "c:dhnl::v",
+				  "c:dhmnl::v",
 #endif
 				  long_options, NULL)) != -1) {
 		switch (opt) {
--- a/po/POTFILES.in	Mon Dec 03 14:46:54 2007 +0000
+++ b/po/POTFILES.in	Mon Dec 03 16:20:59 2007 +0000
@@ -118,6 +118,7 @@
 libpurple/protocols/myspace/zap.c
 libpurple/protocols/novell/nmuser.c
 libpurple/protocols/novell/novell.c
+libpurple/protocols/oscar/family_chatnav.c
 libpurple/protocols/oscar/flap_connection.c
 libpurple/protocols/oscar/libaim.c
 libpurple/protocols/oscar/libicq.c