changeset 21882:6332fbeeab27

propagate from branch 'im.pidgin.pidgin' (head 1d77c730a1160c77d14354d4ce61455e59c94fbc) to branch 'im.pidgin.pidgin.sadrul.tooltips' (head 171ddd847ab5e59a2d8e1f67c8773930b6a425a4)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Wed, 12 Dec 2007 00:11:11 +0000
parents a69a4307c9a8 (current diff) 690486065969 (diff)
children bb409f0d26b6
files libpurple/protocols/bonjour/mdns_howl.c
diffstat 33 files changed, 1055 insertions(+), 565 deletions(-) [+]
line wrap: on
line diff
--- a/.mtn-ignore	Tue Dec 11 19:13:14 2007 +0000
+++ b/.mtn-ignore	Wed Dec 12 00:11:11 2007 +0000
@@ -33,6 +33,7 @@
 pidgin.spec$
 pidgin-.*.tar.gz
 pidgin-.*.tar.bz2
+pidgin-*.*.*-win32bin$
 pidgin/pidgin$
 pidgin/pixmaps/emotes/default/24/theme
 pidgin/pixmaps/emotes/none/theme
--- a/ChangeLog	Tue Dec 11 19:13:14 2007 +0000
+++ b/ChangeLog	Wed Dec 12 00:11:11 2007 +0000
@@ -4,6 +4,9 @@
 	libpurple:
 	* Fixed various problems with loss of status messages when going
 	  or returning from idle on MySpaceIM.
+	* Eliminated unmaintained Howl backend implementation for the
+	  Bonjour protocol.  Avahi (or Apple's Bonjour runtime on win32) is
+	  now required to use Bonjour.
 
 	Finch:
 	* Color is used in the buddylist to indicate status, and the conversation
--- a/Makefile.am	Tue Dec 11 19:13:14 2007 +0000
+++ b/Makefile.am	Wed Dec 12 00:11:11 2007 +0000
@@ -51,7 +51,7 @@
 	@doxygen
 if HAVE_XSLTPROC
 	@echo "Generating devhelp index..."
-	@xsltproc doxy2devhelp.xsl doc/xml/index.xml > doc/html/pidgin.devhelp
+	@xsltproc $(top_srcdir)/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"
--- a/configure.ac	Tue Dec 11 19:13:14 2007 +0000
+++ b/configure.ac	Wed Dec 12 00:11:11 2007 +0000
@@ -46,7 +46,7 @@
 m4_define([purple_lt_current], [3])
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [3])
-m4_define([purple_micro_version], [1])
+m4_define([purple_micro_version], [2])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
@@ -55,7 +55,7 @@
 m4_define([gnt_lt_current], [3])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [3])
-m4_define([gnt_micro_version], [1])
+m4_define([gnt_micro_version], [2])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -722,59 +722,6 @@
 AC_SUBST(AVAHI_CFLAGS)
 AC_SUBST(AVAHI_LIBS)
 
-AM_CONDITIONAL(MDNS_AVAHI, test "x$avahiincludes" = "xyes" -a "x$avahilibs" = "xyes")
-
-dnl #######################################################################
-dnl # Check for Howl headers (for Bonjour)
-dnl #######################################################################
-AC_ARG_WITH(howl-includes, [AC_HELP_STRING([--with-howl-includes=DIR], [compile the Bonjour plugin against the Howl includes in DIR])], [ac_howl_includes="$withval"], [ac_howl_includes="no"])
-AC_ARG_WITH(howl-libs, [AC_HELP_STRING([--with-howl-libs=DIR], [compile the Bonjour plugin against the Howl libs in DIR])], [ac_howl_libs="$withval"], [ac_howl_libs="no"])
-HOWL_CFLAGS=""
-HOWL_LIBS=""
-
-dnl Attempt to autodetect avahi-compat-howl
-dnl TODO: (This should be removed when the native avahi stuff is stable)
-PKG_CHECK_MODULES(HOWL, avahi-compat-howl, [
-	howlincludes="yes"
-	howllibs="yes"
-], [
-	AC_MSG_RESULT(no)
-	howlincludes="no"
-	howllibs="no"
-])
-
-dnl Attempt to autodetect Howl
-if test "x$howlincludes" = "xno"; then
-	PKG_CHECK_MODULES(HOWL, howl, [
-		howlincludes="yes"
-		howllibs="yes"
-	], [
-		AC_MSG_RESULT(no)
-		howlincludes="no"
-		howllibs="no"
-	])
-fi
-
-dnl Override HOWL_CFLAGS if the user specified an include dir
-if test "$ac_howl_includes" != "no"; then
-	HOWL_CFLAGS="-I$ac_howl_includes"
-fi
-CPPFLAGS_save="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $HOWL_CFLAGS"
-AC_CHECK_HEADER(howl.h, [howlincludes=yes], [howlincludes=no])
-CPPFLAGS="$CPPFLAGS_save"
-
-dnl Override HOWL_LIBS if the user specified a libs dir
-if test "$ac_howl_libs" != "no"; then
-	HOWL_LIBS="-L$ac_howl_libs -lhowl"
-fi
-AC_CHECK_LIB(howl, sw_discovery_init, [howllibs=yes], [howllibs=no], $HOWL_LIBS)
-
-AC_SUBST(HOWL_CFLAGS)
-AC_SUBST(HOWL_LIBS)
-
-AM_CONDITIONAL(MDNS_HOWL, test "x$howlincludes" = "xyes" -a "x$howllibs" = "xyes")
-
 
 dnl #######################################################################
 dnl # Check for SILC client includes and libraries
@@ -960,9 +907,7 @@
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'`
 fi
 if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
-	if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then
-		STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
-	fi
+	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
 fi
 if test "x$enable_msnp14" != "xyes" ; then
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/msn/msnp9/'`
@@ -1049,9 +994,7 @@
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'`
 fi
 if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
-	if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then
-		DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
-	fi
+	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
 fi
 if test "x$enable_msnp14" != "xyes" ; then
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/msn/msnp9/'`
@@ -1087,7 +1030,7 @@
 		*)			echo "Invalid dynamic protocol $i!!" ; exit ;;
 	esac
 done
-AM_CONDITIONAL(DYNAMIC_BONJOUR, test "x$dynamic_bonjour" = "xyes"  -a [ [ "x$avahiincludes" = "xyes" -a "x$avahilibs " = "xyes" ] -o [ "x$howlincludes" = "xyes" -a "x$howllibs" = "xyes" ] ] )
+AM_CONDITIONAL(DYNAMIC_BONJOUR, test "x$dynamic_bonjour" = "xyes"  -a [ "x$avahiincludes" = "xyes" -a "x$avahilibs " = "xyes" ] )
 AM_CONDITIONAL(DYNAMIC_GG, test "x$dynamic_gg" = "xyes")
 AM_CONDITIONAL(DYNAMIC_IRC, test "x$dynamic_irc" = "xyes")
 AM_CONDITIONAL(DYNAMIC_JABBER, test "x$dynamic_jabber" = "xyes")
@@ -1336,6 +1279,34 @@
 
 AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes")
 
+dnl Check for Python headers (currently useful only for libgnt)
+dnl (Thanks to XChat)
+AC_PATH_PROG(pythonpath, python)
+if test "_$pythonpath" != _ ; then
+	AC_MSG_CHECKING(for Python compile flags)
+	PY_PREFIX=`$pythonpath -c 'import sys ; print sys.prefix'`
+	PY_EXEC_PREFIX=`$pythonpath -c 'import sys ; print sys.exec_prefix'`
+	changequote(<<, >>)dnl
+	PY_VERSION=`$pythonpath -c 'import sys ; print sys.version[0:3]'`
+	PY_MAJOR=`$pythonpath -c 'import sys ; print sys.version[0:2]'`
+	changequote([, ])dnl
+	if test -f $PY_PREFIX/include/python$PY_VERSION/Python.h -a "$PY_MAJOR" = "2."; then
+		AC_CHECK_LIB(pthread, pthread_create, )
+		AC_CHECK_LIB(util, openpty, )
+		AC_CHECK_LIB(db, dbopen, )
+		PY_LIBS="-lpython$PY_VERSION -L$PY_EXEC_PREFIX/lib/python$PY_VERSION/config"
+		PY_CFLAGS="-I$PY_PREFIX/include/python$PY_VERSION"
+		AC_DEFINE(USE_PYTHON, [1], [Define if python headers are available.])
+		AC_MSG_RESULT(ok)
+	else
+		AC_MSG_RESULT([Can't find Python.h])
+		PY_LIBS=""
+		PY_CFLAGS=""
+	fi
+fi
+AC_SUBST(PY_CFLAGS)
+AC_SUBST(PY_LIBS)
+
 dnl #######################################################################
 dnl # Check for Mono support
 dnl #######################################################################
--- a/finch/gntdebug.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/finch/gntdebug.c	Wed Dec 12 00:11:11 2007 +0000
@@ -344,6 +344,9 @@
 	REGISTER_G_LOG_HANDLER("GModule");
 	REGISTER_G_LOG_HANDLER("GLib-GObject");
 	REGISTER_G_LOG_HANDLER("GThread");
+#ifdef USE_GSTREAMER
+	REGISTER_G_LOG_HANDLER("GStreamer");
+#endif
 
 	g_set_print_handler(print_stderr);   /* Redirect the debug messages to stderr */
 	if (!purple_debug_is_enabled())
--- a/finch/gntplugin.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/finch/gntplugin.c	Wed Dec 12 00:11:11 2007 +0000
@@ -53,6 +53,13 @@
 static GntWidget *process_pref_frame(PurplePluginPrefFrame *frame);
 
 static void
+free_stringlist(GList *list)
+{
+	g_list_foreach(list, (GFunc)g_free, NULL);
+	g_list_free(list);
+}
+
+static void
 decide_conf_button(PurplePlugin *plugin)
 {
 	if (purple_plugin_is_loaded(plugin) && 
@@ -426,11 +433,14 @@
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group = NULL;
 	GList *prefs;
-	
+	GList *stringlist = NULL;
+	GntWidget *ret = NULL;
+
 	fields = purple_request_fields_new();
 
 	for (prefs = purple_plugin_pref_frame_get_prefs(frame); prefs; prefs = prefs->next) {
 		PurplePluginPref *pref = prefs->data;
+		PurplePrefType type;
 		const char *name = purple_plugin_pref_get_name(pref);
 		const char *label = purple_plugin_pref_get_label(pref);
 		if(name == NULL) {
@@ -448,20 +458,67 @@
 		}
 
 		field = NULL;
-		switch(purple_prefs_get_type(name)) {
-			case PURPLE_PREF_BOOLEAN:
-				field = purple_request_field_bool_new(name, label, purple_prefs_get_bool(name));
-				break;
-			case PURPLE_PREF_INT:
-				field = purple_request_field_int_new(name, label, purple_prefs_get_int(name));
-				break;
-			case PURPLE_PREF_STRING:
-				field = purple_request_field_string_new(name, label, purple_prefs_get_string(name),
-						purple_plugin_pref_get_format_type(pref) & PURPLE_STRING_FORMAT_TYPE_MULTILINE);
-				break;
-			default:
-				break;
+		type = purple_prefs_get_type(name);
+		if(purple_plugin_pref_get_type(pref) == PURPLE_PLUGIN_PREF_CHOICE) {
+			GList *list = purple_plugin_pref_get_choices(pref);
+			gpointer current_value = NULL;
+
+			switch(type) {
+				case PURPLE_PREF_BOOLEAN:
+					current_value = g_strdup_printf("%d", (int)purple_prefs_get_bool(name));
+					break;
+				case PURPLE_PREF_INT:
+					current_value = g_strdup_printf("%d", (int)purple_prefs_get_int(name));
+					break;
+				case PURPLE_PREF_STRING:
+					current_value = g_strdup(purple_prefs_get_string(name));
+					break;
+				default:
+					continue;
+			}
+
+			field = purple_request_field_list_new(name, label);
+			purple_request_field_list_set_multi_select(field, FALSE);
+			while (list && list->next) {
+				const char *label = list->data;
+				char *value = NULL;
+				switch(type) {
+					case PURPLE_PREF_BOOLEAN:
+						value = g_strdup_printf("%d", (int)list->next->data);
+						break;
+					case PURPLE_PREF_INT:
+						value = g_strdup_printf("%d", (int)list->next->data);
+						break;
+					case PURPLE_PREF_STRING:
+						value = g_strdup(list->next->data);
+						break;
+					default:
+						break;
+				}
+				stringlist = g_list_prepend(stringlist, value);
+				purple_request_field_list_add(field, label, value);
+				if (strcmp(value, current_value) == 0)
+					purple_request_field_list_add_selected(field, label);
+				list = list->next->next;
+			}
+			g_free(current_value);
+		} else {
+			switch(type) {
+				case PURPLE_PREF_BOOLEAN:
+					field = purple_request_field_bool_new(name, label, purple_prefs_get_bool(name));
+					break;
+				case PURPLE_PREF_INT:
+					field = purple_request_field_int_new(name, label, purple_prefs_get_int(name));
+					break;
+				case PURPLE_PREF_STRING:
+					field = purple_request_field_string_new(name, label, purple_prefs_get_string(name),
+							purple_plugin_pref_get_format_type(pref) & PURPLE_STRING_FORMAT_TYPE_MULTILINE);
+					break;
+				default:
+					break;
+			}
 		}
+
 		if (field) {
 			if (group == NULL) {
 				group = purple_request_field_group_new(_("Preferences"));
@@ -471,9 +528,11 @@
 		}
 	}
 
-	return purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
+	ret = purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
 			_("Save"), G_CALLBACK(finch_request_save_in_prefs), _("Cancel"), NULL,
 			NULL, NULL, NULL,
 			NULL);
+	g_signal_connect_swapped(G_OBJECT(ret), "destroy", G_CALLBACK(free_stringlist), stringlist);
+	return ret;
 }
 
--- a/finch/libgnt/Makefile.am	Tue Dec 11 19:13:14 2007 +0000
+++ b/finch/libgnt/Makefile.am	Wed Dec 12 00:11:11 2007 +0000
@@ -84,10 +84,12 @@
 libgnt_la_LIBADD = \
 	$(GLIB_LIBS) \
 	$(GNT_LIBS) \
-	$(LIBXML_LIBS)
+	$(LIBXML_LIBS) \
+	$(PY_LIBS)
 
 AM_CPPFLAGS = \
 	$(GLIB_CFLAGS) \
 	$(GNT_CFLAGS) \
 	$(DEBUG_CFLAGS) \
-	$(LIBXML_CFLAGS)
+	$(LIBXML_CFLAGS) \
+	$(PY_CFLAGS)
--- a/finch/libgnt/configure.ac	Tue Dec 11 19:13:14 2007 +0000
+++ b/finch/libgnt/configure.ac	Wed Dec 12 00:11:11 2007 +0000
@@ -27,7 +27,7 @@
 m4_define([gnt_lt_current], [3])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [3])
-m4_define([gnt_micro_version], [1])
+m4_define([gnt_micro_version], [2])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -302,6 +302,34 @@
 *** You need ncursesw or ncurses and its header files.])
 fi
 
+dnl Check for Python headers (currently useful only for libgnt)
+dnl (Thanks to XChat)
+AC_PATH_PROG(pythonpath, python)
+if test "_$pythonpath" != _ ; then
+	AC_MSG_CHECKING(for Python compile flags)
+	PY_PREFIX=`$pythonpath -c 'import sys ; print sys.prefix'`
+	PY_EXEC_PREFIX=`$pythonpath -c 'import sys ; print sys.exec_prefix'`
+	changequote(<<, >>)dnl
+	PY_VERSION=`$pythonpath -c 'import sys ; print sys.version[0:3]'`
+	PY_MAJOR=`$pythonpath -c 'import sys ; print sys.version[0:2]'`
+	changequote([, ])dnl
+	if test -f $PY_PREFIX/include/python$PY_VERSION/Python.h -a "$PY_MAJOR" = "2."; then
+		AC_CHECK_LIB(pthread, pthread_create, )
+		AC_CHECK_LIB(util, openpty, )
+		AC_CHECK_LIB(db, dbopen, )
+		PY_LIBS="-lpython$PY_VERSION -L$PY_EXEC_PREFIX/lib/python$PY_VERSION/config"
+		PY_CFLAGS="-I$PY_PREFIX/include/python$PY_VERSION"
+		AC_DEFINE(USE_PYTHON, [1], [Define if python headers are available.])
+		AC_MSG_RESULT(ok)
+	else
+		AC_MSG_RESULT([Can't find Python.h])
+		PY_LIBS=""
+		PY_CFLAGS=""
+	fi
+fi
+AC_SUBST(PY_CFLAGS)
+AC_SUBST(PY_LIBS)
+
 dnl Check for libxml
 have_libxml=yes
 PKG_CHECK_MODULES(LIBXML, [libxml-2.0], , [
--- a/finch/libgnt/gntwm.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/finch/libgnt/gntwm.c	Wed Dec 12 00:11:11 2007 +0000
@@ -20,12 +20,16 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "config.h"
+
+#ifdef USE_PYTHON
+#include <Python.h>
+#else
 #define _GNU_SOURCE
 #if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__)
 #define _XOPEN_SOURCE_EXTENDED
 #endif
-
-#include "config.h"
+#endif
 
 #include <glib.h>
 #include <glib/gstdio.h>
@@ -1198,12 +1202,50 @@
 	return ignore_keys ? !(ignore_keys = FALSE) : FALSE;
 }
 
+#ifdef USE_PYTHON
+static void
+python_script_selected(GntFileSel *fs, const char *path, const char *f, gpointer n)
+{
+	char *dir = g_path_get_dirname(path);
+	FILE *file = fopen(path, "r");
+	PyObject *pp = PySys_GetObject("path"), *dirobj = PyString_FromString(dir);
+
+	PyList_Insert(pp, 0, dirobj);
+	Py_DECREF(dirobj);
+	PyRun_SimpleFile(file, path);
+	fclose(file);
+
+	if (PyErr_Occurred()) {
+		PyErr_Print();
+	}
+	g_free(dir);
+
+	gnt_widget_destroy(GNT_WIDGET(fs));
+}
+
+static gboolean
+run_python(GntBindable *bindable, GList *n)
+{
+	GntWidget *window = gnt_file_sel_new();
+	GntFileSel *sel = GNT_FILE_SEL(window);
+
+	g_object_set(G_OBJECT(window), "vertical", TRUE, NULL);
+	gnt_box_add_widget(GNT_BOX(window), gnt_label_new("Please select the python script you want to run."));
+	gnt_box_set_title(GNT_BOX(window), "Select Python Script...");
+
+	g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(python_script_selected), NULL);
+	g_signal_connect_swapped(G_OBJECT(sel->cancel), "activate", G_CALLBACK(gnt_widget_destroy), sel);
+	gnt_widget_show(window);
+	return TRUE;
+}
+#endif  /* USE_PYTHON */
+
 static gboolean
 help_for_bindable(GntWM *wm, GntBindable *bindable)
 {
 	gboolean ret = TRUE;
 	GntBindableClass *klass = GNT_BINDABLE_GET_CLASS(bindable);
- 
+
 	if (klass->help_window) {
 		gnt_wm_raise_window(wm, GNT_WIDGET(klass->help_window));
 	} else {
@@ -1271,6 +1313,9 @@
 		g_object_unref(wm->workspaces->data);
 		wm->workspaces = g_list_delete_link(wm->workspaces, wm->workspaces);
 	}
+#ifdef USE_PYTHON
+	Py_Finalize();
+#endif
 }
 
 static void
@@ -1441,6 +1486,12 @@
 				GNT_KEY_CTRL_G, NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end, 
 				"\033" GNT_KEY_CTRL_G, NULL);
+#ifdef USE_PYTHON
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "run-python", run_python,
+				GNT_KEY_F3, NULL);
+	Py_SetProgramName("gnt");
+	Py_Initialize();
+#endif
 
 	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
 
--- a/libpurple/Makefile.am	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/Makefile.am	Wed Dec 12 00:11:11 2007 +0000
@@ -135,8 +135,6 @@
 
 purple_builtheaders = purple.h version.h
 
-BUILT_SOURCES = $(purple_builtheaders)
-
 if ENABLE_DBUS
 
 CLEANFILES = \
@@ -211,6 +209,17 @@
 
 bin_SCRIPTS = purple-remote purple-send purple-send-async purple-url-handler
 
+BUILT_SOURCES = $(purple_builtheaders) \
+	dbus-types.c \
+	dbus-types.h \
+	dbus-bindings.c \
+	purple-client-bindings.c \
+	purple-client-bindings.h
+
+else
+
+BUILT_SOURCES = $(purple_builtheaders)
+
 endif
 
 lib_LTLIBRARIES = libpurple.la $(libpurple_client_lib)
--- a/libpurple/account.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/account.c	Wed Dec 12 00:11:11 2007 +0000
@@ -336,8 +336,13 @@
 	xmlnode_insert_data(child, type_str, -1);
 
 	child = xmlnode_new_child(node, "description");
-	if(err->description)
-		xmlnode_insert_data(child, err->description, -1);
+	if(err->description) {
+		char *utf8ized = purple_utf8_try_convert(err->description);
+		if(utf8ized == NULL)
+			utf8ized = purple_utf8_salvage(err->description);
+		xmlnode_insert_data(child, utf8ized, -1);
+		g_free(utf8ized);
+	}
 
 	return node;
 }
--- a/libpurple/account.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/account.h	Wed Dec 12 00:11:11 2007 +0000
@@ -256,7 +256,7 @@
  * @param remote_user  The name of the user that added this account.
  * @param id           The optional ID of the local account. Rarely used.
  * @param alias        The optional alias of the remote user.
- * @param message      The optional message sent from the uer requesting you
+ * @param message      The optional message sent by the user wanting to add you.
  * @param on_list      Is the remote user already on the buddy list?
  * @param auth_cb      The callback called when the local user accepts
  * @param deny_cb      The callback called when the local user rejects
--- a/libpurple/protocols/bonjour/Makefile.am	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Wed Dec 12 00:11:11 2007 +0000
@@ -13,6 +13,7 @@
 	buddy.h \
 	jabber.c \
 	jabber.h \
+	mdns_avahi.c \
 	mdns_common.c \
 	mdns_common.h \
 	mdns_interface.h \
@@ -22,14 +23,6 @@
 	bonjour_ft.c \
 	bonjour_ft.h 
 
-if MDNS_AVAHI
-  BONJOURSOURCES += mdns_avahi.c
-else
-if MDNS_HOWL
-    BONJOURSOURCES += mdns_howl.c
-endif
-endif
-
 AM_CFLAGS = $(st)
 
 libbonjour_la_LDFLAGS = -module -avoid-version
@@ -40,29 +33,14 @@
 noinst_LIBRARIES     = libbonjour.a
 libbonjour_a_SOURCES = $(BONJOURSOURCES)
 libbonjour_a_CFLAGS  = $(AM_CFLAGS)
-libbonjour_a_LIBADD  =
-
-if MDNS_AVAHI
-  libbonjour_a_LIBADD  += $(AVAHI_LIBS)
-else
-if MDNS_HOWL
-    libbonjour_a_LIBADD  += $(HOWL_LIBS)
-endif
-endif
+libbonjour_a_LIBADD  = $(AVAHI_LIBS)
 
 else
 
 st =
 pkg_LTLIBRARIES       = libbonjour.la
 libbonjour_la_SOURCES = $(BONJOURSOURCES)
-libbonjour_la_LIBADD  = $(GLIB_LIBS) $(LIBXML_LIBS)
-if MDNS_AVAHI
-  libbonjour_la_LIBADD  += $(AVAHI_LIBS)
-else
-if MDNS_HOWL
-    libbonjour_la_LIBADD  += $(HOWL_LIBS)
-endif
-endif
+libbonjour_la_LIBADD  = $(GLIB_LIBS) $(LIBXML_LIBS) $(AVAHI_LIBS)
 
 endif
 
@@ -72,13 +50,6 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
-	$(LIBXML_CFLAGS)
+	$(LIBXML_CFLAGS) \
+	$(AVAHI_CFLAGS)
 
-if MDNS_AVAHI
-  AM_CPPFLAGS += $(AVAHI_CFLAGS)
-else
-if MDNS_HOWL
-    AM_CPPFLAGS += $(HOWL_CFLAGS)
-endif
-endif
-
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Wed Dec 12 00:11:11 2007 +0000
@@ -387,7 +387,7 @@
 bonjour_xfer_init(PurpleXfer *xfer)
 {
 	PurpleBuddy *buddy = NULL;
-	BonjourBuddy *bd = NULL;
+	BonjourBuddy *bb = NULL;
 	XepXfer *xf = NULL;
 
 	xf = (XepXfer*)xfer->data;
@@ -401,8 +401,10 @@
 	if (buddy == NULL)
 		return;
 
-	bd = (BonjourBuddy *)buddy->proto_data;
-	xf->buddy_ip = g_strdup(bd->ip);
+	bb = (BonjourBuddy *)buddy->proto_data;
+	/* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
+	if (bb->ips)
+		xf->buddy_ip = g_strdup(bb->ips->data);
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
 		/* initiate file transfer, send SI offer. */
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
--- a/libpurple/protocols/bonjour/buddy.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Wed Dec 12 00:11:11 2007 +0000
@@ -237,7 +237,10 @@
 bonjour_buddy_delete(BonjourBuddy *buddy)
 {
 	g_free(buddy->name);
-	g_free(buddy->ip);
+	while (buddy->ips != NULL) {
+		g_free(buddy->ips->data);
+		buddy->ips = g_slist_delete_link(buddy->ips, buddy->ips);
+	}
 	g_free(buddy->first);
 	g_free(buddy->phsh);
 	g_free(buddy->status);
--- a/libpurple/protocols/bonjour/buddy.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Wed Dec 12 00:11:11 2007 +0000
@@ -27,8 +27,7 @@
 	PurpleAccount *account;
 
 	gchar *name;
-	/* TODO: Remove and just use the hostname */
-	gchar *ip;
+	GSList *ips;
 	gint port_p2pj;
 
 	gchar *first;
--- a/libpurple/protocols/bonjour/jabber.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Dec 12 00:11:11 2007 +0000
@@ -32,6 +32,12 @@
 #include "libc_interface.h"
 #endif
 #include <sys/types.h>
+
+/* Solaris */
+#if defined (__SVR4) && defined (__sun)
+#include <sys/sockio.h>
+#endif
+
 #include <glib.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -233,13 +239,24 @@
 	/*
 	 * If the current PurpleBuddy's data is not null and the PurpleBuddy's account
 	 * is the same as the account requesting the check then continue to determine
-	 * whether the buddies IP matches the target IP.
+	 * whether one of the buddies IPs matches the target IP.
 	 */
 	if (cbba->bj->account == pb->account)
 	{
 		bb = pb->proto_data;
-		if ((bb != NULL) && (g_ascii_strcasecmp(bb->ip, cbba->address) == 0))
-			*(cbba->pb) = pb;
+		if (bb != NULL) {
+			const char *ip;
+			GSList *tmp = bb->ips;
+
+			while(tmp) {
+				ip = tmp->data;
+				if (ip != NULL && g_ascii_strcasecmp(ip, cbba->address) == 0) {
+					*(cbba->pb) = pb;
+					break;
+				}
+				tmp = tmp->next;
+			}
+		}
 	}
 }
 
@@ -443,9 +460,14 @@
 	else if (ret <= 0) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
+		const char *ip = NULL;
+
+		/* For better or worse, use the first IP*/
+		if (bb->ips)
+			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -498,9 +520,14 @@
 		ret = 0;
 	else if (ret <= 0) {
 		const char *err = g_strerror(errno);
+		const char *ip = NULL;
+
+		/* For better or worse, use the first IP*/
+		if (bb->ips)
+			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
 
 		close(client_socket);
 		g_free(stream_start);
@@ -538,9 +565,14 @@
 	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(pb, bconv->socket)) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
+		const char *ip = NULL;
+
+		/* For better or worse, use the first IP*/
+		if (bb->ips)
+			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -715,9 +747,14 @@
 
 	if (source < 0) {
 		PurpleConversation *conv;
+		const char *ip = NULL;
+
+		/* For better or worse, use the first IP*/
+		if (bb->ips)
+			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, error ? error : "(null)");
+				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, error ? error : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -733,9 +770,14 @@
 	if (!bonjour_jabber_send_stream_init(pb, source)) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
+		const char *ip = NULL;
+
+		/* For better or worse, use the first IP*/
+		if (bb->ips)
+			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -776,6 +818,11 @@
 	{
 		PurpleProxyConnectData *connect_data;
 		PurpleProxyInfo *proxy_info;
+		const char *ip = NULL;
+
+		/* For better or worse, use the first IP*/
+		if (bb->ips)
+			ip = bb->ips->data;
 
 		purple_debug_info("bonjour", "Starting conversation with %s\n", to);
 
@@ -788,8 +835,8 @@
 		}
 		purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
 
-		connect_data = purple_proxy_connect(data->account->gc, data->account,
-						    bb->ip, bb->port_p2pj, _connected_to_buddy, pb);
+		connect_data = purple_proxy_connect(NULL, data->account,
+						    ip, bb->port_p2pj, _connected_to_buddy, pb);
 
 		if (connect_data == NULL) {
 			purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Wed Dec 12 00:11:11 2007 +0000
@@ -47,11 +47,62 @@
 	AvahiEntryGroup *buddy_icon_group;
 } AvahiSessionImplData;
 
+typedef struct _avahi_buddy_service_resolver_data {
+	AvahiServiceResolver *resolver;
+	AvahiIfIndex interface;
+	AvahiProtocol protocol;
+	gchar *name;
+	gchar *type;
+	gchar *domain;
+	/* This is a reference to the entry in BonjourBuddy->ips */
+	const char *ip;
+} AvahiSvcResolverData;
+
 typedef struct _avahi_buddy_impl_data {
-	AvahiServiceResolver *resolver;
+	GSList *resolvers;
 	AvahiRecordBrowser *buddy_icon_rec_browser;
 } AvahiBuddyImplData;
 
+static gint
+_find_resolver_data(gconstpointer a, gconstpointer b) {
+	const AvahiSvcResolverData *rd_a = a;
+	const AvahiSvcResolverData *rd_b = b;
+	gint ret = 1;
+
+	if(rd_a->interface == rd_b->interface
+			&& rd_a->protocol == rd_b->protocol
+			&& !strcmp(rd_a->name, rd_b->name)
+			&& !strcmp(rd_a->type, rd_b->type)
+			&& !strcmp(rd_a->domain, rd_b->domain)) {
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static gint
+_find_resolver_data_by_resolver(gconstpointer a, gconstpointer b) {
+	const AvahiSvcResolverData *rd_a = a;
+	const AvahiServiceResolver *resolver = b;
+	gint ret = 1;
+
+	if(rd_a->resolver == resolver)
+		ret = 0;
+
+	return ret;
+}
+
+static void
+_cleanup_resolver_data(AvahiSvcResolverData *rd) {
+	if (rd->resolver)
+		avahi_service_resolver_free(rd->resolver);
+	g_free(rd->name);
+	g_free(rd->type);
+	g_free(rd->domain);
+	g_free(rd);
+}
+
+
 static void
 _resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
 		  AvahiResolverEvent event, const char *name, const char *type, const char *domain,
@@ -65,6 +116,10 @@
 	size_t size;
 	char *key, *value;
 	int ret;
+	char ip[AVAHI_ADDRESS_STR_MAX];
+	AvahiBuddyImplData *b_impl;
+	AvahiSvcResolverData *rd;
+	GSList *res;
 
 	g_return_if_fail(r != NULL);
 
@@ -78,30 +133,59 @@
 
 			avahi_service_resolver_free(r);
 			if (bb != NULL) {
-				/* We've already freed the resolver */
-				if (r == ((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver)
-					((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver = NULL;
-				purple_account_remove_buddy(account, pb, NULL);
-				purple_blist_remove_buddy(pb);
+				b_impl = bb->mdns_impl_data;
+				res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
+				if (res != NULL) {
+					rd = res->data;
+					b_impl->resolvers = g_slist_remove_link(b_impl->resolvers, res);
+
+					/* We've already freed the resolver */
+					rd->resolver = NULL;
+					_cleanup_resolver_data(rd);
+
+					/* If this was the last resolver, remove the buddy */
+					if (b_impl->resolvers == NULL) {
+						purple_account_remove_buddy(account, pb, NULL);
+						purple_blist_remove_buddy(pb);
+					}
+				}
 			}
 			break;
 		case AVAHI_RESOLVER_FOUND:
 			/* create a buddy record */
 			if (bb == NULL)
 				bb = bonjour_buddy_new(name, account);
+			b_impl = bb->mdns_impl_data;
 
-			/* If we're reusing an existing buddy, make sure if it is a different resolver to clean up the old one.
-			 * I don't think this should ever happen, but I'm afraid we might get events out of sequence. */
-			if (((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver != NULL
-					&& ((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver != r) {
-				avahi_service_resolver_free(((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver);
+			/* If we're reusing an existing buddy, it may be a new resolver or an existing one. */
+			res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
+			if (res != NULL)
+				rd = res->data;
+			else {
+				rd = g_new0(AvahiSvcResolverData, 1);
+				rd->resolver = r;
+				rd->interface = interface;
+				rd->protocol = protocol;
+				rd->name = g_strdup(name);
+				rd->type = g_strdup(type);
+				rd->domain = g_strdup(domain);
+
+				b_impl->resolvers = g_slist_prepend(b_impl->resolvers, rd);
 			}
-			((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver = r;
+
+
+			/* Get the ip as a string */
+			avahi_address_snprint(ip, AVAHI_ADDRESS_STR_MAX, a);
 
-			g_free(bb->ip);
-			/* Get the ip as a string */
-			bb->ip = g_malloc(AVAHI_ADDRESS_STR_MAX);
-			avahi_address_snprint(bb->ip, AVAHI_ADDRESS_STR_MAX, a);
+			if (rd->ip == NULL || strcmp(rd->ip, ip) != 0) {
+				/* We store duplicates in bb->ips, so we always remove the one */
+				if (rd->ip != NULL) {
+					bb->ips = g_slist_remove(bb->ips, rd->ip);
+					g_free((gchar *) rd->ip);
+				}
+				bb->ips = g_slist_prepend(bb->ips, g_strdup(ip));
+				rd->ip = bb->ips->data;
+			}
 
 			bb->port_p2pj = port;
 
@@ -118,11 +202,16 @@
 			}
 
 			if (!bonjour_buddy_check(bb)) {
-				if (pb != NULL) {
-					purple_account_remove_buddy(account, pb, NULL);
-					purple_blist_remove_buddy(pb);
-				} else
-					bonjour_buddy_delete(bb);
+				_cleanup_resolver_data(rd);
+				b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
+				/* If this was the last resolver, remove the buddy */
+				if (b_impl->resolvers == NULL) {
+					if (pb != NULL) {
+						purple_account_remove_buddy(account, pb, NULL);
+						purple_blist_remove_buddy(pb);
+					} else
+						bonjour_buddy_delete(bb);
+				}
 			} else
 				/* Add or update the buddy in our buddy list */
 				bonjour_buddy_add_to_purple(bb, pb);
@@ -166,8 +255,44 @@
 			purple_debug_info("bonjour", "_browser_callback - Remove service\n");
 			pb = purple_find_buddy(account, name);
 			if (pb != NULL) {
-				purple_account_remove_buddy(account, pb, NULL);
-				purple_blist_remove_buddy(pb);
+				BonjourBuddy *bb = pb->proto_data;
+				AvahiBuddyImplData *b_impl;
+				GSList *l;
+				AvahiSvcResolverData *rd_search;
+
+				g_return_if_fail(bb != NULL);
+
+				b_impl = bb->mdns_impl_data;
+
+				/* There may be multiple presences, we should only get rid of this one */
+
+				rd_search = g_new0(AvahiSvcResolverData, 1);
+				rd_search->interface = interface;
+				rd_search->protocol = protocol;
+				rd_search->name = (gchar *) name;
+				rd_search->type = (gchar *) type;
+				rd_search->domain = (gchar *) domain;
+
+				l = g_slist_find_custom(b_impl->resolvers, rd_search, _find_resolver_data);
+
+				g_free(rd_search);
+
+				if (l != NULL) {
+					AvahiSvcResolverData *rd = l->data;
+					b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
+					/* This IP is no longer available */
+					if (rd->ip != NULL) {
+						bb->ips = g_slist_remove(bb->ips, rd->ip);
+						g_free((gchar *) rd->ip);
+					}
+					_cleanup_resolver_data(rd);
+
+					/* If this was the last resolver, remove the buddy */
+					if (b_impl->resolvers == NULL) {
+						purple_account_remove_buddy(account, pb, NULL);
+						purple_blist_remove_buddy(pb);
+					}
+				}
 			}
 			break;
 		case AVAHI_BROWSER_ALL_FOR_NOW:
@@ -188,6 +313,7 @@
 	switch(state) {
 		case AVAHI_ENTRY_GROUP_ESTABLISHED:
 			purple_debug_info("bonjour", "Successfully registered buddy icon data.\n");
+			break;
 		case AVAHI_ENTRY_GROUP_COLLISION:
 			purple_debug_error("bonjour", "Collision registering buddy icon data.\n");
 			break;
@@ -249,8 +375,10 @@
 	}
 
 	/* Stop listening */
-	avahi_record_browser_free(idata->buddy_icon_rec_browser);
-	idata->buddy_icon_rec_browser = NULL;
+	avahi_record_browser_free(b);
+	if (idata->buddy_icon_rec_browser == b) {
+		idata->buddy_icon_rec_browser = NULL;
+	}
 }
 
 /****************************
@@ -462,8 +590,11 @@
 	if (idata->buddy_icon_rec_browser != NULL)
 		avahi_record_browser_free(idata->buddy_icon_rec_browser);
 
-	if (idata->resolver != NULL)
-		avahi_service_resolver_free(idata->resolver);
+	while(idata->resolvers != NULL) {
+		AvahiSvcResolverData *rd = idata->resolvers->data;
+		_cleanup_resolver_data(rd);
+		idata->resolvers = g_slist_delete_link(idata->resolvers, idata->resolvers);
+	}
 
 	g_free(idata);
 
--- a/libpurple/protocols/bonjour/mdns_howl.c	Tue Dec 11 19:13:14 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,288 +0,0 @@
-/*
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Library General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
- */
-
-#include "internal.h"
-
-#include "mdns_interface.h"
-#include "debug.h"
-#include "buddy.h"
-
-#include <howl.h>
-
-/* data used by howl bonjour implementation */
-typedef struct _howl_impl_data {
-	sw_discovery session;
-	sw_discovery_oid session_id;
-	guint session_handler;
-} HowlSessionImplData;
-
-static sw_result HOWL_API
-_publish_reply(sw_discovery discovery, sw_discovery_oid oid,
-			   sw_discovery_publish_status status, sw_opaque extra)
-{
-	purple_debug_warning("bonjour", "_publish_reply --> Start\n");
-
-	/* Check the answer from the mDNS daemon */
-	switch (status)
-	{
-		case SW_DISCOVERY_PUBLISH_STARTED :
-			purple_debug_info("bonjour", "_publish_reply --> Service started\n");
-			break;
-		case SW_DISCOVERY_PUBLISH_STOPPED :
-			purple_debug_info("bonjour", "_publish_reply --> Service stopped\n");
-			break;
-		case SW_DISCOVERY_PUBLISH_NAME_COLLISION :
-			purple_debug_info("bonjour", "_publish_reply --> Name collision\n");
-			break;
-		case SW_DISCOVERY_PUBLISH_INVALID :
-			purple_debug_info("bonjour", "_publish_reply --> Service invalid\n");
-			break;
-	}
-
-	return SW_OKAY;
-}
-
-static sw_result HOWL_API
-_resolve_reply(sw_discovery discovery, sw_discovery_oid oid,
-			   sw_uint32 interface_index, sw_const_string name,
-			   sw_const_string type, sw_const_string domain,
-			   sw_ipv4_address address, sw_port port,
-			   sw_octets text_record, sw_ulong text_record_len,
-			   sw_opaque extra)
-{
-	BonjourBuddy *buddy;
-	PurpleAccount *account = (PurpleAccount*)extra;
-	gint address_length = 16;
-	sw_text_record_iterator iterator;
-	char key[SW_TEXT_RECORD_MAX_LEN];
-	char value[SW_TEXT_RECORD_MAX_LEN];
-	sw_uint32 value_length;
-
-	/* TODO: We want to keep listening for updates*/
-	sw_discovery_cancel(discovery, oid);
-
-	/* create a buddy record */
-	buddy = bonjour_buddy_new(name, account);
-
-	/* Get the ip as a string */
-	buddy->ip = g_malloc(address_length);
-	sw_ipv4_address_name(address, buddy->ip, address_length);
-
-	buddy->port_p2pj = port;
-
-	/* Obtain the parameters from the text_record */
-	if ((text_record_len > 0) && (text_record) && (*text_record != '\0'))
-	{
-		clear_bonjour_buddy_values(buddy);
-		sw_text_record_iterator_init(&iterator, text_record, text_record_len);
-		while (sw_text_record_iterator_next(iterator, key, (sw_octet *)value, &value_length) == SW_OKAY)
-			set_bonjour_buddy_value(buddy, key, value, value_length);
-
-		sw_text_record_iterator_fina(iterator);
-	}
-
-	if (!bonjour_buddy_check(buddy))
-	{
-		bonjour_buddy_delete(buddy);
-		return SW_DISCOVERY_E_UNKNOWN;
-	}
-
-	/* Add or update the buddy in our buddy list */
-	bonjour_buddy_add_to_purple(buddy, NULL);
-
-	return SW_OKAY;
-}
-
-static sw_result HOWL_API
-_browser_reply(sw_discovery discovery, sw_discovery_oid oid,
-			   sw_discovery_browse_status status,
-			   sw_uint32 interface_index, sw_const_string name,
-			   sw_const_string type, sw_const_string domain,
-			   sw_opaque_t extra)
-{
-	sw_discovery_resolve_id rid;
-	PurpleAccount *account = (PurpleAccount*)extra;
-	PurpleBuddy *pb = NULL;
-
-	switch (status)
-	{
-		case SW_DISCOVERY_BROWSE_INVALID:
-			purple_debug_warning("bonjour", "_browser_reply --> Invalid\n");
-			break;
-		case SW_DISCOVERY_BROWSE_RELEASE:
-			purple_debug_warning("bonjour", "_browser_reply --> Release\n");
-			break;
-		case SW_DISCOVERY_BROWSE_ADD_DOMAIN:
-			purple_debug_warning("bonjour", "_browser_reply --> Add domain\n");
-			break;
-		case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN:
-			purple_debug_warning("bonjour", "_browser_reply --> Add default domain\n");
-			break;
-		case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN:
-			purple_debug_warning("bonjour", "_browser_reply --> Remove domain\n");
-			break;
-		case SW_DISCOVERY_BROWSE_ADD_SERVICE:
-			/* A new peer has joined the network and uses iChat bonjour */
-			purple_debug_info("bonjour", "_browser_reply --> Add service\n");
-			if (g_ascii_strcasecmp(name, account->username) != 0)
-			{
-				if (sw_discovery_resolve(discovery, interface_index, name, type,
-						domain, _resolve_reply, extra, &rid) != SW_OKAY)
-				{
-					purple_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n");
-				}
-			}
-			break;
-		case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
-			purple_debug_info("bonjour", "_browser_reply --> Remove service\n");
-			pb = purple_find_buddy(account, name);
-			if (pb != NULL) {
-				purple_account_remove_buddy(account, pb, NULL);
-				purple_blist_remove_buddy(pb);
-			}
-			break;
-		case SW_DISCOVERY_BROWSE_RESOLVED:
-			purple_debug_info("bonjour", "_browse_reply --> Resolved\n");
-			break;
-		default:
-			break;
-	}
-
-	return SW_OKAY;
-}
-
-static void
-_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition)
-{
-	sw_discovery_read_socket((sw_discovery)data);
-}
-
-/****************************
- * mdns_interface functions *
- ****************************/
-
-gboolean _mdns_init_session(BonjourDnsSd *data) {
-	HowlSessionImplData *idata = g_new0(HowlSessionImplData, 1);
-
-	if (sw_discovery_init(&idata->session) != SW_OKAY) {
-		purple_debug_error("bonjour", "Unable to initialize an mDNS session.\n");
-
-		/* In Avahi, sw_discovery_init frees data->session but doesn't clear it */
-		idata->session = NULL;
-
-		g_free(idata);
-
-		return FALSE;
-	}
-
-	data->mdns_impl_data = idata;
-
-	return TRUE;
-}
-
-
-gboolean _mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records) {
-	sw_text_record dns_data;
-	sw_result publish_result = SW_OKAY;
-	HowlSessionImplData *idata = data->mdns_impl_data;
-
-	g_return_val_if_fail(idata != NULL, FALSE);
-
-	/* Fill the data for the service */
-	if (sw_text_record_init(&dns_data) != SW_OKAY) {
-		purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n");
-		return FALSE;
-	}
-
-	while (records) {
-		PurpleKeyValuePair *kvp = records->data;
-		sw_text_record_add_key_and_string_value(dns_data, kvp->key, kvp->value);
-		records = records->next;
-	}
-
-	/* Publish the service */
-	switch (type) {
-		case PUBLISH_START:
-			publish_result = sw_discovery_publish(idata->session, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL,
-								NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data),
-								_publish_reply, NULL, &idata->session_id);
-			break;
-		case PUBLISH_UPDATE:
-			publish_result = sw_discovery_publish_update(idata->session, idata->session_id,
-								sw_text_record_bytes(dns_data), sw_text_record_len(dns_data));
-			break;
-	}
-
-	/* Free the memory used by temp data */
-	sw_text_record_fina(dns_data);
-
-	if (publish_result != SW_OKAY) {
-		purple_debug_error("bonjour", "Unable to publish or change the status of the " ICHAT_SERVICE " service.\n");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-gboolean _mdns_browse(BonjourDnsSd *data) {
-	HowlSessionImplData *idata = data->mdns_impl_data;
-	/* TODO: don't we need to hang onto this to cancel later? */
-	sw_discovery_oid session_id;
-
-	g_return_val_if_fail(idata != NULL, FALSE);
-
-	if (sw_discovery_browse(idata->session, 0, ICHAT_SERVICE, NULL, _browser_reply,
-				    data->account, &session_id) == SW_OKAY) {
-		idata->session_handler = purple_input_add(sw_discovery_socket(idata->session),
-				PURPLE_INPUT_READ, _mdns_handle_event, idata->session);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) {
-	return FALSE;
-}
-
-void _mdns_stop(BonjourDnsSd *data) {
-	HowlSessionImplData *idata = data->mdns_impl_data;
-
-	if (idata == NULL || idata->session == NULL)
-		return;
-
-	sw_discovery_cancel(idata->session, idata->session_id);
-
-	purple_input_remove(idata->session_handler);
-
-	/* TODO: should this really be g_free()'d ??? */
-	g_free(idata->session);
-
-	g_free(idata);
-
-	data->mdns_impl_data = NULL;
-}
-
-void _mdns_init_buddy(BonjourBuddy *buddy) {
-}
-
-void _mdns_delete_buddy(BonjourBuddy *buddy) {
-}
-
-void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) {
-}
-
-
--- a/libpurple/protocols/bonjour/mdns_win32.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Wed Dec 12 00:11:11 2007 +0000
@@ -28,17 +28,7 @@
 #include "dnsquery.h"
 #include "mdns_common.h"
 
-
-/* data structure for the resolve callback */
-typedef struct _ResolveCallbackArgs {
-	DNSServiceRef resolver;
-	guint resolver_handler;
-	gchar *full_service_name;
-
-	PurpleDnsQueryData *query;
-
-	BonjourBuddy* buddy;
-} ResolveCallbackArgs;
+static GSList *pending_buddies = NULL;
 
 /* data used by win32 bonjour implementation */
 typedef struct _win32_session_impl_data {
@@ -50,20 +40,69 @@
 	guint browser_handler;
 } Win32SessionImplData;
 
-typedef struct _win32_buddy_impl_data {
+typedef struct _win32_buddy_service_resolver_data {
 	DNSServiceRef txt_query;
 	guint txt_query_handler;
+	uint32_t if_idx;
+	gchar *name;
+	gchar *type;
+	gchar *domain;
+	/* This is a reference to the entry in BonjourBuddy->ips */
+	const char *ip;
+} Win32SvcResolverData;
+
+typedef struct _win32_buddy_impl_data {
+	GSList *resolvers;
 	DNSServiceRef null_query;
 	guint null_query_handler;
 } Win32BuddyImplData;
 
+/* data structure for the resolve callback */
+typedef struct _ResolveCallbackArgs {
+	DNSServiceRef resolver;
+	guint resolver_handler;
+	PurpleAccount *account;
+	BonjourBuddy *bb;
+	Win32SvcResolverData *res_data;
+	gchar *full_service_name;
+	PurpleDnsQueryData *query;
+} ResolveCallbackArgs;
+
+static gint
+_find_resolver_data(gconstpointer a, gconstpointer b) {
+	const Win32SvcResolverData *rd_a = a;
+	const Win32SvcResolverData *rd_b = b;
+	gint ret = 1;
+
+	if(rd_a->if_idx == rd_b->if_idx
+			&& !strcmp(rd_a->name, rd_b->name)
+			&& !strcmp(rd_a->type, rd_b->type)
+			&& !strcmp(rd_a->domain, rd_b->domain)) {
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void
+_cleanup_resolver_data(Win32SvcResolverData *rd) {
+	if (rd->txt_query != NULL) {
+		purple_input_remove(rd->txt_query_handler);
+		DNSServiceRefDeallocate(rd->txt_query);
+	}
+	g_free(rd->name);
+	g_free(rd->type);
+	g_free(rd->domain);
+	g_free(rd);
+}
+
 static void
 _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
 	DNSServiceProcessResult((DNSServiceRef) data);
 }
 
 static void
-_mdns_parse_text_record(BonjourBuddy* buddy, const char* record, uint16_t record_len)
+_mdns_parse_text_record(BonjourBuddy *buddy, const char *record, uint16_t record_len)
 {
 	const char *txt_entry;
 	uint8_t txt_len;
@@ -114,35 +153,44 @@
 static void
 _mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message)
 {
-	ResolveCallbackArgs* args = (ResolveCallbackArgs*)data;
-	BonjourBuddy* bb = args->buddy;
+	ResolveCallbackArgs* args = (ResolveCallbackArgs*) data;
+	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
+	gboolean delete_buddy = FALSE;
+	PurpleBuddy *pb;
+
+	if ((pb = purple_find_buddy(args->account, args->bb->name)))
+		if (pb->proto_data != args->bb)
+			purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record. "
+				"This is going to be ugly!.\n", args->bb->name);
 
 	if (!hosts || !hosts->data) {
 		purple_debug_error("bonjour", "host resolution - callback error.\n");
-		bonjour_buddy_delete(bb);
+		delete_buddy = TRUE;
 	} else {
-		struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1);
-		Win32BuddyImplData *idata = bb->mdns_impl_data;
-
-		g_return_if_fail(idata != NULL);
-
-		g_free(bb->ip);
-		bb->ip = g_strdup(inet_ntoa(addr->sin_addr));
+		struct sockaddr_in *addr = g_slist_nth_data(hosts, 1);
 
 		/* finally, set up the continuous txt record watcher, and add the buddy to purple */
 
-		if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->txt_query, kDNSServiceFlagsLongLivedQuery,
+		if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&args->res_data->txt_query, kDNSServiceFlagsLongLivedQuery,
 				kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
-				kDNSServiceClass_IN, _mdns_record_query_callback, bb)) {
+				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb)) {
 
-			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", bb->name, bb->ip, bb->port_p2pj);
+			const char *ip = inet_ntoa(addr->sin_addr);
+
+			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
+
 
-			idata->txt_query_handler = purple_input_add(DNSServiceRefSockFD(idata->txt_query),
-				PURPLE_INPUT_READ, _mdns_handle_event, idata->txt_query);
+			args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip));
+			args->res_data->ip = args->bb->ips->data;
+
+			args->res_data->txt_query_handler = purple_input_add(DNSServiceRefSockFD(args->res_data->txt_query),
+				PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query);
 
-			bonjour_buddy_add_to_purple(bb, NULL);
-		} else
-			bonjour_buddy_delete(bb);
+			bonjour_buddy_add_to_purple(args->bb, NULL);
+		} else {
+			purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s\n", args->bb->name);
+			delete_buddy = TRUE;
+		}
 
 	}
 
@@ -153,6 +201,26 @@
 		hosts = g_slist_remove(hosts, hosts->data);
 	}
 
+	if (delete_buddy) {
+		idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
+		_cleanup_resolver_data(args->res_data);
+
+		/* If this was the last resolver, remove the buddy */
+		if (idata->resolvers == NULL) {
+			if (pb) {
+				purple_account_remove_buddy(args->account, pb, NULL);
+				purple_blist_remove_buddy(pb);
+			} else
+				bonjour_buddy_delete(args->bb);
+
+			/* Remove from the pending list */
+			pending_buddies = g_slist_remove(pending_buddies, args->bb);
+		}
+	} else {
+		/* Remove from the pending list */
+		pending_buddies = g_slist_remove(pending_buddies, args->bb);
+	}
+
 	/* free the remaining args memory */
 	g_free(args->full_service_name);
 	g_free(args);
@@ -163,37 +231,54 @@
     const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
 {
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*)context;
+	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 
 	/* remove the input fd and destroy the service ref */
 	purple_input_remove(args->resolver_handler);
+	args->resolver_handler = 0;
 	DNSServiceRefDeallocate(args->resolver);
+	args->resolver = NULL;
 
 	if (kDNSServiceErr_NoError != errorCode)
-	{
 		purple_debug_error("bonjour", "service resolver - callback error.\n");
-		bonjour_buddy_delete(args->buddy);
-		g_free(args);
-	}
-	else
-	{
-		args->buddy->port_p2pj = ntohs(port);
+	else {
+		/* set more arguments, and start the host resolver */
+
+		if ((args->query =
+				purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) {
+
+			args->full_service_name = g_strdup(fullname);
+
+			/* TODO: Should this be per resolver? */
+			args->bb->port_p2pj = ntohs(port);
 
-		/* parse the text record */
-		_mdns_parse_text_record(args->buddy, txtRecord, txtLen);
+			/* We don't want to hit the cleanup code */
+			return;
+		} else
+			purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
+	}
 
-		/* set more arguments, and start the host resolver */
-		args->full_service_name = g_strdup(fullname);
+	/* If we get this far, clean up */
+
+	idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
+	_cleanup_resolver_data(args->res_data);
 
-		if (!(args->query =
-			purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)))
-		{
-			purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
-			bonjour_buddy_delete(args->buddy);
-			g_free(args->full_service_name);
-			g_free(args);
+	/* If this was the last resolver, remove the buddy */
+	if (idata->resolvers == NULL) {
+		PurpleBuddy *pb;
+		/* See if this is now attached to a PurpleBuddy */
+		if ((pb = purple_find_buddy(args->account, args->bb->name))) {
+			purple_account_remove_buddy(args->account, pb, NULL);
+			purple_blist_remove_buddy(pb);
+		} else {
+			/* Remove from the pending list */
+			pending_buddies = g_slist_remove(pending_buddies, args->bb);
+			bonjour_buddy_delete(args->bb);
 		}
 	}
 
+	g_free(args);
+
 }
 
 static void DNSSD_API
@@ -212,7 +297,6 @@
     DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context)
 {
 	PurpleAccount *account = (PurpleAccount*)context;
-	PurpleBuddy *pb = NULL;
 
 	if (kDNSServiceErr_NoError != errorCode)
 		purple_debug_error("bonjour", "service browser - callback error\n");
@@ -221,28 +305,118 @@
 		if (purple_utf8_strcasecmp(serviceName, account->username) != 0) {
 			/* OK, lets go ahead and resolve it to add to the buddy list */
 			ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
-			args->buddy = bonjour_buddy_new(serviceName, account);
+
+			purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n",
+							  serviceName, interfaceIndex, regtype ? regtype : "",
+							  replyDomain ? replyDomain : "");
+
+			if (kDNSServiceErr_NoError == DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype,
+					replyDomain, _mdns_service_resolve_callback, args)) {
+				GSList *tmp = pending_buddies;
+				PurpleBuddy *pb;
+				BonjourBuddy* bb = NULL;
+				Win32SvcResolverData *rd;
+				Win32BuddyImplData *idata;
+				gint fd;
+
+				/* Is there an existing buddy? */
+				if ((pb = purple_find_buddy(account, serviceName)))
+					bb = pb->proto_data;
+				/* Is there a pending buddy? */
+				else {
+					while (tmp) {
+						BonjourBuddy *bb_tmp = tmp->data;
+						if (!strcmp(bb_tmp->name, serviceName)) {
+							bb = bb_tmp;
+							break;
+						}
+						tmp = tmp->next;
+					}
+				}
 
-			if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype,
-					replyDomain, _mdns_service_resolve_callback, args)) {
-				bonjour_buddy_delete(args->buddy);
+				if (bb == NULL) {
+					bb = bonjour_buddy_new(serviceName, account);
+
+					/* This is only necessary for the wacky case where someone previously manually added a buddy. */
+					if (pb == NULL)
+						pending_buddies = g_slist_prepend(pending_buddies, bb);
+					else
+						pb->proto_data = bb;
+				}
+
+
+				rd = g_new0(Win32SvcResolverData, 1);
+				rd->if_idx = interfaceIndex;
+				rd->name = g_strdup(serviceName);
+				rd->type = g_strdup(regtype);
+				rd->domain = g_strdup(replyDomain);
+
+				idata = bb->mdns_impl_data;
+				idata->resolvers = g_slist_prepend(idata->resolvers, rd);
+
+				args->bb = bb;
+				args->res_data = rd;
+				args->account = account;
+
+				/* get a file descriptor for this service ref, and add it to the input list */
+				fd = DNSServiceRefSockFD(args->resolver);
+				args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
+			} else {
+				purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
 				g_free(args);
-				purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
-			} else {
-				/* get a file descriptor for this service ref, and add it to the input list */
-				gint fd = DNSServiceRefSockFD(args->resolver);
-				args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
 			}
 		}
 	} else {
+		PurpleBuddy *pb = NULL;
+
 		/* A peer has sent a goodbye packet, remove them from the buddy list */
-		purple_debug_info("bonjour", "service browser - remove notification\n");
+		purple_debug_info("bonjour", "Received remove notification for '%s' on iface %u (%s, %s)\n",
+						  serviceName, interfaceIndex, regtype ? regtype : "",
+						  replyDomain ? replyDomain : "");
+
 		pb = purple_find_buddy(account, serviceName);
 		if (pb != NULL) {
-			purple_account_remove_buddy(account, pb, NULL);
-			purple_blist_remove_buddy(pb);
-		} else
+			GSList *l;
+			/* There may be multiple presences, we should only get rid of this one */
+			Win32SvcResolverData *rd_search;
+			BonjourBuddy *bb = pb->proto_data;
+			Win32BuddyImplData *idata;
+
+			g_return_if_fail(bb != NULL);
+
+			idata = bb->mdns_impl_data;
+
+			rd_search = g_new0(Win32SvcResolverData, 1);
+			rd_search->if_idx = interfaceIndex;
+			rd_search->name = (gchar *) serviceName;
+			rd_search->type = (gchar *) regtype;
+			rd_search->domain = (gchar *) replyDomain;
+
+			l = g_slist_find_custom(idata->resolvers, rd_search, _find_resolver_data);
+
+			g_free(rd_search);
+
+			if (l != NULL) {
+				Win32SvcResolverData *rd = l->data;
+				idata->resolvers = g_slist_delete_link(idata->resolvers, l);
+				/* This IP is no longer available */
+				if (rd->ip != NULL) {
+					bb->ips = g_slist_remove(bb->ips, rd->ip);
+					g_free((gchar *) rd->ip);
+				}
+				_cleanup_resolver_data(rd);
+
+				/* If this was the last resolver, remove the buddy */
+				if (idata->resolvers == NULL) {
+					purple_debug_info("bonjour", "Removed last presence for buddy '%s'; removing buddy.\n", serviceName);
+					purple_account_remove_buddy(account, pb, NULL);
+					purple_blist_remove_buddy(pb);
+				}
+			}
+		} else {
 			purple_debug_warning("bonjour", "Unable to find buddy (%s) to remove\n", serviceName ? serviceName : "(null)");
+			/* TODO: Should we look in the pending buddies list? */
+		}
 	}
 }
 
@@ -385,9 +559,10 @@
 
 	g_return_if_fail(idata != NULL);
 
-	if (idata->txt_query != NULL) {
-		purple_input_remove(idata->txt_query_handler);
-		DNSServiceRefDeallocate(idata->txt_query);
+	while (idata->resolvers) {
+		Win32SvcResolverData *rd = idata->resolvers->data;
+		_cleanup_resolver_data(rd);
+		idata->resolvers = g_slist_delete_link(idata->resolvers, idata->resolvers);
 	}
 
 	if (idata->null_query != NULL) {
--- a/libpurple/protocols/jabber/jabber.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed Dec 12 00:11:11 2007 +0000
@@ -536,12 +536,13 @@
 	purple_input_remove(js->gc->inpa);
 	js->gc->inpa = 0;
 	js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd,
-			jabber_login_callback_ssl, jabber_ssl_connect_failure, js->serverFQDN, js->gc);
+			jabber_login_callback_ssl, jabber_ssl_connect_failure, js->host, js->gc);
 }
 
 static void jabber_login_connect(JabberStream *js, const char *fqdn, const char *host, int port)
 {
 	js->serverFQDN = g_strdup(fqdn);
+	js->host = g_strdup(host);
 
 	if (purple_proxy_connect(js->gc, js->gc->account, host,
 			port, jabber_login_callback, js->gc) == NULL)
@@ -1279,6 +1280,7 @@
 		js->commands = g_list_delete_link(js->commands, js->commands);
 	}
 	g_free(js->server_name);
+	g_free(js->host);
 	g_free(js->gmail_last_time);
 	g_free(js->gmail_last_tid);
 	g_free(js->old_msg);
--- a/libpurple/protocols/jabber/jabber.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Wed Dec 12 00:11:11 2007 +0000
@@ -191,6 +191,8 @@
 	char *old_uri;
 	int old_length;
 	char *old_track;
+	
+	char *host;
 };
 
 typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace);
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Wed Dec 12 00:11:11 2007 +0000
@@ -493,13 +493,14 @@
 static void yahoo_process_cookie(struct yahoo_data *yd, char *c)
 {
 	if (c[0] == 'Y') {
-		if (yd->cookie_y)
-			g_free(yd->cookie_y);
+		g_free(yd->cookie_y);
 		yd->cookie_y = _getcookie(c);
 	} else if (c[0] == 'T') {
-		if (yd->cookie_t)
-			g_free(yd->cookie_t);
+		g_free(yd->cookie_t);
 		yd->cookie_t = _getcookie(c);
+	} else if (c[0] == 'C') {
+		g_free(yd->cookie_c);
+		yd->cookie_c = _getcookie(c);
 	} else
 		purple_debug_info("yahoo", "Ignoring unrecognized cookie '%c'\n", c[0]);
 }
@@ -899,7 +900,6 @@
 		purple_util_chrreplace(m, '\r', '\n');
 
 		if (!strcmp(m, "<ding>")) {
-			PurpleBuddy *buddy;
 			PurpleAccount *account;
 			PurpleConversation *c;
 			char *username;
@@ -909,13 +909,8 @@
 			if (c == NULL)
 				c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from);
 
-			if ((buddy = purple_find_buddy(account, im->from)) != NULL)
-				username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
-			else
-				username = g_markup_escape_text(im->from, -1);
-
+			username = g_markup_escape_text(im->from, -1);
 			serv_got_attention(gc, username, YAHOO_BUZZ);
-
 			g_free(username);
 			g_free(m);
 			g_free(im);
@@ -2439,6 +2434,12 @@
 	case YAHOO_SERVICE_AUDIBLE:
 		yahoo_process_audible(gc, pkt);
 		break;
+	case YAHOO_SERVICE_Y7_FILETRANSFER:
+		yahoo_process_y7_filetransfer(gc, pkt);
+		break;
+	case YAHOO_SERVICE_Y7_FILETRANSFER_INFO:
+		yahoo_process_y7_filetransfer_info(gc, pkt);
+		break;
 	default:
 		purple_debug(PURPLE_DEBUG_ERROR, "yahoo",
 				   "Unhandled service 0x%02x\n", pkt->service);
@@ -3018,6 +3019,7 @@
 
 	g_free(yd->cookie_y);
 	g_free(yd->cookie_t);
+	g_free(yd->cookie_c);
 
 	if (yd->txhandler)
 		purple_input_remove(yd->txhandler);
--- a/libpurple/protocols/yahoo/yahoo.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Wed Dec 12 00:11:11 2007 +0000
@@ -134,6 +134,7 @@
 	gsize auth_written;
 	char *cookie_y;
 	char *cookie_t;
+	char *cookie_c;
 	int session_id;
 	gboolean jp;
 	gboolean wm; /* connected w/ web messenger method */
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Wed Dec 12 00:11:11 2007 +0000
@@ -46,6 +46,10 @@
 	guint tx_handler;
 	gchar *rxqueue;
 	guint rxlen;
+
+	gboolean y7;	/* true for Y7 transfers (receive only for now) */
+	gchar *token;
+	gchar *tid;
 };
 
 static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
@@ -53,11 +57,70 @@
 	g_free(xd->host);
 	g_free(xd->path);
 	g_free(xd->txbuf);
+	g_free(xd->token);
+	g_free(xd->tid);
 	if (xd->tx_handler)
 		purple_input_remove(xd->tx_handler);
 	g_free(xd);
 }
 
+
+static void yahoo_xfer_y7_request_next_file(PurpleXfer *xfer)
+{
+	struct yahoo_packet *pack;
+	struct yahoo_xfer_data *xd = xfer->data;
+	PurpleConnection *gc = xd->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(xd->y7);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pack, "sssi",
+		1, purple_connection_get_display_name(xd->gc),
+		5, xfer->who,
+		265, xd->tid,
+		271, 1);
+	yahoo_packet_send_and_free(pack, yd);
+}
+
+static void yahoo_xfer_y7_cancel_receive(PurpleXfer *xfer)
+{
+	struct yahoo_packet *pack;
+	struct yahoo_xfer_data *xd = xfer->data;
+	PurpleConnection *gc = xd->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(xd->y7);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, -1, 0);
+	yahoo_packet_hash(pack, "sssi",
+		1, purple_connection_get_display_name(gc),
+		5, xfer->who,
+		265, xd->tid,
+		66, -1);
+	yahoo_packet_send_and_free(pack, yd);
+}
+
+static void yahoo_xfer_y7_accept_file(PurpleXfer *xfer)
+{
+	struct yahoo_packet *pack;
+	struct yahoo_xfer_data *xd = xfer->data;
+	PurpleConnection *gc = xd->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(xd->y7);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pack, "ssssis",
+		1, purple_connection_get_display_name(gc),
+		5, xfer->who,				/* XXX this needs an accessor */
+		265, xd->tid,
+		27, purple_xfer_get_filename(xfer),	/* XXX this might be of incorrect encoding */
+		249, 3,
+		251, xd->token);
+	yahoo_packet_send_and_free(pack, yd);
+}
+
 static void yahoo_receivefile_send_cb(gpointer data, gint source, PurpleInputCondition condition)
 {
 	PurpleXfer *xfer;
@@ -97,6 +160,7 @@
 {
 	PurpleXfer *xfer;
 	struct yahoo_xfer_data *xd;
+	struct yahoo_data *yd;
 
 	purple_debug(PURPLE_DEBUG_INFO, "yahoo",
 			   "AAA - in yahoo_receivefile_connected\n");
@@ -112,11 +176,22 @@
 	}
 
 	xfer->fd = source;
+	yd = xd->gc->proto_data;
 
 	/* The first time we get here, assemble the tx buffer */
 	if (xd->txbuflen == 0) {
-		xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
-			      xd->path, xd->host);
+		if (!xd->y7)
+			xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
+				      xd->path, xd->host);
+		else
+			xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\n"
+				"Connection: close\r\n"
+				"Accept: */*\r\n"
+				"Host: %s\r\n"
+				"Cookie: Y=%s; T=%s\r\n"
+				"\r\n",
+				xd->path, xd->host, yd->cookie_y, yd->cookie_t);
+		purple_debug(PURPLE_DEBUG_INFO, "yahoo_filexfer", "HTTP request: [%s]\n", xd->txbuf);
 		xd->txbuflen = strlen(xd->txbuf);
 		xd->txbuf_written = 0;
 	}
@@ -281,6 +356,9 @@
 			}
 		}
 	} else {
+		if (xfer_data->y7)
+			yahoo_xfer_y7_accept_file(xfer);
+
 		xfer->fd = -1;
 		if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port,
 		                              yahoo_receivefile_connected, xfer) == NULL) {
@@ -340,6 +418,8 @@
 		if ((purple_xfer_get_size(xfer) > 0) &&
 		    (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer))) {
 			purple_xfer_set_completed(xfer, TRUE);
+			if (xd->y7)
+				yahoo_xfer_y7_request_next_file(xfer);
 			return 0;
 		} else
 			return -1;
@@ -430,11 +510,24 @@
 
 	xfer_data = xfer->data;
 
-	if (xfer_data)
+	if (xfer_data) {
+		if (xfer_data->y7)
+			yahoo_xfer_y7_cancel_receive(xfer);
 		yahoo_xfer_data_free(xfer_data);
+	}
 	xfer->data = NULL;
 }
 
+static void yahoo_xfer_request_denied(PurpleXfer *xfer)
+{
+	struct yahoo_xfer_data *xfer_data;
+
+	xfer_data = xfer->data;
+
+	if (xfer_data->y7)
+		yahoo_xfer_y7_cancel_receive(xfer);
+}
+
 void yahoo_process_p2pfilexfer(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	GSList *l = pkt->hash;
@@ -628,6 +721,165 @@
 	}
 }
 
+void yahoo_process_y7_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	char *who = NULL, *name = NULL;
+	int ttype = 0;
+	char *tid = NULL;
+	GSList *l = pkt->hash;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 4:
+			/* them */
+			who = pair->value;
+			break;
+		case 5:
+			/* us */
+			name = pair->value;
+			break;
+		case 222:
+			/* 1=send, 2=cancel, 3=accept, 4=reject */
+			if(pair->value)
+				ttype = atoi(pair->value);
+			break;
+		case 265:
+			/* transfer ID */
+			tid = pair->value;
+			break;
+		case 266:
+			/* number of files */
+			break;
+		case 27:
+			/* filename */
+			break;
+		case 28:
+			/* filesize */
+			break;
+		}
+
+		l = l->next;
+	}
+	if (ttype == 1 && tid) {
+		/* We auto-accept all offers here, and ask the user about each individual
+		 * file in yahoo_process_y7_filetransfer_info. This works fine for receiving
+		 * a single file; when receiving multiple canceling one in the middle
+		 * will also cancel the rest of them.
+		 * Maybe TODO: UI and API allowing transfer of multiple files as a package. */
+		struct yahoo_packet *pack;
+		pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pack, "sssi", 1, name, 5, who, 265, tid, 222, 3);
+		yahoo_packet_send_and_free(pack, yd);
+	}
+}
+
+void yahoo_process_y7_filetransfer_info(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	char *who = NULL, *name = NULL;
+	int medium = 0;
+	char *tid = NULL, *server_host = NULL, *server_token = NULL, *filename = NULL;
+	GSList *l = pkt->hash;
+	struct yahoo_packet *pack;
+	PurpleXfer *xfer;
+	struct yahoo_xfer_data *xfer_data;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 4:
+			/* them */
+			who = pair->value;
+			break;
+		case 5:
+			/* us */
+			name = pair->value;
+			break;
+		case 249:
+			/* 1=p2p, 3=reflection server */
+			if(pair->value)
+				medium = atoi(pair->value);
+			break;
+		case 265:
+			/* transfer ID */
+			tid = pair->value;
+			break;
+		case 27:
+			filename = pair->value;
+			break;
+		case 250:
+			server_host = pair->value;
+			break;
+		case 251:
+			server_token = pair->value;
+			break;
+		}
+
+		l = l->next;
+	}
+	if (medium == 1) {
+		/* reject P2P transfers */
+		pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pack, "sssi", 1, name, 5, who, 265, tid, 66, -3);
+		yahoo_packet_send_and_free(pack, yd);
+		return;
+	}
+
+	if (medium != 3) {
+		purple_debug_error("yahoo", "Unexpected medium %d.\n", medium);
+		/* weird */
+		return;
+	}
+
+	/* Setup the Yahoo-specific file transfer data */
+	xfer_data = g_new0(struct yahoo_xfer_data, 1);
+	xfer_data->gc = gc;
+	xfer_data->host = g_strdup(server_host);
+	xfer_data->token = g_strdup(server_token);
+	xfer_data->tid = g_strdup(tid);
+	xfer_data->port = 80;
+	xfer_data->y7 = TRUE;
+	
+	/* TODO: full urlencode here */
+	server_token = purple_strreplace(server_token, "\002", "%02");
+	xfer_data->path = g_strdup_printf("relay?token=%s&sender=%s&recver=%s",
+		server_token, who, name);
+	g_free(server_token);
+
+	purple_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s.\n",
+	                xfer_data->host, xfer_data->port, xfer_data->path);
+
+	/* Build the file transfer handle. */
+	xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, who);
+	xfer->data = xfer_data;
+
+	/* Set the info about the incoming file. */
+	{
+		char *utf8_filename = yahoo_string_decode(gc, filename, TRUE);
+		purple_xfer_set_filename(xfer, utf8_filename);
+		g_free(utf8_filename);
+	}
+
+	/* purple_xfer_set_size(xfer, filesize); */
+
+	/* Setup our I/O op functions */
+	purple_xfer_set_init_fnc(xfer,        yahoo_xfer_init);
+	purple_xfer_set_start_fnc(xfer,       yahoo_xfer_start);
+	purple_xfer_set_end_fnc(xfer,         yahoo_xfer_end);
+	purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
+	purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
+	purple_xfer_set_read_fnc(xfer,        yahoo_xfer_read);
+	purple_xfer_set_write_fnc(xfer,       yahoo_xfer_write);
+	purple_xfer_set_request_denied_fnc(xfer, yahoo_xfer_request_denied);
+
+	/* Now perform the request */
+	purple_xfer_request(xfer);
+}
+
 PurpleXfer *yahoo_new_xfer(PurpleConnection *gc, const char *who)
 {
 	PurpleXfer *xfer;
--- a/libpurple/protocols/yahoo/yahoo_filexfer.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.h	Wed Dec 12 00:11:11 2007 +0000
@@ -30,6 +30,16 @@
 void yahoo_process_p2pfilexfer( PurpleConnection *gc, struct yahoo_packet *pkt );
 
 /**
+ * Process ymsg version 7 file receive invites.
+ */
+void yahoo_process_y7_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt);
+
+/**
+ * Process ymsg version 7 file receive connection setups.
+ */
+void yahoo_process_y7_filetransfer_info(PurpleConnection *gc, struct yahoo_packet *pkt);
+
+/**
  * Process ymsg file receive invites.
  */
 void yahoo_process_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt);
--- a/libpurple/protocols/yahoo/yahoo_packet.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.h	Wed Dec 12 00:11:11 2007 +0000
@@ -99,9 +99,12 @@
 	YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8,
 	YAHOO_SERVICE_AUDIBLE = 0xd0,
 	YAHOO_SERVICE_AUTH_REQ_15 = 0xd6,
+	YAHOO_SERVICE_Y7_FILETRANSFER = 0xdc,
+	YAHOO_SERVICE_Y7_FILETRANSFER_INFO = 0xdd,
+	YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT = 0xde,
 	YAHOO_SERVICE_CHGRP_15 = 0xe7,
 	YAHOO_SERVICE_STATUS_15 = 0xf0,
-	YAHOO_SERVICE_LIST_15 = 0Xf1,
+	YAHOO_SERVICE_LIST_15 = 0xf1,
 	YAHOO_SERVICE_WEBLOGIN = 0x0226,
 	YAHOO_SERVICE_SMS_MSG = 0x02ea
 };
--- a/libpurple/purple.h.in	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/purple.h.in	Wed Dec 12 00:11:11 2007 +0000
@@ -2,10 +2,11 @@
  * @file purple.h  Header files and defines
  * This file contains all the necessary preprocessor directives to include
  * libpurple's headers and other preprocessor directives required for plugins
- * or UIs to build.  Inlcuding this file eliminates the need to directly
+ * or UIs to build.  Including this file eliminates the need to directly
  * include any other libpurple files.
  *
  * @ingroup core libpurple
+ * @since 2.3.0
  */
 
 /* purple
--- a/libpurple/tests/check_libpurple.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/tests/check_libpurple.c	Wed Dec 12 00:11:11 2007 +0000
@@ -1,11 +1,12 @@
 #include <glib.h>
 #include <stdlib.h>
 
+#include "tests.h"
+
 #include "../core.h"
 #include "../eventloop.h"
 #include "../util.h"
 
-#include "tests.h"
 
 /******************************************************************************
  * libpurple goodies
--- a/libpurple/tests/tests.h	Tue Dec 11 19:13:14 2007 +0000
+++ b/libpurple/tests/tests.h	Wed Dec 12 00:11:11 2007 +0000
@@ -1,7 +1,8 @@
 #ifndef TESTS_H
 #  define TESTS_H
 
-#include <glib.h>
+#include "../purple.h"
+
 #include <check.h>
 
 /* define the test suites here */
--- a/pidgin/gtkconv.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/pidgin/gtkconv.c	Wed Dec 12 00:11:11 2007 +0000
@@ -4375,7 +4375,7 @@
 	{
 		GtkWidget *hbox, *label;
 		PidginChatPane *gtkchat = gtkconv->u.chat;
-		
+
 		hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 
@@ -4524,16 +4524,25 @@
 	conv = gtkconv->active_conv;
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name));
+#if 0
+		/* Using the transient blist nodes to show the tooltip doesn't quite work yet. */
+		if (!node)
+			node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat");
+#endif
 	} else {
 		node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name));
-	}
-
-	if (node) 
+#if 0
+		if (!node)
+			node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
+#endif
+	}
+
+	if (node)
 		pidgin_blist_draw_tooltip(node, gtkconv->infopane);
 	return FALSE;
 }
 
-static void 
+static void
 pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv)
 {
 	pidgin_blist_tooltip_destroy();
@@ -7477,10 +7486,13 @@
 	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);
+	if (!PIDGIN_CONVERSATION(conv))
+		private_gtkconv_new(conv, FALSE);
 	timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
-	if (timer)
+	if (timer) {
 		purple_timeout_remove(timer);
+		purple_conversation_set_data(conv, "close-timer", NULL);
+	}
 }
 
 gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv)
@@ -7489,6 +7501,7 @@
 	PidginConversation *gtkconv;
 
 	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
+		/* This is pretty much always the case now. */
 		gtkconv = PIDGIN_CONVERSATION(conv);
 		if (gtkconv->win != hidden_convwin)
 			return FALSE;
@@ -7496,6 +7509,11 @@
 		pidgin_conv_placement_place(gtkconv);
 		purple_signal_emit(pidgin_conversations_get_handle(),
 				"conversation-displayed", gtkconv);
+		list = gtkconv->convs;
+		while (list) {
+			pidgin_conv_attach(list->data);
+			list = list->next;
+		}
 		return TRUE;
 	}
 
--- a/pidgin/gtkdebug.c	Tue Dec 11 19:13:14 2007 +0000
+++ b/pidgin/gtkdebug.c	Wed Dec 12 00:11:11 2007 +0000
@@ -985,6 +985,9 @@
 	REGISTER_G_LOG_HANDLER("GModule");
 	REGISTER_G_LOG_HANDLER("GLib-GObject");
 	REGISTER_G_LOG_HANDLER("GThread");
+#ifdef USE_GSTREAMER
+	REGISTER_G_LOG_HANDLER("GStreamer");
+#endif
 
 #ifdef _WIN32
 	if (!purple_debug_is_enabled())
--- a/po/de.po	Tue Dec 11 19:13:14 2007 +0000
+++ b/po/de.po	Wed Dec 12 00:11:11 2007 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-30 10:22+0100\n"
-"PO-Revision-Date: 2007-11-30 10:21+0100\n"
+"POT-Creation-Date: 2007-12-06 15:10+0100\n"
+"PO-Revision-Date: 2007-12-06 15:10+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -5766,6 +5766,12 @@
 msgid "Server port"
 msgstr "Server-Port"
 
+msgid "Could not join chat room"
+msgstr "Konnte Chatraum nicht betreten"
+
+msgid "Invalid chat room name"
+msgstr "Ungültiger Chatraumname"
+
 msgid "Server closed the connection."
 msgstr "Der Server hat die Verbindung beendet."
 
@@ -6569,9 +6575,6 @@
 msgid "_Exchange:"
 msgstr "A_ustausch:"
 
-msgid "Invalid chat name specified."
-msgstr "Ungültiger Chat-Name angegeben."
-
 msgid "Your IM Image was not sent. You cannot send IM Images in AIM chats."
 msgstr ""
 "Ihr IM-Bild wurde nicht gesendet. Sie können keine IM-Bilder in AIM-Chats "
@@ -6707,11 +6710,13 @@
 msgstr "Anzeigen, wie lange ich untätig war"
 
 msgid ""
-"Always use ICQ proxy server for file transfers\n"
-"(slower, but does not reveal your IP address)"
-msgstr ""
-"Benutze immer den AIM/ICQ-Proxyserver\n"
-"(langsamer, aber zeigt Ihre IP-Adresse nicht)"
+"Always use AIM/ICQ proxy server for\n"
+"file transfers and direct IM (slower,\n"
+"but does not reveal your IP address)"
+msgstr ""
+"Benutze immer den AIM/ICQ-Proxyserver für\n"
+"Dateiübertragungen und Direkt-IM (langsamer,\n"
+"aber zeigt Ihre IP-Adresse nicht)"
 
 #, c-format
 msgid "Asking %s to connect to us at %s:%hu for Direct IM."
@@ -9995,6 +10000,17 @@
 "\n"
 "<b>Konto:</b> %s"
 
+#, c-format
+msgid ""
+"\n"
+"<b>Topic:</b> %s"
+msgstr ""
+"\n"
+"<b>Thema:</b> %s"
+
+msgid "(no topic set)"
+msgstr "(kein Thema gesetzt)"
+
 msgid "Buddy Alias"
 msgstr "Buddy-Alias"
 
@@ -11055,15 +11071,6 @@
 msgid "_Reset formatting"
 msgstr "Formatierung _zurücksetzen"
 
-msgid "_Smile!"
-msgstr "_Lächeln!"
-
-msgid "_Insert"
-msgstr "_Einfügen"
-
-msgid "_Font"
-msgstr "_Schrift"
-
 msgid "Hyperlink color"
 msgstr "Hyperlink-Farbe"
 
@@ -11130,6 +11137,15 @@
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
+msgid "_Font"
+msgstr "_Schrift"
+
+msgid "_Insert"
+msgstr "_Einfügen"
+
+msgid "S_mile!"
+msgstr "_Lächeln!"
+
 msgid "Select Font"
 msgstr "Schriftart wählen"
 
@@ -11256,6 +11272,9 @@
 msgid "_Horizontal rule"
 msgstr "_Horizontale Linie"
 
+msgid "_Smile!"
+msgstr "_Lächeln!"
+
 #, c-format
 msgid ""
 "Are you sure you want to permanently delete the log of the conversation with "
@@ -11699,6 +11718,9 @@
 msgid "Ports"
 msgstr "Ports"
 
+msgid "_Enable automatic router port forwarding"
+msgstr "Automatischer _Router-Port-Weiterleitung aktivieren"
+
 msgid "_Manually specify range of ports to listen on"
 msgstr "Port-Bereich, auf dem gehört werden soll, _manuell bestimmen"