changeset 27732:73ef026191e7

propagate from branch 'im.pidgin.pidgin' (head 8d61a119c53ac77e595d5ec300d30482b914bdf7) to branch 'im.pidgin.pidgin.yaz' (head 456e82d794038f69ba0113eecb77dc8ce0f3d605)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Tue, 16 Oct 2007 07:48:36 +0000
parents 271d154bbb91 (current diff) 17ee8c81c99b (diff)
children bc47a0388c66
files configure.ac libpurple/notify.c libpurple/protocols/jabber/jabber.c libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn.c libpurple/protocols/msn/switchboard.c libpurple/protocols/oscar/oscar.c libpurple/util.c libpurple/util.h pidgin/gtkconv.c pidgin/gtkutils.c pidgin/gtkutils.h
diffstat 126 files changed, 1890 insertions(+), 551 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun Oct 07 03:21:27 2007 +0000
+++ b/COPYRIGHT	Tue Oct 16 07:48:36 2007 +0000
@@ -196,6 +196,7 @@
 Akuke Kok
 Konstantin Korikov
 Cole Kowalski
+Matt Kramer
 Gary Kramlich
 Jan Kratochvil
 Andrej Krivulčík
--- a/ChangeLog	Sun Oct 07 03:21:27 2007 +0000
+++ b/ChangeLog	Tue Oct 16 07:48:36 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/ChangeLog.API	Sun Oct 07 03:21:27 2007 +0000
+++ b/ChangeLog.API	Tue Oct 16 07:48:36 2007 +0000
@@ -1,6 +1,17 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-Version 2.2.0 (09/13/2007):
+version 2.2.2 (??/??/????):
+	libpurple:
+		Changed:
+		* The size parameter of purple_util_write_data_to_file_absolute
+		  has been changed to gssize instead of a size_t to correctly
+		  indicate that -1 can be used for a nul-delimited string.
+		* The documentation for purple_savedstatuses_get_popular used to
+		  incorrectly claim that the active status is excluded from the
+		  returned list. The documentation has been corrected. Also, the
+		  function now returns a correct list when called with a value of 0.
+
+version 2.2.0 (09/13/2007):
 	libpurple:
 		Added:
 		* PURPLE_MESSAGE_INVISIBLE flag, which can be used by
@@ -57,7 +68,7 @@
 		* gnt_util_parse_xhtml_to_textview to parse XHTML strings in a
 		  GntTextView (this works only if libxml2 is available)
 
-Version 2.1.1 (08/20/2007):
+version 2.1.1 (08/20/2007):
 	libpurple:
 		Changed:
 		* PurpleAccountUiOps.request_authorize's authorize_cb and
@@ -89,9 +100,6 @@
 		* purple_timeout_add_seconds
 		    Callers should prefer this to purple_timeout_add for timers
 		    longer than 1 second away.  Be aware of the rounding, though.
-		* purple_timeout_add_seconds
-		    Callers should prefer this to purple_timeout_add for timers
-		    longer than 1 second away.  Be aware of the rounding, though.
 		* purple_xfer_get_remote_user
 		* purple_pounces_get_all_for_ui
 		* purple_prefs_get_children_names
--- a/Doxyfile.in	Sun Oct 07 03:21:27 2007 +0000
+++ b/Doxyfile.in	Tue Oct 16 07:48:36 2007 +0000
@@ -169,7 +169,8 @@
                          "endsignalproto=@endcode" \
                          "signaldesc=@par Description:" \
                          "signals=@b Signals:" \
-                         "endsignals="
+                         "endsignals=" \
+                         "constreturn=@note The return value of this function must not be modified or freed. @return"
 
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
 # only. Doxygen will then generate output that is more tailored for C. 
@@ -457,7 +458,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 +859,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 +1162,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	Sun Oct 07 03:21:27 2007 +0000
+++ b/Makefile.am	Tue Oct 16 07:48:36 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	Sun Oct 07 03:21:27 2007 +0000
+++ b/configure.ac	Tue Oct 16 07:48:36 2007 +0000
@@ -142,13 +142,21 @@
 dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
 dnl AM_GLIB_GNU_GETTEXT found it.
 
-if test x$MSGFMT = xno
+if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT = x
 then
 	AC_ERROR([
 
 The msgfmt command is required to build libpurple.  If it is installed
 on your system, ensure that it is in your path.  If it is not, install
 GNU gettext to continue.
+
+If you have msgfmt installed, but for some reason this error message
+is still displayed, you have encountered what appears to be a bug in
+third-party configure macros.  Try setting the MSGFMT environment
+variable to the absolute path to your msgfmt binary and trying
+configure again, like this:
+
+MSGFMT=/path/to/msgfmt ./configure ...
 ])
 fi
 
@@ -2093,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)
@@ -2112,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)
--- a/doc/C-HOWTO.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/C-HOWTO.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -269,3 +269,4 @@
   you may have guessed, this also gets read when libpurple is probing your
   plugin.  If this is missing, the plugin will not load.
  */
+// vim: syntax=c.doxygen
--- a/doc/account-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/account-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -11,6 +11,8 @@
   @signal account-status-changed
  @endsignals
 
+ @see account.h
+
  <hr>
 
  @signaldef account-added
@@ -101,4 +103,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/blist-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/blist-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -13,6 +13,8 @@
   @signal blist-node-aliased
  @endsignals
 
+ @see blist.h
+
  <hr>
 
  @signaldef buddy-status-changed
@@ -105,4 +107,4 @@
   @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/certificate-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/certificate-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -5,6 +5,8 @@
   @signal certificate-deleted
  @endsignals
 
+ @see certificate.h
+
  <hr>
 
  @signaldef certificate-stored
@@ -28,4 +30,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/cipher-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/cipher-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -5,6 +5,8 @@
   @signal cipher-removed
  @endsignals
 
+ @see cipher.h
+
  <hr>
 
  @signaldef cipher-added
@@ -26,4 +28,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/connection-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/connection-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -7,6 +7,8 @@
   @signal signed-off
  @endsignals
 
+ @see connection.h
+
  <hr>
 
  @signaldef signing-on
@@ -46,4 +48,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/conversation-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/conversation-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -32,6 +32,8 @@
   @signal conversation-extended-menu
  @endsignals
 
+ @see conversation.h
+
  @signaldef writing-im-msg
   @signalproto
 gboolean (*writing_im_msg)(PurpleAccount *account, const char *who,
@@ -427,6 +429,7 @@
    conversation.
   @param conv   The conversation.
   @param list   A pointer to the list of actions.
+  @since 2.1.0
  @endsignaldef
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/core-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/core-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal quitting
  @endsignals
 
+ @see core.h
+
  <hr>
 
  @signaldef quitting
@@ -15,4 +17,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/dbus-server-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/dbus-server-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -5,6 +5,8 @@
   @signal dbus-introspect
  @endsignals
 
+ @see dbus-server.h
+
  <hr>
 
  @signaldef dbus-method-called
@@ -29,4 +31,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkaccount-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/gtkaccount-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal account-modified
  @endsignals
 
+ @see gtkaccount.h
+
  <hr>
 
  @signaldef account-modified
@@ -15,4 +17,4 @@
   @param account The account that has been modified.
  @endsignaldef
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkblist-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/gtkblist-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -7,6 +7,8 @@
   @signal drawing-tooltip
  @endsignals
 
+ @see gtkblist.h
+
  <hr>
 
  @signaldef gtkblist-hiding
@@ -52,4 +54,4 @@
               a compact tooltip for a non-priority buddy.
  @endsignaldef
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkconv-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/gtkconv-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -127,6 +127,7 @@
   @signaldesc
    Emitted immediately before an existing conversation is hidden.
   @param gtkconv  The PidginConversation
+  @since 2.2.0
  @endsignaldef
 
  @signaldef conversation-displayed
@@ -136,7 +137,8 @@
   @signaldesc
    Emitted right after the Pidgin UI is attached to a new or a hidden conversation.
   @param gtkconv  The PidginConversation
+  @since 2.2.0
  @endsignaldef
 
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkimhtml-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/gtkimhtml-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -8,6 +8,8 @@
   @signal format_function_update
  @endsignals
 
+ @see gtkimhtml.h
+
  <hr>
 
  @signaldef url_clicked
@@ -57,4 +59,4 @@
   @param data   User defined data.
  @endsignaldef
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/gtklog-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/gtklog-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal log-displaying
  @endsignals
 
+ @see gtklog.h
+
  <hr>
 
  @signaldef log-displaying
@@ -17,4 +19,4 @@
  @endsignaldef
 
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/imgstore-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/imgstore-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal image-deleting
  @endsignals
 
+ @see imgstore.h
+
  <hr>
 
  @signaldef image-deleting
@@ -11,7 +13,7 @@
 char *(*image_deleting)(const PurpleStoredImage *img);
   @endsignalproto
   @signaldesc
-   Emitted when a PurpleStoredImage is about to be destroyed.  This allows
+   Emitted when a #PurpleStoredImage is about to be destroyed.  This allows
    for what amounts to weak references.  Code can hold onto a pointer to
    the PurpleStoredImage without actually "holding" a reference.  They can
    then use a signal handler to let them know when their img is about to
@@ -21,4 +23,4 @@
  @endsignaldef
 
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/log-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/log-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal log-timestamp
  @endsignals
 
+ @see log.h
+
  <hr>
 
  @signaldef log-timestamp
@@ -21,4 +23,4 @@
  @endsignaldef
 
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/notify-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/notify-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -6,6 +6,8 @@
   @signal displaying-emails-notification
  @endsignals
 
+ @see notify.h
+
  @signaldef displaying-userinfo
   @signalproto
 void (*displaying_userinfo)(PurpleAccount *account, const char *who, PurpleNotifyUserInfo *user_info);
@@ -33,6 +35,7 @@
   @param from      Who the email is from.
   @param to        Who the email is to.
   @param url       A url to view the email.
+  @since 2.1.0
  @endsignaldef
 
  @signaldef displaying-emails-notification
@@ -50,7 +53,8 @@
   @param tos        Who the emails are to.
   @param urls       The urls to view the emails.
   @param count      Number of emails being notified of.
+  @since 2.1.0
  @endsignaldef
 
 */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/plugin-ids.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/plugin-ids.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -2,10 +2,10 @@
 
  @section Introduction
   Every plugin contains a unique identifier to prevent duplicate plugin
-  loading and conflicts. This, which will be called a plugin ID from here
-  on, must follow a specific format. This format categorizes a plugin and
-  makes duplicate IDs unlikely.
-
+  loading and conflicts. Third-party plugins (that is, plugins written by
+  anyone who is not a libpurple, Pidgin, or Finch developer) are expected
+  to use a plugin ID that follows a specific format. This format
+  categorizes plugins and makes duplicate IDs highly unlikely.
 
  @section Format
   The basic format of a plugin ID is as follows:
@@ -15,27 +15,79 @@
   The @em type indicator specifies the type of plugin. This must be one
   of the following:
 
-    - core      - Core plugin, capable of being loaded in any program using
-                  libpurple. It must not use any UI-specific code.
-    - prpl      - Protocol plugin, providing additional protocols to
-                  connect to.
-    - lopl      - Loader plugin, which loads scripts as plugins (like Perl
-                  or TCL).
-    - gtk       - GTK+ 2.x plugin. It may use GTK+ code, but cannot use any
-                  window toolkit code (such as X11 or Win32).
-    - gtk-x11   - GTK+ 2.x plugin using X11 code.
-    - gtk-win32 - GTK+ 2.x plugin using Win32 code.
-    - qpe       - Gaim for Qtopia plugin.
+    - core      - A core libpurple plugin, capable of being loaded in any
+                  program using libpurple. Core plugins may not contain any
+                  UI-specific code.
+    - prpl      - A protocol plugin. This is a special type of core plugin,
+                  which provides libpurple the ability to connect to
+                  another IM or chat network.
+    - lopl      - A loader plugin, which loads scripts as plugins. Perl and
+                  Tcl plugins are made possible by loader plugins.
+    - gtk       - A GTK+ 2.x (a.k.a. Pidgin) plugin. These plugins may use
+                  GTK+ code, but may not use window toolkit code, such as
+                  X11, Win32, Cocoa, or Carbon.
+    - gtk-x11   - A GTK+ 2.x plugin that uses X11 code. These plugins may
+                  use both GTK+ code and X11 code, allowing to hook into
+                  features specific to X11.
+    - gtk-win32 - A GTK+ plugin that uses Win32 code. These plugins may use
+                  both GTK+ code and Win32 code, allowing to hook into
+                  features available on Windows.
+    - gnt       - A GNT (a.k.a. Finch) plugin. These plugins may use GNT code.
+    - qpe       - A plugin for the (now-abandoned) Qutopia user interface.
+
+  The @em username must be a unique identifier for you. It
+  @em should be your http://developer.pidgin.im Trac user ID. Failing that, you
+  could use your SourceForge user ID or your Freenode IRC nickname, if you
+  have either. The http://developer.pidgin.im Trac user ID is preferred.
+  Do @em not leave this field blank!
+
+  The @em pluginname is the name of your plugin. It is usually all
+  lowercase letters and matches the static plugin ID (the first argument to
+  the PURPLE_INIT_PLUGIN() macro call), although it can be anything you
+  like. Do @em not include version information in the plugin ID--the
+  #PurplePluginInfo structure already has a field for this.
+
+ @section nospaces One Last Rule for Plugin IDs
+
+  The last rule of plugin IDs is the most important of all. Plugin IDs may
+  @em NOT contain spaces. If you need a space, use another hyphen (-).
 
-  The @em username must be a unique identifier for that person. It
-  @em should be your SourceForge ID. Do @em not leave this field
-  blank.
+ @section exceptions Exceptions to the Rule
+
+  As with any rule there are exceptions. If you browse through the source
+  tree you will see that the plugins we distribute with the Pidgin source
+  do not contain a username field. This is because while one developer may
+  have written each specific plugin, the plugins are maintained
+  collectively by the entire development team. This lack of a username
+  field is also an indicator that the plugin is one of our plugins and not
+  a third-party plugin.
+
+  Another exception to the rule is the <a
+  href="http://plugins.guifications.org/trac/wiki/PluginPack">Purple Plugin
+  Pack</a>. All plugins whose lives started in the Purple Plugin Pack use
+  <tt>"plugin_pack"</tt> for the username field to indicate origination in
+  the Purple Plugin Pack.
 
-  The @em pluginname is the name of your plugin. It can be whatever you like,
-  though it's common to keep it all lowercase. Do not use spaces! If you
-  want a space, use a '-'. Please do not put a version indicator in the ID.
-  The PurplePlugin structure already has a field for this.
+  These two exceptions are mentioned here for completeness. We don't
+  encourage breaking the conventions set forth by the rules outlined above.
+
+ @section examples Examples of Well-Chosen Plugin IDs
+
+  The following is a list of well-chosen Plugin IDs listing a few good examples.
 
+    - <tt>"gtk-amc_grim-guifications"</tt> - This is the plugin ID for the
+                                             Guifications 2.x plugin.
+    - <tt>"gtk-rlaager-album"</tt> - This is the plugin ID for the Album
+                                     plugin, which is now part of the
+                                     Purple Plugin Pack. Its ID follows the
+                                     rules because its life started prior
+                                     to its inclusion in the Plugin Pack.
+    - <tt>"core-rlaager-irchelper"</tt> - This is the plugin ID for the IRC
+                                          Helper plugin, which is now part
+                                          of the Purple Plugin Pack. Its ID
+                                          follows the rules because its
+                                          life started prior to its
+                                          inclusion in the Plugin Pack.
 
  @section plugin-db Plugin Database
   Although it doesn't exist yet, in time there will be a plugin database
@@ -45,4 +97,4 @@
 
  */
 
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/plugin-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/plugin-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -5,6 +5,8 @@
   @signal plugin-unload
  @endsignals
 
+ @see plugin.h
+
  <hr>
 
  @signaldef plugin-load
@@ -26,4 +28,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/savedstatus-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/savedstatus-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal savedstatus-changed
  @endsignals
 
+ @see savedstatus.h
+
  <hr>
 
  @signaldef savedstatus-changed
@@ -15,4 +17,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/sound-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/sound-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -4,6 +4,8 @@
   @signal playing-sound-event
  @endsignals
 
+ @see sound.h
+
  <hr>
 
  @signaldef playing-sound-event
@@ -18,4 +20,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/xfer-signals.dox	Sun Oct 07 03:21:27 2007 +0000
+++ b/doc/xfer-signals.dox	Tue Oct 16 07:48:36 2007 +0000
@@ -12,6 +12,8 @@
   @signal file-send-complete
  @endsignals
 
+ @see ft.h
+
  <hr>
 
  @signaldef file-recv-accept
@@ -109,4 +111,4 @@
  @endsignaldef
 
  */
-// vim: syntax=c tw=75 et
+// vim: syntax=c.doxygen tw=75 et
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doxy2devhelp.xsl	Tue Oct 16 07:48:36 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/gntaccount.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/gntaccount.h	Tue Oct 16 07:48:36 2007 +0000
@@ -59,6 +59,8 @@
  * Show the edit dialog for an account.
  *
  * @param account  The account to edit, or @c NULL to create a new account.
+ *
+ * @since 2.2.0
  */
 void finch_account_dialog_show(PurpleAccount *account);
 
--- a/finch/gntblist.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/gntblist.h	Tue Oct 16 07:48:36 2007 +0000
@@ -98,6 +98,8 @@
  * @param name   The user to get information about.
  *
  * @return  Returns the ui-handle for the userinfo notification.
+ *
+ * @since 2.1.0
  */
 gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name);
 
--- a/finch/gntnotify.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/gntnotify.c	Tue Oct 16 07:48:36 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/finch/gntsound.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/gntsound.h	Tue Oct 16 07:48:36 2007 +0000
@@ -37,6 +37,8 @@
  * Get the name of the active sound profile.
  *
  * @return The name of the profile
+ *
+ * @since 2.1.0
  */
 const char *finch_sound_get_active_profile(void);
 
@@ -44,6 +46,8 @@
  * Set the active profile.  If the profile doesn't exist, nothing is changed.
  * 
  * @param name  The name of the profile
+ *
+ * @since 2.1.0
  */
 void finch_sound_set_active_profile(const char *name);
 
@@ -52,6 +56,8 @@
  *
  * @return A list of strings denoting sound profile names.
  *         Caller must free the list (but not the data).
+ *
+ * @since 2.1.0
  */
 GList *finch_sound_get_profiles(void);
 
@@ -60,6 +66,8 @@
  *
  * @return Returns FALSE if preference is set to 'No sound', or if volume is
  *         set to zero.
+ *
+ * @since 2.2.0
  */
 gboolean finch_sound_is_enabled(void);
 
@@ -67,11 +75,15 @@
  * Gets GNT sound UI ops.
  *
  * @return The UI operations structure.
+ *
+ * @since 2.1.0
  */
 PurpleSoundUiOps *finch_sound_get_ui_ops(void);
 
 /**
  * Show the sound settings dialog.
+ *
+ * @since 2.1.0
  */
 void finch_sounds_show_all(void);
 
--- a/finch/libgnt/gnt.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gnt.h	Tue Oct 16 07:48:36 2007 +0000
@@ -62,6 +62,14 @@
  */
 gboolean gnt_ascii_only(void);
 
+/**
+ * Present a window. If the event was triggered because of user interaction,
+ * the window is moved to the foreground. Otherwise, the Urgent hint is set.
+ *
+ * @param window   The window the present.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_window_present(GntWidget *window);
 /**
  * 
--- a/finch/libgnt/gntfilesel.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gntfilesel.c	Tue Oct 16 07:48:36 2007 +0000
@@ -265,6 +265,7 @@
 		}
 		g_free(fp);
 	}
+	g_dir_close(dir);
 
 	*files = g_list_reverse(*files);
 	return TRUE;
--- a/finch/libgnt/gntslider.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gntslider.h	Tue Oct 16 07:48:36 2007 +0000
@@ -75,6 +75,8 @@
 
 /**
  * @return The GType for GntSlider
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 GType gnt_slider_get_gtype(void);
 
@@ -89,6 +91,8 @@
  * @param min    The minimum value for the slider
  *
  * @return  The newly created slider
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 GntWidget * gnt_slider_new(gboolean orient, int max, int min);
 
@@ -98,6 +102,8 @@
  * @param slider  The slider
  * @param max     The maximum value
  * @param min     The minimum value
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_slider_set_range(GntSlider *slider, int max, int min);
 
@@ -106,6 +112,8 @@
  * 
  * @param slider  The slider
  * @param step    The amount for each step
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_slider_set_step(GntSlider *slider, int step);
 
@@ -114,6 +122,8 @@
  * 
  * @param slider  The slider
  * @param step    The amount for a small step (for the slider)
+ *
+ * @since 2.2.0
  */
 void gnt_slider_set_small_step(GntSlider *slider, int step);
 
@@ -122,6 +132,8 @@
  * 
  * @param slider  The slider
  * @param step    The amount for a large step (for the slider)
+ *
+ * @since 2.2.0
  */
 void gnt_slider_set_large_step(GntSlider *slider, int step);
 
@@ -133,6 +145,8 @@
  *                 forward, negative to change backward
  *
  * @return   The value of the slider after the change
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 int gnt_slider_advance_step(GntSlider *slider, int steps);
 
@@ -141,6 +155,8 @@
  *
  * @param slider  The slider
  * @param value   The current value
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_slider_set_value(GntSlider *slider, int value);
 
@@ -149,6 +165,8 @@
  *
  * @param slider The slider
  *
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 int gnt_slider_get_value(GntSlider *slider);
 
@@ -157,6 +175,8 @@
  *
  * @param slider   The slider
  * @param label    The label to update
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_slider_reflect_label(GntSlider *slider, GntLabel *label);
 
--- a/finch/libgnt/gntstyle.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Tue Oct 16 07:48:36 2007 +0000
@@ -53,6 +53,8 @@
  * @param key     The key
  *
  * @return  The value of the setting as a string, or @c NULL
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 char *gnt_style_get_from_name(const char *group, const char *key);
 
@@ -62,6 +64,8 @@
  *
  * @param value   The value of the boolean setting as a string
  * @return    The boolean value
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 gboolean gnt_style_parse_bool(const char *value);
 
--- a/finch/libgnt/gnttextview.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gnttextview.h	Tue Oct 16 07:48:36 2007 +0000
@@ -204,6 +204,8 @@
  *
  * @param view  The textview widget
  * @param flag  The flag to set
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_text_view_set_flag(GntTextView *view, GntTextViewFlag flag);
 
--- a/finch/libgnt/gnttree.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gnttree.h	Tue Oct 16 07:48:36 2007 +0000
@@ -383,6 +383,8 @@
  *
  * @see gnt_tree_set_column_titles
  * @see gnt_tree_set_show_title
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_tree_set_column_title(GntTree *tree, int index, const char *title);
 
@@ -486,6 +488,8 @@
  *
  * @see gnt_tree_set_col_width
  * @see gnt_tree_set_column_width_ratio
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res);
 
@@ -505,6 +509,8 @@
  * @param tree  The tree
  * @param col   The index of the column
  * @param right @c TRUE if the text in the column should be right aligned
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_tree_set_column_is_right_aligned(GntTree *tree, int col, gboolean right);
 
@@ -519,6 +525,8 @@
  *
  * @see gnt_tree_set_col_width
  * @see gnt_tree_set_column_resizable
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[]);
 
@@ -527,6 +535,8 @@
  *
  * @param tree   The tree
  * @param col    The index of the column
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_tree_set_search_column(GntTree *tree, int col);
 
@@ -535,6 +545,8 @@
  *
  * @param tree   The tree
  * @return  @c TRUE if the user is searching, @c FALSE otherwise.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 gboolean gnt_tree_is_searching(GntTree *tree);
 
@@ -547,6 +559,8 @@
  *              string and the content of row in the search column.
  *              If the function returns @c TRUE, the row is dislayed,
  *              otherwise it's not.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_tree_set_search_function(GntTree *tree,
 		gboolean (*func)(GntTree *tree, gpointer key, const char *search, const char *current));
--- a/finch/libgnt/gntutils.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gntutils.h	Tue Oct 16 07:48:36 2007 +0000
@@ -139,6 +139,8 @@
  * @param string   The XHTML string
  * @param tv       The GntTextView
  * @return  @c TRUE if the string was added to the textview properly, @c FALSE otherwise.
+ *
+ * @since 2.2.0
  */
 gboolean gnt_util_parse_xhtml_to_textview(const char *string, GntTextView *tv);
 
@@ -148,6 +150,8 @@
  * @param widget  The widget
  * @param key     The key to trigger the button
  * @param button  The button to trigger
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
  */
 void gnt_util_set_trigger_widget(GntWidget *wid, const char *text, GntWidget *button);
 
--- a/finch/libgnt/gntwm.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gntwm.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1717,12 +1717,11 @@
 void gnt_wm_window_close(GntWM *wm, GntWidget *widget)
 {
 	GntWS *s;
-	GntNode *node;
 	int pos;
 
 	s = gnt_wm_widget_find_workspace(wm, widget);
 
-	if ((node = g_hash_table_lookup(wm->nodes, widget)) == NULL)
+	if (g_hash_table_lookup(wm->nodes, widget) == NULL)
 		return;
 
 	g_signal_emit(wm, signals[SIG_CLOSE_WIN], 0, widget);
--- a/finch/libgnt/gntws.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/gntws.h	Tue Oct 16 07:48:36 2007 +0000
@@ -69,18 +69,112 @@
 
 G_BEGIN_DECLS
 
+/**
+ * @return The GType for GntWS.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 GType gnt_ws_get_gtype(void);
 
+/**
+ * Create a new workspace with the specified name.
+ *
+ * @param name  The desired name of the workspace, or @c NULL.
+ *
+ * @return The newly created workspace.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 GntWS *gnt_ws_new(const char *name);
+
+/**
+ * Set the name of a workspace.
+ *
+ * @param ws    The workspace to rename.
+ * @param name  The new name of the workspace.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_set_name(GntWS *ws, const gchar *name);
+
+/**
+ * Add a widget to a workspace.
+ *
+ * @param ws     The workspace.
+ * @param widget The widget to add.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_add_widget(GntWS *ws, GntWidget *widget);
+
+/**
+ * Remove a widget from a workspace.
+ *
+ * @param ws      The workspace
+ * @param widget  The widget to remove from the workspace.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_remove_widget(GntWS *ws, GntWidget *widget);
+
+/**
+ * Hide a widget in a workspace.
+ *
+ * @param widget  The widget to hide.
+ * @param nodes   A hashtable containing information about the widgets.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes);
+
+/**
+ * Show a widget in a workspace.
+ *
+ * @param widget   The widget to show.
+ * @param nodes   A hashtable containing information about the widgets.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes);
+
+/**
+ * Draw the taskbar in a workspace.
+ *
+ * @param ws         The workspace.
+ * @param reposition Whether the workspace should reposition the taskbar.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_draw_taskbar(GntWS *ws, gboolean reposition);
+
+/**
+ * Hide a workspace.
+ *
+ * @param ws      The workspace to hide.
+ * @param table   A hashtable containing information about the widgets.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_hide(GntWS *ws, GHashTable *table);
+
+/**
+ * Show a workspace.
+ *
+ * @param ws      The workspace to hide.
+ * @param table   A hashtable containing information about the widgets.
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 void gnt_ws_show(GntWS *ws, GHashTable *table);
 
+/**
+ * Get the name of a workspace.
+ *
+ * @param ws   The workspace.
+ * @return  The name of the workspace (can be @c NULL).
+ *
+ * @since 2.0.0 (gnt), 2.1.0 (pidgin)
+ */
 const char * gnt_ws_get_name(GntWS *ws);
 
 #endif
--- a/finch/libgnt/test/Makefile	Sun Oct 07 03:21:27 2007 +0000
+++ b/finch/libgnt/test/Makefile	Tue Oct 16 07:48:36 2007 +0000
@@ -1,5 +1,5 @@
 CC=gcc
-CFLAGS=`pkg-config --cflags gobject-2.0 gmodule-2.0` -g -I../ -DSTANDALONE
+CFLAGS=`pkg-config --cflags gobject-2.0 gmodule-2.0` -g -I../ -DSTANDALONE -I/usr/inclue/ncursesw/
 LDFLAGS=`pkg-config --libs gobject-2.0 gmodule-2.0 gnt` -pg
 
 EXAMPLES=combo focus tv multiwin keys menu parse
--- a/libpurple/account.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/account.c	Tue Oct 16 07:48:36 2007 +0000
@@ -52,7 +52,7 @@
 	{
 		int integer;
 		char *string;
-		gboolean bool;
+		gboolean boolean;
 
 	} value;
 
@@ -104,7 +104,7 @@
 	}
 	else if (setting->type == PURPLE_PREF_BOOLEAN) {
 		xmlnode_set_attrib(child, "type", "bool");
-		snprintf(buf, sizeof(buf), "%d", setting->value.bool);
+		snprintf(buf, sizeof(buf), "%d", setting->value.boolean);
 		xmlnode_insert_data(child, buf, -1);
 	}
 }
@@ -1578,7 +1578,7 @@
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type       = PURPLE_PREF_BOOLEAN;
-	setting->value.bool = value;
+	setting->value.boolean = value;
 
 	g_hash_table_insert(account->settings, g_strdup(name), setting);
 
@@ -1664,7 +1664,7 @@
 
 	setting->type       = PURPLE_PREF_BOOLEAN;
 	setting->ui         = g_strdup(ui);
-	setting->value.bool = value;
+	setting->value.boolean = value;
 
 	table = get_ui_settings_table(account, ui);
 
@@ -1939,7 +1939,7 @@
 
 	g_return_val_if_fail(setting->type == PURPLE_PREF_BOOLEAN, default_value);
 
-	return setting->value.bool;
+	return setting->value.boolean;
 }
 
 int
@@ -2005,7 +2005,7 @@
 
 	g_return_val_if_fail(setting->type == PURPLE_PREF_BOOLEAN, default_value);
 
-	return setting->value.bool;
+	return setting->value.boolean;
 }
 
 PurpleLog *
--- a/libpurple/buddyicon.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/buddyicon.c	Tue Oct 16 07:48:36 2007 +0000
@@ -505,37 +505,33 @@
 		purple_buddy_icon_set_data(icon, icon_data, icon_len, checksum);
 	else if (icon_data && icon_len > 0)
 	{
-		if (icon_data != NULL && icon_len > 0)
-		{
-			PurpleBuddyIcon *icon = purple_buddy_icon_new(account, username, icon_data, icon_len, checksum);
+		PurpleBuddyIcon *icon = purple_buddy_icon_new(account, username, icon_data, icon_len, checksum);
 
-			/* purple_buddy_icon_new() calls
-			 * purple_buddy_icon_set_data(), which calls
-			 * purple_buddy_icon_update(), which has the buddy list
-			 * and conversations take references as appropriate.
-			 * This function doesn't return icon, so we can't
-			 * leave a reference dangling. */
-			purple_buddy_icon_unref(icon);
-		}
-		else
+		/* purple_buddy_icon_new() calls
+		 * purple_buddy_icon_set_data(), which calls
+		 * purple_buddy_icon_update(), which has the buddy list
+		 * and conversations take references as appropriate.
+		 * This function doesn't return icon, so we can't
+		 * leave a reference dangling. */
+		purple_buddy_icon_unref(icon);
+	}
+	else
+	{
+		/* If the buddy list or a conversation was holding a
+		 * reference, we'd have found the icon in the cache.
+		 * Since we know we're deleting the icon, we only
+		 * need a subset of purple_buddy_icon_update(). */
+
+		GSList *buddies = purple_find_buddies(account, username);
+		while (buddies != NULL)
 		{
-			/* If the buddy list or a conversation was holding a
-			 * reference, we'd have found the icon in the cache.
-			 * Since we know we're deleting the icon, we only
-			 * need a subset of purple_buddy_icon_update(). */
+			PurpleBuddy *buddy = (PurpleBuddy *)buddies->data;
 
-			GSList *buddies = purple_find_buddies(account, username);
-			while (buddies != NULL)
-			{
-				PurpleBuddy *buddy = (PurpleBuddy *)buddies->data;
+			unref_filename(purple_blist_node_get_string((PurpleBlistNode *)buddy, "buddy_icon"));
+			purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "buddy_icon");
+			purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "icon_checksum");
 
-				unref_filename(purple_blist_node_get_string((PurpleBlistNode *)buddy, "buddy_icon"));
-				purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "buddy_icon");
-				purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "icon_checksum");
-
-				buddies = g_slist_delete_link(buddies, buddies);
-			}
-
+			buddies = g_slist_delete_link(buddies, buddies);
 		}
 	}
 }
--- a/libpurple/certificate.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/certificate.h	Tue Oct 16 07:48:36 2007 +0000
@@ -2,6 +2,7 @@
  * @file certificate.h Public-Key Certificate API
  * @ingroup core
  * @see @ref certificate-signals
+ * @since 2.2.0
  */
 
 /*
@@ -434,7 +435,7 @@
  *
  * @return TRUE if 'crt' has a valid signature made by 'issuer',
  *         otherwise FALSE
- * @TODO Find a way to give the reason (bad signature, not the issuer, etc.) 
+ * @todo Find a way to give the reason (bad signature, not the issuer, etc.) 
  */
 gboolean
 purple_certificate_signed_by(PurpleCertificate *crt, PurpleCertificate *issuer);
@@ -449,7 +450,7 @@
  * @param chain      List of PurpleCertificate instances comprising the chain,
  *                   in the order certificate, issuer, issuer's issuer, etc.
  * @return TRUE if the chain is valid. See description.
- * @TODO Specify which certificate in the chain caused a failure
+ * @todo Specify which certificate in the chain caused a failure
  */
 gboolean
 purple_certificate_check_signature_chain(GList *chain);
@@ -780,7 +781,7 @@
  * Displays a window showing X.509 certificate information
  *
  * @param crt    Certificate under an "x509" Scheme
- * @TODO Will break on CA certs, as they have no Common Name
+ * @todo Will break on CA certs, as they have no Common Name
  */
 void
 purple_certificate_display_x509(PurpleCertificate *crt);
--- a/libpurple/conversation.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/conversation.h	Tue Oct 16 07:48:36 2007 +0000
@@ -285,6 +285,8 @@
 
 /**
  * Description of a conversation message
+ *
+ * @since 2.2.0
  */
 struct _PurpleConvMessage
 {
@@ -670,6 +672,8 @@
  * @return  A GList of PurpleConvMessage's. The must not modify the list or the data within.
  *          The list contains the newest message at the beginning, and the oldest message at
  *          the end.
+ *
+ * @since 2.2.0
  */
 GList *purple_conversation_get_message_history(PurpleConversation *conv);
 
@@ -677,6 +681,8 @@
  * Clear the message history of a conversation.
  *
  * @param conv  The conversation
+ *
+ * @since 2.2.0
  */
 void purple_conversation_clear_message_history(PurpleConversation *conv);
 
@@ -686,6 +692,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
+ *
+ * @since 2.2.0
  */
 const char *purple_conversation_message_get_sender(PurpleConvMessage *msg);
 
@@ -695,6 +703,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
+ *
+ * @since 2.2.0
  */
 const char *purple_conversation_message_get_message(PurpleConvMessage *msg);
 
@@ -704,6 +714,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
+ *
+ * @since 2.2.0
  */
 PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg);
 
@@ -713,6 +725,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
+ *
+ * @since 2.2.0
  */
 time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg);
 
@@ -1318,6 +1332,8 @@
  * @return  A list of PurpleMenuAction items, harvested by the
  *          chat-extended-menu signal. The list and the menuaction
  *          items should be freed by the caller.
+ *
+ * @since 2.1.0
  */
 GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
 
@@ -1331,6 +1347,8 @@
  *                message, if not @c NULL. It must be freed by the caller with g_free().
  *
  * @return  @c TRUE if the command was executed successfully, @c FALSE otherwise.
+ *
+ * @since 2.1.0
  */
 gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error);
 
--- a/libpurple/dbus-server.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/dbus-server.c	Tue Oct 16 07:48:36 2007 +0000
@@ -674,6 +674,8 @@
 		int id;
 		gint xint;
 		guint xuint;
+		gint64 xint64;
+		guint64 xuint64;
 		gboolean xboolean;
 		gpointer ptr = NULL;
 		gpointer val;
@@ -694,6 +696,14 @@
 			xuint = my_arg(guint);
 			dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &xuint);
 			break;
+		case PURPLE_TYPE_INT64:
+			xint = my_arg(gint64);
+			dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &xint);
+			break;
+		case PURPLE_TYPE_UINT64:
+			xuint = my_arg(guint64);
+			dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &xuint);
+			break;
 		case PURPLE_TYPE_BOOLEAN:
 			xboolean = my_arg(gboolean);
 			dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &xboolean);
--- a/libpurple/dnsquery.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/dnsquery.h	Tue Oct 16 07:48:36 2007 +0000
@@ -59,7 +59,7 @@
 	                         PurpleDnsQueryFailedCallback failed_cb);
 
 	/** Called just before @a query_data is freed; this should cancel any
-	 *  further use of @q query_data the UI would make. Unneeded if
+	 *  further use of @a query_data the UI would make. Unneeded if
 	 *  #resolve_host is not implemented.
 	 */
 	void (*destroy)(PurpleDnsQueryData *query_data);
--- a/libpurple/eventloop.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/eventloop.h	Tue Oct 16 07:48:36 2007 +0000
@@ -138,6 +138,8 @@
  * @param data		data to pass to @a function.
  * @return A handle to the timer which can be passed to 
  *         purple_timeout_remove to remove the timer.
+ *
+ * @since 2.1.0
  */
 guint purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data);
 
--- a/libpurple/ft.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/ft.h	Tue Oct 16 07:48:36 2007 +0000
@@ -242,6 +242,8 @@
  * @param xfer The file transfer.
  *
  * @return The name of the remote user.
+ *
+ * @since 2.1.0
  */
 const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
 
--- a/libpurple/network.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/network.c	Tue Oct 16 07:48:36 2007 +0000
@@ -263,6 +263,7 @@
 purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
 {
 	int listenfd = -1;
+	int flags;
 	const int on = 1;
 	PurpleNetworkListenData *listen_data;
 	unsigned short actual_port;
@@ -340,7 +341,8 @@
 		close(listenfd);
 		return NULL;
 	}
-	fcntl(listenfd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(listenfd, F_GETFL);
+	fcntl(listenfd, F_SETFL, flags | O_NONBLOCK);
 
 	actual_port = purple_network_get_port_from_fd(listenfd);
 
--- a/libpurple/notify.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/notify.c	Tue Oct 16 07:48:36 2007 +0000
@@ -691,8 +691,11 @@
 void
 purple_notify_user_info_remove_last_item(PurpleNotifyUserInfo *user_info)
 {
-	user_info->user_info_entries = g_list_remove(user_info->user_info_entries,
-												 g_list_last(user_info->user_info_entries)->data);
+	GList *last = g_list_last(user_info->user_info_entries);
+	if (last) {
+		purple_notify_user_info_entry_destroy(last->data);
+		user_info->user_info_entries = g_list_remove_link(user_info->user_info_entries, last);
+	}
 }
 
 void *
--- a/libpurple/notify.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/notify.h	Tue Oct 16 07:48:36 2007 +0000
@@ -539,7 +539,7 @@
 void purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
 /**
- * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object
+ * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object without freeing the entry.
  *
  * @param user_info          The PurpleNotifyUserInfo
  * @param user_info_entry    The PurpleNotifyUserInfoEntry
--- a/libpurple/pluginpref.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/pluginpref.h	Tue Oct 16 07:48:36 2007 +0000
@@ -35,16 +35,16 @@
  */
 typedef enum
 {
-	PURPLE_STRING_FORMAT_TYPE_NONE      = 0,
-	PURPLE_STRING_FORMAT_TYPE_MULTILINE = 1 << 0,
-	PURPLE_STRING_FORMAT_TYPE_HTML      = 1 << 1
+	PURPLE_STRING_FORMAT_TYPE_NONE      = 0,          /**< The string is plain text. */
+	PURPLE_STRING_FORMAT_TYPE_MULTILINE = 1 << 0,     /**< The string can have newlines. */
+	PURPLE_STRING_FORMAT_TYPE_HTML      = 1 << 1      /**< The string can be in HTML. */
 } PurpleStringFormatType;
 
 typedef enum {
 	PURPLE_PLUGIN_PREF_NONE,
 	PURPLE_PLUGIN_PREF_CHOICE,
-	PURPLE_PLUGIN_PREF_INFO,   /**< no-value label */
-	PURPLE_PLUGIN_PREF_STRING_FORMAT
+	PURPLE_PLUGIN_PREF_INFO,              /**< no-value label */
+	PURPLE_PLUGIN_PREF_STRING_FORMAT      /**< The preference has a string value. */
 } PurplePluginPrefType;
 
 #include <glib.h>
--- a/libpurple/plugins/log_reader.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Tue Oct 16 07:48:36 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                                                              *
@@ -103,9 +116,10 @@
 				} else {
 					char *filename = g_build_filename(path, file, NULL);
 					FILE *handle = g_fopen(filename, "rb");
-					char *contents;
+					char contents[57];   /* XXX: This is really inflexible. */
 					char *contents2;
 					struct adium_logger_data *data;
+					size_t rd;
 					PurpleLog *log;
 
 					if (!handle) {
@@ -113,11 +127,9 @@
 						continue;
 					}
 
-					/* XXX: This is really inflexible. */
-					contents = g_malloc(57);
-					fread(contents, 56, 1, handle);
+					rd = fread(contents, 1, 56, handle) == 0;
 					fclose(handle);
-					contents[56] = '\0';
+					contents[rd] = '\0';
 
 					/* XXX: This is fairly inflexible. */
 					contents2 = contents;
@@ -135,11 +147,9 @@
 
 						purple_debug_error("Adium log parse",
 						                   "Contents timestamp parsing error\n");
-						g_free(contents);
 						g_free(filename);
 						continue;
 					}
-					g_free(contents);
 
 					data = g_new0(struct adium_logger_data, 1);
 					data->path = filename;
@@ -168,21 +178,20 @@
 				} else {
 					char *filename = g_build_filename(path, file, NULL);
 					FILE *handle = g_fopen(filename, "rb");
-					char *contents;
+					char contents[14];   /* XXX: This is really inflexible. */
 					char *contents2;
 					struct adium_logger_data *data;
 					PurpleLog *log;
+					size_t rd;
 
 					if (!handle) {
 						g_free(filename);
 						continue;
 					}
 
-					/* XXX: This is really inflexible. */
-					contents = g_malloc(14);
-					fread(contents, 13, 1, handle);
+					rd = fread(contents, 1, 13, handle);
 					fclose(handle);
-					contents[13] = '\0';
+					contents[rd] = '\0';
 
 					contents2 = contents;
 					while (*contents2 && *contents2 != '(')
@@ -195,13 +204,10 @@
 
 						purple_debug_error("Adium log parse",
 						                   "Contents timestamp parsing error\n");
-						g_free(contents);
 						g_free(filename);
 						continue;
 					}
 
-					g_free(contents);
-
 					tm.tm_year -= 1900;
 					tm.tm_mon  -= 1;
 
@@ -1355,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);
@@ -1446,7 +1423,7 @@
 
 	file = g_fopen(data->path, "rb");
 	fseek(file, data->offset, SEEK_SET);
-	fread(read, data->length, 1, file);
+	data->length = fread(read, 1, data->length, file);
 	fclose(file);
 
 	if (read[data->length-1] == '\n') {
@@ -1939,15 +1916,13 @@
 	g_return_val_if_fail(data->path != NULL, g_strdup(""));
 	g_return_val_if_fail(data->length > 0, g_strdup(""));
 
-	error = NULL;
-	
-	contents = g_malloc(data->length + 2);
-
 	file = g_fopen(data->path, "rb");
 	g_return_val_if_fail(file != NULL, g_strdup(""));
-	
+
+	contents = g_malloc(data->length + 2);
+
 	fseek(file, data->offset, SEEK_SET);
-	fread(contents, data->length, 1, file);
+	data->length = fread(contents, 1, data->length, file);
 	fclose(file);
 
 	contents[data->length] = '\n';
@@ -2026,7 +2001,7 @@
 					g_string_append(formatted, "</font> ");
 
 					if (is_in_message) {
-						if (buddy_name != NULL && buddy->alias) {
+						if (buddy_name != NULL && buddy != NULL && buddy->alias) {
 							g_string_append_printf(formatted,
 								"<span style=\"color: #A82F2F;\">"
 								"<b>%s</b></span>: ", buddy->alias);
@@ -2056,7 +2031,9 @@
 				g_string_append(formatted, line);
 				g_string_append(formatted, "<br>");
 			}
-			line = ++c;
+
+			if (c)
+				line = ++c;
 		}
 	}
 	g_free(contents);
@@ -2098,6 +2075,347 @@
 	g_free(data);
 }
 
+/*************************************************************************
+ * aMSN Logger                                                           *
+ *************************************************************************/
+
+/* The aMSN logger doesn't write logs, only reads them.  This is to include
+ * aMSN logs in the log viewer transparently.
+ */
+
+static PurpleLogLogger *amsn_logger;
+
+struct amsn_logger_data {
+	char *path;
+	int offset;
+	int length;
+};
+
+#define AMSN_LOG_CONV_START "|\"LRED[Conversation started on "
+#define AMSN_LOG_CONV_END "|\"LRED[You have closed the window on "
+#define AMSN_LOG_CONV_EXTRA "01 Aug 2001 00:00:00]"
+
+static GList *amsn_logger_parse_file(char *filename, const char *sn, PurpleAccount *account)
+{
+	GList *list = NULL;
+	GError *error;
+	char *contents;
+	struct amsn_logger_data *data;
+	PurpleLog *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_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;
+				}
+			} 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);
+				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);
+			}
+			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_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;
+}
+
+/* Really it's |"L, but the string's been escaped */
+#define AMSN_LOG_FORMAT_TAG "|&quot;L"
+
+static char *amsn_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+{
+	struct amsn_logger_data *data;
+	FILE *file;
+	char *contents;
+	char *escaped;
+	GString *formatted;
+	char *start;
+	gboolean in_span = FALSE;
+
+	if (flags != NULL)
+		*flags = PURPLE_LOG_READ_NO_NEWLINE;
+
+	g_return_val_if_fail(log != NULL, g_strdup(""));
+
+	data = log->logger_data;
+
+	g_return_val_if_fail(data->path != NULL, g_strdup(""));
+	g_return_val_if_fail(data->length > 0, g_strdup(""));
+
+	contents = g_malloc(data->length + 2);
+
+	file = g_fopen(data->path, "rb");
+	g_return_val_if_fail(file != NULL, g_strdup(""));
+	
+	fseek(file, data->offset, SEEK_SET);
+	data->length = fread(contents, 1, data->length, file);
+	fclose(file);
+
+	contents[data->length] = '\n';
+	contents[data->length + 1] = '\0';
+
+	escaped = g_markup_escape_text(contents, -1);
+	g_free(contents);
+	contents = escaped;
+
+	formatted = g_string_sized_new(data->length + 2);
+
+	start = contents;
+	while (start && *start) {
+		char *end;
+		char *old_tag;
+		char *tag;
+		end = strstr(start, "\n");
+		if (!end)
+			break;
+		*end = '\0';
+		if (purple_str_has_prefix(start, AMSN_LOG_FORMAT_TAG) && in_span) {
+			/* New format for this line */
+			g_string_append(formatted, "</span><br>");
+			in_span = FALSE;
+		} else if (start != contents) {
+			/* Continue format from previous line */
+			g_string_append(formatted, "<br>");
+		}
+		old_tag = start;
+		tag = strstr(start, AMSN_LOG_FORMAT_TAG);
+		while (tag) {
+			g_string_append_len(formatted, old_tag, tag - old_tag);
+			tag += strlen(AMSN_LOG_FORMAT_TAG);
+			if (in_span) {
+				g_string_append(formatted, "</span>");
+				in_span = FALSE;
+			}
+			if (*tag == 'C') {
+				/* |"LCxxxxxx is a hex colour */
+				char colour[7];
+				strncpy(colour, tag + 1, 6);
+				colour[6] = '\0';
+				g_string_append_printf(formatted, "<span style=\"color: #%s;\">", colour);
+				/* This doesn't appear to work? */
+				/* g_string_append_printf(formatted, "<span style=\"color: #%6s;\">", tag + 1); */
+				in_span = TRUE;
+				old_tag = tag + 7; /* C + xxxxxx */
+			} else {
+				/* |"Lxxx is a 3-digit colour code */
+				if (purple_str_has_prefix(tag, "RED")) {
+					g_string_append(formatted, "<span style=\"color: red;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "GRA")) {
+					g_string_append(formatted, "<span style=\"color: gray;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "NOR")) {
+					g_string_append(formatted, "<span style=\"color: black;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "ITA")) {
+					g_string_append(formatted, "<span style=\"color: blue;\">");
+					in_span = TRUE;
+				} else if (purple_str_has_prefix(tag, "GRE")) {
+					g_string_append(formatted, "<span style=\"color: darkgreen;\">");
+					in_span = TRUE;
+				} else {
+					purple_debug_info("aMSN logger", "Unknown colour format: %3s\n", tag);
+				}
+				old_tag = tag + 3;
+			}
+			tag = strstr(tag, AMSN_LOG_FORMAT_TAG);
+		}
+		g_string_append(formatted, old_tag);
+		start = end + 1;
+	}
+	if (in_span)
+		g_string_append(formatted, "</span>");
+
+	g_free(contents);
+
+	return g_string_free(formatted, FALSE);
+}
+
+static int amsn_logger_size(PurpleLog *log)
+{
+	struct amsn_logger_data *data;
+	char *text;
+	int size;
+
+	g_return_val_if_fail(log != NULL, 0);
+
+	data = log->logger_data;
+	
+	if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) {
+		return data ? data->length : 0;
+	}
+
+	text = amsn_logger_read(log, NULL);
+	size = strlen(text);
+	g_free(text);
+
+	return size;
+}
+
+static void amsn_logger_finalize(PurpleLog *log)
+{
+	struct amsn_logger_data *data;
+
+	g_return_if_fail(log != NULL);
+
+	data = log->logger_data;
+	g_free(data->path);
+	g_free(data);
+}
+
 /*****************************************************************************
  * Plugin Code                                                               *
  *****************************************************************************/
@@ -2105,14 +2423,16 @@
 static void
 init_plugin(PurplePlugin *plugin)
 {
+
+}
+
+static void log_reader_init_prefs() {
 	char *path;
 #ifdef _WIN32
 	char *folder;
 	gboolean found = FALSE;
 #endif
 
-	g_return_if_fail(plugin != NULL);
-
 	purple_prefs_add_none("/plugins/core/log_reader");
 
 
@@ -2347,6 +2667,23 @@
 #endif
 	purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path ? path : "");
 	g_free(path);
+
+	/* Add aMSN Messenger log directory preference. */
+	purple_prefs_add_none("/plugins/core/log_reader/amsn");
+
+	/* Calculate default aMSN log directory. */
+#ifdef _WIN32
+	path = NULL;
+	folder = wpurple_get_special_folder(CSIDL_PROFILE); /* Silly aMSN, not using CSIDL_APPDATA */
+	if (folder) {
+		path = g_build_filename(folder, "amsn", NULL);
+		g_free(folder);
+	}
+#else
+	path = g_build_filename(purple_home_dir(), ".amsn", NULL);
+#endif
+	purple_prefs_add_string("/plugins/core/log_reader/amsn/log_directory", path ? path : "");
+	g_free(path);
 }
 
 static gboolean
@@ -2354,6 +2691,8 @@
 {
 	g_return_val_if_fail(plugin != NULL, FALSE);
 
+	log_reader_init_prefs();
+
 	/* The names of IM clients are marked for translation at the request of
 	   translators who wanted to transliterate them.  Many translators
 	   choose to leave them alone.  Choose what's best for your language. */
@@ -2429,6 +2768,18 @@
 										  trillian_logger_size);
 	purple_log_logger_add(trillian_logger);
 
+	/* The names of IM clients are marked for translation at the request of
+	   translators who wanted to transliterate them.  Many translators
+	   choose to leave them alone.  Choose what's best for your language. */
+	amsn_logger = purple_log_logger_new("amsn", _("aMSN"), 6,
+									   NULL,
+									   NULL,
+									   amsn_logger_finalize,
+									   amsn_logger_list,
+									   amsn_logger_read,
+									   amsn_logger_size);
+	purple_log_logger_add(amsn_logger);
+
 	return TRUE;
 }
 
@@ -2445,6 +2796,7 @@
 	purple_log_logger_remove(msn_logger);
 	purple_log_logger_remove(trillian_logger);
 	purple_log_logger_remove(qip_logger);
+	purple_log_logger_remove(amsn_logger);
 
 	return TRUE;
 }
@@ -2505,6 +2857,10 @@
 		"/plugins/core/log_reader/trillian/log_directory", _("Trillian"));
 	purple_plugin_pref_frame_add(frame, ppref);
 
+	ppref = purple_plugin_pref_new_with_name_and_label(
+		"/plugins/core/log_reader/amsn/log_directory", _("aMSN"));
+	purple_plugin_pref_frame_add(frame, ppref);
+
 	return frame;
 }
 
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Tue Oct 16 07:48:36 2007 +0000
@@ -884,7 +884,7 @@
 	gnutls_x509_crt crt_dat;
 	/* GnuTLS time functions return this on error */
 	const time_t errval = (time_t) (-1);
-
+	gboolean success = TRUE;
 
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE);
@@ -893,16 +893,16 @@
 
 	if (activation) {
 		*activation = gnutls_x509_crt_get_activation_time(crt_dat);
+		if (*activation == errval)
+			success = FALSE;
 	}
 	if (expiration) {
 		*expiration = gnutls_x509_crt_get_expiration_time(crt_dat);
+		if (*expiration == errval)
+			success = FALSE;
 	}
 
-	if (*activation == errval || *expiration == errval) {
-		return FALSE;
-	}
-
-	return TRUE;
+	return success;
 }
 
 /* X.509 certificate operations provided by this plugin */
--- a/libpurple/plugins/ssl/ssl-nss.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-nss.c	Tue Oct 16 07:48:36 2007 +0000
@@ -386,6 +386,7 @@
 static GList *
 ssl_nss_peer_certs(PurpleSslConnection *gsc)
 {
+#if 0
 	PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
 	CERTCertificate *cert;
 /*
@@ -397,6 +398,10 @@
 	/* TODO: this is a blind guess */
 	cert = SSL_PeerCertificate(nss_data->fd);
 
+	if (cert)
+		CERT_DestroyCertificate(cert);
+#endif
+
 	
 
 	return NULL;
@@ -430,11 +435,12 @@
 			  filename);
 	
 	/* Load the raw data up */
-	g_return_val_if_fail(
-		g_file_get_contents(filename,
-				    &rawcert, &len,
-				    NULL ),
-		NULL);
+	if (!g_file_get_contents(filename,
+				 &rawcert, &len,
+				 NULL)) {
+		purple_debug_error("nss/x509", "Unable to read certificate file.\n");
+		return NULL;
+	}
 
 	/* Decode the certificate */
 	crt_dat = CERT_DecodeCertFromPackage(rawcert, len);
--- a/libpurple/prefs.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/prefs.c	Tue Oct 16 07:48:36 2007 +0000
@@ -297,6 +297,7 @@
 						g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL));
 			}
 		}
+		g_string_free(pref_name_full, TRUE);
 	} else {
 		char *decoded;
 
--- a/libpurple/prefs.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/prefs.h	Tue Oct 16 07:48:36 2007 +0000
@@ -291,6 +291,8 @@
  * @return A list of newly allocated strings denoting the names of the children.
  *         Returns @c NULL if there are no children or if pref doesn't exist.
  *         The caller must free all the strings and the list.
+ *
+ * @since 2.1.0
  */
 GList *purple_prefs_get_children_names(const char *name);
 
--- a/libpurple/protocols/bonjour/Makefile.mingw	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Tue Oct 16 07:48:36 2007 +0000
@@ -36,7 +36,6 @@
 			-I$(PIDGIN_TREE_TOP)
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
-			-L$(BONJOUR_TOP)/lib \
 			-L$(LIBXML2_TOP)/lib \
 			-L$(PURPLE_TOP)
 
@@ -66,6 +65,7 @@
 
 ifeq ($(LINK_DNS_SD_DIRECTLY), 1)
 	CFLAGS += -DLINK_DNS_SD_DIRECTLY
+	LIB_PATHS += -L$(BONJOUR_TOP)/lib/win32 -L$(BONJOUR_TOP)/lib
 	LIBS += -ldnssd
 endif
 
--- a/libpurple/protocols/bonjour/bonjour.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Tue Oct 16 07:48:36 2007 +0000
@@ -188,6 +188,8 @@
 	if (bonjour_group != NULL)
 		purple_blist_remove_group(bonjour_group);
 
+	g_free(bd);
+	connection->proto_data = NULL;
 }
 
 static const char *
@@ -581,7 +583,7 @@
 			fullname = g_utf16_to_utf8(username, -1, NULL, NULL, NULL);
 	}
 
-	g_idle_add(_set_default_name_cb, fullname);
+	purple_timeout_add(0, _set_default_name_cb, fullname);
 
 	return NULL;
 }
--- a/libpurple/protocols/bonjour/buddy.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Tue Oct 16 07:48:36 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	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Tue Oct 16 07:48:36 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/bonjour/jabber.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Tue Oct 16 07:48:36 2007 +0000
@@ -521,6 +521,7 @@
 	struct sockaddr_in their_addr; /* connector's address information */
 	socklen_t sin_size = sizeof(struct sockaddr);
 	int client_socket;
+	int flags;
 	BonjourBuddy *bb;
 	char *address_text = NULL;
 	PurpleBuddyList *bl = purple_get_blist();
@@ -533,7 +534,8 @@
 	if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1)
 		return;
 
-	fcntl(client_socket, F_SETFL, O_NONBLOCK);
+	flags = fcntl(client_socket, F_GETFL);
+	fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
 
 	/* Look for the buddy that has opened the conversation and fill information */
 	address_text = inet_ntoa(their_addr.sin_addr);
--- a/libpurple/protocols/bonjour/parser.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Tue Oct 16 07:48:36 2007 +0000
@@ -64,7 +64,7 @@
 			char *attrib_ns = NULL;
 
 			if (attributes[i+2]) {
-				attrib_ns = g_strdup((char*)attributes[i+2]);;
+				attrib_ns = g_strdup((char*)attributes[i+2]);
 			}
 
 			memcpy(attrib, attributes[i+3], attrib_len);
@@ -101,7 +101,7 @@
 		if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
 			/* Asynchronously close the conversation to prevent bonjour_parser_setup()
 			 * being called from within this context */
-			g_idle_add(_async_bonjour_jabber_stream_ended_cb, pb);
+			purple_timeout_add(0, _async_bonjour_jabber_stream_ended_cb, pb);
 		}
 		return;
 	}
--- a/libpurple/protocols/jabber/chat.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/chat.c	Tue Oct 16 07:48:36 2007 +0000
@@ -964,7 +964,7 @@
 static void jabber_chat_disco_traffic_cb(JabberStream *js, xmlnode *packet, gpointer data)
 {
 	JabberChat *chat;
-	xmlnode *query;
+	/*xmlnode *query;*/
 	int id = GPOINTER_TO_INT(data);
 
 	if(!(chat = jabber_chat_find_by_id(js, id)))
@@ -974,6 +974,8 @@
 	 * support this request */
 	chat->xhtml = TRUE;
 
+	/* disabling this until more MUC servers support
+	 * announcing this
 	if(xmlnode_get_child(packet, "error")) {
 		return;
 	}
@@ -981,8 +983,6 @@
 	if(!(query = xmlnode_get_child(packet, "query")))
 		return;
 
-	/* disabling this until more MUC servers support
-	 * announcing this
 	chat->xhtml = FALSE;
 
 	for(x = xmlnode_get_child(query, "feature"); x; x = xmlnode_get_next_twin(x)) {
--- a/libpurple/protocols/jabber/google.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/google.c	Tue Oct 16 07:48:36 2007 +0000
@@ -110,7 +110,7 @@
 		tos[i] = (to_name != NULL ?  to_name : "");
 		froms[i] = (from != NULL ?  from : "");
 		subjects[i] = (subject != NULL ? subject : g_strdup(""));
-		urls[i] = (url != NULL ? url : "");
+		urls[i] = url;
 
 		tid = xmlnode_get_attrib(message, "tid");
 		if (tid &&
--- a/libpurple/protocols/jabber/iq.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/iq.c	Tue Oct 16 07:48:36 2007 +0000
@@ -248,7 +248,6 @@
 	JabberIq *iq;
 	const char *type, *from, *id;
 	xmlnode *query;
-	char *os = NULL;
 
 	type = xmlnode_get_attrib(packet, "type");
 
@@ -256,6 +255,7 @@
 		GHashTable *ui_info;
 		const char *ui_name = NULL, *ui_version = NULL;
 #if 0
+		char *os = NULL;
 		if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) {
 			struct utsname osinfo;
 
@@ -290,10 +290,12 @@
 			xmlnode_insert_data(xmlnode_new_child(query, "version"), VERSION, -1);
 		}
 
+#if 0
 		if(os) {
 			xmlnode_insert_data(xmlnode_new_child(query, "os"), os, -1);
 			g_free(os);
 		}
+#endif
 
 		jabber_iq_send(iq);
 	}
--- a/libpurple/protocols/jabber/jabber.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1915,7 +1915,7 @@
 			text = _("Authentication Failure");
 		}
 	} else if(!strcmp(packet->name, "stream:error") ||
-			 (!strcmp(packet->name, "error") &&
+			 (!strcmp(packet->name, "error") && xmlns &&
 				!strcmp(xmlns, "http://etherx.jabber.org/streams"))) {
 		if(xmlnode_get_child(packet, "bad-format")) {
 			text = _("Bad Format");
@@ -2190,55 +2190,67 @@
 	return PURPLE_CMD_RET_OK;
 }
 
+static gboolean _jabber_send_buzz(JabberStream *js, const char *username, char **error) {
+
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+	GList *iter;
+
+	if(!username)
+		return FALSE;
+
+	jb = jabber_buddy_find(js, username, FALSE);
+	if(!jb) {
+		*error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), username);
+		return FALSE;
+	}
+
+	jbr = jabber_buddy_find_resource(jb, NULL);
+	if(!jbr) {
+		*error = g_strdup_printf(_("Unable to buzz, because user %s might be offline."), username);
+		return FALSE;
+	}
+
+	if(!jbr->caps) {
+		*error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), username);
+		return FALSE;
+	}
+
+	for(iter = jbr->caps->features; iter; iter = g_list_next(iter)) {
+		if(!strcmp(iter->data, "http://www.xmpp.org/extensions/xep-0224.html#ns")) {
+			xmlnode *buzz, *msg = xmlnode_new("message");
+			gchar *to;
+
+			to = g_strdup_printf("%s/%s", username, jbr->name);
+			xmlnode_set_attrib(msg, "to", to);
+			g_free(to);
+
+			/* avoid offline storage */
+			xmlnode_set_attrib(msg, "type", "headline");
+
+			buzz = xmlnode_new_child(msg, "attention");
+			xmlnode_set_namespace(buzz, "http://www.xmpp.org/extensions/xep-0224.html#ns");
+
+			jabber_send(js, msg);
+			xmlnode_free(msg);
+
+			return TRUE;
+		}
+	}
+
+	*error = g_strdup_printf(_("Unable to buzz, because the user %s does not support it."), username);
+	return FALSE;
+}
+
 static PurpleCmdRet jabber_cmd_buzz(PurpleConversation *conv,
 		const char *cmd, char **args, char **error, void *data)
 {
 	JabberStream *js = conv->account->gc->proto_data;
-	xmlnode *msg, *buzz;
-	JabberBuddy *jb;
-	JabberBuddyResource *jbr;
-	char *to;
-	GList *iter;
 
 	if(!args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
-	
-	jb = jabber_buddy_find(js, args[0], FALSE);
-	if(!jb) {
-		*error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), args[0]);
-		return PURPLE_CMD_RET_FAILED;
-	}
-	
-	jbr = jabber_buddy_find_resource(jb, NULL);
-	if(!jbr) {
-		*error = g_strdup_printf(_("Unable to buzz, because user %s might be offline."), args[0]);
-		return PURPLE_CMD_RET_FAILED;
-	}
-	if(!jbr->caps) {
-		*error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), args[0]);
-		return PURPLE_CMD_RET_FAILED;
-	}
-	for(iter = jbr->caps->features; iter; iter = g_list_next(iter)) {
-		if(!strcmp(iter->data, "http://www.xmpp.org/extensions/xep-0224.html#ns")) {
-			msg = xmlnode_new("message");
-			to = g_strdup_printf("%s/%s", args[0], jbr->name);
-			xmlnode_set_attrib(msg,"to",to);
-			g_free(to);
-			
-			/* avoid offline storage */
-			xmlnode_set_attrib(msg,"type","headline");
-			
-			buzz = xmlnode_new_child(msg,"attention");
-			xmlnode_set_namespace(buzz,"http://www.xmpp.org/extensions/xep-0224.html#ns");
-			
-			jabber_send(js,msg);
-			xmlnode_free(msg);
-			
-			return PURPLE_CMD_RET_OK;
-		}
-	}
-	*error = g_strdup_printf(_("Unable to buzz, because the user %s does not support it."), args[0]);
-	return PURPLE_CMD_RET_FAILED;
+
+	return _jabber_send_buzz(js, args[0], error)  ? PURPLE_CMD_RET_OK : PURPLE_CMD_RET_FAILED;
 }
 
 GList *jabber_attention_types(PurpleAccount *account)
@@ -2259,23 +2271,16 @@
 
 gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code)
 {
-	PurpleConversation *conv;
-	char *error;
-	char *args[1];
-	PurpleCmdRet ret;
-
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, gc->account);
-
-	args[0] = (char *)username;
+	JabberStream *js = gc->proto_data;
+	gchar *error = NULL;
 
-	ret = jabber_cmd_buzz(conv, "buzz", args, &error, NULL);
-
-	if (ret == PURPLE_CMD_RET_FAILED) {
+	if (!_jabber_send_buzz(js, username, &error)) {
 		purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)");
+		g_free(error);
 		return FALSE;
-	} else {
-		return TRUE;
 	}
+
+	return TRUE;
 }
 
 
--- a/libpurple/protocols/jabber/parser.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/parser.c	Tue Oct 16 07:48:36 2007 +0000
@@ -80,7 +80,7 @@
 			char *attrib_ns = NULL;
 
 			if (attributes[i+2]) {
-				attrib_ns = g_strdup((char*)attributes[i+2]);;
+				attrib_ns = g_strdup((char*)attributes[i+2]);
 			}
 
 			memcpy(attrib, attributes[i+3], attrib_len);
--- a/libpurple/protocols/jabber/roster.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/roster.c	Tue Oct 16 07:48:36 2007 +0000
@@ -67,8 +67,10 @@
 	if(!groups) {
 		if(!buddies)
 			g2 = g_slist_append(g2, g_strdup(_("Buddies")));
-		else
+		else {
+			g_slist_free(buddies);
 			return;
+		}
 	}
 
 	my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
@@ -229,6 +231,11 @@
 			remove_purple_buddies(js, jid);
 		} else {
 			GSList *groups = NULL;
+
+			if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+				if (!jabber_google_roster_incoming(js, item))
+					continue;
+
 			for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_next_twin(group)) {
 				char *group_name;
 
@@ -237,10 +244,9 @@
 
 				if (g_slist_find_custom(groups, group_name, (GCompareFunc)purple_utf8_strcasecmp) == NULL)
 					groups = g_slist_append(groups, group_name);
+				else
+					g_free(group_name);
 			}
-			if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
-				if (!jabber_google_roster_incoming(js, item))
-					continue;
 			add_purple_buddies_to_groups(js, jid, name, groups);
 		}
 	}
@@ -263,6 +269,9 @@
 	JabberIq *iq;
 	xmlnode *query, *item, *group;
 
+	if(!(b = purple_find_buddy(js->gc->account, name)))
+		return;
+
 	if(grps) {
 		groups = grps;
 	} else {
@@ -277,9 +286,6 @@
 		}
 	}
 
-	if(!(b = purple_find_buddy(js->gc->account, name)))
-		return;
-
 	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
 
 	query = xmlnode_get_child(iq->node, "query");
@@ -397,12 +403,12 @@
 void jabber_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 		PurpleGroup *group) {
 	GSList *buddies = purple_find_buddies(gc->account, buddy->name);
-	GSList *groups = NULL;
 
 	buddies = g_slist_remove(buddies, buddy);
 	if(buddies != NULL) {
 		PurpleBuddy *tmpbuddy;
 		PurpleGroup *tmpgroup;
+		GSList *groups = NULL;
 
 		while(buddies) {
 			tmpbuddy = buddies->data;
@@ -412,6 +418,7 @@
 		}
 
 		jabber_roster_update(gc->proto_data, buddy->name, groups);
+		g_slist_free(groups);
 	} else {
 		JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET,
 				"jabber:iq:roster");
@@ -423,9 +430,4 @@
 
 		jabber_iq_send(iq);
 	}
-
-	if(buddies)
-		g_slist_free(buddies);
-	if(groups)
-		g_slist_free(groups);
 }
--- a/libpurple/protocols/jabber/usermood.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Tue Oct 16 07:48:36 2007 +0000
@@ -26,6 +26,7 @@
 #include <string.h>
 #include "internal.h"
 #include "request.h"
+#include "debug.h"
 
 static const char *moodstrings[] = {
 	"afraid",
@@ -145,9 +146,26 @@
 }
 
 static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) {
-	JabberStream *js = gc->proto_data;
-	
-	jabber_mood_set(js, moodstrings[purple_request_fields_get_choice(fields, "mood")], purple_request_fields_get_string(fields, "text"));
+	JabberStream *js;
+	int max_mood_idx;
+	int selected_mood = purple_request_fields_get_choice(fields, "mood");
+
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		purple_debug_error("jabber", "Unable to set mood; account offline.\n");
+		return;
+	}
+
+	js = gc->proto_data;
+
+	/* This is ugly, but protects us from unexpected values. */
+	for (max_mood_idx = 0; moodstrings[max_mood_idx]; max_mood_idx++);
+
+	if (selected_mood < 0 || selected_mood >= max_mood_idx) {
+		purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood);
+		return;
+	}
+
+	jabber_mood_set(js, moodstrings[selected_mood], purple_request_fields_get_string(fields, "text"));
 }
 
 static void do_mood_set_mood(PurplePluginAction *action) {
--- a/libpurple/protocols/msn/contact.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1177,7 +1177,7 @@
 		purple_debug_warning("MSN CL", "Unable to retrieve user %s from the userlist!\n", passport);
 	}
 
-	if (user->uid != NULL) {
+	if (user != NULL && user->uid != NULL) {
 		contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
 	} else {
 		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
--- a/libpurple/protocols/msn/directconn.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Tue Oct 16 07:48:36 2007 +0000
@@ -80,6 +80,7 @@
 create_listener(int port)
 {
 	int fd;
+	int flags;
 	const int on = 1;
 
 #if 0
@@ -155,7 +156,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/msn/httpconn.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/httpconn.c	Tue Oct 16 07:48:36 2007 +0000
@@ -169,7 +169,7 @@
 	/* Now we should be able to process the data. */
 	if ((s = purple_strcasestr(header, "X-MSN-Messenger: ")) != NULL)
 	{
-		char *full_session_id, *gw_ip, *session_action;
+		gchar *full_session_id = NULL, *gw_ip = NULL, *session_action = NULL;
 		char *t, *session_id;
 		char **elems, **cur, **tokens;
 
@@ -196,13 +196,16 @@
 		{
 			tokens = g_strsplit(*cur, "=", 2);
 
-			if (strcmp(tokens[0], "SessionID") == 0)
+			if (strcmp(tokens[0], "SessionID") == 0) {
+				g_free(full_session_id);
 				full_session_id = tokens[1];
-			else if (strcmp(tokens[0], "GW-IP") == 0)
+			} else if (strcmp(tokens[0], "GW-IP") == 0) {
+				g_free(gw_ip);
 				gw_ip = tokens[1];
-			else if (strcmp(tokens[0], "Session") == 0)
+			} else if (strcmp(tokens[0], "Session") == 0) {
+				g_free(session_action);
 				session_action = tokens[1];
-			else
+			} else
 				g_free(tokens[1]);
 
 			g_free(tokens[0]);
@@ -684,6 +687,17 @@
 
 	g_free(httpconn->host);
 
+	while (httpconn->queue != NULL) {
+		MsnHttpQueueData *queue_data;
+
+		queue_data = (MsnHttpQueueData *) httpconn->queue->data;
+
+		httpconn->queue = g_list_remove_link(httpconn->queue, httpconn->queue);
+
+		g_free(queue_data->body);
+		g_free(queue_data);
+	}
+
 	purple_circ_buffer_destroy(httpconn->tx_buf);
 	if (httpconn->tx_handler > 0)
 		purple_input_remove(httpconn->tx_handler);
--- a/libpurple/protocols/msn/msg.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/msg.c	Tue Oct 16 07:48:36 2007 +0000
@@ -662,10 +662,11 @@
 
 		tokens = g_strsplit(*cur, ": ", 2);
 
-		if (tokens[0] != NULL && tokens[1] != NULL)
+		if (tokens[0] != NULL && tokens[1] != NULL) {
 			g_hash_table_insert(table, tokens[0], tokens[1]);
-
-		g_free(tokens);
+			g_free(tokens);
+		} else
+			g_strfreev(tokens);
 	}
 
 	g_strfreev(elems);
--- a/libpurple/protocols/msn/msn.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Tue Oct 16 07:48:36 2007 +0000
@@ -571,7 +571,7 @@
 	status = purple_presence_get_active_status(presence);
 
 	msg = purple_status_get_attr_string(status, "message");
-	cmedia = purple_status_get_attr_string(status, "currentmedia");
+	cmedia = purple_status_get_attr_string(status, PURPLE_TUNE_FULL);
 
 	if (cmedia)
 		return g_markup_escape_text(cmedia, -1);
@@ -596,7 +596,7 @@
 		char *tmp;
 
 		psm = purple_status_get_attr_string(status, "message");
-		currentmedia = purple_status_get_attr_string(status, "currentmedia");
+		currentmedia = purple_status_get_attr_string(status, PURPLE_TUNE_FULL);
 
 		if (!purple_presence_is_available(presence)) {
 			name = purple_status_get_name(status);
@@ -651,40 +651,40 @@
 	status = purple_status_type_new_with_attrs(
 				PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
 				"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-				"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+				PURPLE_TUNE_FULL, _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 				NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_FULL, _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_FULL, _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_FULL, _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_FULL, _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 	status = purple_status_type_new_with_attrs(
 			PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_FULL, _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
@@ -962,7 +962,7 @@
 			imdata->msg = body_str;
 			imdata->flags = flags;
 			imdata->when = time(NULL);
-			g_idle_add(msn_send_me_im, imdata);
+			purple_timeout_add(0, msn_send_me_im, imdata);
 		}
 
 		msn_message_destroy(msg);
@@ -1122,7 +1122,7 @@
 	userlist = session->userlist;
 	who = msn_normalize(gc->account, buddy->name);
 
-	purple_debug_info("MSN","Add user:%s to group:%s\n", who, group->name);
+	purple_debug_info("MSN","Add user:%s to group:%s\n", who, (group && group->name) ? group->name : "(null)");
 	if (!session->logged_in)
 	{
 #if 0
@@ -1613,7 +1613,6 @@
 	gboolean sect_info = FALSE;
 	gboolean has_contact_info = FALSE;
 	char *url_buffer;
-	GString *s, *s2;
 	int stripped_len;
 #if PHOTO_SUPPORT
 	char *photo_url_text = NULL;
@@ -1698,11 +1697,6 @@
 	purple_debug_misc("msn", "stripped = %p\n", stripped);
 	purple_debug_misc("msn", "url_buffer = %p\n", url_buffer);
 
-	/* Gonna re-use the memory we've already got for url_buffer */
-	/* No we're not. */
-	s = g_string_sized_new(strlen(url_buffer));
-	s2 = g_string_sized_new(strlen(url_buffer));
-
 	/* General section header */
 	if (has_tooltip_text)
 		purple_notify_user_info_add_section_break(user_info);
@@ -2049,7 +2043,7 @@
 		purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n");
 		g_free(stripped);
 		g_free(url_buffer);
-		g_free(user_info);
+		purple_notify_user_info_destroy(user_info);
 		g_free(info_data->name);
 		g_free(info_data);
 		g_free(photo_url_text);
--- a/libpurple/protocols/msn/notification.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Tue Oct 16 07:48:36 2007 +0000
@@ -230,11 +230,9 @@
 {
 	MsnSession *session;
 	PurpleAccount *account;
-	PurpleConnection *gc;
 
 	session = cmdproc->session;
 	account = session->account;
-	gc = purple_account_get_connection(account);
 
 	if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
 	{
@@ -264,14 +262,15 @@
 		for (cur = elems; *cur != NULL; cur++)
 		{
 			tokens = g_strsplit(*cur, "=", 2);
-			if(tokens[0]&&tokens[1])
+			if(tokens[0] && tokens[1])
 			{
 				purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n",
 									session->nexus->challenge_data,tokens[0],tokens[1]);
 				g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
-			}
-			/* Don't free each of the tokens, only the array. */
-			g_free(tokens);
+				/* Don't free each of the tokens, only the array. */
+				g_free(tokens);
+			} else
+				g_strfreev(tokens);
 		}
 
 		g_strfreev(elems);
@@ -450,7 +449,7 @@
 	const char *passport;
 	const char *content_type;
 
-	purple_debug_info("MSNP14","Process UBM payload:%s\n",payload);
+	purple_debug_info("MSNP14","Process UBM payload:%.*s\n", len, payload);
 	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
 
 	msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
@@ -533,7 +532,7 @@
 	}else{
 		g_return_if_fail(cmd->payload_cb != NULL);
 
-		purple_debug_info("MSNP14","UBM payload:{%s}\n",cmd->payload);
+		purple_debug_info("MSNP14","UBM payload:{%.*s}\n", cmd->payload_len, cmd->payload);
 		ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len);
 	}
 }
@@ -737,7 +736,7 @@
 	msn_cmdproc_send_trans(cmdproc, trans);
 
 	g_free(payload);
-	g_free(tokens);
+	g_strfreev(tokens);
 }
 
 static void
@@ -1616,7 +1615,6 @@
 {
 	MsnSession *session;
 	PurpleAccount *account;
-	PurpleConnection *gc;
 	MsnUser *user;
 	const char *passport;
 	char *psm_str, *currentmedia_str, *str;
@@ -1626,7 +1624,6 @@
 
 	session = cmdproc->session;
 	account = session->account;
-	gc = purple_account_get_connection(account);
 
 	passport = cmd->params[0];
 	user = msn_userlist_find_user(session->userlist, passport);
--- a/libpurple/protocols/msn/oim.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/oim.c	Tue Oct 16 07:48:36 2007 +0000
@@ -608,8 +608,9 @@
 	purple_debug_info("MSN OIM:OIM", "%s", xmlmsg);
 
 	node = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
-	if (strcmp(node->name, "MD") != 0) {
-		xmlnode_free(node);
+	if (!node || !node->name || strcmp(node->name, "MD") != 0) {
+		if (node)
+			xmlnode_free(node);
 		return;
 	}
 
--- a/libpurple/protocols/msn/servconn.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Tue Oct 16 07:48:36 2007 +0000
@@ -480,6 +480,7 @@
 create_listener(int port)
 {
 	int fd;
+	int flags;
 	const int on = 1;
 
 #if 0
@@ -555,7 +556,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/msn/slplink.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/slplink.c	Tue Oct 16 07:48:36 2007 +0000
@@ -120,6 +120,8 @@
 	while (slplink->slp_calls != NULL)
 		msn_slp_call_destroy(slplink->slp_calls->data);
 
+	g_queue_free(slplink->slp_msg_queue);
+
 	session->slplinks =
 		g_list_remove(session->slplinks, slplink);
 
--- a/libpurple/protocols/msn/soap.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/soap.c	Tue Oct 16 07:48:36 2007 +0000
@@ -131,6 +131,7 @@
 				MsnSoapSslErrorCbFunction error_cb)
 {
 	purple_debug_misc("MSN SOAP","Initializing SOAP connection\n");
+	g_free(soapconn->login_host);
 	soapconn->login_host = g_strdup(host);
 	soapconn->ssl_conn = ssl;
 	soapconn->connect_cb = connect_cb;
@@ -204,11 +205,9 @@
 void
 msn_soap_destroy(MsnSoapConn *soapconn)
 {
-	if(soapconn->login_host)
-		g_free(soapconn->login_host);
+	g_free(soapconn->login_host);
 
-	if(soapconn->login_path)
-		g_free(soapconn->login_path);
+	g_free(soapconn->login_path);
 
 	/*remove the write handler*/
 	if (soapconn->output_handler > 0){
--- a/libpurple/protocols/msn/switchboard.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Tue Oct 16 07:48:36 2007 +0000
@@ -108,8 +108,8 @@
 	g_free(swboard->auth_key);
 	g_free(swboard->session_id);
 
-	for (l = swboard->users; l != NULL; l = l->next)
-		g_free(l->data);
+	for (; swboard->users; swboard->users = g_list_remove_link(swboard->users, swboard->users))
+		g_free(swboard->users->data);
 
 	session = swboard->session;
 	session->switches = g_list_remove(session->switches, swboard);
--- a/libpurple/protocols/msn/user.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Tue Oct 16 07:48:36 2007 +0000
@@ -94,9 +94,9 @@
 	if (user->statusline != NULL && user->currentmedia != NULL) {
 		purple_prpl_got_user_status(account, user->passport, user->status,
 		                          "message", user->statusline,
-		                          "currentmedia", user->currentmedia, NULL);
+		                          PURPLE_TUNE_FULL, user->currentmedia, NULL);
 	} else if (user->currentmedia != NULL) {
-		purple_prpl_got_user_status(account, user->passport, user->status, "currentmedia",
+		purple_prpl_got_user_status(account, user->passport, user->status, PURPLE_TUNE_FULL,
 		                          user->currentmedia, NULL);
 	} else if (user->statusline != NULL) {
 		//char *status = g_strdup_printf("%s - %s", user->status, user->statusline);
--- a/libpurple/protocols/msn/userlist.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Tue Oct 16 07:48:36 2007 +0000
@@ -227,11 +227,8 @@
 	}
 	else if (list_id == MSN_LIST_RL)
 	{
-		PurpleConnection *gc;
 		PurpleConversation *convo;
 
-		gc = purple_account_get_connection(account);
-
 		purple_debug_info("msn",
 						"%s has added you to his or her buddy list.\n",
 						passport);
--- a/libpurple/protocols/myspace/markup.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/myspace/markup.c	Tue Oct 16 07:48:36 2007 +0000
@@ -400,6 +400,8 @@
 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, 
 		gchar **end)
 {
+	g_return_if_fail(root != NULL);
+
 	if (g_str_equal(root->name, "f")) {
 		msim_markup_f_to_html(session, root, begin, end);
 	} else if (g_str_equal(root->name, "a")) {
@@ -415,7 +417,7 @@
 	} else {
 		purple_debug_info("msim", "msim_markup_tag_to_html: "
 				"unknown tag name=%s, ignoring", 
-				(root && root->name) ? root->name : "(NULL)");
+				root->name ? root->name : "(NULL)");
 		*begin = g_strdup("");
 		*end = g_strdup("");
 	}
--- a/libpurple/protocols/myspace/message.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/myspace/message.c	Tue Oct 16 07:48:36 2007 +0000
@@ -314,7 +314,7 @@
 	MsimMessageElement *elem;
 	MsimMessage **new;
 	gpointer new_data;
-				
+
 	GString *gs;
 	MsimMessage *dict;
 
@@ -349,7 +349,7 @@
 
 		default:
 			purple_debug_info("msim", "msim_msg_clone_element: unknown type %d\n", elem->type);
-			g_return_if_fail(NULL);
+			g_return_if_reached();
 	}
 
 	/* Append cloned data. Note that the 'name' field is a static string, so it
@@ -905,7 +905,7 @@
 
 		default:
 			g_free(data_string);
-			g_return_if_fail(FALSE);
+			g_return_if_reached();
 			break;
 	}
 
--- a/libpurple/protocols/myspace/myspace.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Tue Oct 16 07:48:36 2007 +0000
@@ -2035,18 +2035,14 @@
 msim_do_postprocessing(MsimMessage *msg, const gchar *uid_before, 
 		const gchar *uid_field_name, guint uid)
 {
+	MsimMessageElement *elem;
 	msim_msg_dump("msim_do_postprocessing msg: %s\n", msg);
 
 	/* First, check - if the field already exists, replace <uid> within it */
-	if (msim_msg_get(msg, uid_field_name)) {
-		MsimMessageElement *elem;
+	if ((elem = msim_msg_get(msg, uid_field_name)) != NULL) {
 		gchar *fmt_string;
 		gchar *uid_str, *new_str;
 
-		/* Warning: this is a delicate, but safe, operation */
-
-		elem = msim_msg_get(msg, uid_field_name);
-
 		/* Get the packed element, flattening it. This allows <uid> to be
 		 * replaced within nested data structures, since the replacement is done
 		 * on the linear, packed data, not on a complicated data structure.
--- a/libpurple/protocols/oscar/oscar.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1149,8 +1149,10 @@
 	aim_clientready(od, conn);
 
 	chatcon = find_oscar_chat_by_conn(gc, conn);
-	chatcon->id = id;
-	chatcon->conv = serv_got_joined_chat(gc, id++, chatcon->show);
+	if (chatcon) {
+		chatcon->id = id;
+		chatcon->conv = serv_got_joined_chat(gc, id++, chatcon->show);
+	}
 }
 
 static void
@@ -1742,7 +1744,6 @@
 {
 	PurpleConnection *gc;
 	PurpleAccount *account;
-	PurplePresence *presence;
 	struct buddyinfo *bi;
 	time_t time_idle = 0, signon = 0;
 	int type = 0;
@@ -1755,7 +1756,6 @@
 
 	gc = od->gc;
 	account = purple_connection_get_account(gc);
-	presence = purple_account_get_presence(account);
 
 	va_start(ap, fr);
 	info = va_arg(ap, aim_userinfo_t *);
@@ -1822,7 +1822,7 @@
 
 	if (have_status_message)
 	{
-		if ((status_id == OSCAR_STATUS_ID_AVAILABLE) && (info->itmsurl != NULL))
+		if ((!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE)) && (info->itmsurl != NULL))
 		{
 			char *itmsurl;
 			itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
@@ -5033,6 +5033,7 @@
 					g = purple_group_new(gname_utf8);
 					purple_blist_add_group(g, NULL);
 				}
+				g_free(gname_utf8);
 			} break;
 
 			case 0x0002: { /* Permit buddy */
--- a/libpurple/protocols/oscar/peer.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/oscar/peer.c	Tue Oct 16 07:48:36 2007 +0000
@@ -607,6 +607,7 @@
 	PurpleConnection *gc;
 	struct sockaddr addr;
 	socklen_t addrlen = sizeof(addr);
+	int flags;
 
 	conn = data;
 	od = conn->od;
@@ -633,7 +634,8 @@
 		return;
 	}
 
-	fcntl(conn->fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(conn->fd, F_GETFL);
+	fcntl(conn->fd, F_SETFL, flags | O_NONBLOCK);
 	purple_input_remove(conn->watcher_incoming);
 
 	peer_connection_finalize_connection(conn);
--- a/libpurple/protocols/qq/buddy_info.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Tue Oct 16 07:48:36 2007 +0000
@@ -306,12 +306,10 @@
 	g_free(mid);
 }
 
-static gchar *parse_field(GList **list, gboolean choice)
+static gchar *parse_field(PurpleRequestField *field, gboolean choice)
 {
 	gchar *value;
-	PurpleRequestField *field;
 
-	field = (PurpleRequestField *) (*list)->data;
 	if (choice) {
 		value = g_strdup_printf("%d", purple_request_field_choice_get_value(field));
 	} else {
@@ -321,7 +319,6 @@
 		else
 			value = utf8_to_qq(value, QQ_CHARSET_DEFAULT);
 	}
-	*list = g_list_remove_link(*list, *list);
 
 	return value;
 }
@@ -331,7 +328,7 @@
 {
 	PurpleConnection *gc;
 	qq_data *qd;
-	GList *list,  *groups;
+	GList *groups;
 	contact_info *info;
 
 	gc = mid->gc;
@@ -341,33 +338,76 @@
 	info = mid->info;
 
 	groups = purple_request_fields_get_groups(fields);
-	list = purple_request_field_group_get_fields(groups->data);
-	info->uid = parse_field(&list, FALSE);
-	info->nick = parse_field(&list, FALSE);
-	info->name = parse_field(&list, FALSE);
-	info->age = parse_field(&list, FALSE);
-	info->gender = parse_field(&list, TRUE);
-	info->country = parse_field(&list, FALSE);
-	info->province = parse_field(&list, FALSE);
-	info->city = parse_field(&list, FALSE);
-	groups = g_list_remove_link(groups, groups);
-	list = purple_request_field_group_get_fields(groups->data);
-	info->horoscope = parse_field(&list, TRUE);
-	info->occupation = parse_field(&list, FALSE);
-	info->zodiac = parse_field(&list, TRUE);
-	info->blood = parse_field(&list, TRUE);
-	info->college = parse_field(&list, FALSE);
-	info->email = parse_field(&list, FALSE);
-	info->address = parse_field(&list, FALSE);
-	info->zipcode = parse_field(&list, FALSE);
-	info->hp_num = parse_field(&list, FALSE);
-	info->tel = parse_field(&list, FALSE);
-	info->homepage = parse_field(&list, FALSE);
-	groups = g_list_remove_link(groups, groups);
-	list = purple_request_field_group_get_fields(groups->data);
-	info->intro = parse_field(&list, FALSE);
-	groups = g_list_remove_link(groups, groups);
+	while (groups != NULL) {
+		PurpleRequestFieldGroup *group = groups->data;
+		const char *g_name = purple_request_field_group_get_title(group);
+		GList *fields = purple_request_field_group_get_fields(group);
+
+		if (g_name == NULL)
+			continue;
+
+		while (fields != NULL) {
+			PurpleRequestField *field = fields->data;
+			const char *f_id = purple_request_field_get_id(field);
+
+			if (!strcmp(QQ_PRIMARY_INFORMATION, g_name)) {
+
+				if (!strcmp(f_id, "uid"))
+					info->uid = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "nick"))
+					info->nick = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "name"))
+					info->name = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "age"))
+					info->age = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "gender"))
+					info->gender = parse_field(field, TRUE);
+				else if (!strcmp(f_id, "country"))
+					info->country = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "province"))
+					info->province = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "city"))
+					info->city = parse_field(field, FALSE);
+
+			} else if (!strcmp(QQ_ADDITIONAL_INFORMATION, g_name)) {
 
+				if (!strcmp(f_id, "horoscope"))
+					info->horoscope = parse_field(field, TRUE);
+				else if (!strcmp(f_id, "occupation"))
+					info->occupation = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "zodiac"))
+					info->zodiac = parse_field(field, TRUE);
+				else if (!strcmp(f_id, "blood"))
+					info->blood = parse_field(field, TRUE);
+				else if (!strcmp(f_id, "college"))
+					info->college = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "email"))
+					info->email = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "address"))
+					info->address = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "zipcode"))
+					info->zipcode = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "hp_num"))
+					info->hp_num = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "tel"))
+					info->tel = parse_field(field, FALSE);
+				else if (!strcmp(f_id, "homepage"))
+					info->homepage = parse_field(field, FALSE);
+
+			} else if (!strcmp(QQ_INTRO, g_name)) {
+
+				if (!strcmp(f_id, "intro"))
+					info->intro = parse_field(field, FALSE);
+
+			}
+
+			fields = fields->next;
+		}
+
+		groups = groups->next;
+	}
+
+	/* This casting looks like a horrible idea to me -DAA */
 	qq_send_packet_modify_info(gc, (gchar **) info);
 
 	g_strfreev((gchar **) mid->info);
@@ -437,6 +477,7 @@
 		add_string_field_to_group(group, "country", QQ_COUNTRY, info->country);
 		add_string_field_to_group(group, "province", QQ_PROVINCE, info->province);
 		add_string_field_to_group(group, "city", QQ_CITY, info->city);
+
 		group = setup_field_group(fields, QQ_ADDITIONAL_INFORMATION);
 		add_choice_field_to_group(group, "horoscope", QQ_HOROSCOPE, info->horoscope, horoscope_names, QQ_HOROSCOPE_SIZE);
 		add_string_field_to_group(group, "occupation", QQ_OCCUPATION, info->occupation);
--- a/libpurple/protocols/qq/qq_proxy.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/qq/qq_proxy.c	Tue Oct 16 07:48:36 2007 +0000
@@ -258,6 +258,7 @@
 static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
 {
 	gint fd = -1;
+	int flags;
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n");
 	fd = socket(PF_INET, SOCK_DGRAM, 0);
@@ -269,7 +270,8 @@
 	}
 
 	/* we use non-blocking mode to speed up connection */
-	fcntl(fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 
 	/* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
 	 *
@@ -301,7 +303,8 @@
 		}		/* if errno */
 	} else {		/* connect returns 0 */
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
-		fcntl(fd, F_SETFL, 0);
+		flags = fcntl(fd, F_GETFL);
+		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
 		phb->func(phb->data, fd, NULL);
 	}
 
--- a/libpurple/protocols/qq/udp_proxy_s5.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/qq/udp_proxy_s5.c	Tue Oct 16 07:48:36 2007 +0000
@@ -33,6 +33,7 @@
 	struct sockaddr_in sin;
 	int len, error;
 	socklen_t errlen;
+	int flags;
 
 	purple_input_remove(phb->inpa);
 	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
@@ -89,7 +90,8 @@
 		close(phb->udpsock);
 		return;
 	}
-	fcntl(phb->udpsock, F_SETFL, 0);
+	flags = fcntl(phb->udpsock, F_GETFL);
+	fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK);
 
 	if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
 		phb->func(phb->data, phb->udpsock, NULL);
@@ -106,6 +108,7 @@
 	struct sockaddr_in sin, ctlsin;
 	int port; 
 	socklen_t ctllen;
+	int flags;
 
 	purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
 
@@ -133,7 +136,8 @@
 		return;
 	}
 
-	fcntl(phb->udpsock, F_SETFL, O_NONBLOCK);
+	flags = fcntl(phb->udpsock, F_GETFL);
+	fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK);
 
 	port = g_ntohs(ctlsin.sin_port) + 1;
 	while (1) {
@@ -287,6 +291,7 @@
 	struct PHB *phb = data;
 	socklen_t len;
 	int error = ETIMEDOUT;
+	int flags;
 
 	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n");
 
@@ -306,7 +311,8 @@
 		g_free(phb);
 		return;
 	}
-	fcntl(source, F_SETFL, 0);
+	flags = fcntl(source, F_GETFL);
+	fcntl(source, F_SETFL, flags & ~O_NONBLOCK);
 
 	i = 0;
 	buf[0] = 0x05;		/* SOCKS version 5 */
@@ -343,6 +349,8 @@
 gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
 {
 	gint fd;
+	int flags;
+
 	purple_debug(PURPLE_DEBUG_INFO, "QQ",
 		   "Connecting to %s:%d via %s:%d using SOCKS5\n",
 		   phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi));
@@ -352,7 +360,8 @@
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
 
-	fcntl(fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 	if (connect(fd, addr, addrlen) < 0) {
 		if ((errno == EINPROGRESS) || (errno == EINTR)) {
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
@@ -363,7 +372,8 @@
 		}
 	} else {
 		purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
-		fcntl(fd, F_SETFL, 0);
+		flags = fcntl(fd, F_GETFL);
+		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
 		_qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE);
 	}
 
--- a/libpurple/protocols/sametime/sametime.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Tue Oct 16 07:48:36 2007 +0000
@@ -5563,7 +5563,7 @@
     msgA = _("No matches");
     msgB = _("The identifier '%s' did not match any users in your"
 	     " Sametime community.");
-    msg = g_strdup_printf(msgB, NSTR(res->name));
+    msg = g_strdup_printf(msgB, (res && res->name) ? NSTR(res->name) : "");
 
     purple_notify_error(gc, _("No Matches"), msgA, msg);
 
--- a/libpurple/protocols/simple/simple.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Tue Oct 16 07:48:36 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/protocols/yahoo/yahoo_aliases.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Tue Oct 16 07:48:36 2007 +0000
@@ -117,7 +117,7 @@
 					if (alias != NULL) {
 						serv_got_alias(cb->gc, yid, alias);
 						purple_debug_info("yahoo","Fetched alias '%s' (%s)\n",alias,id);
-					} else if (g_strcasecmp((alias!=NULL?alias:""),(b->alias!=NULL?b->alias:"")) != 0) {
+					} else if (b->alias != alias && strcmp(b->alias, "") != 0) {
 					/* Or if we have an alias that Yahoo doesn't, send it up */
 						yahoo_update_alias(cb->gc, yid, b->alias);
 						purple_debug_info("yahoo","Sent alias '%s'\n", b->alias);
@@ -216,7 +216,7 @@
 	struct callback_data *cb;
 	PurpleBuddy *buddy;
 	PurpleUtilFetchUrlData *url_data;
-   
+
 	g_return_if_fail(alias!= NULL);
 	g_return_if_fail(who!=NULL);
 	g_return_if_fail(gc!=NULL);
@@ -224,7 +224,7 @@
 	purple_debug_info("yahoo", "Sending '%s' as new alias for user '%s'.\n",alias, who);
 
 	buddy = purple_find_buddy(gc->account, who);
-	if (buddy->proto_data == NULL) {
+	if (buddy == NULL || buddy->proto_data == NULL) {
 		purple_debug_info("yahoo", "Missing proto_data (get_yahoo_aliases must have failed), bailing out\n");
 		return;
 	}
--- a/libpurple/proxy.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/proxy.c	Tue Oct 16 07:48:36 2007 +0000
@@ -449,6 +449,8 @@
 static void
 proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
 {
+	int flags;
+
 	purple_debug_info("proxy", "Connecting to %s:%d with no proxy\n",
 			connect_data->host, connect_data->port);
 
@@ -460,7 +462,8 @@
 		return;
 	}
 
-	fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(connect_data->fd, F_GETFL);
+	fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
 #ifndef _WIN32
 	fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
 #endif
@@ -881,6 +884,8 @@
 static void
 proxy_connect_http(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
 {
+	int flags;
+
 	purple_debug_info("proxy",
 			   "Connecting to %s:%d via %s:%d using HTTP\n",
 			   connect_data->host, connect_data->port,
@@ -895,14 +900,16 @@
 		return;
 	}
 
-	fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(connect_data->fd, F_GETFL);
+	fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
 #ifndef _WIN32
 	fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
 #endif
 
 	if (connect(connect_data->fd, addr, addrlen) != 0)
 	{
-		if ((errno == EINPROGRESS) || (errno == EINTR)) {
+		if ((errno == EINPROGRESS) || (errno == EINTR))
+		{
 			purple_debug_info("proxy", "Connection in progress\n");
 
 			if (connect_data->port != 80)
@@ -1036,6 +1043,8 @@
 static void
 proxy_connect_socks4(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
 {
+	int flags;
+
 	purple_debug_info("proxy",
 			   "Connecting to %s:%d via %s:%d using SOCKS4\n",
 			   connect_data->host, connect_data->port,
@@ -1050,7 +1059,8 @@
 		return;
 	}
 
-	fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(connect_data->fd, F_GETFL);
+	fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
 #ifndef _WIN32
 	fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
 #endif
@@ -1604,6 +1614,8 @@
 static void
 proxy_connect_socks5(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
 {
+	int flags;
+
 	purple_debug_info("proxy",
 			   "Connecting to %s:%d via %s:%d using SOCKS5\n",
 			   connect_data->host, connect_data->port,
@@ -1618,7 +1630,8 @@
 		return;
 	}
 
-	fcntl(connect_data->fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(connect_data->fd, F_GETFL);
+	fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
 #ifndef _WIN32
 	fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
 #endif
--- a/libpurple/prpl.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/prpl.h	Tue Oct 16 07:48:36 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,14 @@
 	/** new user registration */
 	void (*register_user)(PurpleAccount *);
 
-	/* get "chat buddy" info and away message */
+	/**
+	 * @deprecated Use #PurplePluginProtocolInfo.get_info instead.
+	 */
 	void (*get_cb_info)(PurpleConnection *, int, const char *who);
+	/**
+	 * @deprecated Use #PurplePluginProtocolInfo.get_cb_real_name and
+	 *             #PurplePluginProtocolInfo.status_text instead.
+	 */
 	void (*get_cb_away)(PurpleConnection *, int, const char *who);
 
 	/** save/store buddy's alias on server list/roster */
@@ -348,9 +364,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/savedstatuses.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/savedstatuses.c	Tue Oct 16 07:48:36 2007 +0000
@@ -761,10 +761,13 @@
 {
 	GList *popular = NULL;
 	GList *cur;
-	int i;
+	unsigned int i;
 	PurpleSavedStatus *next;
 
-	/* Copy 'how_many' elements to a new list */
+	/* Copy 'how_many' elements to a new list. If 'how_many' is 0, then copy all of 'em. */
+	if (how_many == 0)
+		how_many = (unsigned int) -1;
+
 	i = 0;
 	cur = saved_statuses;
 	while ((i < how_many) && (cur != NULL))
@@ -773,7 +776,7 @@
 		if ((!purple_savedstatus_is_transient(next)
 			|| purple_savedstatus_get_message(next) != NULL))
 		{
-			popular = g_list_prepend(popular, cur->data);
+			popular = g_list_prepend(popular, next);
 			i++;
 		}
 		cur = cur->next;
--- a/libpurple/savedstatuses.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/savedstatuses.h	Tue Oct 16 07:48:36 2007 +0000
@@ -170,11 +170,8 @@
 /**
  * Returns the n most popular saved statuses.  "Popularity" is
  * determined by when the last time a saved_status was used and
- * how many times it has been used.  If the current status would
- * normally show up in this list, then it is omited and instead
- * the "how_many+1" saved status will appear in the list.  Also
- * transient statuses without messages are not included in the
- * list.
+ * how many times it has been used. Transient statuses without
+ * messages are not included in the list.
  *
  * @param how_many The maximum number of saved statuses
  *                 to return, or '0' to get all saved
--- a/libpurple/sslconn.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/sslconn.h	Tue Oct 16 07:48:36 2007 +0000
@@ -204,17 +204,19 @@
  									   void *data);
 
 /**
-  * Makes a SSL connection using an already open file descriptor.
-  *
-  * @param account    The account making the connection.
-  * @param fd         The file descriptor.
-  * @param func       The SSL input handler function.
-  * @param error_func The SSL error handler function.
-  * @param host       The hostname of the other peer (to verify the CN)
-  * @param data       User-defined data.
-  *
-  * @return The SSL connection handle.
-  */
+ * Makes a SSL connection using an already open file descriptor.
+ *
+ * @param account    The account making the connection.
+ * @param fd         The file descriptor.
+ * @param func       The SSL input handler function.
+ * @param error_func The SSL error handler function.
+ * @param host       The hostname of the other peer (to verify the CN)
+ * @param data       User-defined data.
+ *
+ * @return The SSL connection handle.
+ *
+ * @since 2.2.0
+ */
 PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
                                            PurpleSslInputFunction func,
                                            PurpleSslErrorFunction error_func,
@@ -268,6 +270,8 @@
  *
  * @return The peer certificate chain, in the order of certificate, issuer,
  *         issuer's issuer, etc. @a NULL if no certificates have been provided,
+ *
+ * @since 2.2.0
  */
 GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc);
 
--- a/libpurple/status.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/status.h	Tue Oct 16 07:48:36 2007 +0000
@@ -121,7 +121,8 @@
 #define PURPLE_TUNE_TRACK	"tune_track"
 #define PURPLE_TUNE_TIME	"tune_time"
 #define PURPLE_TUNE_YEAR	"tune_year"
-#define PURPLE_TUNE_URL	"tune_url"
+#define PURPLE_TUNE_URL		"tune_url"
+#define PURPLE_TUNE_FULL	"tune_full"
 
 #ifdef __cplusplus
 extern "C" {
--- a/libpurple/tests/test_cipher.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/tests/test_cipher.c	Tue Oct 16 07:48:36 2007 +0000
@@ -8,7 +8,6 @@
 #include "tests.h"
 
 #include "../cipher.h"
-#include "../signal.h"
 
 /******************************************************************************
  * MD4 Tests
--- a/libpurple/util.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/util.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1517,8 +1517,8 @@
 							plain = g_string_append(plain, alt->str);
 						if(!src && xhtml)
 							xhtml = g_string_append(xhtml, alt->str);
+						g_string_free(alt, TRUE);
 					}
-					g_string_free(alt, TRUE);
 					g_string_free(src, TRUE);
 					continue;
 				}
@@ -2550,15 +2550,14 @@
 
 	filename_full = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", user_dir, filename);
 
-	ret = purple_util_write_data_to_file_absolute(filename_full,
-						      data,size);
+	ret = purple_util_write_data_to_file_absolute(filename_full, data, size);
 
 	g_free(filename_full);
 	return ret;
 }
 
 gboolean
-purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, size_t size)
+purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, gssize size)
 {
 	gchar *filename_temp;
 	FILE *file;
@@ -2568,6 +2567,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 */
@@ -2593,7 +2594,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 */
@@ -3494,7 +3495,7 @@
 	gboolean full;
 	int len;
 
-	if ((s = g_strstr_len(data, data_len, "Location: ")) == NULL)
+	if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL)
 		/* We're not being redirected */
 		return FALSE;
 
--- a/libpurple/util.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/util.h	Tue Oct 16 07:48:36 2007 +0000
@@ -607,7 +607,7 @@
  *
  */
 gboolean
-purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, size_t size);
+purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, gssize size);
 
 /**
  * Read the contents of a given file and parse the results into an
--- a/libpurple/win32/global.mak	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/win32/global.mak	Tue Oct 16 07:48:36 2007 +0000
@@ -11,7 +11,7 @@
 # Locations of our various dependencies
 WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
 ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3
-GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11
+GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11-daa1
 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0
 GTK_BIN ?= $(GTK_TOP)/bin
 BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
@@ -22,6 +22,7 @@
 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl58
 SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.2
 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
+GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13
 
 # Where we installing this stuff to?
 PIDGIN_INSTALL_DIR := $(PIDGIN_TREE_TOP)/win32-install-dir
--- a/libpurple/win32/libc_interface.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/win32/libc_interface.c	Tue Oct 16 07:48:36 2007 +0000
@@ -138,12 +138,22 @@
 
 /* fcntl.h */
 /* This is not a full implementation of fcntl. Update as needed.. */
-int wpurple_fcntl(int socket, int command, int val) {
+int wpurple_fcntl(int socket, int command, ...) {
+
 	switch( command ) {
+	case F_GETFL:
+		return 0;
+
 	case F_SETFL:
 	{
+		va_list args;
+		int val;
 		int ret=0;
 
+		va_start(args, command);
+		val = va_arg(args, int);
+		va_end(args);
+
 		switch( val ) {
 		case O_NONBLOCK:
 		{
@@ -152,7 +162,7 @@
 			break;
 		}
 		case 0:
-	        {
+		{
 			u_long imode=0;
 			ret = ioctlsocket(socket, FIONBIO, &imode);
 			break;
--- a/libpurple/win32/libc_interface.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/win32/libc_interface.h	Tue Oct 16 07:48:36 2007 +0000
@@ -75,8 +75,8 @@
 wpurple_ioctl( fd, command, val )
 
 /* fcntl.h */
-#define fcntl( fd, command, val ) \
-wpurple_fcntl( fd, command, val )
+#define fcntl( fd, command, ... ) \
+wpurple_fcntl( fd, command, ##__VA_ARGS__ )
 
 /* arpa/inet.h */
 #define inet_aton( name, addr ) \
--- a/libpurple/win32/libc_internal.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/win32/libc_internal.h	Tue Oct 16 07:48:36 2007 +0000
@@ -49,9 +49,10 @@
 char* wpurple_strerror( int errornum );
 
 /* fcntl.h */
-int wpurple_fcntl(int socket, int command, int val);
-#define F_SETFL 1
-#define O_NONBLOCK 1
+int wpurple_fcntl(int socket, int command, ...);
+#define F_GETFL 3
+#define F_SETFL 4
+#define O_NONBLOCK 04000
 
 /* sys/ioctl.h */
 #define SIOCGIFCONF 0x8912 /* get iface list */
--- a/libpurple/xmlnode.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/libpurple/xmlnode.c	Tue Oct 16 07:48:36 2007 +0000
@@ -131,7 +131,7 @@
 		if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
 				!strcmp(attr_node->name, attr))
 		{
-			if(node->child == attr_node) {
+			if(sibling == NULL) {
 				node->child = attr_node->next;
 			} else {
 				sibling->next = attr_node->next;
@@ -146,6 +146,19 @@
 	}
 }
 
+/* Compare two nullable xmlns strings.
+ * They are considered equal if they're both NULL or the strings are equal
+ */
+static gboolean _xmlnode_compare_xmlns(const char *xmlns1, const char *xmlns2) {
+	gboolean equal = FALSE;
+
+	if (xmlns1 == NULL && xmlns2 == NULL)
+		equal = TRUE;
+	else if (xmlns1 != NULL && xmlns2 != NULL && !strcmp(xmlns1, xmlns2))
+		equal = TRUE;
+
+	return equal;
+}
 
 void
 xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
@@ -159,9 +172,9 @@
 	{
 		if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
 		   !strcmp(attr_node->name, attr) &&
-		   !strcmp(attr_node->xmlns, xmlns))
+		   _xmlnode_compare_xmlns(xmlns, attr_node->xmlns))
 		{
-			if(node->child == attr_node) {
+			if(sibling == NULL) {
 				node->child = attr_node->next;
 			} else {
 				sibling->next = attr_node->next;
@@ -238,7 +251,8 @@
 
 	for(x = node->child; x; x = x->next) {
 		if(x->type == XMLNODE_TYPE_ATTRIB &&
-		   !strcmp(attr, x->name) && !strcmp(x->xmlns, xmlns)) {
+		   !strcmp(attr, x->name) &&
+		   _xmlnode_compare_xmlns(xmlns, x->xmlns)) {
 			return x->data;
 		}
 	}
@@ -326,6 +340,7 @@
 	child_name = names[1];
 
 	for(x = parent->child; x; x = x->next) {
+		/* XXX: Is it correct to ignore the namespace for the match if none was specified? */
 		const char *xmlns = NULL;
 		if(ns)
 			xmlns = xmlnode_get_namespace(x);
@@ -673,6 +688,7 @@
 	g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL);
 
 	for(sibling = node->next; sibling; sibling = sibling->next) {
+		/* XXX: Is it correct to ignore the namespace for the match if none was specified? */
 		const char *xmlns = NULL;
 		if(ns)
 			xmlns = xmlnode_get_namespace(sibling);
--- a/pidgin/gtkaccount.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkaccount.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1328,8 +1328,9 @@
 					break;
 
 				case PURPLE_PREF_STRING_LIST:
-					gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter);
-					gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)), &iter, 1, &value2, -1);
+					value2 = NULL;
+					if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
+						gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)), &iter, 1, &value2, -1);
 					purple_account_set_string(account, setting, value2);
 					break;
 
--- a/pidgin/gtkblist.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkblist.c	Tue Oct 16 07:48:36 2007 +0000
@@ -3877,13 +3877,26 @@
 {
 	static GtkWidget *menu = NULL;
 	GList *convs = NULL;
+	GList *chats, *ims;
 
 	if (menu) {
 		gtk_widget_destroy(menu);
 		menu = NULL;
 	}
 
-	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+	ims = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
+				PIDGIN_UNSEEN_TEXT, FALSE, 0);
+
+	chats = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
+				PIDGIN_UNSEEN_NICK, FALSE, 0);
+
+	if(ims && chats)
+		convs = g_list_concat(ims, chats);
+	else if(ims && !chats)
+		convs = ims;
+	else if(!ims && chats)
+		convs = chats;
+
 	if (!convs)
 		/* no conversations added, don't show the menu */
 		return;
@@ -3905,9 +3918,13 @@
 	switch (event->button) {
 		case 1:
 			convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
-															PIDGIN_UNSEEN_TEXT, TRUE, 1);
+							PIDGIN_UNSEEN_TEXT, FALSE, 1);
+
+			if(!convs)
+				convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
+								PIDGIN_UNSEEN_NICK, FALSE, 1);
 			if (convs) {
-				purple_conversation_present((PurpleConversation*)convs->data);
+				pidgin_conv_present_conversation((PurpleConversation*)convs->data);
 				g_list_free(convs);
 			}
 			break;
@@ -3923,6 +3940,7 @@
                         PidginBuddyList *gtkblist)
 {
 	GList *convs = NULL;
+	GList *ims, *chats;
 	GList *l = NULL;
 
 	if (type != PURPLE_CONV_UPDATE_UNSEEN)
@@ -3939,7 +3957,19 @@
 		gtkblist->menutrayicon = NULL;
 	}
 
-	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+	ims = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
+				PIDGIN_UNSEEN_TEXT, FALSE, 0);
+
+	chats = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
+				PIDGIN_UNSEEN_NICK, FALSE, 0);
+
+	if(ims && chats)
+		convs = g_list_concat(ims, chats);
+	else if(ims && !chats)
+		convs = ims;
+	else if(!ims && chats)
+		convs = chats;
+
 	if (convs) {
 		GtkWidget *img = NULL;
 		GString *tooltip_text = NULL;
@@ -3947,7 +3977,14 @@
 		tooltip_text = g_string_new("");
 		l = convs;
 		while (l != NULL) {
-			int count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count"));
+			int count = 0;
+			PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
+
+			if(gtkconv)
+				count = gtkconv->unseen_count;
+			else if(purple_conversation_get_data(l->data, "unseen-count"))
+				count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count"));
+
 			g_string_append_printf(tooltip_text,
 					ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
 					count, purple_conversation_get_name(l->data));
@@ -4347,7 +4384,6 @@
                                 gpointer user_data)
 {
 	PurpleAccount *account;
-	PurpleStatusType *status_type;
 	gchar *escaped, *text;
 	GtkWidget *button, *label, *image, *hbox;
 	GdkPixbuf *pixbuf;
@@ -4362,8 +4398,8 @@
 	hbox = gtk_hbox_new(FALSE, 6);
 
 	/* Create the icon */
-	if ((status_type = purple_account_get_status_type_with_primitive(account,
-							PURPLE_STATUS_OFFLINE))) {
+	if (purple_account_get_status_type_with_primitive(account,
+							PURPLE_STATUS_OFFLINE) != NULL) {
 		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
 		if (pixbuf != NULL) {
 			image = gtk_image_new_from_pixbuf(pixbuf);
@@ -5301,7 +5337,7 @@
 static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node)
 {
 	PurplePresence *presence;
-	GdkPixbuf *status, *avatar, *emblem;
+	GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
 	char *mark;
 	char *idle = NULL;
 	gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded;
@@ -5311,7 +5347,7 @@
 
 	if (editing_blist)
 		return;
-	
+
 	status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy,
 						PIDGIN_STATUS_ICON_SMALL);
 
@@ -5358,6 +5394,8 @@
 		}
 	}
 
+	prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL);
+
 	gtk_tree_store_set(gtkblist->treemodel, iter,
 			   STATUS_ICON_COLUMN, status,
 			   STATUS_ICON_VISIBLE_COLUMN, TRUE,
@@ -5367,8 +5405,8 @@
 			   BUDDY_ICON_COLUMN, avatar,
 			   BUDDY_ICON_VISIBLE_COLUMN, biglist,
 			   EMBLEM_COLUMN, emblem,
-			   EMBLEM_VISIBLE_COLUMN, emblem,
-			   PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL),
+			   EMBLEM_VISIBLE_COLUMN, (emblem != NULL),
+			   PROTOCOL_ICON_COLUMN, prpl_icon,
 			   PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
 			   BGCOLOR_COLUMN, NULL,
 			   CONTACT_EXPANDER_COLUMN, NULL,
@@ -5378,10 +5416,14 @@
 
 	g_free(mark);
 	g_free(idle);
+	if(emblem)
+		g_object_unref(emblem);
 	if(status)
 		g_object_unref(status);
 	if(avatar)
 		g_object_unref(avatar);
+	if(prpl_icon)
+		g_object_unref(prpl_icon);
 }
 
 /* This is a variation on the original gtk_blist_update_contact. Here we
@@ -5502,9 +5544,7 @@
 
 	if(purple_account_is_connected(chat->account)) {
 		GtkTreeIter iter;
-		GdkPixbuf *status;
-		GdkPixbuf *avatar;
-		GdkPixbuf *emblem;
+		GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
 		char *mark;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 		PidginBlistNode *ui;
@@ -5535,6 +5575,8 @@
 			mark = bold;
 		}
 
+		prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL);
+
 		gtk_tree_store_set(gtkblist->treemodel, &iter,
 				STATUS_ICON_COLUMN, status,
 				STATUS_ICON_VISIBLE_COLUMN, TRUE,
@@ -5542,17 +5584,21 @@
 				BUDDY_ICON_VISIBLE_COLUMN,  purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"),
 				EMBLEM_COLUMN, emblem,
 				EMBLEM_VISIBLE_COLUMN, emblem != NULL,
-				PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL),
+				PROTOCOL_ICON_COLUMN, prpl_icon,
 				PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
 				NAME_COLUMN, mark,
 				GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
 				-1);
 
 		g_free(mark);
+		if(emblem)
+			g_object_unref(emblem);
 		if(status)
 			g_object_unref(status);
 		if(avatar)
 			g_object_unref(avatar);
+		if(prpl_icon)
+			g_object_unref(prpl_icon);
 	} else {
 		pidgin_blist_hide_node(list, node, TRUE);
 	}
--- a/pidgin/gtkblist.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkblist.h	Tue Oct 16 07:48:36 2007 +0000
@@ -371,6 +371,8 @@
  * @param selected  Whether this buddy is selected. If TRUE, the markup will not change the color.
  * @param aliased  TRUE to return the appropriate alias of this buddy, FALSE to return its screenname and status information
  * @return The markup for this buddy
+ *
+ * @since 2.1.0
  */
 gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased);
 
@@ -382,11 +384,15 @@
  *
  * @param node The buddy list node to show a tooltip for
  * @param widget The widget to draw the tooltip on
+ *
+ * @since 2.1.0
  */
 void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget);
 
 /**
  * Destroys the current (if any) Buddy List tooltip
+ *
+ * @since 2.1.0
  */
 void pidgin_blist_tooltip_destroy(void);
 
--- a/pidgin/gtkconv.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkconv.c	Tue Oct 16 07:48:36 2007 +0000
@@ -152,6 +152,7 @@
 static void add_chat_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const char *old_name);
 static gboolean tab_complete(PurpleConversation *conv);
 static void pidgin_conv_updated(PurpleConversation *conv, PurpleConvUpdateType type);
+static void conv_set_unseen(PurpleConversation *gtkconv, PidginUnseenState state);
 static void gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state);
 static void update_typing_icon(PidginConversation *gtkconv);
 static const char *item_factory_translate_func (const char *path, gpointer func_data);
@@ -2502,11 +2503,16 @@
 	gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model), 
 			&(gtkconv->infopane_iter),
 			CONV_EMBLEM_COLUMN, emblem, -1);
+	if (emblem)
+		g_object_unref(emblem);
 
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) {
+		emblem = pidgin_create_prpl_icon(gtkconv->active_conv->account, PIDGIN_PRPL_ICON_SMALL);
 		gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model),
 			&(gtkconv->infopane_iter),
-			CONV_PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(gtkconv->active_conv->account, PIDGIN_PRPL_ICON_SMALL), -1);
+			CONV_PROTOCOL_ICON_COLUMN, emblem, -1);
+		if (emblem)
+			g_object_unref(emblem);
 	}
 
 	/* XXX seanegan Why do I have to do this? */
@@ -2852,8 +2858,9 @@
 		if (gtkconv != NULL && gtkconv->active_conv != conv)
 			continue;
 		if (gtkconv == NULL) {
-			if (!hidden_only ||
-					!purple_conversation_get_data(conv, "unseen-count"))
+			if (!purple_conversation_get_data(conv, "unseen-count") ||
+				!purple_conversation_get_data(conv, "unseen-state") ||
+				GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-state"))<min_state)
 				continue;
 			r = g_list_prepend(r, conv);
 			c++;
@@ -3536,6 +3543,7 @@
 
 		if (b == item_buddy) {
 			gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
+			g_list_free(child);
 			break;
 		}
 	}
@@ -4386,45 +4394,54 @@
 	gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1);
 }
 
-
 static void resize_imhtml_cb(PidginConversation *gtkconv)
 {
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
-        int wrapped_lines;
-        int lines;
-        GdkRectangle oneline;
+	int wrapped_lines;
+	int lines;
+	GdkRectangle oneline;
 	GtkRequisition sr;
-        int height;
-        int pad_top, pad_inside, pad_bottom;
+	int height, diff;
+	int pad_top, pad_inside, pad_bottom;
 
 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
 
-        wrapped_lines = 1;
-        gtk_text_buffer_get_start_iter(buffer, &iter);
-        gtk_text_view_get_iter_location(GTK_TEXT_VIEW(gtkconv->entry), &iter, &oneline);
-        while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(gtkconv->entry), &iter))
-                wrapped_lines++;
-
-        lines = gtk_text_buffer_get_line_count(buffer);
-
-        /* Show a maximum of 4 lines */
-        lines = MIN(lines, 4);
-        wrapped_lines = MIN(wrapped_lines, 4);
-
-        pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
-        pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
-        pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(gtkconv->entry));
-
-        height = (oneline.height + pad_top + pad_bottom) * lines;
-        height += (oneline.height + pad_inside) * (wrapped_lines - lines);
+	wrapped_lines = 1;
+	gtk_text_buffer_get_start_iter(buffer, &iter);
+	gtk_text_view_get_iter_location(GTK_TEXT_VIEW(gtkconv->entry), &iter, &oneline);
+	while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(gtkconv->entry), &iter))
+		wrapped_lines++;
+
+	lines = gtk_text_buffer_get_line_count(buffer);
+
+	/* Show a maximum of 4 lines */
+	lines = MIN(lines, 4);
+	wrapped_lines = MIN(wrapped_lines, 4);
+
+	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
+	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
+	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(gtkconv->entry));
+
+	height = (oneline.height + pad_top + pad_bottom) * lines;
+	height += (oneline.height + pad_inside) * (wrapped_lines - lines);
 
 	gtkconv->auto_resize = TRUE;
-        g_idle_add(reset_auto_resize_cb, gtkconv);
-	gtk_widget_size_request(gtkconv->lower_hbox, &sr);
-	if (sr.height < height + PIDGIN_HIG_BOX_SPACE) {
+	g_idle_add(reset_auto_resize_cb, gtkconv);
+
+	diff = height - gtkconv->entry->allocation.height;
+
+	if (diff > 0) {
+		gtk_widget_size_request(gtkconv->lower_hbox, &sr);
 		gtkconv->entry_growing = TRUE;
-	        gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height + PIDGIN_HIG_BOX_SPACE);
+
+		/* uncomment this to auto resize even after the user manually
+		   resizes
+		gtk_paned_set_position(GTK_PANED(gtkconv->lower_hbox->parent->parent),
+			-1);
+		*/
+		gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
+			diff + gtkconv->lower_hbox->allocation.height);
 	}
 }
 
@@ -4756,7 +4773,6 @@
 	g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 #endif
 
-
 	rend = gtk_cell_renderer_pixbuf_new();
 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_PROTOCOL_ICON_COLUMN, NULL);
@@ -5177,6 +5193,8 @@
 			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
 			purple_conversation_set_ui_ops(conv, NULL);
 			ui_ops->create_conversation = pidgin_conv_new;
+		} else {
+			/* TODO: update the unseen_state data on the conv here */
 		}
 	} else {
 		/* new message for an IM */
@@ -6535,6 +6553,7 @@
 		gboolean ellipsis = FALSE;
 		/* 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);
@@ -6568,7 +6587,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);
@@ -6587,15 +6606,18 @@
 			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\"";
+			else
+				style = "color=\"#cc0000\"";
 		} 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;
 		}
 		
 		// nosuke's tab width patch
@@ -6609,21 +6631,28 @@
 		else
 			title_tmp = title;
 
-		
-		if (*style != '\0')
+
+		if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT ||
+				gtkconv->unseen_state == PIDGIN_UNSEEN_NICK ||
+				gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT)
+			bold = TRUE;
+
+		if (style || bold)
 		{
 			char *html_title,*label;
 
 			html_title = g_markup_escape_text(title_tmp, -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_tmp);
-		
+
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
 
@@ -6685,13 +6714,24 @@
 
 static void
 wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message,
-		PurpleConversation *conv, PurpleMessageFlags flag, gpointer null)
+		PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
 {
 	if (conv == NULL || PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 		return;
-	if (flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) {
-		purple_conversation_set_data(conv, "unseen-count",
-				GINT_TO_POINTER(GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")) + 1));
+	if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) {
+		PidginUnseenState unseen = PIDGIN_UNSEEN_NONE;
+
+		if ((flags & PURPLE_MESSAGE_NICK) == PURPLE_MESSAGE_NICK)
+			unseen = PIDGIN_UNSEEN_NICK;
+		else if (((flags & PURPLE_MESSAGE_SYSTEM) == PURPLE_MESSAGE_SYSTEM) ||
+			  ((flags & PURPLE_MESSAGE_ERROR) == PURPLE_MESSAGE_ERROR))
+			unseen = PIDGIN_UNSEEN_EVENT;
+		else if ((flags & PURPLE_MESSAGE_NO_LOG) == PURPLE_MESSAGE_NO_LOG)
+			unseen = PIDGIN_UNSEEN_NO_LOG;
+		else
+			unseen = PIDGIN_UNSEEN_TEXT;
+
+		conv_set_unseen(conv, unseen);
 		purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
 	}
 }
@@ -7485,6 +7525,7 @@
 		return FALSE;
 
 	purple_conversation_set_data(conv, "unseen-count", NULL);
+	purple_conversation_set_data(conv, "unseen-state", NULL);
 	purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
 	private_gtkconv_new(conv, FALSE);
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -7957,6 +7998,38 @@
 }
 
 static void
+conv_set_unseen(PurpleConversation *conv, PidginUnseenState state)
+{
+	int unseen_count = 0;
+	PidginUnseenState unseen_state = PIDGIN_UNSEEN_NONE;
+
+	if(purple_conversation_get_data(conv, "unseen-count"))
+		unseen_count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"));
+
+	if(purple_conversation_get_data(conv, "unseen-state"))
+		unseen_state = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-state"));
+
+	if (state == PIDGIN_UNSEEN_NONE)
+	{
+		unseen_count = 0;
+		unseen_state = PIDGIN_UNSEEN_NONE;
+	}
+	else
+	{
+		if (state >= PIDGIN_UNSEEN_TEXT)
+			unseen_count++;
+
+		if (state > unseen_state)
+			unseen_state = state;
+	}
+
+	purple_conversation_set_data(conv, "unseen-count", GINT_TO_POINTER(unseen_count));
+	purple_conversation_set_data(conv, "unseen-state", GINT_TO_POINTER(unseen_state));
+
+	purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+}
+
+static void
 gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state)
 {
 	if (state == PIDGIN_UNSEEN_NONE)
--- a/pidgin/gtkconv.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkconv.h	Tue Oct 16 07:48:36 2007 +0000
@@ -253,6 +253,8 @@
  * @param conv  The conversation.
  *
  * @return  Wheter Pidgin UI was successfully attached.
+ *
+ * @since 2.2.0
  */
 gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv);
 
--- a/pidgin/gtkdocklet.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkdocklet.c	Tue Oct 16 07:48:36 2007 +0000
@@ -38,6 +38,7 @@
 #include "gtkprefs.h"
 #include "gtksavedstatuses.h"
 #include "gtksound.h"
+#include "gtkstatusbox.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
 #include "gtkdocklet.h"
@@ -144,15 +145,22 @@
 		if (ui_ops->set_tooltip) {
 			GString *tooltip_text = g_string_new("");
 			for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
-				if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) {
-					PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
-					if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1)
-						g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
-					else
-						g_string_append_printf(tooltip_text,
-							ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
-							gtkconv->unseen_count,
-							gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
+				PurpleConversation *conv = (PurpleConversation *)l->data;
+				PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+
+				if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) {
+					g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
+				} else if(gtkconv) {
+					g_string_append_printf(tooltip_text,
+						ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
+						gtkconv->unseen_count,
+						gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
+				} else {
+					g_string_append_printf(tooltip_text,
+						ngettext("%d unread message from %s\n", "%d unread messages from %s\n",
+						GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))),
+						GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")),
+						purple_conversation_get_name(conv));
 				}
 			}
 
@@ -356,6 +364,10 @@
 }
 #endif
 
+/* There is a lot of code here for handling the status submenu, much of
+ * which is duplicated from the gtkstatusbox. It'd be nice to add API
+ * somewhere to simplify this (either in the statusbox, or in libpurple).
+ */
 static void
 show_custom_status_editor_cb(GtkMenuItem *menuitem, gpointer user_data)
 {
@@ -369,6 +381,70 @@
 		purple_savedstatus_is_transient(saved_status) ? saved_status : NULL);
 }
 
+static PurpleSavedStatus *
+create_transient_status(PurpleStatusPrimitive primitive, PurpleStatusType *status_type)
+{
+	PurpleSavedStatus *saved_status = purple_savedstatus_new(NULL, primitive);
+
+	if(status_type != NULL) {
+		GList *tmp, *active_accts = purple_accounts_get_all_active();
+		for (tmp = active_accts; tmp != NULL; tmp = tmp->next) {
+			purple_savedstatus_set_substatus(saved_status,
+				(PurpleAccount*) tmp->data, status_type, NULL);
+		}
+		g_list_free(active_accts);
+	}
+
+	return saved_status;
+}
+
+static void
+activate_status_account_cb(GtkMenuItem *menuitem, gpointer user_data)
+{
+	PurpleStatusType *status_type;
+	PurpleStatusPrimitive primitive;
+	PurpleSavedStatus *saved_status = NULL;
+	GList *iter = purple_savedstatuses_get_all();
+	GList *tmp, *active_accts = purple_accounts_get_all_active();
+
+	status_type = (PurpleStatusType *)user_data;
+	primitive = purple_status_type_get_primitive(status_type);
+
+	for (; iter != NULL; iter = iter->next) {
+		PurpleSavedStatus *ss = iter->data;
+		if ((purple_savedstatus_get_type(ss) == primitive) && purple_savedstatus_is_transient(ss) &&
+			purple_savedstatus_has_substatuses(ss))
+		{
+			gboolean found = FALSE;
+			/* The currently enabled accounts must have substatuses for all the active accts */
+			for(tmp = active_accts; tmp != NULL; tmp = tmp->next) {
+				PurpleAccount *acct = tmp->data;
+				PurpleSavedStatusSub *sub = purple_savedstatus_get_substatus(ss, acct);
+				if (sub) {
+					const PurpleStatusType *sub_type = purple_savedstatus_substatus_get_type(sub);
+					const char *subtype_status_id = purple_status_type_get_id(sub_type);
+					if (subtype_status_id && !strcmp(subtype_status_id,
+							purple_status_type_get_id(status_type)))
+						found = TRUE;
+				}
+			}
+			if (!found)
+				continue;
+			saved_status = ss;
+			break;
+		}
+	}
+
+	g_list_free(active_accts);
+
+	/* Create a new transient saved status if we weren't able to find one */
+	if (saved_status == NULL)
+		saved_status = create_transient_status(primitive, status_type);
+
+	/* Set the status for each account */
+	purple_savedstatus_activate(saved_status);
+}
+
 static void
 activate_status_primitive_cb(GtkMenuItem *menuitem, gpointer user_data)
 {
@@ -382,7 +458,7 @@
 
 	/* Create a new transient saved status if we weren't able to find one */
 	if (saved_status == NULL)
-		saved_status = purple_savedstatus_new(NULL, primitive);
+		saved_status = create_transient_status(primitive, NULL);
 
 	/* Set the status for each account */
 	purple_savedstatus_activate(saved_status);
@@ -407,7 +483,7 @@
 	GdkPixbuf *pixbuf;
 	GtkWidget *image;
 
-	menuitem = gtk_image_menu_item_new_with_mnemonic(str);
+	menuitem = gtk_image_menu_item_new_with_label(str);
 
 	if (menu)
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
@@ -425,31 +501,67 @@
 	return menuitem;
 }
 
+static void
+add_account_statuses(GtkWidget *menu, PurpleAccount *account)
+{
+	GList *l;
+
+	for (l = purple_account_get_status_types(account); l != NULL; l = l->next) {
+		PurpleStatusType *status_type = (PurpleStatusType *)l->data;
+		PurpleStatusPrimitive prim;
+
+		if (!purple_status_type_is_user_settable(status_type))
+			continue;
+
+		prim = purple_status_type_get_primitive(status_type);
+
+		new_menu_item_with_status_icon(menu,
+			purple_status_type_get_name(status_type),
+			prim, G_CALLBACK(activate_status_account_cb),
+			status_type, 0, 0, NULL);
+	}
+}
+
 static GtkWidget *
 docklet_status_submenu()
 {
 	GtkWidget *submenu, *menuitem;
 	GList *popular_statuses, *cur;
+	PidginStatusBox *statusbox = NULL;
 
 	submenu = gtk_menu_new();
 	menuitem = gtk_menu_item_new_with_label(_("Change Status"));
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
 
-	new_menu_item_with_status_icon(submenu, _("Available"),
-		PURPLE_STATUS_AVAILABLE, G_CALLBACK(activate_status_primitive_cb),
-		GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE), 0, 0, NULL);
+	if(pidgin_blist_get_default_gtk_blist() != NULL) {
+		statusbox = PIDGIN_STATUS_BOX(pidgin_blist_get_default_gtk_blist()->statusbox);
+	}
 
-	new_menu_item_with_status_icon(submenu, _("Away"),
-		PURPLE_STATUS_AWAY, G_CALLBACK(activate_status_primitive_cb),
-		GINT_TO_POINTER(PURPLE_STATUS_AWAY), 0, 0, NULL);
+	if(statusbox && statusbox->account != NULL) {
+		add_account_statuses(submenu, statusbox->account);
+	} else if(statusbox && statusbox->token_status_account != NULL) {
+		add_account_statuses(submenu, statusbox->token_status_account);
+	} else {
+		new_menu_item_with_status_icon(submenu, _("Available"),
+			PURPLE_STATUS_AVAILABLE, G_CALLBACK(activate_status_primitive_cb),
+			GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE), 0, 0, NULL);
 
-	new_menu_item_with_status_icon(submenu, _("Invisible"),
-		PURPLE_STATUS_INVISIBLE, G_CALLBACK(activate_status_primitive_cb),
-		GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE), 0, 0, NULL);
+		new_menu_item_with_status_icon(submenu, _("Away"),
+			PURPLE_STATUS_AWAY, G_CALLBACK(activate_status_primitive_cb),
+			GINT_TO_POINTER(PURPLE_STATUS_AWAY), 0, 0, NULL);
+
+		new_menu_item_with_status_icon(submenu, _("Do not disturb"),
+			PURPLE_STATUS_UNAVAILABLE, G_CALLBACK(activate_status_primitive_cb),
+			GINT_TO_POINTER(PURPLE_STATUS_UNAVAILABLE), 0, 0, NULL);
 
-	new_menu_item_with_status_icon(submenu, _("Offline"),
-		PURPLE_STATUS_OFFLINE, G_CALLBACK(activate_status_primitive_cb),
-		GINT_TO_POINTER(PURPLE_STATUS_OFFLINE), 0, 0, NULL);
+		new_menu_item_with_status_icon(submenu, _("Invisible"),
+			PURPLE_STATUS_INVISIBLE, G_CALLBACK(activate_status_primitive_cb),
+			GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE), 0, 0, NULL);
+
+		new_menu_item_with_status_icon(submenu, _("Offline"),
+			PURPLE_STATUS_OFFLINE, G_CALLBACK(activate_status_primitive_cb),
+			GINT_TO_POINTER(PURPLE_STATUS_OFFLINE), 0, 0, NULL);
+	}
 
 	popular_statuses = purple_savedstatuses_get_popular(6);
 	if (popular_statuses != NULL)
@@ -473,6 +585,87 @@
 	return menuitem;
 }
 
+
+
+static void
+plugin_act(GtkObject *obj, PurplePluginAction *pam)
+{
+	if (pam && pam->callback)
+		pam->callback(pam);
+}
+
+static void
+build_plugin_actions(GtkWidget *menu, PurplePlugin *plugin,
+		gpointer context)
+{
+	GtkWidget *menuitem;
+	PurplePluginAction *action = NULL;
+	GList *actions, *l;
+
+	actions = PURPLE_PLUGIN_ACTIONS(plugin, context);
+
+	for (l = actions; l != NULL; l = l->next)
+	{
+		if (l->data)
+		{
+			action = (PurplePluginAction *) l->data;
+			action->plugin = plugin;
+			action->context = context;
+
+			menuitem = gtk_menu_item_new_with_label(action->label);
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+			g_signal_connect(G_OBJECT(menuitem), "activate",
+					G_CALLBACK(plugin_act), action);
+			g_object_set_data_full(G_OBJECT(menuitem), "plugin_action",
+								   action,
+								   (GDestroyNotify)purple_plugin_action_free);
+			gtk_widget_show(menuitem);
+		}
+		else
+			pidgin_separator(menu);
+	}
+
+	g_list_free(actions);
+}
+
+
+static void
+docklet_plugin_actions(GtkWidget *menu)
+{
+	GtkWidget *menuitem, *submenu;
+	PurplePlugin *plugin = NULL;
+	GList *l;
+	int c = 0;
+
+	g_return_if_fail(menu != NULL);
+
+	/* Add a submenu for each plugin with custom actions */
+	for (l = purple_plugins_get_loaded(); l; l = l->next) {
+		plugin = (PurplePlugin *) l->data;
+
+		if (PURPLE_IS_PROTOCOL_PLUGIN(plugin))
+			continue;
+
+		if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin))
+			continue;
+
+		menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name));
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+		gtk_widget_show(menuitem);
+
+		submenu = gtk_menu_new();
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+		gtk_widget_show(submenu);
+
+		build_plugin_actions(submenu, plugin, NULL);
+
+		c++;
+	}
+	if(c>0)
+		pidgin_separator(menu);
+}
+
 static void
 docklet_menu() {
 	static GtkWidget *menu = NULL;
@@ -539,6 +732,9 @@
 
 	pidgin_separator(menu);
 
+	/* add plugin actions */
+	docklet_plugin_actions(menu);
+
 	pidgin_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT, G_CALLBACK(purple_core_quit), NULL, 0, 0, NULL);
 
 #ifdef _WIN32
@@ -569,7 +765,7 @@
 			if (pending) {
 				GList *l = get_pending_list(1);
 				if (l != NULL) {
-					purple_conversation_present((PurpleConversation *)l->data);
+					pidgin_conv_present_conversation((PurpleConversation *)l->data);
 					g_list_free(l);
 				}
 			} else {
--- a/pidgin/gtkft.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkft.c	Tue Oct 16 07:48:36 2007 +0000
@@ -912,7 +912,7 @@
 
 		gtk_widget_show(tmp->window);
 	} else {
-		gtk_widget_show(dialog->window);
+		gtk_window_present(GTK_WINDOW(dialog->window));
 	}
 }
 
--- a/pidgin/gtkimhtml.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkimhtml.h	Tue Oct 16 07:48:36 2007 +0000
@@ -441,6 +441,8 @@
  * @param id       The id to associate with the image.
  *
  * @return A new IM/HTML Scalable object with an image.
+ *
+ * @since 2.1.0
  */
 /*
  * TODO: All this animation code could be combined much better with
@@ -837,6 +839,8 @@
  *
  * @param imhtml  The GTK+ IM/HTML.
  * @param flags   The connection flag which describes the allowed types of formatting.
+ *
+ * @since 2.1.0
  */
 void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
 
--- a/pidgin/gtklog.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtklog.c	Tue Oct 16 07:48:36 2007 +0000
@@ -680,6 +680,7 @@
 	PidginLogViewer *lv = NULL;
 	const char *name = screenname;
 	char *title;
+	GdkPixbuf *prpl_icon;
 
 	g_return_if_fail(account != NULL);
 	g_return_if_fail(screenname != NULL);
@@ -717,9 +718,14 @@
 		title = g_strdup_printf(_("Conversations with %s"), name);
 	}
 
+	prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
+
 	display_log_viewer(ht, purple_log_get_logs(type, screenname, account),
-			title, gtk_image_new_from_pixbuf(pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM)), 
+			title, gtk_image_new_from_pixbuf(prpl_icon),
 			purple_log_get_total_size(type, screenname, account));
+
+	if (prpl_icon)
+		g_object_unref(prpl_icon);
 	g_free(title);
 }
 
--- a/pidgin/gtknotify.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtknotify.c	Tue Oct 16 07:48:36 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	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtksound.c	Tue Oct 16 07:48:36 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	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkutils.c	Tue Oct 16 07:48:36 2007 +0000
@@ -111,19 +111,20 @@
 		desc = pango_font_description_from_string(font);
 	} else if (purple_running_gnome()) {
 		/* Use the GNOME "document" font, if applicable */
-		char *path, *font;
+		char *path;
 
 		if ((path = g_find_program_in_path("gconftool-2"))) {
+			char *font = NULL;
 			g_free(path);
-			if (!g_spawn_command_line_sync(
+			if (g_spawn_command_line_sync(
 					"gconftool-2 -g /desktop/gnome/interface/document_font_name",
-					&font, NULL, NULL, NULL))
-				return;
+					&font, NULL, NULL, NULL)) {
+				desc = pango_font_description_from_string(font);
+			}
+			g_free(font);
 		}
-		desc = pango_font_description_from_string(font);
-		g_free(font);
 	}
-	
+
 	if (desc) {
 		gtk_widget_modify_font(imhtml, desc);
 		pango_font_description_free(desc);
@@ -849,16 +850,14 @@
 gboolean
 pidgin_check_if_dir(const char *path, GtkFileSelection *filesel)
 {
-	char *dirname;
+	char *dirname = NULL;
 
 	if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
 		/* append a / if needed */
 		if (path[strlen(path) - 1] != G_DIR_SEPARATOR) {
 			dirname = g_strconcat(path, G_DIR_SEPARATOR_S, NULL);
-		} else {
-			dirname = g_strdup(path);
 		}
-		gtk_file_selection_set_filename(filesel, dirname);
+		gtk_file_selection_set_filename(filesel, (dirname != NULL) ? dirname : path);
 		g_free(dirname);
 		return TRUE;
 	}
@@ -1177,14 +1176,15 @@
 	label = gtk_widget_get_accessible (l);
 
 	/* Make sure mnemonics work */
-        gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
-	
+	gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
+
 	/* Create the labeled-by relation */
 	set = atk_object_ref_relation_set (acc);
 	rel_obj[0] = label;
 	relation = atk_relation_new (rel_obj, 1, ATK_RELATION_LABELLED_BY);
 	atk_relation_set_add (set, relation);
 	g_object_unref (relation);
+	g_object_unref(set);
 
 	/* Create the label-for relation */
 	set = atk_object_ref_relation_set (label);
@@ -1192,6 +1192,7 @@
 	relation = atk_relation_new (rel_obj, 1, ATK_RELATION_LABEL_FOR);
 	atk_relation_set_add (set, relation);
 	g_object_unref (relation);
+	g_object_unref(set);
 }
 
 void
@@ -1525,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,
@@ -1558,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;
 		}
 
--- a/pidgin/gtkutils.h	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/gtkutils.h	Tue Oct 16 07:48:36 2007 +0000
@@ -376,6 +376,8 @@
  *
  * @param conn   The connection to get information from.
  * @param name   The user to get information about.
+ *
+ * @since 2.1.0
  */
 void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name);
 
@@ -385,6 +387,8 @@
  * @param conn   The connection to get information from.
  * @param name   The user to get information about.
  * @param chatid The chat id.
+ *
+ * @since 2.1.0
  */
 void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name, int chatid);
 
@@ -423,6 +427,8 @@
  *
  * @param w The widget that we want to label.
  * @param l A GtkLabel that we want to use as the label for the widget.
+ *
+ * @since 2.2.0
  */
 void pidgin_set_accessible_relations(GtkWidget *w, GtkWidget *l);
 
@@ -437,6 +443,8 @@
  *        where the menu shall be drawn. This is an output parameter.
  * @param push_in This is an output parameter?
  * @param data Not used by this particular position function.
+ *
+ * @since 2.1.0
  */
 void pidgin_menu_position_func_helper(GtkMenu *menu, gint *x, gint *y,
 										gboolean *push_in, gpointer data);
@@ -671,6 +679,8 @@
  *
  * @return               A newly created text GtkComboBox containing a GtkEntry
  *                       child.
+ *
+ * @since 2.2.0
  */
 GtkWidget *pidgin_text_combo_box_entry_new(const char *default_item, GList *items);
 
@@ -680,6 +690,8 @@
  * @param widget         The simple text GtkComboBoxEntry equivalent widget
  *
  * @return               The text in the widget's entry. It must not be freed
+ *
+ * @since 2.2.0
  */
 const char *pidgin_text_combo_box_entry_get_text(GtkWidget *widget);
 
@@ -688,6 +700,8 @@
  *
  * @param widget         The simple text GtkComboBoxEntry equivalent widget
  * @param text           The text to set
+ *
+ * @since 2.2.0
  */
 void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
 
--- a/pidgin/plugins/history.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/plugins/history.c	Tue Oct 16 07:48:36 2007 +0000
@@ -42,6 +42,7 @@
 	GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
 	char *header;
 	char *protocol;
+	char *escaped_alias;
 
 	convtype = purple_conversation_get_type(c);
 	gtkconv = PIDGIN_CONVERSATION(c);
@@ -120,10 +121,12 @@
 	if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
 		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", options);
 
-	header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias,
+	escaped_alias = g_markup_escape_text(alias, -1);
+	header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), escaped_alias,
 							 purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time)));
 	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
 	g_free(header);
+	g_free(escaped_alias);
 
 	g_strchomp(history);
 	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), history, options);
--- a/pidgin/win32/winpidgin.c	Sun Oct 07 03:21:27 2007 +0000
+++ b/pidgin/win32/winpidgin.c	Tue Oct 16 07:48:36 2007 +0000
@@ -545,7 +545,7 @@
 	if (strstr(lpszCmdLine, "-d") || strstr(lpszCmdLine, "-h") || strstr(lpszCmdLine, "-v")) {
 		/* If stdout hasn't been redirected to a file, alloc a console
 		 *  (_istty() doesn't work for stuff using the GUI subsystem) */
-		if (_fileno(stdout) == -1) {
+		if (_fileno(stdout) == -1 || _fileno(stdout) == -2) {
 			LPFNATTACHCONSOLE MyAttachConsole = NULL;
 			if ((hmod = GetModuleHandle("kernel32.dll"))) {
 				MyAttachConsole =