# HG changeset patch # User Sadrul Habib Chowdhury # Date 1197418271 0 # Node ID 6332fbeeab2743be165822bf61eec40a7e691e1c # Parent a69a4307c9a839c3dec21d378cb8a375394025cf# Parent 69048606596935d1a54dff269a34721d0f6ee19a propagate from branch 'im.pidgin.pidgin' (head 1d77c730a1160c77d14354d4ce61455e59c94fbc) to branch 'im.pidgin.pidgin.sadrul.tooltips' (head 171ddd847ab5e59a2d8e1f67c8773930b6a425a4) diff -r a69a4307c9a8 -r 6332fbeeab27 .mtn-ignore --- 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 diff -r a69a4307c9a8 -r 6332fbeeab27 ChangeLog --- 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 diff -r a69a4307c9a8 -r 6332fbeeab27 Makefile.am --- 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" diff -r a69a4307c9a8 -r 6332fbeeab27 configure.ac --- 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 ####################################################################### diff -r a69a4307c9a8 -r 6332fbeeab27 finch/gntdebug.c --- 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()) diff -r a69a4307c9a8 -r 6332fbeeab27 finch/gntplugin.c --- 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; } diff -r a69a4307c9a8 -r 6332fbeeab27 finch/libgnt/Makefile.am --- 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) diff -r a69a4307c9a8 -r 6332fbeeab27 finch/libgnt/configure.ac --- 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], , [ diff -r a69a4307c9a8 -r 6332fbeeab27 finch/libgnt/gntwm.c --- 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 +#else #define _GNU_SOURCE #if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__) #define _XOPEN_SOURCE_EXTENDED #endif - -#include "config.h" +#endif #include #include @@ -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)); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/Makefile.am --- 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) diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/account.c --- 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; } diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/account.h --- 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 diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/Makefile.am --- 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 - diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/bonjour_ft.c --- 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"); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/buddy.c --- 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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/buddy.h --- 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; diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/jabber.c --- 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 + +/* Solaris */ +#if defined (__SVR4) && defined (__sun) +#include +#endif + #include #include #include @@ -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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/mdns_avahi.c --- 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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/mdns_howl.c --- 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 - -/* 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) { -} - - diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/bonjour/mdns_win32.c --- 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) { diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/jabber/jabber.c --- 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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/jabber/jabber.h --- 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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/yahoo/yahoo.c --- 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, "")) { - 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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/yahoo/yahoo.h --- 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 */ diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/yahoo/yahoo_filexfer.c --- 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; diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/yahoo/yahoo_filexfer.h --- 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); diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/protocols/yahoo/yahoo_packet.h --- 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 }; diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/purple.h.in --- 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 diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/tests/check_libpurple.c --- 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 #include +#include "tests.h" + #include "../core.h" #include "../eventloop.h" #include "../util.h" -#include "tests.h" /****************************************************************************** * libpurple goodies diff -r a69a4307c9a8 -r 6332fbeeab27 libpurple/tests/tests.h --- 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 +#include "../purple.h" + #include /* define the test suites here */ diff -r a69a4307c9a8 -r 6332fbeeab27 pidgin/gtkconv.c --- 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; } diff -r a69a4307c9a8 -r 6332fbeeab27 pidgin/gtkdebug.c --- 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()) diff -r a69a4307c9a8 -r 6332fbeeab27 po/de.po --- 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 \n" "Language-Team: Deutsch \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" "Konto: %s" +#, c-format +msgid "" +"\n" +"Topic: %s" +msgstr "" +"\n" +"Thema: %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"