changeset 21094:7deceebc696e

merge of '44d5142a2e45fb899043fbf65242432eceffafdc' and '49d0219884ede2c6c571f2df73e29dffa86f54ad'
author Richard Laager <rlaager@wiktel.com>
date Sun, 14 Oct 2007 02:49:43 +0000
parents 957556ba7b38 (current diff) 5e46cdf9ef2b (diff)
children b9680933c2c6
files
diffstat 17 files changed, 397 insertions(+), 243 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sat Oct 13 17:46:15 2007 +0000
+++ b/COPYRIGHT	Sun Oct 14 02:49:43 2007 +0000
@@ -196,6 +196,7 @@
 Akuke Kok
 Konstantin Korikov
 Cole Kowalski
+Matt Kramer
 Gary Kramlich
 Jan Kratochvil
 Andrej Krivulčík
--- a/ChangeLog	Sat Oct 13 17:46:15 2007 +0000
+++ b/ChangeLog	Sun Oct 14 02:49:43 2007 +0000
@@ -31,6 +31,8 @@
 	* Pidgin's display is now saved with the command line for session
 	  restoration.  (David Mohr)
 	* ICQ Birthday notifications are shown as buddy list emblems.
+	* Plugin actions are now available from the docklet context menu
+	  in addition to the Tool menu of the buddy list.
 
 version 2.2.1 (09/29/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.2.1
--- a/Doxyfile.in	Sat Oct 13 17:46:15 2007 +0000
+++ b/Doxyfile.in	Sun Oct 14 02:49:43 2007 +0000
@@ -457,7 +457,8 @@
 # excluded from the INPUT source files. This way you can easily exclude a 
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
 
-EXCLUDE                = 
+EXCLUDE                = libpurple/purple-client.h \
+                         libpurple/purple-client-bindings.h
 
 # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
 # that are symbolic links (a Unix filesystem feature) are excluded from the input.
@@ -857,7 +858,7 @@
 # feature is still experimental and incomplete at the 
 # moment.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. 
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be 
@@ -1160,7 +1161,7 @@
 # not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
 # If 0 is used for the depth value (the default), the graph is not depth-constrained.
 
-MAX_DOT_GRAPH_DEPTH    = 0
+MAX_DOT_GRAPH_DEPTH    = 2
 
 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
 # background. This is disabled by default, which results in a white background. 
--- a/Makefile.am	Sat Oct 13 17:46:15 2007 +0000
+++ b/Makefile.am	Sun Oct 14 02:49:43 2007 +0000
@@ -48,6 +48,13 @@
 if HAVE_DOXYGEN
 	@echo "Running doxygen..."
 	@doxygen
+if HAVE_XSLTPROC
+	@echo "Generating devhelp index..."
+	@xsltproc doxy2devhelp.xsl doc/xml/index.xml > doc/html/pidgin.devhelp
+	@echo "(Symlink doc/html to ~/.local/share/gtk-doc/html/pidgin to make devhelp see the documentation)"
+else
+	@echo "Not generating devhelp index: xsltproc was not found by configure"
+endif
 else
 	@echo "doxygen was not found during configure.  Aborting."
 	@echo;
--- a/configure.ac	Sat Oct 13 17:46:15 2007 +0000
+++ b/configure.ac	Sun Oct 14 02:49:43 2007 +0000
@@ -2101,6 +2101,10 @@
 	[AC_HELP_STRING([--enable-dot],
 		[enable graphs in doxygen via 'dot'])],
 	enable_dot="$enableval", enable_dot="yes")
+AC_ARG_ENABLE(devhelp,
+	[AC_HELP_STRING([--enable-devhelp],
+		[enable building index for devhelp documentation browser])],
+	enable_devhelp="$enableval", enable_devhelp="yes")
 
 if test "x$enable_doxygen" = xyes; then
 	AC_CHECK_PROG(DOXYGEN, doxygen, true, false)
@@ -2120,14 +2124,28 @@
 				AC_DEFINE_UNQUOTED(HAVE_DOT, 1, [whether or not we have dot])
 			fi
 		fi
+
+		if test "x$enable_devhelp" = "xyes"; then
+			AC_CHECK_PROG(XSLTPROC, xsltproc, true, false)
+
+			if test $XSLTPROC = false; then
+				enable_devhelp="no";
+				AC_MSG_WARN([*** xsltproc not found; devhelp index will not be created])
+			else
+				AC_DEFINE_UNQUOTED(HAVE_XSLTPROC, 1, [whether or not we have xsltproc for devhelp index])
+			fi
+		fi
 	fi
 else
 	enable_dot="no"
+	enable_devhelp="no"
 fi
 
 AC_SUBST(enable_doxygen)
 AC_SUBST(enable_dot)
+AC_SUBST(enable_devhelp)
 AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes")
+AM_CONDITIONAL(HAVE_XSLTPROC, test "x$enable_devhelp" = "xyes")
 
 AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug],
 	[compile with debugging support])], , enable_debug=no)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doxy2devhelp.xsl	Sun Oct 14 02:49:43 2007 +0000
@@ -0,0 +1,98 @@
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:fo="http://www.w3.org/1999/XSL/Format"
+    version="1.0">
+
+<!-- Based on http://bur.st/~eleusis/devhelp/doxy2devhelp.xsl
+             (http://bur.st/~eleusis/devhelp/README)
+     which is based on http://bugzilla.gnome.org/show_bug.cgi?id=122450
+-->
+
+<xsl:output method="xml" version="1.0" indent="yes"/>
+
+<xsl:param name="reference_prefix"></xsl:param>
+
+<xsl:template match="/">
+  <book title="Pidgin Documentation"
+        name="pidgin"
+        link="{$reference_prefix}main.html">
+  <chapters>
+    <sub name="Modules" link="{$reference_prefix}modules.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='group']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    <!-- annotated.html has the short descriptions beside each struct.  is
+         that more useful than being grouped alphabetically?
+      -->
+    <sub name="Structs" link="{$reference_prefix}classes.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='struct']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    <!-- This is redundant given Modules -->
+    <!--
+    <sub name="Directories" link="{$reference_prefix}dirs.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='dir']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    -->
+    <!-- FIXME: Some files show up here but are broken links; mostly
+                files that are under pages...
+      -->
+    <sub name="Files" link="{$reference_prefix}files.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='file']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+    <sub name="Signals, HOWTOs, Other" link="{$reference_prefix}pages.html">
+      <xsl:apply-templates select="doxygenindex/compound[@kind='page']">
+        <xsl:sort select="."/>
+      </xsl:apply-templates>
+    </sub>
+  </chapters>
+
+  <functions>
+    <!-- @todo: maybe select only the real functions, ie those with kind=="function"? -->
+    <xsl:apply-templates select="doxygenindex/compound/member" mode="as-function"/>
+  </functions>
+  </book>
+</xsl:template>
+
+<xsl:template match="compound">
+  <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
+  <xsl:param name="link"><xsl:value-of select="@refid"/>.html</xsl:param>
+  <sub name="{$name}" link="{$reference_prefix}{$link}">
+  <xsl:apply-templates select="member" mode="as-sub">
+    <xsl:sort select="."/>
+  </xsl:apply-templates>
+  </sub>
+</xsl:template>
+
+<xsl:template match="member" mode="as-function">
+  <!--
+  <function name="atk_set_value" link="atk-atkvalue.html#ATK-SET-VALUE"/>
+  -->
+  <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
+  <!-- Link is refid attribute of parent element + "#" + diff between refid of parent and own refid -->
+  <xsl:param name="refid_parent"><xsl:value-of select="parent::node()/@refid"/></xsl:param>
+  <xsl:param name="own_refid"><xsl:value-of select="@refid"/></xsl:param>
+  <xsl:param name="offset"><xsl:value-of select="string-length($refid_parent) + 3"/></xsl:param>
+  <xsl:param name="ref_diff"><xsl:value-of select="substring($own_refid, $offset, 33)"/></xsl:param>
+  <xsl:param name="link"><xsl:value-of select="$refid_parent"/>.html#<xsl:value-of select="$ref_diff"/></xsl:param>
+  <function name="{$name}" link="{$reference_prefix}{$link}"/>
+</xsl:template>
+
+<xsl:template match="member" mode="as-sub">
+  <xsl:param name="name"><xsl:value-of select="name"/></xsl:param>
+  <!-- Link is refid attribute of parent element + "#" + diff between refid of parent and own refid -->
+  <xsl:param name="refid_parent"><xsl:value-of select="parent::node()/@refid"/></xsl:param>
+  <xsl:param name="own_refid"><xsl:value-of select="@refid"/></xsl:param>
+  <xsl:param name="offset"><xsl:value-of select="string-length($refid_parent) + 3"/></xsl:param>
+  <xsl:param name="ref_diff"><xsl:value-of select="substring($own_refid, $offset, 33)"/></xsl:param>
+  <xsl:param name="link"><xsl:value-of select="$refid_parent"/>.html#<xsl:value-of select="$ref_diff"/></xsl:param>
+  <sub name="{$name}" link="{$reference_prefix}{$link}"/>
+</xsl:template>
+
+</xsl:stylesheet>
--- a/finch/gntnotify.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/finch/gntnotify.c	Sun Oct 14 02:49:43 2007 +0000
@@ -194,6 +194,7 @@
 	PurpleAccount *account = purple_connection_get_account(gc);
 	GString *message = g_string_new(NULL);
 	void *ret;
+	static int key = 0;
 
 	if (!detailed)
 	{
@@ -212,7 +213,7 @@
 
 		to = g_strdup_printf("%s (%s)", tos ? *tos : purple_account_get_username(account),
 					purple_account_get_protocol_name(account));
-		gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(time(NULL)),
+		gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(++key),
 				gnt_tree_create_row(GNT_TREE(emaildialog.tree), to,
 					froms ? *froms : "[Unknown sender]",
 					*subjects),
@@ -360,7 +361,8 @@
 	i = 0;
 	for (iter = results->columns; iter; iter = iter->next)
 	{
-		gnt_tree_set_column_title(GNT_TREE(tree), i, iter->data);
+		PurpleNotifySearchColumn *column = iter->data;
+		gnt_tree_set_column_title(GNT_TREE(tree), i, column->title);
 		i++;
 	}
 
--- a/libpurple/plugins/log_reader.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Sun Oct 14 02:49:43 2007 +0000
@@ -28,6 +28,19 @@
 	NAME_GUESS_THEM
 };
 
+/* Some common functions. */
+static int get_month(const char *month)
+{
+	int iter;
+	const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+	for (iter = 0; months[iter]; iter++) {
+		if (strcmp(month, months[iter]) == 0)
+			break;
+	}
+	return iter;
+}
+
 
 /*****************************************************************************
  * Adium Logger                                                              *
@@ -114,7 +127,7 @@
 						continue;
 					}
 
-					rd = fread(contents, 56, 1, handle) == 0;
+					rd = fread(contents, 1, 56, handle) == 0;
 					fclose(handle);
 					contents[rd] = '\0';
 
@@ -176,7 +189,7 @@
 						continue;
 					}
 
-					rd = fread(contents, 13, 1, handle);
+					rd = fread(contents, 1, 13, handle);
 					fclose(handle);
 					contents[rd] = '\0';
 
@@ -1348,36 +1361,7 @@
 						 * daylight savings time.
 						 */
 						tm.tm_isdst = -1;
-
-						/* Ugly hack, in case current locale
-						 * is not English. This code is taken
-						 * from log.c.
-						 */
-						if (strcmp(month, "Jan") == 0) {
-							tm.tm_mon= 0;
-						} else if (strcmp(month, "Feb") == 0) {
-							tm.tm_mon = 1;
-						} else if (strcmp(month, "Mar") == 0) {
-							tm.tm_mon = 2;
-						} else if (strcmp(month, "Apr") == 0) {
-							tm.tm_mon = 3;
-						} else if (strcmp(month, "May") == 0) {
-							tm.tm_mon = 4;
-						} else if (strcmp(month, "Jun") == 0) {
-							tm.tm_mon = 5;
-						} else if (strcmp(month, "Jul") == 0) {
-							tm.tm_mon = 6;
-						} else if (strcmp(month, "Aug") == 0) {
-							tm.tm_mon = 7;
-						} else if (strcmp(month, "Sep") == 0) {
-							tm.tm_mon = 8;
-						} else if (strcmp(month, "Oct") == 0) {
-							tm.tm_mon = 9;
-						} else if (strcmp(month, "Nov") == 0) {
-							tm.tm_mon = 10;
-						} else if (strcmp(month, "Dec") == 0) {
-							tm.tm_mon = 11;
-						}
+						tm.tm_mon = get_month(month);
 
 						data = g_new0(
 							struct trillian_logger_data, 1);
@@ -1439,7 +1423,7 @@
 
 	file = g_fopen(data->path, "rb");
 	fseek(file, data->offset, SEEK_SET);
-	data->length = fread(read, data->length, 1, file);
+	data->length = fread(read, 1, data->length, file);
 	fclose(file);
 
 	if (read[data->length-1] == '\n') {
@@ -1938,7 +1922,7 @@
 	contents = g_malloc(data->length + 2);
 
 	fseek(file, data->offset, SEEK_SET);
-	data->length = fread(contents, data->length, 1, file);
+	data->length = fread(contents, 1, data->length, file);
 	fclose(file);
 
 	contents[data->length] = '\n';
@@ -2111,168 +2095,54 @@
 #define AMSN_LOG_CONV_END "|\"LRED[You have closed the window on "
 #define AMSN_LOG_CONV_EXTRA "01 Aug 2001 00:00:00]"
 
-/* `log_dir`/username@hotmail.com/logs/buddyname@hotmail.com.log */
-/* `log_dir`/username@hotmail.com/logs/Month Year/buddyname@hotmail.com.log */
-static GList *amsn_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+static GList *amsn_logger_parse_file(char *filename, const char *sn, PurpleAccount *account)
 {
 	GList *list = NULL;
-	struct amsn_logger_data *data;
-	const char *logdir;
-	char *username;
-	char *log_path;
-	char *buddy_log;
-	char *filename;
-	GDir *dir;
-	const char *name;
 	GError *error;
 	char *contents;
+	struct amsn_logger_data *data;
 	PurpleLog *log;
-	GList *files = NULL;
-	GList *f;
-
-	logdir = purple_prefs_get_string("/plugins/core/log_reader/amsn/log_directory");
-
-	/* By clearing the log directory path, this logger can be (effectively) disabled. */
-	if (!logdir || !*logdir)
-		return NULL;
-
-	/* aMSN only works with MSN/WLM */
-	if (strcmp(account->protocol_id, "prpl-msn"))
-		return NULL;
-
-	username = g_strdup(purple_normalize(account, account->username));
-	buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn));
-	log_path = g_build_filename(logdir, username, "logs", NULL);
-
-	/* First check in the top-level */
-	filename = g_build_filename(log_path, buddy_log, NULL);
-	if (g_file_test(filename, G_FILE_TEST_EXISTS))
-		files = g_list_prepend(files, filename);
-	else
-		g_free(filename);
-
-	/* Check in previous months */
-	dir = g_dir_open(log_path, 0, NULL);
-	if (dir) {
-		while ((name = g_dir_read_name(dir)) != NULL) {
-			filename = g_build_filename(log_path, name, buddy_log, NULL);
-			if (g_file_test(filename, G_FILE_TEST_EXISTS))
-				files = g_list_prepend(files, filename);
-			else
-				g_free(filename);
-		}
-		g_dir_close(dir);
-	}
-
-	g_free(log_path);
-
-	/* New versions use 'friendlier' directory names */
-	purple_util_chrreplace(username, '@', '_');
-	purple_util_chrreplace(username, '.', '_');
-
-	log_path = g_build_filename(logdir, username, "logs", NULL);
-
-	/* First check in the top-level */
-	filename = g_build_filename(log_path, buddy_log, NULL);
-	if (g_file_test(filename, G_FILE_TEST_EXISTS))
-		files = g_list_prepend(files, filename);
-	else
-		g_free(filename);
-
-	/* Check in previous months */
-	dir = g_dir_open(log_path, 0, NULL);
-	if (dir) {
-		while ((name = g_dir_read_name(dir)) != NULL) {
-			filename = g_build_filename(log_path, name, buddy_log, NULL);
-			if (g_file_test(filename, G_FILE_TEST_EXISTS))
-				files = g_list_prepend(files, filename);
-			else
-				g_free(filename);
-		}
-		g_dir_close(dir);
-	}
-
-	g_free(log_path);
-	g_free(username);
-	g_free(buddy_log);
-
-	/* Loop through files looking for logs */
-	for(f = g_list_first(files); f; f = g_list_next(f)) {
-		filename = f->data;
-		purple_debug_info("aMSN logger", "Reading %s\n", filename);
-		error = NULL;
-		if (!g_file_get_contents(filename, &contents, NULL, &error)) {
-			purple_debug_error("aMSN logger",
-			                   "Couldn't read file %s: %s \n", filename,
-			                   (error && error->message) ?
-			                    error->message : "Unknown error");
-			if (error)
-				g_error_free(error);
-		} else {
-			char *c = contents;
-			gboolean found_start = FALSE;
-			char *start_log = c;
-			int offset = 0;
-			struct tm tm;
-			while (c && *c) {
-				if (purple_str_has_prefix(c, AMSN_LOG_CONV_START)) {
-					char month[4];
-					if (sscanf(c + strlen(AMSN_LOG_CONV_START),
-					           "%u %3s %u %u:%u:%u",
-					           &tm.tm_mday, (char*)&month, &tm.tm_year,
-					           &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
-						found_start = FALSE;
-						purple_debug_error("aMSN logger",
-						                   "Error parsing start date for %s\n",
-						                   filename);
-					} else {
-						const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-								"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
-						tm.tm_year -= 1900;
-
-						/* Let the C library deal with
-						 * daylight savings time.
-						 */
-						tm.tm_isdst = -1;
-
-						/* Ugly hack, in case current locale
-						 * is not English. This code is taken
-						 * from log.c.
-						 */
-						for (tm.tm_mon = 0; months[tm.tm_mon]; tm.tm_mon++) {
-							if (strcmp(month, months[tm.tm_mon]) == 0)
-								break;
-						}
-						found_start = TRUE;
-						offset = c - contents;
-						start_log = c;
-					}
-				} else if (purple_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) {
-					data = g_new0(struct amsn_logger_data, 1);
-					data->path = g_strdup(filename);
-					data->offset = offset;
-					data->length = c - start_log
-					             + strlen(AMSN_LOG_CONV_END)
-					             + strlen(AMSN_LOG_CONV_EXTRA);
-					log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
-					log->logger = amsn_logger;
-					log->logger_data = data;
-					list = g_list_prepend(list, log);
+
+	purple_debug_info("aMSN logger", "Reading %s\n", filename);
+	error = NULL;
+	if (!g_file_get_contents(filename, &contents, NULL, &error)) {
+		purple_debug_error("aMSN logger",
+		                   "Couldn't read file %s: %s \n", filename,
+		                   (error && error->message) ?
+		                    error->message : "Unknown error");
+		if (error)
+			g_error_free(error);
+	} else {
+		char *c = contents;
+		gboolean found_start = FALSE;
+		char *start_log = c;
+		int offset = 0;
+		struct tm tm;
+		while (c && *c) {
+			if (purple_str_has_prefix(c, AMSN_LOG_CONV_START)) {
+				char month[4];
+				if (sscanf(c + strlen(AMSN_LOG_CONV_START),
+				           "%u %3s %u %u:%u:%u",
+				           &tm.tm_mday, (char*)&month, &tm.tm_year,
+				           &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
 					found_start = FALSE;
-
-					purple_debug_info("aMSN logger",
-					                  "Found log for %s:"
-					                  " path = (%s),"
-					                  " offset = (%d),"
-					                  " length = (%d)\n",
-					                  sn, data->path, data->offset, data->length);
+					purple_debug_error("aMSN logger",
+					                   "Error parsing start date for %s\n",
+					                   filename);
+				} else {
+					tm.tm_year -= 1900;
+
+					/* Let the C library deal with
+					 * daylight savings time.
+					 */
+					tm.tm_isdst = -1;
+					tm.tm_mon = get_month(month);
+
+					found_start = TRUE;
+					offset = c - contents;
+					start_log = c;
 				}
-				c = strstr(c, "\n");
-				c++;
-			}
-
-			/* I've seen the file end without the AMSN_LOG_CONV_END bit */
-			if (found_start) {
+			} else if (purple_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) {
 				data = g_new0(struct amsn_logger_data, 1);
 				data->path = g_strdup(filename);
 				data->offset = offset;
@@ -2292,12 +2162,112 @@
 				                  " length = (%d)\n",
 				                  sn, data->path, data->offset, data->length);
 			}
-			g_free(contents);
+			c = strstr(c, "\n");
+			c++;
+		}
+
+		/* I've seen the file end without the AMSN_LOG_CONV_END bit */
+		if (found_start) {
+			data = g_new0(struct amsn_logger_data, 1);
+			data->path = g_strdup(filename);
+			data->offset = offset;
+			data->length = c - start_log
+				             + strlen(AMSN_LOG_CONV_END)
+				             + strlen(AMSN_LOG_CONV_EXTRA);
+			log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
+			log->logger = amsn_logger;
+			log->logger_data = data;
+			list = g_list_prepend(list, log);
+			found_start = FALSE;
+
+			purple_debug_info("aMSN logger",
+			                  "Found log for %s:"
+			                  " path = (%s),"
+			                  " offset = (%d),"
+			                  " length = (%d)\n",
+			                  sn, data->path, data->offset, data->length);
 		}
+		g_free(contents);
+	}
+
+	return list;
+}
+
+/* `log_dir`/username@hotmail.com/logs/buddyname@hotmail.com.log */
+/* `log_dir`/username@hotmail.com/logs/Month Year/buddyname@hotmail.com.log */
+static GList *amsn_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+{
+	GList *list = NULL;
+	const char *logdir;
+	char *username;
+	char *log_path;
+	char *buddy_log;
+	char *filename;
+	GDir *dir;
+	const char *name;
+
+	logdir = purple_prefs_get_string("/plugins/core/log_reader/amsn/log_directory");
+
+	/* By clearing the log directory path, this logger can be (effectively) disabled. */
+	if (!logdir || !*logdir)
+		return NULL;
+
+	/* aMSN only works with MSN/WLM */
+	if (strcmp(account->protocol_id, "prpl-msn"))
+		return NULL;
+
+	username = g_strdup(purple_normalize(account, account->username));
+	buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn));
+	log_path = g_build_filename(logdir, username, "logs", NULL);
+
+	/* First check in the top-level */
+	filename = g_build_filename(log_path, buddy_log, NULL);
+	if (g_file_test(filename, G_FILE_TEST_EXISTS))
+		list = amsn_logger_parse_file(filename, sn, account);
+	else
 		g_free(filename);
+
+	/* Check in previous months */
+	dir = g_dir_open(log_path, 0, NULL);
+	if (dir) {
+		while ((name = g_dir_read_name(dir)) != NULL) {
+			filename = g_build_filename(log_path, name, buddy_log, NULL);
+			if (g_file_test(filename, G_FILE_TEST_EXISTS))
+				list = g_list_concat(list, amsn_logger_parse_file(filename, sn, account));
+			g_free(filename);
+		}
+		g_dir_close(dir);
 	}
 
-	g_list_free(files);
+	g_free(log_path);
+
+	/* New versions use 'friendlier' directory names */
+	purple_util_chrreplace(username, '@', '_');
+	purple_util_chrreplace(username, '.', '_');
+
+	log_path = g_build_filename(logdir, username, "logs", NULL);
+
+	/* First check in the top-level */
+	filename = g_build_filename(log_path, buddy_log, NULL);
+	if (g_file_test(filename, G_FILE_TEST_EXISTS))
+		list = g_list_concat(list, amsn_logger_parse_file(filename, sn, account));
+	g_free(filename);
+
+	/* Check in previous months */
+	dir = g_dir_open(log_path, 0, NULL);
+	if (dir) {
+		while ((name = g_dir_read_name(dir)) != NULL) {
+			filename = g_build_filename(log_path, name, buddy_log, NULL);
+			if (g_file_test(filename, G_FILE_TEST_EXISTS))
+				list = g_list_concat(list, amsn_logger_parse_file(filename, sn, account));
+			g_free(filename);
+		}
+		g_dir_close(dir);
+	}
+
+	g_free(log_path);
+	g_free(username);
+	g_free(buddy_log);
 
 	return list;
 }
@@ -2331,7 +2301,7 @@
 	g_return_val_if_fail(file != NULL, g_strdup(""));
 	
 	fseek(file, data->offset, SEEK_SET);
-	data->length = fread(contents, data->length, 1, file);
+	data->length = fread(contents, 1, data->length, file);
 	fclose(file);
 
 	contents[data->length] = '\n';
--- a/libpurple/protocols/bonjour/buddy.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Sun Oct 14 02:49:43 2007 +0000
@@ -62,9 +62,11 @@
 }
 
 void
-set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, uint32_t len){
+set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, guint32 len){
 	gchar **fld = NULL;
 
+	g_return_if_fail(record_key != NULL);
+
 	if (!strcmp(record_key, "1st"))
 		fld = &buddy->first;
 	else if(!strcmp(record_key, "email"))
--- a/libpurple/protocols/bonjour/buddy.h	Sat Oct 13 17:46:15 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Sun Oct 14 02:49:43 2007 +0000
@@ -83,7 +83,7 @@
 /**
  * Sets a value in the BonjourBuddy struct, destroying the old value
  */
-void set_bonjour_buddy_value(BonjourBuddy *buddy, const char *record_key, const char *value, uint32_t len);
+void set_bonjour_buddy_value(BonjourBuddy *buddy, const char *record_key, const char *value, guint32 len);
 
 /**
  * Check if all the compulsory buddy data is present.
--- a/libpurple/protocols/simple/simple.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Sun Oct 14 02:49:43 2007 +0000
@@ -80,14 +80,15 @@
 static gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc);
 static void send_notify(struct simple_account_data *sip, struct simple_watcher *);
 
-static void send_publish(struct simple_account_data *sip);
+static void send_open_publish(struct simple_account_data *sip);
+static void send_closed_publish(struct simple_account_data *sip);
 
 static void do_notifies(struct simple_account_data *sip) {
 	GSList *tmp = sip->watcher;
 	purple_debug_info("simple", "do_notifies()\n");
 	if((sip->republish != -1) || sip->republish < time(NULL)) {
 		if(purple_account_get_bool(sip->account, "dopublish", TRUE)) {
-			send_publish(sip);
+			send_open_publish(sip);
 		}
 	}
 
@@ -1020,7 +1021,7 @@
 		case 200:
 			if(sip->registerstatus < SIMPLE_REGISTER_COMPLETE) { /* registered */
 				if(purple_account_get_bool(sip->account, "dopublish", TRUE)) {
-					send_publish(sip);
+					send_open_publish(sip);
 				}
 			}
 			sip->registerstatus = SIMPLE_REGISTER_COMPLETE;
@@ -1072,7 +1073,7 @@
 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) {
 	gchar *from;
 	gchar *fromhdr;
-	gchar *tmp2;
+	gchar *basicstatus_data;
 	xmlnode *pidf;
 	xmlnode *basicstatus = NULL, *tuple, *status;
 	gboolean isonline = FALSE;
@@ -1085,8 +1086,9 @@
 
 	if(!pidf) {
 		purple_debug_info("simple", "process_incoming_notify: no parseable pidf\n");
+		purple_prpl_got_user_status(sip->account, from, "offline", NULL);
+		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		g_free(from);
-		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		return;
 	}
 
@@ -1101,27 +1103,28 @@
 		return;
 	}
 
-	tmp2 = xmlnode_get_data(basicstatus);
+	basicstatus_data = xmlnode_get_data(basicstatus);
 
-	if(!tmp2) {
+	if(!basicstatus_data) {
 		purple_debug_info("simple", "process_incoming_notify: no basic data found\n");
 		xmlnode_free(pidf);
 		g_free(from);
 		return;
 	}
 
-	if(strstr(tmp2, "open")) {
+	if(strstr(basicstatus_data, "open"))
 		isonline = TRUE;
-	}
+
 
-	g_free(tmp2);
-
-	if(isonline) purple_prpl_got_user_status(sip->account, from, "available", NULL);
-	else purple_prpl_got_user_status(sip->account, from, "offline", NULL);
+	if(isonline) 
+		purple_prpl_got_user_status(sip->account, from, "available", NULL);
+	else 
+		purple_prpl_got_user_status(sip->account, from, "offline", NULL);
 
 	xmlnode_free(pidf);
+	g_free(from);
+	g_free(basicstatus_data);
 
-	g_free(from);
 	send_sip_response(sip->gc, msg, 200, "OK", NULL);
 }
 
@@ -1188,28 +1191,27 @@
 	return doc;
 }
 
-
-
-static gchar* gen_pidf(struct simple_account_data *sip) {
+static gchar* gen_pidf(struct simple_account_data *sip, gboolean open) {
 	gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
 			"<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n"
 			"xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"\n"
 			"entity=\"sip:%s@%s\">\n"
 			"<tuple id=\"bs35r9f\">\n"
 			"<status>\n"
-			"<basic>open</basic>\n"
+			"<basic>%s</basic>\n"
 			"</status>\n"
 			"<note>%s</note>\n"
 			"</tuple>\n"
 			"</presence>",
 			sip->username,
 			sip->servername,
-			sip->status);
+			(open == TRUE) ? "open" : "closed",
+			(open == TRUE) ? sip->status : "");
 	return doc;
 }
 
 static void send_notify(struct simple_account_data *sip, struct simple_watcher *watcher) {
-	gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
+	gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip, TRUE);
 	gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
 	send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
 	g_free(doc);
@@ -1223,9 +1225,9 @@
 	return TRUE;
 }
 
-static void send_publish(struct simple_account_data *sip) {
+static void send_open_publish(struct simple_account_data *sip) {
 	gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
-	gchar *doc = gen_pidf(sip);
+	gchar *doc = gen_pidf(sip, TRUE);
 	send_sip_request(sip->gc, "PUBLISH", uri, uri,
 		"Expires: 600\r\nEvent: presence\r\n"
 		"Content-Type: application/pidf+xml\r\n",
@@ -1235,6 +1237,18 @@
 	g_free(doc);
 }
 
+static void send_closed_publish(struct simple_account_data *sip) {
+	gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
+	gchar *doc = gen_pidf(sip, FALSE);
+	send_sip_request(sip->gc, "PUBLISH", uri, uri,
+		"Expires: 600\r\nEvent: presence\r\n"
+		"Content-Type: application/pidf+xml\r\n",
+		doc, NULL, process_publish_response);
+	/*sip->republish = time(NULL) + 500;*/
+	g_free(uri);
+	g_free(doc);
+}
+
 static void process_incoming_subscribe(struct simple_account_data *sip, struct sipmsg *msg) {
 	const char *from_hdr = sipmsg_find_header(msg, "From");
 	gchar *from = parse_from(from_hdr);
@@ -1738,7 +1752,14 @@
 	if(sip) {
 		/* unregister */
 		if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE)
+		{
+			if(purple_account_get_bool(sip->account, 
+				"dopublish", 
+				TRUE))
+				send_closed_publish(sip);
+			
 			do_register_exp(sip, 0);
+		}
 		connection_free_all(sip);
 
 		if (sip->query_data != NULL)
--- a/libpurple/prpl.h	Sat Oct 13 17:46:15 2007 +0000
+++ b/libpurple/prpl.h	Sun Oct 14 02:49:43 2007 +0000
@@ -226,11 +226,17 @@
 	void (*tooltip_text)(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full);
 
 	/**
-	 * This must be implemented, and must add at least the offline
-	 * and online states.
+	 * Returns a list of #PurpleStatusType which exist for this account;
+	 * this must be implemented, and must add at least the offline and
+	 * online states.
 	 */
 	GList *(*status_types)(PurpleAccount *account);
 
+	/**
+	 * Returns a list of #PurpleMenuAction structs, which represent extra
+	 * actions to be shown in (for example) the right-click menu for @a
+	 * node.
+	 */
 	GList *(*blist_node_menu)(PurpleBlistNode *node);
 	GList *(*chat_info)(PurpleConnection *);
 	GHashTable *(*chat_info_defaults)(PurpleConnection *, const char *chat_name);
@@ -258,6 +264,10 @@
 
 	void (*set_info)(PurpleConnection *, const char *info);
 	unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleTypingState state);
+	/**
+	 * Should arrange for purple_notify_userinfo() to be called with
+	 * @a who's user info.
+	 */
 	void (*get_info)(PurpleConnection *, const char *who);
 	void (*set_status)(PurpleAccount *account, PurpleStatus *status);
 
@@ -287,8 +297,13 @@
 	/** new user registration */
 	void (*register_user)(PurpleAccount *);
 
-	/* get "chat buddy" info and away message */
+	/** Deprecated and vestigal; use #get_cb_real_name and #get_info
+	 *  instead.
+	 */
 	void (*get_cb_info)(PurpleConnection *, int, const char *who);
+	/** Also deprecated and vestigal; use #get_cb_real_name and
+	 *  #status_text instead.
+	 */
 	void (*get_cb_away)(PurpleConnection *, int, const char *who);
 
 	/** save/store buddy's alias on server list/roster */
@@ -348,9 +363,12 @@
 	/* room list serialize */
 	char *(*roomlist_room_serialize)(PurpleRoomlistRoom *room);
 
-	/* Remove the user from the server. (This is only at the bottom to keep binary compatibility.)
-	 * The account can either be connected or disconnected. After the removal is finished,
-	 * the connection will stay open and has to be closed!
+	/** Remove the user from the server.  The account can either be
+	 * connected or disconnected. After the removal is finished, the
+	 * connection will stay open and has to be closed!
+	 */
+	/* This is here rather than next to register_user for API compatibility
+	 * reasons.
 	 */
 	void (*unregister_user)(PurpleAccount *, PurpleAccountUnregistrationCb cb, void *user_data);
 	
--- a/libpurple/util.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/libpurple/util.c	Sun Oct 14 02:49:43 2007 +0000
@@ -2565,6 +2565,8 @@
 	purple_debug_info("util", "Writing file %s\n",
 					filename_full);
 
+	g_return_val_if_fail((size >= -1), FALSE);
+
 	filename_temp = g_strdup_printf("%s.save", filename_full);
 
 	/* Remove an old temporary file, if one exists */
@@ -2590,7 +2592,7 @@
 	}
 
 	/* Write to file */
-	real_size = (size == -1) ? strlen(data) : size;
+	real_size = (size == -1) ? strlen(data) : (size_t) size;
 	byteswritten = fwrite(data, 1, real_size, file);
 
 	/* Close file */
--- a/pidgin/gtkconv.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/pidgin/gtkconv.c	Sun Oct 14 02:49:43 2007 +0000
@@ -6519,6 +6519,7 @@
 		AtkObject *accessibility_obj;
 		/* I think this is a little longer than it needs to be but I'm lazy. */
 		char *style;
+		gboolean bold = FALSE;
 
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 			im = PURPLE_CONV_IM(conv);
@@ -6552,7 +6553,7 @@
 		gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
 				CONV_TEXT_COLUMN, markup, -1);
 	        /* XXX seanegan Why do I have to do this? */
-        	gtk_widget_queue_draw(gtkconv->infopane);
+		gtk_widget_queue_draw(gtkconv->infopane);
 	
 		if (title != markup)
 			g_free(markup);
@@ -6571,31 +6572,41 @@
 			style = "color=\"#c4a000\"";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK)	{
 			atk_object_set_description(accessibility_obj, _("Nick Said"));
-			style = "color=\"#204a87\" weight=\"bold\"";
+			style = "color=\"#cc0000\"";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT)	{
 			atk_object_set_description(accessibility_obj, _("Unread Messages"));
-			style = "color=\"#cc0000\" weight=\"bold\"";
+			if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_CHAT)
+				style = "color=\"#204a87\" weight=\"bold\"";
+			else
+				style = "color=\"#cc0000\" weight=\"bold\"";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) {
 			atk_object_set_description(accessibility_obj, _("New Event"));
-			style = "color=\"#888a85\" weight=\"bold\"";
+			style = "color=\"#888a85\"";
 		} else {
-			style = "";
+			style = NULL;
 		}
+
+		if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT ||
+				gtkconv->unseen_state == PIDGIN_UNSEEN_NICK ||
+				gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT)
+			bold = TRUE;
 		
-		if (*style != '\0')
+		if (style || bold)
 		{
 			char *html_title,*label;
 
 			html_title = g_markup_escape_text(title, -1);
-			label = g_strdup_printf("<span %s>%s</span>",
-			                        style, html_title);
+			label = g_strdup_printf("<span %s %s>%s</span>",
+			                        style ? style : "",
+			                        bold ? "weight=\"bold\"" : "",
+			                        html_title);
 			g_free(html_title);
 			gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label);
 			g_free(label);
 		}
 		else
 			gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
-		
+
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
 
--- a/pidgin/gtknotify.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/pidgin/gtknotify.c	Sun Oct 14 02:49:43 2007 +0000
@@ -740,7 +740,7 @@
 	GtkListStore *model;
 	GtkCellRenderer *renderer;
 	guint col_num;
-	GList *column;
+	GList *columniter;
 	guint i;
 
 	GtkWidget *vbox;
@@ -824,11 +824,12 @@
 					-1, "", renderer, "pixbuf", 0, NULL);
 
 	i = 1;
-	for (column = results->columns; column != NULL; column = column->next) {
+	for (columniter = results->columns; columniter != NULL; columniter = columniter->next) {
+		PurpleNotifySearchColumn *column = columniter->data;
 		renderer = gtk_cell_renderer_text_new();
 
 		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1,
-				column->data, renderer, "text", i, NULL);
+				column->title, renderer, "text", i, NULL);
 		i++;
 	}
 
--- a/pidgin/gtksound.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/pidgin/gtksound.c	Sun Oct 14 02:49:43 2007 +0000
@@ -118,12 +118,9 @@
 	if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 	{
 		PidginConversation *gtkconv;
-		PidginWindow *win;
 		gboolean has_focus;
 
 		gtkconv = PIDGIN_CONVERSATION(conv);
-		win = gtkconv->win;
-
 		has_focus = purple_conversation_has_focus(conv);
 
 		if (!gtkconv->make_sound ||
--- a/pidgin/gtkutils.c	Sat Oct 13 17:46:15 2007 +0000
+++ b/pidgin/gtkutils.c	Sun Oct 14 02:49:43 2007 +0000
@@ -1526,6 +1526,8 @@
 
 			if (prpl_info && prpl_info->can_receive_file)
 				ft = prpl_info->can_receive_file(gc, who);
+			else if (prpl_info && prpl_info->send_file)
+				ft = TRUE;
 
 			if (im && ft)
 				purple_request_choice(NULL, NULL,
@@ -1559,6 +1561,7 @@
 						    _("Set as buddy icon"), DND_BUDDY_ICON,
 						    (ft ? _("Send image file") : _("Insert in message")), (ft ? DND_FILE_TRANSFER : DND_IM_IMAGE),
 							NULL);
+			gdk_pixbuf_unref(pb);
 			return;
 		}