changeset 27817:3c26ef82d556

propagate from branch 'im.pidgin.pidgin' (head a32d1e1358009ce730a50b32e63fbe6c3a63c4fb) to branch 'im.pidgin.pidgin.yaz' (head 45a0829edb54b3543727448c64974e743fefe761)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sat, 05 Apr 2008 13:15:49 +0000
parents f2d96d120309 (current diff) 29f20ae16bf9 (diff)
children bc6cc2c04476
files autogen.sh configure.ac libpurple/notify.c libpurple/protocols/jabber/jabber.c libpurple/protocols/oscar/oscar.c libpurple/util.h pidgin/gtkconv.c
diffstat 17 files changed, 346 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/autogen.sh	Wed Apr 02 04:58:28 2008 +0000
+++ b/autogen.sh	Sat Apr 05 13:15:49 2008 +0000
@@ -134,5 +134,5 @@
 ###############################################################################
 # Run configure
 ###############################################################################
-echo "running ./configure ${CONFIGURE_ARGS} $@"
-#./configure ${CONFIGURE_ARGS} $@
+echo "running ./configure ${CONFIGURE_FLAGS} $@"
+#./configure ${CONFIGURE_FLAGS} $@
--- a/configure.ac	Wed Apr 02 04:58:28 2008 +0000
+++ b/configure.ac	Sat Apr 05 13:15:49 2008 +0000
@@ -404,11 +404,18 @@
 					X11_LIBS="$x_libpath_add"
 					X11_CFLAGS="$x_incpath_add"
 				else
-					with_x=no
+					AC_MSG_ERROR([
+X11 development headers not found.
+Use --without-x if you do not need X11 support.
+])
 				fi
 			])
 		AC_SUBST(X11_LIBS)
 		AC_SUBST(X11_CFLAGS)
+	else
+		enable_screensaver=no
+		enable_sm=no
+		enable_gestures=no
 	fi
 
 	dnl #######################################################################
@@ -438,9 +445,15 @@
 			if test "x$enable_screensaver" = "xyes" ; then
 				AC_DEFINE(USE_SCREENSAVER, 1, [Define if we're using XScreenSaver.])
 				AC_SUBST(XSS_LIBS)
+			else
+				AC_MSG_ERROR([
+XScreenSaver extension development headers not found.
+Use --disable-screensaver if you do not need XScreenSaver extension support,
+this is required for detecting idle time by mouse and keyboard usage.
+])
 			fi
 		else
-			enable_screensaver=no
+			AC_MSG_ERROR([X support is required to build with XScreenSaver extensions])
 		fi
 	fi
 
@@ -461,9 +474,14 @@
 			if test "x$enable_sm" = "xyes"; then
 				AC_DEFINE(USE_SM, 1, [Define if we're using X Session Management.])
 				AC_SUBST(SM_LIBS)
+			else
+				AC_MSG_ERROR([
+X session management development headers not found.
+Use --disable-sm if you do not need session management support.
+])
 			fi
 		else
-			enable_sm=no
+			AC_MSG_ERROR([X support is required to build with X session management support])
 		fi
 	fi
 
@@ -482,8 +500,10 @@
 	if test "x$enable_startup_notification" = "xyes"; then
 		PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [
 			AC_MSG_RESULT(no)
-			enable_startup_notification=no
-		])
+			AC_MSG_ERROR([
+Startup notification development headers not found.
+Use --disable-startup-notification if you do not need it.
+])])
 
 		if test "x$enable_startup_notification" = "xyes"; then
 			AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.])
@@ -498,8 +518,10 @@
 	if test "x$enable_gtkspell" = "xyes" ; then
 		PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , [
 			AC_MSG_RESULT(no)
-			enable_gtkspell=no
-		])
+			AC_MSG_ERROR([
+GtkSpell development headers not found.
+Use --disable-gtkspell if you do not need it.
+])])
 		if test "x$enable_gtkspell" = "xyes" ; then
 			AC_DEFINE(USE_GTKSPELL, 1, [Define if we're using GtkSpell])
 			AC_SUBST(GTKSPELL_CFLAGS)
@@ -528,6 +550,11 @@
 			AC_DEFINE(HAVE_EVOLUTION_ADDRESSBOOK, 1, [Define if we're using evolution addressbook.])
 			AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS)
 			AC_SUBST(EVOLUTION_ADDRESSBOOK_LIBS)
+		else
+			AC_MSG_ERROR([
+Evolution development headers not found.
+Use --disable-gevolution if you do not need it.
+])
 		fi
 	fi
 
@@ -537,8 +564,10 @@
 	if test "x$enable_cap" = "xyes"; then
 		PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.3,,[
 			AC_MSG_RESULT(no)
-			enable_cap="no"
-			])
+			AC_MSG_ERROR([
+sqlite3 development headers not found.
+Use --disable-cap if you do not need the Contact Availability Prediction plugin.
+])])
 	fi
         
 
@@ -675,24 +704,39 @@
 			[], [$GSTREAMER_LIBS])
 	], [
 		AC_MSG_RESULT(no)
-		enable_gst="no"
-	])
+		AC_MSG_ERROR([
+GStreamer development headers not found.
+Use --disable-gstreamer if you do not need GStreamer (sound) support.
+])])
 fi
 
 dnl #######################################################################
 dnl # Check for Meanwhile headers (for Sametime)
 dnl #######################################################################
-PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [
-	have_meanwhile="yes"
-], [
-	have_meanwhile="no"
-])
+AC_ARG_ENABLE(meanwhile,
+	[AC_HELP_STRING([--disable-meanwhile],
+		[compile without meanwhile (required for Sametime support)])],
+	enable_meanwhile="$enableval", enable_meanwhile="yes")
+if test "x$enable_meanwhile" = "xyes"; then
+	PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [
+		have_meanwhile="yes"
+	], [
+		have_meanwhile="no"
+		AC_MSG_ERROR([
+Meanwhile development headers not found.
+Use --disable-meanwhile if you do not need meanwhile (Sametime) support.
+])])
+fi
 AC_SUBST(MEANWHILE_CFLAGS)
 AC_SUBST(MEANWHILE_LIBS)
 
 dnl #######################################################################
 dnl # Check for Native Avahi headers (for Bonjour)
 dnl #######################################################################
+AC_ARG_ENABLE(avahi,
+	[AC_HELP_STRING([--disable-avahi],
+		[compile without avahi (required for Bonjour support)])],
+	enable_avahi="$enableval", enable_avahi="yes")
 AC_ARG_WITH(avahi-client-includes, [AC_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"])
 AC_ARG_WITH(avahi-client-libs, [AC_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"])
 AVAHI_CFLAGS=""
@@ -724,6 +768,12 @@
 fi
 AC_CHECK_LIB(avahi-client, avahi_client_new, [avahilibs=yes], [avahilibs=no], $AVAHI_LIBS)
 
+if test "x$enable_avahi" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then
+	AC_MSG_ERROR([
+avahi development headers not found.
+Use --disable-avahi if you do not need avahi (Bonjour) support.
+])
+fi
 AC_SUBST(AVAHI_CFLAGS)
 AC_SUBST(AVAHI_LIBS)
 
@@ -1150,13 +1200,15 @@
 fi
 
 if test "x$enable_dbus" = "xyes" ; then
-	PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.35 dbus-glib-1 >= 0.35], [
+	PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.60 dbus-glib-1 >= 0.60], [
 		AC_SUBST(DBUS_CFLAGS)
 		AC_SUBST(DBUS_LIBS)
 		enable_dbus=yes
 	], [
-		enable_dbus=no
-	])
+		AC_MSG_ERROR([
+D-Bus development headers not found.
+Use --disable-dbus if you do not need D-Bus support.
+])])
 
 dnl Check for libnm_glib; if we don't have it, oh well
 	if test "x$enable_libnm" = "xyes" ; then
@@ -1169,8 +1221,10 @@
 		],
 		[
 			AC_MSG_RESULT(no)
-			enable_libnm=no
-		])
+			AC_MSG_ERROR([
+NetworkManager development headers not found.
+Use --disable-nm if you do not need NetworkManager support.
+])])
 		AC_SUBST(LIBNM_CFLAGS)
 		AC_SUBST(LIBNM_LIBS)
 	fi
@@ -1318,7 +1372,10 @@
 		enable_mono=yes
 	], [
 		AC_MSG_RESULT(no)
-		enable_mono=no
+		AC_MSG_ERROR([
+Mono development headers not found.
+Use --disable-mono if you do not need Mono support.
+])
 	])
 	if test x"$enable_mono" = x"yes"; then
 		oldLIBS="$LIBS"
@@ -1353,8 +1410,9 @@
 if test "$enable_plugins" = no ; then
 	enable_perl=no
 fi
-
+looked_for_perl="no"
 if test "$enable_perl" = yes ; then
+	looked_for_perl="yes"
 	AC_PATH_PROG(perlpath, perl)
 	AC_MSG_CHECKING(for Perl compile flags)
 	PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null`
@@ -1481,6 +1539,13 @@
 	AM_CONDITIONAL(USE_PERL, false)
 fi
 
+if test "x$looked_for_perl" = "xyes" -a "x$enable_perl" = "xno"; then
+	AC_MSG_ERROR([
+Perl development headers not found.
+Use --disable-perl if you do not need Perl scripting support.
+])
+fi
+
 dnl #######################################################################
 dnl # SSL support
 dnl #
@@ -1490,7 +1555,7 @@
 dnl These two are inverses of each other <-- stolen from evolution!
 
 AC_ARG_ENABLE(gnutls,
-	[  --enable-gnutls=[yes,no]  attempt to use GnuTLS for SSL support (preferred) [default=yes]],
+	[  --enable-gnutls=[yes,no]  attempt to use GnuTLS for SSL support [default=yes]],
 	[enable_gnutls="$enableval"],
 	[enable_gnutls="yes"])
 
@@ -1500,13 +1565,14 @@
 	[enable_nss="yes"])
 
 msg_ssl="None. MSN, Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable!"
-
+looked_for_gnutls="no"
 dnl #
 dnl # Check for GnuTLS if it's specified.
 dnl #
 if test "x$enable_gnutls" != "xno"; then
 	enable_gnutls="no"
 	prefix=`eval echo $prefix`
+	looked_for_gnutls="yes"
 
 	AC_ARG_WITH(gnutls-includes,
 		[  --with-gnutls-includes=PREFIX   location of GnuTLS includes.],
@@ -1590,7 +1656,9 @@
 dnl #
 dnl # Check for NSS if it's specified, or if GnuTLS checks failed.
 dnl #
+looked_for_nss="no"
 if test "x$enable_nss" != "xno"; then
+	looked_for_nss="yes"
 
 	AC_ARG_WITH(nspr-includes,
 		[AC_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])],
@@ -1870,6 +1938,24 @@
 	msg_ssl=$msg_nss
 elif test "x$msg_gnutls" != "x"; then
 	msg_ssl=$msg_gnutls
+elif test "x$looked_for_gnutls" = "xyes" -a "x$looked_for_nss" = "xyes"; then
+	AC_MSG_ERROR([
+Neither GnuTLS or NSS SSL development headers found.
+Use --disable-nss --disable-gnutls if you do not need SSL support.
+MSN, Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable!
+])
+elif test "x$looked_for_gnutls" = "xyes"; then
+	AC_MSG_ERROR([
+GnuTLS SSL development headers not found.
+Use --disable-gnutls if you do not need SSL support.
+MSN, Novell Groupwise and Google Talk will not work without SSL support.
+])
+elif test "x$looked_for_nss" = "xyes"; then
+	AC_MSG_ERROR([
+NSS SSL development headers not found.
+Use --disable-nss if you do not need SSL support.
+MSN, Novell Groupwise and Google Talk will not work without SSL support.
+])
 fi
 
 dnl #######################################################################
@@ -1903,6 +1989,10 @@
 	if test "$TCLCONFIG" = "no"; then
 		AC_MSG_RESULT([no])
 		enable_tcl=no
+		AC_MSG_ERROR([
+Tcl development headers not found.
+Use --disable-tcl if you do not need Tcl scripting support.
+])
 	else
 		. $TCLCONFIG
 		AC_MSG_CHECKING([Tcl version compatability])
@@ -1967,6 +2057,10 @@
 	if test "$TKCONFIG" = "no"; then
 		AC_MSG_RESULT([no])
 		enable_tk=no
+		AC_MSG_ERROR([
+Tk development headers not found.
+Use --disable-tk if you do not need Tk scripting support.
+])
 	else
 		. $TKCONFIG
 		eval "TK_LIB_SPEC=\"$TK_LIB_SPEC\""
@@ -2134,7 +2228,7 @@
 dnl # Check for check
 dnl #######################################################################
 PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[
-					ifdef([AM_PATH_CHECK],
+					ifdef([[AM_PATH_CHECK]],
 					[AM_PATH_CHECK(0.8.2,:,:)],
 					[AC_MSG_RESULT([no, testing is disabled])])
 				  ])
--- a/libpurple/buddyicon.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/buddyicon.c	Sat Apr 05 13:15:49 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file icon.c Buddy Icon API
+ * @file buddyicon.c Buddy Icon API
  * @ingroup core
  */
 
@@ -31,36 +31,85 @@
 #include "imgstore.h"
 #include "util.h"
 
-typedef struct _PurpleBuddyIconData PurpleBuddyIconData;
-
 /* NOTE: Instances of this struct are allocated without zeroing the memory, so
  * NOTE: be sure to update purple_buddy_icon_new() if you add members. */
 struct _PurpleBuddyIcon
 {
 	PurpleAccount *account;    /**< The account the user is on.          */
-	PurpleStoredImage *img;    /**< The id of the stored image with the
+	PurpleStoredImage *img;    /**< The stored image containing
 	                                the icon data.                       */
 	char *username;            /**< The username the icon belongs to.    */
 	char *checksum;            /**< The protocol checksum.               */
 	int ref_count;             /**< The buddy icon reference count.      */
 };
 
+/**
+ * This is the big grand daddy hash table that contains references to
+ * everybody's buddy icons.
+ *
+ * Key is a PurpleAccount.
+ * Value is another hash table, usually referred to as "icon_cache."
+ * For this inner hash table:
+ *    Key is the username of the buddy whose icon is being stored.
+ *    Value is the PurpleBuddyIcon for this buddy.
+ */
 static GHashTable *account_cache = NULL;
+
+/**
+ * This hash table contains a bunch of PurpleStoredImages that are
+ * shared across all accounts.
+ *
+ * Key is the filename for this image as constructed by
+ * purple_util_get_image_filename().  So it is the base16 encoded
+ * sha-1 hash plus an appropriate file extension.  For example:
+ *   "0f4972d17d1e70e751c43c90c948e72efbff9796.gif"
+ *
+ * The value is a PurpleStoredImage containing the icon data.  These
+ * images are reference counted, and when the count reaches 0
+ * imgstore.c emits the image-deleting signal and we remove the image
+ * from the hash table (but it might still be saved on disk, if the
+ * icon is being used by offline accounts or some such).
+ */
 static GHashTable *icon_data_cache = NULL;
+
+/**
+ * This hash table contains references counts for how many times each
+ * icon in the ~/.purple/icons/ directory is being used.  It's pretty
+ * crazy.  It maintains the reference count across sessions, too, so
+ * if you exit Pidgin then this hash table is reconstructed the next
+ * time Pidgin starts.
+ *
+ * Key is the filename for this image as constructed by
+ * purple_util_get_image_filename().  So it is the base16 encoded
+ * sha-1 hash plus an appropriate file extension.  For example:
+ *   "0f4972d17d1e70e751c43c90c948e72efbff9796.gif"
+ *
+ * The value is a GINT_TO_POINTER count of the number of times this
+ * icon is used.  So if four of your buddies are using an icon, and
+ * you have the icon set for two of your accounts, then this number
+ * will be six.  When this reference count reaches 0 the icon will
+ * be deleted from disk.
+ */
 static GHashTable *icon_file_cache = NULL;
 
-static void delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name);
-
 /* This one is used for both custom buddy icons
  * on PurpleContacts and account icons. */
 static GHashTable *pointer_icon_cache = NULL;
 
 static char       *cache_dir     = NULL;
+
+/** "Should icons be cached to disk?" */
 static gboolean    icon_caching  = TRUE;
 
 /* For ~/.gaim to ~/.purple migration. */
 static char *old_icons_dir = NULL;
 
+static void delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name);
+
+/*
+ * Begin functions for dealing with the on-disk icon cache
+ */
+
 static void
 ref_filename(const char *filename)
 {
@@ -158,6 +207,14 @@
 	g_free(path);
 }
 
+/*
+ * End functions for dealing with the on-disk icon cache
+ */
+
+/*
+ * Begin functions for dealing with the in-memory icon cache
+ */
+
 static gboolean
 value_equals(gpointer key, gpointer value, gpointer user_data)
 {
@@ -222,6 +279,10 @@
 	return img;
 }
 
+/*
+ * End functions for dealing with the in-memory icon cache
+ */
+
 static PurpleBuddyIcon *
 purple_buddy_icon_create(PurpleAccount *account, const char *username)
 {
--- a/libpurple/buddyicon.h	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/buddyicon.h	Sat Apr 05 13:15:49 2008 +0000
@@ -45,7 +45,7 @@
 /*@{*/
 
 /**
- * Creates a new buddy icon structure and populate it.
+ * Creates a new buddy icon structure and populates it.
  *
  * If the buddy icon already exists, you'll get a reference to that structure,
  * which will have been updated with the data supplied.
--- a/libpurple/imgstore.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/imgstore.c	Sat Apr 05 13:15:49 2008 +0000
@@ -34,11 +34,9 @@
 #include "util.h"
 
 static GHashTable *imgstore;
-static int nextid = 0;
+static unsigned int nextid = 0;
 
-/**
- * Stored image
- *
+/*
  * NOTE: purple_imgstore_add() creates these without zeroing the memory, so
  * NOTE: make sure to update that function when adding members.
  */
@@ -75,7 +73,14 @@
 {
 	PurpleStoredImage *img = purple_imgstore_add(data, size, filename);
 	if (img) {
-		img->id = ++nextid;
+		/*
+		 * Use the next unused id number.  We do it in a loop on the
+		 * off chance that nextid wraps back around to 0 and the hash
+		 * table still contains entries from the first time around.
+		 */
+		do {
+			img->id = ++nextid;
+		} while (img->id == 0 || g_hash_table_lookup(imgstore, &(img->id)) != NULL);
 
 		g_hash_table_insert(imgstore, &(img->id), img);
 	}
--- a/libpurple/imgstore.h	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/imgstore.h	Sat Apr 05 13:15:49 2008 +0000
@@ -51,7 +51,11 @@
  *                      ownership of and free as appropriate.  If you want a
  *                      copy of the data, make it before calling this function.
  * @param size		Image data's size.
- * @param filename	Filename associated with image.
+ * @param filename	Filename associated with image.  This is for your
+ *                  convenience.  It could be the full path to the
+ *                  image or, more commonly, the filename of the image
+ *                  without any directory information.  It can also be
+ *                  NULL, if you don't need to keep track of a filename.
  *
  * @return The stored image.
  */
@@ -69,9 +73,14 @@
  *                      ownership of and free as appropriate.  If you want a
  *                      copy of the data, make it before calling this function.
  * @param size		Image data's size.
- * @param filename	Filename associated with image.
+ * @param filename	Filename associated with image.  This is for your
+ *                  convenience.  It could be the full path to the
+ *                  image or, more commonly, the filename of the image
+ *                  without any directory information.  It can also be
+ *                  NULL, if you don't need to keep track of a filename.
 
- * @return ID for the image.
+ * @return ID for the image.  This is a unique number that can be used
+ *         within libpurple to reference the image.
  */
 int purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename);
 
@@ -116,11 +125,13 @@
 const char *purple_imgstore_get_filename(const PurpleStoredImage *img);
 
 /**
- * Returns an extension corresponding to the image's file type.
+ * Looks at the magic numbers of the image data (the first few bytes)
+ * and returns an extension corresponding to the image's file type.
  *
  * @param img  The image.
  *
- * @return The icon's extension or "icon" if unknown.
+ * @return The image's extension (for example "png") or "icon"
+ *         if unknown.
  */
 const char *purple_imgstore_get_extension(PurpleStoredImage *img);
 
--- a/libpurple/notify.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/notify.c	Sat Apr 05 13:15:49 2008 +0000
@@ -589,7 +589,7 @@
 }
 
 
-gchar *
+const gchar *
 purple_notify_user_info_entry_get_label(PurpleNotifyUserInfoEntry *user_info_entry)
 {
 	g_return_val_if_fail(user_info_entry != NULL, NULL);
@@ -606,7 +606,7 @@
 	user_info_entry->label = g_strdup(label);
 }
 
-gchar *
+const gchar *
 purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry)
 {
 	g_return_val_if_fail(user_info_entry != NULL, NULL);
--- a/libpurple/notify.h	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/notify.h	Sat Apr 05 13:15:49 2008 +0000
@@ -596,7 +596,7 @@
  *
  * @result                    The label
  */
-gchar *purple_notify_user_info_entry_get_label(PurpleNotifyUserInfoEntry *user_info_entry);
+const gchar *purple_notify_user_info_entry_get_label(PurpleNotifyUserInfoEntry *user_info_entry);
 
 /**
  * Set the label for a PurpleNotifyUserInfoEntry
@@ -613,7 +613,7 @@
  *
  * @result                    The value
  */
-gchar *purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry);
+const gchar *purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry);
 
 /**
  * Set the value for a PurpleNotifyUserInfoEntry
--- a/libpurple/protocols/jabber/jabber.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Apr 05 13:15:49 2008 +0000
@@ -2379,7 +2379,7 @@
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
 	                  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
 	                  jabber_cmd_chat_ban,
-	                  _("ban &lt;user&gt; [room]:  Ban a user from the room."),
+	                  _("ban &lt;user&gt; [reason]:  Ban a user from the room."),
 	                  NULL);
 	purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -2403,13 +2403,13 @@
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
 	                  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
 	                  jabber_cmd_chat_join,
-	                  _("join: &lt;room&gt; [server]:  Join a chat on this server."),
+	                  _("join: &lt;room&gt; [password]:  Join a chat on this server."),
 	                  NULL);
 	purple_cmd_register("kick", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
 	                  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
 	                  jabber_cmd_chat_kick,
-	                  _("kick &lt;user&gt; [room]:  Kick a user from the room."),
+	                  _("kick &lt;user&gt; [reason]:  Kick a user from the room."),
 	                  NULL);
 	purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
--- a/libpurple/protocols/oscar/family_locate.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Sat Apr 05 13:15:49 2008 +0000
@@ -1354,7 +1354,7 @@
 }
 
 /*
- * Subtype 0x0015 - Request the info a user using the short method.  This is
+ * Subtype 0x0015 - Request the info of a user using the short method.  This is
  * what iChat uses.  It normally is VERY leniently rate limited.
  *
  * @param sn The screen name whose info you wish to request.
--- a/libpurple/protocols/oscar/oscar.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sat Apr 05 13:15:49 2008 +0000
@@ -3662,7 +3662,11 @@
 	if (purple_account_get_user_info(account) != NULL)
 		serv_set_info(gc, purple_account_get_user_info(account));
 
-	if (!od->icq)
+	if (!od->icq && strcmp(purple_account_get_username(account), purple_connection_get_display_name(gc)) != 0)
+		/*
+		 * Format the screen name for AIM accounts if it's different
+		 * than what's currently set.
+		 */
 		oscar_format_screenname(gc, account->username);
 
 	/* Set our available message based on the current status */
--- a/libpurple/status.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/status.c	Sat Apr 05 13:15:49 2008 +0000
@@ -1547,14 +1547,37 @@
 	return purple_presence_is_online(presence) ? presence->login_time : 0;
 }
 
+static int
+purple_presence_compute_score(const PurplePresence *presence)
+{
+	GList *l;
+	int score = 0;
+
+	for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
+		PurpleStatus *status = (PurpleStatus *)l->data;
+		PurpleStatusType *type = purple_status_get_type(status);
+
+		if (purple_status_is_active(status)) {
+			score += primitive_scores[purple_status_type_get_primitive(type)];
+			if (!purple_status_is_online(status)) {
+				PurpleBuddy *b = purple_presence_get_buddy(presence);
+				if (b && purple_account_supports_offline_message(purple_buddy_get_account(b), b))
+					score += primitive_scores[SCORE_OFFLINE_MESSAGE];
+			}
+		}
+	}
+	score += purple_account_get_int(purple_presence_get_account(presence), "score", 0);
+	if (purple_presence_is_idle(presence))
+		score += primitive_scores[SCORE_IDLE];
+	return score;
+}
+
 gint
 purple_presence_compare(const PurplePresence *presence1,
 		const PurplePresence *presence2)
 {
-	gboolean idle1, idle2;
 	time_t idle_time_1, idle_time_2;
 	int score1 = 0, score2 = 0;
-	GList *l;
 
 	if (presence1 == presence2)
 		return 0;
@@ -1563,49 +1586,18 @@
 	else if (presence2 == NULL)
 		return -1;
 
-	/* Compute the score of the first set of statuses. */
-	for (l = purple_presence_get_statuses(presence1); l != NULL; l = l->next)
-	{
-		PurpleStatus *status = (PurpleStatus *)l->data;
-		PurpleStatusType *type = purple_status_get_type(status);
+	if (purple_presence_is_online(presence1) &&
+			!purple_presence_is_online(presence2))
+		return -1;
+	else if (purple_presence_is_online(presence2) &&
+			!purple_presence_is_online(presence1))
+		return 1;
 
-		if (purple_status_is_active(status)) {
-			score1 += primitive_scores[purple_status_type_get_primitive(type)];
-			if (!purple_status_is_online(status)) {
-				PurpleBuddy *b = purple_presence_get_buddy(presence1);
-				if (b && purple_account_supports_offline_message(purple_buddy_get_account(b),b))
-					score1 += primitive_scores[SCORE_OFFLINE_MESSAGE];
-			}
-		}
-	}
-	score1 += purple_account_get_int(purple_presence_get_account(presence1), "score", 0);
+	/* Compute the score of the first set of statuses. */
+	score1 = purple_presence_compute_score(presence1);
 
 	/* Compute the score of the second set of statuses. */
-	for (l = purple_presence_get_statuses(presence2); l != NULL; l = l->next)
-	{
-		PurpleStatus *status = (PurpleStatus *)l->data;
-		PurpleStatusType *type = purple_status_get_type(status);
-
-		if (purple_status_is_active(status)) {
-			score2 += primitive_scores[purple_status_type_get_primitive(type)];
-			if (!purple_status_is_online(status)) {
-				PurpleBuddy *b = purple_presence_get_buddy(presence2);
-				if (b && purple_account_supports_offline_message(purple_buddy_get_account(b),b))
-					score2 += primitive_scores[SCORE_OFFLINE_MESSAGE];
-			}
-
-		}
-	}
-	score2 += purple_account_get_int(purple_presence_get_account(presence2), "score", 0);
-
-	idle1 = purple_presence_is_idle(presence1);
-	idle2 = purple_presence_is_idle(presence2);
-
-	if (idle1)
-		score1 += primitive_scores[SCORE_IDLE];
-
-	if (idle2)
-		score2 += primitive_scores[SCORE_IDLE];
+	score2 = purple_presence_compute_score(presence2);
 
 	idle_time_1 = time(NULL) - purple_presence_get_idle_time(presence1);
 	idle_time_2 = time(NULL) - purple_presence_get_idle_time(presence2);
--- a/libpurple/util.h	Wed Apr 02 04:58:28 2008 +0000
+++ b/libpurple/util.h	Sat Apr 05 13:15:49 2008 +0000
@@ -702,8 +702,11 @@
 purple_util_get_image_extension(gconstpointer data, size_t len);
 
 /**
- * Returns a SHA-1 hash string of the data passed in with the correct file
- * extention appended.
+ * @return A hex encoded version of the SHA-1 hash of the data passed
+ *         in with the correct file extention appended.  The file
+ *         extension is determined by calling
+ *         purple_util_get_image_extension().  This return value must
+ *         be g_freed by the caller.
  */
 char *purple_util_get_image_filename(gconstpointer image_data, size_t image_len);
 
--- a/pidgin/gtkblist.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/pidgin/gtkblist.c	Sat Apr 05 13:15:49 2008 +0000
@@ -3165,11 +3165,16 @@
 					chat->account);
 			g_free(chat_name);
 		}
-		if (conv && prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC) &&
-				!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
-			char *topic = g_markup_escape_text(purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)), -1);
-			g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)"));
-			g_free(topic);
+
+		if (conv && !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
+			g_string_append_printf(str, _("\n<b>Occupants:</b> %d"),
+					g_list_length(purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv))));
+
+			if (prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC)) {
+				char *topic = g_markup_escape_text(purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)), -1);
+				g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)"));
+				g_free(topic);
+			}
 		}
 
 		if (prpl_info->chat_info != NULL)
--- a/pidgin/gtkconv.c	Wed Apr 02 04:58:28 2008 +0000
+++ b/pidgin/gtkconv.c	Sat Apr 05 13:15:49 2008 +0000
@@ -4437,33 +4437,35 @@
 {
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
-	int wrapped_lines;
 	int lines;
 	GdkRectangle oneline;
 	int height, diff;
 	int pad_top, pad_inside, pad_bottom;
 	int max_height = gtkconv->tab_cont->allocation.height / 2;
 
-	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 at least two lines */
-	wrapped_lines = MAX(wrapped_lines, 2);
-
 	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;
-	if (wrapped_lines > lines)
-		height += (oneline.height + pad_inside) * (wrapped_lines - lines);
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
+	gtk_text_buffer_get_start_iter(buffer, &iter);
+	gtk_text_view_get_iter_location(GTK_TEXT_VIEW(gtkconv->entry), &iter, &oneline);
+
+	lines = gtk_text_buffer_get_line_count(buffer);
+
+	height = 0;
+	do {
+		int lineheight = 0;
+		gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(gtkconv->entry), &iter, NULL, &lineheight);
+		height += lineheight;
+		lines--;
+	} while (gtk_text_iter_forward_line(&iter));
+	height += lines * (oneline.height + pad_top + pad_bottom);
+
+	/* Make sure there's enough room for at least two lines. Allocate enough space to
+	 * prevent scrolling when the second line is a continuation of the first line, or
+	 * is the beginning of a new paragraph. */
+	height = MAX(height, 2 * (oneline.height + MAX(pad_inside, pad_top + pad_bottom)));
 
 	height = MIN(height, max_height);
 
--- a/pidgin/win32/nsis/translations/finnish.nsh	Wed Apr 02 04:58:28 2008 +0000
+++ b/pidgin/win32/nsis/translations/finnish.nsh	Sat Apr 05 13:15:49 2008 +0000
@@ -4,33 +4,53 @@
 ;;  Finish language strings for the Windows Pidgin NSIS installer.
 ;;  Windows Code page: 1252
 ;;
-;;  Author: "Toni_"Daigle"_Impiö" <toni.impio@pp1.inet.fi>
+;;  Authors: Toni "Daigle" Impiö <toni.impio@pp1.inet.fi>
+;;           Timo Jyrinki <timo.jyrinki@iki.fi>, 2008
+;;           
 ;;  Version 2
 ;;
 
 ; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"GTK+ runtime ympäristö joko puuttuu tai tarvitsee päivitystä.$\rOle hyvä ja asenna v${GTK_MIN_VERSION} tai uudempi GTK+ runtime"
+!define GTK_INSTALLER_NEEDED			"Ajonaikainen GTK+-ympäristö joko puuttuu tai tarvitsee päivitystä.$\rOle hyvä ja asenna v${GTK_MIN_VERSION} tai uudempi ajonaikainen GTK+-ympäristö."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Seuraava >"
-!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) on julkaistu GPL lisenssin alla. Lisenssi esitetään tässä vain tiedotuksena. $_CLICK"
+!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) on julkaistu GPL-lisenssin alla. Lisenssi esitetään tässä vain tiedotuksena. $_CLICK"
 
 ; Components Page
-!define PIDGIN_SECTION_TITLE			"Pidgin Suoraviestintäohjelma (required)"
-!define GTK_SECTION_TITLE			"GTK+ runtime ympäristö (required)"
-!define PIDGIN_SECTION_DESCRIPTION		"Pidfinin ytimen tiedostot ja dll:t"
-!define GTK_SECTION_DESCRIPTION		"Monipohjainen GUI (käyttäjäulkoasu) työkalupakki, Pidginin käyttämä"
+!define PIDGIN_SECTION_TITLE			"Pidgin-pikaviestin (vaaditaan)"
+!define GTK_SECTION_TITLE			"Ajonaikainen GTK+-ympäristö (vaaditaan)"
+!define PIDGIN_SHORTCUTS_SECTION_TITLE 		"Pikakuvakkeet"
+!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE 	"Työpöytä"
+!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE "Käynnistysvalikko"
+!define PIDGIN_SECTION_DESCRIPTION		"Pidginin ytimen tiedostot ja kirjastot"
+!define GTK_SECTION_DESCRIPTION		"Monialustainen Pidginin käyttämä käyttöliittymäkirjasto"
+
+!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION   	"Pikakuvakkeet Pidginin käynnistämiseksi"
+!define PIDGIN_DESKTOP_SHORTCUT_DESC   		"Tee Pidgin-pikakuvake työpöydälle"
+!define PIDGIN_STARTMENU_SHORTCUT_DESC   	"Tee Pidgin-pikakuvake käynnistysvalikkoon"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Vanha versio GTK+ runtimestä löytynyt. Tahdotko päivittää?$\rHuomio: $(^Name) ei välttämättä toimi mikäli jätät päivittämättä."
+!define GTK_UPGRADE_PROMPT			"Vanha versio ajonaikaisesta GTK+-ympäristöstä löytynyt. Tahdotko päivittää?$\rHuomio: $(^Name) ei välttämättä toimi mikäli jätät päivittämättä."
 
 ; Installer Finish Page
-!define PIDGIN_FINISH_VISIT_WEB_SITE		"Vieraile Pidinin Windows -sivustolla"
+!define PIDGIN_FINISH_VISIT_WEB_SITE		"Vieraile Pidginin WWW-sivustolla"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Virhe asennettaessa GTK+ runtime."
+!define GTK_INSTALL_ERROR			"Virhe asennettaessa ajonaikaista GTK+-ympäristöä."
 !define GTK_BAD_INSTALL_PATH			"Antamasi polku ei toimi tai sitä ei voi luoda."
 
+; URL Handler section
+!define URI_HANDLERS_SECTION_TITLE		"URI-käsittelijät"
+
 ; Uninstall Section Prompts
-!define un.PIDGIN_UNINSTALL_ERROR_1		"Asennuksen poistaja ei löytänyt reksiteristä tietoja Pidginista.$\rOn todennäköistä että joku muu käyttäjä on asentanut ohjelman."
+!define un.PIDGIN_UNINSTALL_ERROR_1		"Asennuksen poistaja ei löytänyt rekisteristä tietoja Pidginista.$\rOn todennäköistä että joku muu käyttäjä on asentanut ohjelman."
 !define un.PIDGIN_UNINSTALL_ERROR_2		"Sinulla ei ole valtuuksia poistaa ohjelmaa."
+
+; Spellcheck Section Prompts
+!define PIDGIN_SPELLCHECK_SECTION_TITLE		"Oikolukutuki"
+!define PIDGIN_SPELLCHECK_ERROR			"Virhe asennettaessa oikolukua"
+!define PIDGIN_SPELLCHECK_DICT_ERROR		"Virhe asennettaessa oikoluvun sanakirjaa"
+!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Tuki oikoluvulle.  (Asennukseen tarvitaan Internet-yhteys)"
+!define ASPELL_INSTALL_FAILED			"Asennus epäonnistui"
+
--- a/po/de.po	Wed Apr 02 04:58:28 2008 +0000
+++ b/po/de.po	Sat Apr 05 13:15:49 2008 +0000
@@ -11,9 +11,9 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-30 22:16+0200\n"
-"PO-Revision-Date: 2008-03-24 23:02+0100\n"
-"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
+"POT-Creation-Date: 2008-04-05 13:11+0200\n"
+"PO-Revision-Date: 2008-04-05 13:11+0200\n"
+"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -4375,8 +4375,8 @@
 msgid "topic [new topic]:  View or change the topic."
 msgstr "topic [neues Thema]:  Thema ändern oder anzeigen."
 
-msgid "ban &lt;user&gt; [room]:  Ban a user from the room."
-msgstr "ban &lt;Benutzer&gt; [Raum]:  Verbanne einen Benutzer aus dem Raum."
+msgid "ban &lt;user&gt; [reason]:  Ban a user from the room."
+msgstr "ban &lt;Benutzer&gt; [Grund]:  Verbanne einen Benutzer aus dem Raum."
 
 msgid ""
 "affiliate &lt;user&gt; &lt;owner|admin|member|outcast|none&gt;: Set a user's "
@@ -4396,11 +4396,11 @@
 msgstr ""
 "invite &lt;Benutzer&gt; [Nachricht]:  Lade einen Benutzer in den Raum ein."
 
-msgid "join: &lt;room&gt; [server]:  Join a chat on this server."
-msgstr "join: &lt;Raum&gt; [Server]:  Betrete einen Chat auf diesem Server."
-
-msgid "kick &lt;user&gt; [room]:  Kick a user from the room."
-msgstr "kick &lt;Benutzer&gt; [Raum]:  Kickt einen Benutzer aus dem Raum."
+msgid "join: &lt;room&gt; [password]:  Join a chat on this server."
+msgstr "join: &lt;Raum&gt; [Passwort]:  Betrete einen Chat auf diesem Server."
+
+msgid "kick &lt;user&gt; [reason]:  Kick a user from the room."
+msgstr "kick &lt;Benutzer&gt; [Grund]:  Kickt einen Benutzer aus dem Raum."
 
 msgid ""
 "msg &lt;user&gt; &lt;message&gt;:  Send a private message to another user."
@@ -10190,6 +10190,14 @@
 #, c-format
 msgid ""
 "\n"
+"<b>Occupants:</b> %d"
+msgstr ""
+"\n"
+"<b>Besucher:</b> %d"
+
+#, c-format
+msgid ""
+"\n"
 "<b>Topic:</b> %s"
 msgstr ""
 "\n"