# HG changeset patch # User Yoshiki Yazawa # Date 1197695731 0 # Node ID e13759a83714ce57707fea2e85ac8b2f830d3ff0 # Parent b9197011ddd6e32a967d8cec524be9299ffd086c# Parent a53f4b1813df8eed16be14c1858e287114669c83 propagate from branch 'im.pidgin.pidgin' (head cff05fbceab1d88163770d13a4c7a6116bdeb8ee) to branch 'im.pidgin.pidgin.yaz' (head 4c2ca466febbc129edc2012fd6ce5769696116d0) diff -r b9197011ddd6 -r e13759a83714 .mtn-ignore --- a/.mtn-ignore Sat Dec 15 05:12:24 2007 +0000 +++ b/.mtn-ignore Sat Dec 15 05:15:31 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 @@ -51,6 +52,7 @@ libpurple/plugins/perl/common/const-c.inc libpurple/plugins/perl/common/const-xs.inc libpurple/plugins/perl/common/lib +libpurple/purple.h$ libpurple/purple-client-bindings.c libpurple/purple-client-bindings.h libpurple/purple-client-example diff -r b9197011ddd6 -r e13759a83714 COPYRIGHT --- a/COPYRIGHT Sat Dec 15 05:12:24 2007 +0000 +++ b/COPYRIGHT Sat Dec 15 05:15:31 2007 +0000 @@ -309,6 +309,7 @@ Tim Ringenbach Dennis Ristuccia Lee Roach +Eion Robb Rhett Robinson Luciano Miguel Ferreira Rocha Andrew Rodland diff -r b9197011ddd6 -r e13759a83714 ChangeLog --- a/ChangeLog Sat Dec 15 05:12:24 2007 +0000 +++ b/ChangeLog Sat Dec 15 05:15:31 2007 +0000 @@ -1,5 +1,21 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.3.2 (??/??/????): + 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 + window to indicate various message attributes. Look at the sample gntrc + file in the man-page for details. + * The default keybinding for dump-screen is now M-D and uses a file + request dialog. M-d will properly delete-forward-word, and M-f has been + fixed to imitate readline's behavior. + version 2.3.0 (11/24/2007): http://developer.pidgin.im/query?status=closed&milestone=2.3.0 NOTE: Some bugs marked fixed in 2.2.1, 2.2.2 or 2.2.3 may not diff -r b9197011ddd6 -r e13759a83714 ChangeLog.API --- a/ChangeLog.API Sat Dec 15 05:12:24 2007 +0000 +++ b/ChangeLog.API Sat Dec 15 05:15:31 2007 +0000 @@ -1,5 +1,16 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.3.2 (??/??/????): + Finch: + libgnt: + * Added gnt_tree_set_row_color to set the color for a row in a tree. + * Added gnt_style_get_string_list + * Added gnt_color_add_pair to define a new color. + * Added gnt_colors_get_color to get an ncurses color value from a + string. + * Added gnt_style_get_color to get a color pair from an entry in + ~/.gntrc + version 2.3.0 (11/24/2007): libpurple: Added: diff -r b9197011ddd6 -r e13759a83714 Makefile.am --- a/Makefile.am Sat Dec 15 05:12:24 2007 +0000 +++ b/Makefile.am Sat Dec 15 05:15:31 2007 +0000 @@ -9,6 +9,7 @@ README.MTN \ README.mingw \ config.h.mingw \ + doxy2devhelp.xsl \ gaim.pc.in \ gaim-uninstalled.pc.in \ intltool-extract.in \ @@ -50,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 b9197011ddd6 -r e13759a83714 configure.ac --- a/configure.ac Sat Dec 15 05:12:24 2007 +0000 +++ b/configure.ac Sat Dec 15 05:15:31 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]) @@ -328,7 +328,7 @@ enable_gtkui="$enableval", enable_gtkui="yes") AC_ARG_ENABLE(consoleui, [AC_HELP_STRING([--disable-consoleui], [compile without console user interface])], - enable_consoleui=$enableval, enable_consoleui=yes) + [enable_consoleui=$enableval force_finch=$enableval], [enable_consoleui=yes force_finch=no]) dnl ####################################################################### dnl # Check for GTK+ 2.0 and other things used by the GTK UI @@ -622,6 +622,14 @@ fi fi +if test "x$force_finch" = "xyes" -a "x$enable_consoleui" != "xyes"; then + AC_MSG_ERROR([ + +Finch will not be built. You need to install ncursesw (or ncurses) and its development headers. + +]) +fi + AC_SUBST(GNT_LIBS) AC_SUBST(GNT_CFLAGS) AM_CONDITIONAL(ENABLE_GNT, test "x$enable_consoleui" = "xyes") @@ -714,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 @@ -952,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/'` @@ -1041,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/'` @@ -1079,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") @@ -1328,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 ####################################################################### @@ -1651,16 +1630,24 @@ ]) mozilla_nspr="mozilla-nspr" mozilla_nss="mozilla-nss" - else - if `$PKG_CONFIG --exists nss`; then - PKG_CHECK_MODULES(NSS, nss, [ - have_nss="yes" - ], [ - AC_MSG_RESULT(no) - ]) - mozilla_nspr="nspr" - mozilla_nss="nss" - fi + elif `$PKG_CONFIG --exists nss`; then + PKG_CHECK_MODULES(NSS, nss, [ + have_nss="yes" + ], [ + AC_MSG_RESULT(no) + have_nss="no" + ]) + mozilla_nspr="nspr" + mozilla_nss="nss" + elif `$PKG_CONFIG --exists microb-engine-nss`; then + PKG_CHECK_MODULES(NSS, microb-engine-nss, [ + have_nss="yes" + ], [ + AC_MSG_RESULT(no) + have_nss="no" + ]) + mozilla_nspr="mozilla-nspr" + mozilla_nss="microb-engine-nss" fi if test "x$have_nss" = "xyes"; then diff -r b9197011ddd6 -r e13759a83714 doc/account-signals.dox --- a/doc/account-signals.dox Sat Dec 15 05:12:24 2007 +0000 +++ b/doc/account-signals.dox Sat Dec 15 05:15:31 2007 +0000 @@ -9,6 +9,7 @@ @signal account-setting-info @signal account-set-info @signal account-status-changed + @signal account-alias-changed @signal account-authorization-requested @signal account-authorization-denied @signal account-authorization-granted diff -r b9197011ddd6 -r e13759a83714 doc/blist-signals.dox --- a/doc/blist-signals.dox Sat Dec 15 05:12:24 2007 +0000 +++ b/doc/blist-signals.dox Sat Dec 15 05:15:31 2007 +0000 @@ -82,6 +82,14 @@ Emitted when a new buddy is added to the buddy list. @endsignaldef + @signaldef buddy-removed + @signalproto +void (*buddy_removed)(PurpleBuddy *buddy) + @endsignalproto + @signaldesc + Emitted when a buddy is removed from the buddy list. + @endsignaldef + @signaldef buddy-icon-changed @signalproto void (*buddy_icon_changed)(PurpleBuddy *buddy) @@ -90,14 +98,6 @@ Emitted when a buddy's icon is set. @endsignaldef - @signaldef buddy-removed - @signalproto -void (*buddy_removed)(PurpleBuddy *buddy) - @endsignalproto - @signaldesc - Emitted when a buddy is removed from the buddy list. - @endsignaldef - @signaldef blist-node-aliased @signalproto void (*blist_node_aliased)(PurpleBlistNode *node, const char *old_alias) diff -r b9197011ddd6 -r e13759a83714 doc/finch.1.in --- a/doc/finch.1.in Sat Dec 15 05:12:24 2007 +0000 +++ b/doc/finch.1.in Sat Dec 15 05:15:31 2007 +0000 @@ -135,6 +135,29 @@ .TP A sample file looks like: .br +[Finch] +.br +color-available = green; black +.br +color-away = blue; black +.br +color-idle = gray; black +.br +color-offline = red; black +.br +color-message-sent = cyan; default +.br +color-message-received = red; default +.br +color-message-highlight = black; green +.br +color-message-action = yellow; default +.br +color-timestamp = blue; default +.br +#See below for details on color +.br + [general] .br shadow = 0 diff -r b9197011ddd6 -r e13759a83714 doc/gtkconv-signals.dox --- a/doc/gtkconv-signals.dox Sat Dec 15 05:12:24 2007 +0000 +++ b/doc/gtkconv-signals.dox Sat Dec 15 05:15:31 2007 +0000 @@ -28,12 +28,14 @@ @signaldef conversation-timestamp @signalproto -char *(*conversation_timestamp)(PurpleConversation *conv, time_t when); +char *(*conversation_timestamp)(PurpleConversation *conv, time_t when, + gboolean show_date); @endsignalproto @signaldesc Emitted to allow plugins to customize the timestamp on a message. - @param conv The conversation the message belongs to. - @param when The time to be converted to a string. + @param conv The conversation the message belongs to. + @param when The time to be converted to a string. + @param show_date Whether the date should be displayed. @return A textual representation of the time, or @c NULL to use a default format. @endsignaldef diff -r b9197011ddd6 -r e13759a83714 doc/log-signals.dox --- a/doc/log-signals.dox Sat Dec 15 05:12:24 2007 +0000 +++ b/doc/log-signals.dox Sat Dec 15 05:15:31 2007 +0000 @@ -10,13 +10,14 @@ @signaldef log-timestamp @signalproto -char *(*log_timestamp)(PurpleLog *log, time_t when); +char *(*log_timestamp)(PurpleLog *log, time_t when, gboolean show_date); @endsignalproto @signaldesc Emitted to allow plugins to customize the timestamp on a message being logged. - @param log The log the message belongs to. - @param when The time to be converted to a string. + @param log The log the message belongs to. + @param when The time to be converted to a string. + @param show_date Whether the date should be displayed. @return A textual representation of the time, or @c NULL to use a default format. @note Plugins must be careful of logs with a type of PURPLE_LOG_SYSTEM. diff -r b9197011ddd6 -r e13759a83714 doc/plugin-i18n.dox --- a/doc/plugin-i18n.dox Sat Dec 15 05:12:24 2007 +0000 +++ b/doc/plugin-i18n.dox Sat Dec 15 05:15:31 2007 +0000 @@ -1,13 +1,19 @@ /** @page plugin-i18n Third Party Plugin Translation Support @section Introduction + For the purpose of this document we're going to assume that your plugin: + + - Is set up to use autotools. It may be possible to add translation support + without autotools, but we have no idea how. We may not want to know, either ;) + - Has an autogen.sh. You may have also called this bootstrap.sh or similar. + - Resides in a source tree that has @c configure.ac and @c Makefile.am in the + top-level directory as well as a @c src directory in which the plugin's source + is located. A @c Makefile.am should also exist in the @c src directory. + For a plugin to have translation support there are a few steps that need to followed: - - The plugin must be setup to use autotools. It may be possible to add - translation support without autotools, but I have no idea how. - - In your autogen.sh, bootstrap.sh, or whatever you called it, add the - following after your other utility checks: + - In your autogen.sh, add the following after your other utility checks: @code (intltoolize --version) < /dev/null > /dev/null 2>&1 || { echo; @@ -16,12 +22,11 @@ exit; } @endcode - Then before your call aclocal add: + Then before your call to aclocal add: @code intltoolize --force --copy @endcode - - Now edit configure.ac, configure.in, or whatever you may have called it - and add the following: + - Now edit configure.ac and add the following: @code AC_PROG_INTLTOOL @@ -40,13 +45,13 @@ - Create/edit the file 'POTFILE.in' in your favorite editor. Each line should be the name of a file that could or does have strings marked for translating (we're getting to that step). These file names should be - relative to the top directory of your plugin. - - 'cd' back to the top directory of your plugin. + relative to the top directory of your plugin's source tree. + - 'cd' back to the top directory of your plugin's source tree. - Open 'Makefile.am' and add 'po' to your 'SUBDIRS' variable. - - While still in the top directory of your plugin execute + - While still in the top directory of your plugin's source tree, execute 'intltool-prepare'. This will setup anything extra that intltool needs. - - Fire off an 'autogen.sh' and when it's completed, verify that you have a - 'po/POTFILES', notice the lack of a .in. If you do, everything should be + - Fire off 'autogen.sh' and when it's completed, verify that you have a + 'po/POTFILES' (notice the lack of a .in). If you do, everything should be set on the autotools side. - Take a break, stretch your legs, smoke a cigarette, whatever, because we're done with the autotools part. @@ -60,8 +65,10 @@ #include @endcode Make sure that this include is after you include of your 'config.h', - otherwise you will break your build. - - This is where things get a bit goofy. libpurple is going to try and + otherwise you will break your build. Also note that if you wish to + maintain compatibility with older versions of GLib, you will need to + include additional preprocessor directives, which we won't cover here. + - This is where things get a bit goofy. libpurple is going to try to translate our strings using the libpurple gettext package. So we have to convert them before libpurple attempts to. - To do this, we're going to change the entries for name, summary, and diff -r b9197011ddd6 -r e13759a83714 finch/gntblist.c --- a/finch/gntblist.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/gntblist.c Sat Dec 15 05:15:31 2007 +0000 @@ -37,6 +37,7 @@ #include "debug.h" #include "gntbox.h" +#include "gntcolors.h" #include "gntcombobox.h" #include "gntentry.h" #include "gntft.h" @@ -46,6 +47,7 @@ #include "gntmenuitem.h" #include "gntmenuitemcheck.h" #include "gntpounce.h" +#include "gntstyle.h" #include "gnttree.h" #include "gntutils.h" #include "gntwindow.h" @@ -124,6 +126,37 @@ static int blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2); static int blist_node_compare_log(PurpleBlistNode *n1, PurpleBlistNode *n2); +static int color_available; +static int color_away; +static int color_offline; +static int color_idle; + +static int +get_display_color(PurpleBlistNode *node) +{ + PurpleBuddy *buddy; + int color = 0; + + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node); + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) + return 0; + + buddy = (PurpleBuddy*)node; + if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) { + color = color_idle; + } else if (purple_presence_is_available(purple_buddy_get_presence(buddy))) { + color = color_available; + } else if (purple_presence_is_online(purple_buddy_get_presence(buddy)) && + !purple_presence_is_available(purple_buddy_get_presence(buddy))) { + color = color_away; + } else if (!purple_presence_is_online(purple_buddy_get_presence(buddy))) { + color = color_offline; + } + + return color; +} + static gboolean is_contact_online(PurpleContact *contact) { @@ -228,6 +261,7 @@ gnt_tree_change_text(GNT_TREE(ggblist->tree), node, 0, get_display_name(node)); gnt_tree_sort_row(GNT_TREE(ggblist->tree), node); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node)); } if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { @@ -581,11 +615,11 @@ if (node->ui_data) return; - + name = get_display_name(node); if (name == NULL) return; - + group = (PurpleGroup*)node->parent; add_node((PurpleBlistNode*)group, ggblist); @@ -601,6 +635,7 @@ { PurpleContact *contact; PurpleBlistNode *node = (PurpleBlistNode *)buddy; + int color = 0; if (node->ui_data) return; @@ -615,13 +650,11 @@ node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), contact, NULL); - if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, GNT_TEXT_FLAG_DIM); - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, GNT_TEXT_FLAG_DIM); - } else { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, 0); - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, 0); - } + + color = get_display_color((PurpleBlistNode*)buddy); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color); + if (buddy == purple_contact_get_priority_buddy(contact)) + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color); } #if 0 @@ -1542,7 +1575,8 @@ { PurpleContact *contact; GntTextFormatFlags bflag = 0, cflag = 0; - + int color = 0; + contact = purple_buddy_get_contact(buddy); gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy)); @@ -1556,19 +1590,17 @@ if (ggblist->tnode == (PurpleBlistNode*)buddy) draw_tooltip(ggblist); - if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag | GNT_TEXT_FLAG_DIM); - if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag | GNT_TEXT_FLAG_DIM); - else + color = get_display_color((PurpleBlistNode*)buddy); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color); + if (buddy == purple_contact_get_priority_buddy(contact)) + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color); + + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag); + if (buddy == purple_contact_get_priority_buddy(contact)) + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag); + + if (buddy != purple_contact_get_priority_buddy(contact)) update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist); - } else { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag); - if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag); - else - update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist); - } } static void @@ -1738,6 +1770,19 @@ void finch_blist_init() { + color_available = gnt_style_get_color(NULL, "color-available"); + if (!color_available) + color_available = gnt_color_add_pair(COLOR_GREEN, -1); + color_away = gnt_style_get_color(NULL, "color-away"); + if (!color_away) + color_away = gnt_color_add_pair(COLOR_BLUE, -1); + color_idle = gnt_style_get_color(NULL, "color-idle"); + if (!color_idle) + color_idle = gnt_color_add_pair(COLOR_CYAN, -1); + color_offline = gnt_style_get_color(NULL, "color-offline"); + if (!color_offline) + color_offline = gnt_color_add_pair(COLOR_RED, -1); + purple_prefs_add_none(PREF_ROOT); purple_prefs_add_none(PREF_ROOT "/size"); purple_prefs_add_int(PREF_ROOT "/size/width", 20); @@ -2550,4 +2595,3 @@ { gnt_widget_set_size(ggblist->window, width, height); } - diff -r b9197011ddd6 -r e13759a83714 finch/gntconv.c --- a/finch/gntconv.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/gntconv.c Sat Dec 15 05:15:31 2007 +0000 @@ -49,6 +49,7 @@ #include "gntmenu.h" #include "gntmenuitem.h" #include "gntmenuitemcheck.h" +#include "gntstyle.h" #include "gnttextview.h" #include "gnttree.h" #include "gntutils.h" @@ -64,6 +65,12 @@ const char *message, PurpleMessageFlags flags, time_t mtime); static void generate_send_to_menu(FinchConv *ggc); +static int color_message_receive; +static int color_message_send; +static int color_message_highlight; +static int color_message_action; +static int color_timestamp; + static PurpleBlistNode * get_conversation_blist_node(PurpleConversation *conv) { @@ -753,7 +760,9 @@ /* Unnecessary to print the timestamp for delayed message */ if (purple_prefs_get_bool("/finch/conversations/timestamps")) gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), - purple_utf8_strftime("(%H:%M:%S) ", localtime(&mtime)), GNT_TEXT_FLAG_DIM); + purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), gnt_color_pair(color_timestamp)); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), " ", GNT_TEXT_FLAG_NORMAL); if (flags & PURPLE_MESSAGE_AUTO_RESP) gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), @@ -763,23 +772,31 @@ !(flags & PURPLE_MESSAGE_NOTIFY)) { char * name = NULL; - - if (purple_message_meify((char*)message, -1)) - name = g_strdup_printf("*** %s ", who); - else - name = g_strdup_printf("%s: ", who); + GntTextFormatFlags msgflags = GNT_TEXT_FLAG_NORMAL; + gboolean me = FALSE; + if (purple_message_meify((char*)message, -1)) { + name = g_strdup_printf("*** %s", who); + msgflags = gnt_color_pair(color_message_action); + me = TRUE; + } else { + name = g_strdup_printf("%s", who); + if (flags & PURPLE_MESSAGE_SEND) + msgflags = gnt_color_pair(color_message_send); + else if (flags & PURPLE_MESSAGE_NICK) + msgflags = gnt_color_pair(color_message_highlight); + else + msgflags = gnt_color_pair(color_message_receive); + } gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), - name, GNT_TEXT_FLAG_BOLD); + name, msgflags); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), me ? " " : ": ", GNT_TEXT_FLAG_NORMAL); g_free(name); - } - else + } else fl = GNT_TEXT_FLAG_DIM; if (flags & PURPLE_MESSAGE_ERROR) fl |= GNT_TEXT_FLAG_BOLD; - if (flags & PURPLE_MESSAGE_NICK) - fl |= GNT_TEXT_FLAG_UNDERLINE; /* XXX: Remove this workaround when textview can parse messages. */ newline = purple_strdup_withhtml(message); @@ -1126,6 +1143,21 @@ void finch_conversation_init() { + color_message_send = gnt_style_get_color(NULL, "color-message-sent"); + if (!color_message_send) + color_message_send = gnt_color_add_pair(COLOR_CYAN, -1); + color_message_receive = gnt_style_get_color(NULL, "color-message-received"); + if (!color_message_receive) + color_message_receive = gnt_color_add_pair(COLOR_RED, -1); + color_message_highlight = gnt_style_get_color(NULL, "color-message-highlight"); + if (!color_message_highlight) + color_message_highlight = gnt_color_add_pair(COLOR_GREEN, -1); + color_timestamp = gnt_style_get_color(NULL, "color-timestamp"); + if (!color_timestamp) + color_timestamp = gnt_color_add_pair(COLOR_BLUE, -1); + color_message_action = gnt_style_get_color(NULL, "color-message-action"); + if (!color_message_action) + color_message_action = gnt_color_add_pair(COLOR_YELLOW, -1); purple_prefs_add_none(PREF_ROOT); purple_prefs_add_none(PREF_ROOT "/size"); purple_prefs_add_int(PREF_ROOT "/size/width", 70); diff -r b9197011ddd6 -r e13759a83714 finch/gntdebug.c --- a/finch/gntdebug.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/gntdebug.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 finch/gntplugin.c --- a/finch/gntplugin.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/gntplugin.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 finch/libgnt/Makefile.am --- a/finch/libgnt/Makefile.am Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/Makefile.am Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 finch/libgnt/configure.ac --- a/finch/libgnt/configure.ac Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/configure.ac Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 finch/libgnt/gntcolors.c --- a/finch/libgnt/gntcolors.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntcolors.c Sat Dec 15 05:15:31 2007 +0000 @@ -33,6 +33,7 @@ #include static gboolean hascolors; +static int custom_type = GNT_COLORS; static struct { short r, g, b; @@ -137,8 +138,8 @@ } #if GLIB_CHECK_VERSION(2,6,0) -static int -get_color(char *key) +int +gnt_colors_get_color(char *key) { int color; gboolean custom = can_use_custom_color(); @@ -163,8 +164,12 @@ color = COLOR_MAGENTA; else if (strcmp(key, "cyan") == 0) color = COLOR_CYAN; - else + else if (strcmp(key, "default") == 0) color = -1; + else { + g_warning("Invalid color name: %s\n", key); + color = -1; + } return color; } @@ -196,7 +201,7 @@ int color = -1; key = g_ascii_strdown(key, -1); - color = get_color(key); + color = gnt_colors_get_color(key); g_free(key); if (color == -1) continue; @@ -237,8 +242,8 @@ GntColorType type = 0; gchar *fgc = g_ascii_strdown(list[0], -1); gchar *bgc = g_ascii_strdown(list[1], -1); - int fg = get_color(fgc); - int bg = get_color(bgc); + int fg = gnt_colors_get_color(fgc); + int bg = gnt_colors_get_color(bgc); g_free(fgc); g_free(bgc); if (fg == -1 || bg == -1) @@ -287,3 +292,8 @@ pair == GNT_COLOR_TITLE_D || pair == GNT_COLOR_DISABLED) ? 0 : A_STANDOUT)); } +int gnt_color_add_pair(int fg, int bg) +{ + init_pair(custom_type, fg, bg); + return custom_type++; +} diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gntcolors.h --- a/finch/libgnt/gntcolors.h Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntcolors.h Sat Dec 15 05:15:31 2007 +0000 @@ -86,6 +86,16 @@ */ void gnt_color_pairs_parse(GKeyFile *kfile); +/** + * Parse a string color + * + * @param kfile The string value + * + * @return A color + * + * @since 2.3.1 (gnt), 2.3.1 (pidgin) + */ +int gnt_colors_get_color(char *key); #endif /** @@ -101,4 +111,15 @@ */ int gnt_color_pair(int color); +/** + * Adds a color definition + * + * @param fg Foreground + * @param bg Background + * + * @return A color pair + * + * @since 2.3.1 + */ +int gnt_color_add_pair(int fg, int bg); #endif diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gntentry.c --- a/finch/libgnt/gntentry.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntentry.c Sat Dec 15 05:15:31 2007 +0000 @@ -140,7 +140,6 @@ static gboolean complete_suggest(GntEntry *entry, const char *text) { - gboolean changed = FALSE; int offstart = 0, offend = 0; if (entry->word) { @@ -148,27 +147,22 @@ const char *iter = text; offstart = g_utf8_pointer_to_offset(entry->start, s); while (*iter && toupper(*s) == toupper(*iter)) { - if (*s != *iter) - changed = TRUE; *s++ = *iter++; } if (*iter) { gnt_entry_key_pressed(GNT_WIDGET(entry), iter); - changed = TRUE; } offend = g_utf8_pointer_to_offset(entry->start, entry->cursor); } else { offstart = 0; gnt_entry_set_text_internal(entry, text); - changed = TRUE; offend = g_utf8_strlen(text, -1); } - if (changed) - g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0, - entry->start + offstart, entry->start + offend); + g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0, + entry->start + offstart, entry->start + offend); update_kill_ring(entry, ENTRY_JAIL, NULL, 0); - return changed; + return TRUE; } static int diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gntmain.c --- a/finch/libgnt/gntmain.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntmain.c Sat Dec 15 05:15:31 2007 +0000 @@ -679,8 +679,9 @@ g_free(cp); clean_pid(); wm->mode = GNT_KP_MODE_NORMAL; - clear(); + endwin(); setup_io(); + refresh(); refresh_screen(); } #endif diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gntstyle.c --- a/finch/libgnt/gntstyle.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntstyle.c Sat Dec 15 05:15:31 2007 +0000 @@ -59,6 +59,42 @@ #endif } +int +gnt_style_get_color(char *group, char *key) +{ +#if GLIB_CHECK_VERSION(2,6,0) + int fg = 0, bg = 0; + gsize n; + char **vals; + int ret = 0; + vals = gnt_style_get_string_list(group, key, &n); + if (vals && n == 2) { + fg = gnt_colors_get_color(vals[0]); + bg = gnt_colors_get_color(vals[1]); + ret = gnt_color_add_pair(fg, bg); + } + g_strfreev(vals); + return ret; +#else + return 0; +#endif +} + +char **gnt_style_get_string_list(const char *group, const char *key, gsize *length) +{ +#if GLIB_CHECK_VERSION(2,6,0) + const char *prg = g_get_prgname(); + if ((group == NULL || *group == '\0') && prg && + g_key_file_has_group(gkfile, prg)) + group = prg; + if (!group) + group = "general"; + return g_key_file_get_string_list(gkfile, group, key, length, NULL); +#else + return NULL; +#endif +} + gboolean gnt_style_get_bool(GntStyle style, gboolean def) { const char * str; diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gntstyle.h --- a/finch/libgnt/gntstyle.h Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntstyle.h Sat Dec 15 05:15:31 2007 +0000 @@ -65,6 +65,33 @@ char *gnt_style_get_from_name(const char *group, const char *key); /** + * Get the value of a preference in ~/.gntrc. + * + * @param group The name of the group in the keyfile. If @c NULL, the prgname + * will be used first, if available. Otherwise, "general" will be used. + * @param key The key + * @param length Return location for the number of strings returned, or NULL + * + * @return NULL terminated string array. The array should be freed with g_strfreev(). + * + * @since 2.3.2 + */ +char **gnt_style_get_string_list(const char *group, const char *key, gsize *length); + +/** + * Get the value of a color pair in ~/.gntrc. + * + * @param group The name of the group in the keyfile. If @c NULL, the prgname + * will be used first, if available. Otherwise, "general" will be used. + * @param key The key + * + * @return The value of the color as an int, or 0 on error. + * + * @since 2.3.2 + */ +int gnt_style_get_color(char *group, char *key); + +/** * Parse a boolean preference. For example, if 'value' is "false" (ignoring case) * or "0", the return value will be @c FALSE, otherwise @c TRUE. * diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gnttextview.c --- a/finch/libgnt/gnttextview.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gnttextview.c Sat Dec 15 05:15:31 2007 +0000 @@ -650,8 +650,10 @@ fl |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED)); else if (flags & GNT_TEXT_FLAG_HIGHLIGHT) fl |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT)); + else if ((flags & A_COLOR) == 0) + fl |= gnt_color_pair(GNT_COLOR_NORMAL); else - fl |= gnt_color_pair(GNT_COLOR_NORMAL); + fl |= (flags & A_COLOR); return fl; } diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gnttree.c --- a/finch/libgnt/gnttree.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gnttree.c Sat Dec 15 05:15:31 2007 +0000 @@ -75,6 +75,7 @@ If choice is true, then child will be NULL */ gboolean isselected; GntTextFormatFlags flags; + int color; GntTreeRow *parent; GntTreeRow *child; @@ -522,9 +523,14 @@ else { if (flags & GNT_TEXT_FLAG_DIM) - attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED)); + if (row->color) + attr |= (A_DIM | gnt_color_pair(row->color)); + else + attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED)); else if (flags & GNT_TEXT_FLAG_HIGHLIGHT) attr |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT)); + else if (row->color) + attr |= gnt_color_pair(row->color); else attr |= gnt_color_pair(GNT_COLOR_NORMAL); } @@ -1559,6 +1565,16 @@ redraw_tree(tree); /* XXX: It shouldn't be necessary to redraw the whole darned tree */ } +void gnt_tree_set_row_color(GntTree *tree, void *key, int color) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + if (!row || row->color == color) + return; + + row->color = color; + redraw_tree(tree); +} + void gnt_tree_set_selected(GntTree *tree , void *key) { int dist; diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gnttree.h Sat Dec 15 05:15:31 2007 +0000 @@ -325,6 +325,15 @@ void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags); /** + * Set color for the text in a row in the tree. + * + * @param tree The tree + * @param key The key for the row + * @param color The color + */ +void gnt_tree_set_row_color(GntTree *, void *, int); + +/** * Select a row. * * @param tree The tree diff -r b9197011ddd6 -r e13759a83714 finch/libgnt/gntwm.c --- a/finch/libgnt/gntwm.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/gntwm.c Sat Dec 15 05:15:31 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 @@ -692,6 +696,7 @@ {'\0', NULL} }; + gnt_widget_destroy(GNT_WIDGET(fs)); if ((file = g_fopen(path, "w+")) == NULL) { return; @@ -803,7 +808,6 @@ } fprintf(file, "\n"); fclose(file); - gnt_widget_destroy(GNT_WIDGET(fs)); } static void @@ -817,6 +821,11 @@ { 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 enter the filename to save the screenshot.")); + gnt_box_set_title(GNT_BOX(window), "Save Screenshot..."); + gnt_file_sel_set_suggested_filename(sel, "dump.html"); g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(dump_file_save), NULL); g_signal_connect(G_OBJECT(sel->cancel), "activate", G_CALLBACK(dump_file_cancel), sel); @@ -1193,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 { @@ -1266,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 @@ -1436,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 b9197011ddd6 -r e13759a83714 finch/libgnt/test/tv.c --- a/finch/libgnt/test/tv.c Sat Dec 15 05:12:24 2007 +0000 +++ b/finch/libgnt/test/tv.c Sat Dec 15 05:15:31 2007 +0000 @@ -112,8 +112,8 @@ gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 2nd line\n", GNT_TEXT_FLAG_NORMAL); - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD | gnt_color_pair(GNT_COLOR_HIGHLIGHT)); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL | gnt_color_pair(GNT_COLOR_HIGHLIGHT)); gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 4th line\n", GNT_TEXT_FLAG_NORMAL); diff -r b9197011ddd6 -r e13759a83714 libpurple/Makefile.am --- a/libpurple/Makefile.am Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/Makefile.am Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/account.c --- a/libpurple/account.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/account.c Sat Dec 15 05:15:31 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; } @@ -741,7 +746,7 @@ current_error = g_new0(PurpleConnectionErrorInfo, 1); current_error->type = type; - current_error->description = description; + current_error->description = g_strdup(description); set_current_error(account, current_error); } @@ -2529,22 +2534,19 @@ g_return_val_if_fail(name != NULL, NULL); - who = g_strdup(purple_normalize(NULL, name)); - for (l = purple_accounts_get_all(); l != NULL; l = l->next) { account = (PurpleAccount *)l->data; - if (!strcmp(purple_normalize(NULL, purple_account_get_username(account)), who) && + who = g_strdup(purple_normalize(account, name)); + if (!strcmp(purple_normalize(account, purple_account_get_username(account)), who) && (!protocol_id || !strcmp(account->protocol_id, protocol_id))) { - + g_free(who); break; } - + g_free(who); account = NULL; } - g_free(who); - return account; } diff -r b9197011ddd6 -r e13759a83714 libpurple/account.h --- a/libpurple/account.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/account.h Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/blist.c --- a/libpurple/blist.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/blist.c Sat Dec 15 05:15:31 2007 +0000 @@ -1310,7 +1310,7 @@ g = (PurpleGroup *)((PurpleBlistNode *)c)->parent; } else { if (group) { - /* Add chat to blist if isn't already on it. Fixes #2752. */ + /* Add group to blist if isn't already on it. Fixes #2752. */ if (!purple_find_group(group->name)) { purple_blist_add_group(group, purple_blist_get_last_sibling(purplebuddylist->root)); diff -r b9197011ddd6 -r e13759a83714 libpurple/connection.c --- a/libpurple/connection.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/connection.c Sat Dec 15 05:15:31 2007 +0000 @@ -38,6 +38,8 @@ #include "signals.h" #include "util.h" +#define KEEPALIVE_INTERVAL 30 + static GList *connections = NULL; static GList *connections_connecting = NULL; static PurpleConnectionUiOps *connection_ui_ops = NULL; @@ -73,7 +75,7 @@ if (on && !gc->keepalive) { purple_debug_info("connection", "Activating keepalive.\n"); - gc->keepalive = purple_timeout_add_seconds(30, send_keepalive, gc); + gc->keepalive = purple_timeout_add_seconds(KEEPALIVE_INTERVAL, send_keepalive, gc); } else if (!on && gc->keepalive > 0) { @@ -219,9 +221,6 @@ { PurpleAccount *account; GSList *buddies; -#if 0 - GList *wins; -#endif PurplePluginProtocolInfo *prpl_info = NULL; gboolean remove = FALSE; @@ -269,19 +268,6 @@ purple_signal_emit(purple_connections_get_handle(), "signed-off", gc); -#if 0 - /* see comment later in file on if 0'd same code */ - /* - * XXX This is a hack! Remove this and replace it with a better event - * notification system. - */ - for (wins = purple_get_windows(); wins != NULL; wins = wins->next) { - PurpleConvWindow *win = (PurpleConvWindow *)wins->data; - purple_conversation_update(purple_conv_window_get_conversation_at(win, 0), - PURPLE_CONV_ACCOUNT_OFFLINE); - } -#endif - purple_account_request_close_with_account(account); purple_request_close_with_handle(gc); purple_notify_close_with_handle(gc); diff -r b9197011ddd6 -r e13759a83714 libpurple/ft.c --- a/libpurple/ft.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/ft.c Sat Dec 15 05:15:31 2007 +0000 @@ -1306,8 +1306,12 @@ } void -purple_xfers_uninit(void) { - purple_signals_disconnect_by_handle(purple_xfers_get_handle()); +purple_xfers_uninit(void) +{ + void *handle = purple_xfers_get_handle(); + + purple_signals_disconnect_by_handle(handle); + purple_signals_unregister_by_instance(handle); } void diff -r b9197011ddd6 -r e13759a83714 libpurple/network.c --- a/libpurple/network.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/network.c Sat Dec 15 05:15:31 2007 +0000 @@ -182,22 +182,22 @@ /* Make sure the IP address entered by the user is valid */ if ((ip != NULL) && (purple_network_ip_atoi(ip) != NULL)) return ip; - } - - /* Check if STUN discovery was already done */ - stun = purple_stun_discover(NULL); - if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) - return stun->publicip; + } else { + /* Check if STUN discovery was already done */ + stun = purple_stun_discover(NULL); + if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) + return stun->publicip; - /* Attempt to get the IP from a NAT device using UPnP */ - ip = purple_upnp_get_public_ip(); - if (ip != NULL) - return ip; + /* Attempt to get the IP from a NAT device using UPnP */ + ip = purple_upnp_get_public_ip(); + if (ip != NULL) + return ip; - /* Attempt to get the IP from a NAT device using NAT-PMP */ - ip = purple_pmp_get_public_ip(); - if (ip != NULL) - return ip; + /* Attempt to get the IP from a NAT device using NAT-PMP */ + ip = purple_pmp_get_public_ip(); + if (ip != NULL) + return ip; + } /* Just fetch the IP of the local system */ return purple_network_get_local_system_ip(fd); @@ -362,7 +362,7 @@ listen_data->cb_data = cb_data; listen_data->socket_type = socket_type; - if (!listen_map_external) + if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports")) { purple_debug_info("network", "Skipping external port mapping.\n"); /* The pmp_map_cb does what we want to do */ @@ -677,11 +677,13 @@ purple_prefs_add_none ("/purple/network"); purple_prefs_add_bool ("/purple/network/auto_ip", TRUE); purple_prefs_add_string("/purple/network/public_ip", ""); + purple_prefs_add_bool ("/purple/network/map_ports", TRUE); purple_prefs_add_bool ("/purple/network/ports_range_use", FALSE); purple_prefs_add_int ("/purple/network/ports_range_start", 1024); purple_prefs_add_int ("/purple/network/ports_range_end", 2048); - purple_upnp_discover(NULL, NULL); + if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip")) + purple_upnp_discover(NULL, NULL); #ifdef HAVE_LIBNM nm_context = libnm_glib_init(); @@ -708,4 +710,7 @@ if(nm_context) libnm_glib_shutdown(nm_context); #endif + + purple_signal_unregister(purple_network_get_handle(), + "network-configuration-changed"); } diff -r b9197011ddd6 -r e13759a83714 libpurple/plugin.c --- a/libpurple/plugin.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugin.c Sat Dec 15 05:15:31 2007 +0000 @@ -1175,7 +1175,7 @@ purple_plugins_init(void) { void *handle = purple_plugins_get_handle(); - purple_plugins_add_search_path(LIBDIR); + purple_plugins_add_search_path(LIBDIR); purple_signal_register(handle, "plugin-load", purple_marshal_VOID__POINTER, @@ -1190,8 +1190,12 @@ } void -purple_plugins_uninit(void) { - purple_signals_disconnect_by_handle(purple_plugins_get_handle()); +purple_plugins_uninit(void) +{ + void *handle = purple_plugins_get_handle(); + + purple_signals_disconnect_by_handle(handle); + purple_signals_unregister_by_instance(handle); } /************************************************************************** diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/autoaccept.c --- a/libpurple/plugins/autoaccept.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/autoaccept.c Sat Dec 15 05:15:31 2007 +0000 @@ -114,22 +114,33 @@ case FT_ACCEPT: if (ensure_path_exists(pref)) { - dirname = g_build_filename(pref, xfer->who, NULL); + int count = 1; + const char *escape; + dirname = g_build_filename(pref, purple_normalize(account, xfer->who), NULL); if (!ensure_path_exists(dirname)) { g_free(dirname); break; } - - filename = g_build_filename(dirname, xfer->filename, NULL); + + escape = purple_escape_filename(xfer->filename); + filename = g_build_filename(dirname, escape, NULL); + + /* Make sure the file doesn't exist. Do we want some better checking than this? */ + while (g_file_test(filename, G_FILE_TEST_EXISTS)) { + char *file = g_strdup_printf("%s-%d", escape, count++); + g_free(filename); + filename = g_build_filename(dirname, file, NULL); + g_free(file); + } purple_xfer_request_accepted(xfer, filename); g_free(dirname); g_free(filename); } - + purple_signal_connect(purple_xfers_get_handle(), "file-recv-complete", handle, PURPLE_CALLBACK(auto_accept_complete_cb), xfer); break; diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/newline.c --- a/libpurple/plugins/newline.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/newline.c Sat Dec 15 05:15:31 2007 +0000 @@ -31,6 +31,12 @@ addnewline_msg_cb(PurpleAccount *account, char *sender, char **message, PurpleConversation *conv, int *flags, void *data) { + if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) && + !purple_prefs_get_bool("/plugins/core/newline/im")) || + ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) && + !purple_prefs_get_bool("/plugins/core/newline/chat"))) + return FALSE; + if (g_ascii_strncasecmp(*message, "/me ", strlen("/me "))) { char *tmp = g_strdup_printf("
%s", *message); g_free(*message); @@ -40,6 +46,25 @@ return FALSE; } +static PurplePluginPrefFrame * +get_plugin_pref_frame(PurplePlugin *plugin) { + PurplePluginPrefFrame *frame; + PurplePluginPref *ppref; + + frame = purple_plugin_pref_frame_new(); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/newline/im", _("Add new line in IMs")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/newline/chat", _("Add new line in Chats")); + purple_plugin_pref_frame_add(frame, ppref); + + return frame; +} + + static gboolean plugin_load(PurplePlugin *plugin) { @@ -53,6 +78,17 @@ return TRUE; } +static PurplePluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, /* page_num (Reserved) */ + NULL, /* frame (Reserved) */ + /* Padding */ + NULL, + NULL, + NULL, + NULL +}; + static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, /**< magic */ @@ -80,7 +116,7 @@ NULL, /**< ui_info */ NULL, /**< extra_info */ - NULL, /**< prefs_info */ + &prefs_info, /**< prefs_info */ NULL, /**< actions */ /* padding */ @@ -92,6 +128,9 @@ static void init_plugin(PurplePlugin *plugin) { + purple_prefs_add_none("/plugins/core/newline"); + purple_prefs_add_bool("/plugins/core/newline/im", TRUE); + purple_prefs_add_bool("/plugins/core/newline/chat", TRUE); } PURPLE_INIT_PLUGIN(lastseen, init_plugin, info) diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/BuddyList.xs --- a/libpurple/plugins/perl/common/BuddyList.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/BuddyList.xs Sat Dec 15 05:15:31 2007 +0000 @@ -44,11 +44,13 @@ Purple::Account account const char * name PREINIT: - GSList *l; + GSList *l, *ll; PPCODE: - for (l = purple_find_buddies(account, name); l != NULL; l = l->next) { + ll = purple_find_buddies(account, name); + for (l = ll; l != NULL; l = l->next) { XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::BuddyList::Buddy"))); } + g_slist_free(ll); Purple::BuddyList::Group purple_find_group(name) @@ -101,11 +103,13 @@ purple_group_get_accounts(group) Purple::BuddyList::Group group PREINIT: - GSList *l; + GSList *l, *ll; PPCODE: - for (l = purple_group_get_accounts(group); l != NULL; l = l->next) { + ll = purple_group_get_accounts(group); + for (l = ll; l != NULL; l = l->next) { XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Account"))); } + g_slist_free(ll); gboolean purple_group_on_account(group, account) @@ -268,11 +272,15 @@ purple_blist_node_get_extended_menu(node) Purple::BuddyList::Node node PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_blist_node_get_extended_menu(node); l != NULL; l = g_list_delete_link(l, l)) { + ll = purple_blist_node_get_extended_menu(node); + for (l = ll; l != NULL; l = l->next) { XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Menu::Action"))); } + /* We can free the list here but the script needs to free the + * Purple::Menu::Action 'objects' itself. */ + g_list_free(ll); void purple_blist_node_set_bool(node, key, value) diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Cmds.xs --- a/libpurple/plugins/perl/common/Cmds.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Cmds.xs Sat Dec 15 05:15:31 2007 +0000 @@ -66,21 +66,23 @@ Purple::Conversation conv const gchar *command PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_cmd_help(conv, command); l != NULL; l = l->next) { + for (l = ll = purple_cmd_help(conv, command); l != NULL; l = l->next) { XPUSHs(sv_2mortal(newSVpv(l->data, 0))); } + g_list_free(ll); void purple_cmd_list(conv) Purple::Conversation conv PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_cmd_list(conv); l != NULL; l = l->next) { + for (l = ll = purple_cmd_list(conv); l != NULL; l = l->next) { XPUSHs(sv_2mortal(newSVpv(l->data, 0))); } + g_list_free(ll); Purple::Cmd::Id purple_cmd_register(plugin, command, args, priority, flag, prpl_id, func, helpstr, data = 0) diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Conversation.xs --- a/libpurple/plugins/perl/common/Conversation.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Conversation.xs Sat Dec 15 05:15:31 2007 +0000 @@ -464,6 +464,10 @@ purple_conv_chat_add_users(chat, t_GL_users, t_GL_extra_msgs, t_GL_flags, new_arrivals); + g_list_free(t_GL_users); + g_list_free(t_GL_extra_msgs); + g_list_free(t_GL_flags); + gboolean purple_conv_chat_find_user(chat, user) Purple::Conversation::Chat chat diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Log.xs --- a/libpurple/plugins/perl/common/Log.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Log.xs Sat Dec 15 05:15:31 2007 +0000 @@ -65,11 +65,15 @@ const char *name Purple::Account account PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_log_get_logs(type, name, account); l != NULL; l = l->next) { - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry"))); + ll = purple_log_get_logs(type, name, account); + for (l = ll; l != NULL; l = l->next) { + XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Log"))); } + /* We can free the list here but the script needs to free the + * Purple::Log 'objects' itself. */ + g_list_free(ll); int purple_log_get_size(log) @@ -79,11 +83,15 @@ purple_log_get_system_logs(account) Purple::Account account PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_log_get_system_logs(account); l != NULL; l = l->next) { - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry"))); + ll = purple_log_get_system_logs(account); + for (l = ll; l != NULL; l = l->next) { + XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Log"))); } + /* We can free the list here but the script needs to free the + * Purple::Log 'objects' itself. */ + g_list_free(ll); int purple_log_get_total_size(type, name, account) @@ -101,11 +109,14 @@ void purple_log_logger_get_options() PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_log_logger_get_options(); l != NULL; l = l->next) { - XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry"))); + /* This might want to be massaged to a hash, since that's essentially + * what the key/value list is emulating. */ + for (l = ll = purple_log_logger_get_options(); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(newSVpv(l->data, 0))); } + g_list_free(ll); gchar_own * purple_log_read(log, flags) diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Pounce.xs --- a/libpurple/plugins/perl/common/Pounce.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Pounce.xs Sat Dec 15 05:15:31 2007 +0000 @@ -106,6 +106,18 @@ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Pounce"))); } +void +purple_pounces_get_all_for_ui(ui) + const char *ui +PREINIT: + GList *l, *ll; +PPCODE: + ll = purple_pounces_get_all_for_ui(ui); + for (l = ll; l != NULL; l = l->next) { + XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Pounce"))); + } + g_list_free(ll); + Purple::Handle purple_pounces_get_handle() diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Prefs.xs --- a/libpurple/plugins/perl/common/Prefs.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Prefs.xs Sat Dec 15 05:15:31 2007 +0000 @@ -57,6 +57,7 @@ t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(value), i, 0), t_sl)); } purple_prefs_add_string_list(name, t_GL); + g_list_free(t_GL); void purple_prefs_destroy() @@ -159,6 +160,7 @@ t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(value), i, 0), t_sl)); } purple_prefs_set_string_list(name, t_GL); + g_list_free(t_GL); void purple_prefs_trigger_callback(name) diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Prpl.xs --- a/libpurple/plugins/perl/common/Prpl.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Prpl.xs Sat Dec 15 05:15:31 2007 +0000 @@ -21,13 +21,15 @@ Purple::Account account Purple::Presence presence PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_prpl_get_statuses(account,presence); l != NULL; l = l->next) { - /* XXX Someone please test and make sure this is the right - * type for these things. */ + ll = purple_prpl_get_statuses(account,presence); + for (l = ll; l != NULL; l = l->next) { XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Status"))); } + /* We can free the list here but the script needs to free the + * Purple::Status 'objects' itself. */ + g_list_free(ll); void purple_prpl_got_account_idle(account, idle, idle_time) diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/SavedStatuses.xs --- a/libpurple/plugins/perl/common/SavedStatuses.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/SavedStatuses.xs Sat Dec 15 05:15:31 2007 +0000 @@ -140,11 +140,13 @@ purple_savedstatuses_get_popular(how_many) unsigned int how_many PREINIT: - GList *l; + GList *l, *ll; PPCODE: - for (l = purple_savedstatuses_get_popular(how_many); l != NULL; l = l->next) { + ll = purple_savedstatuses_get_popular(how_many); + for (l = ll; l != NULL; l = l->next) { XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::SavedStatus"))); } + g_list_free(ll); Purple::Handle purple_savedstatuses_get_handle() diff -r b9197011ddd6 -r e13759a83714 libpurple/plugins/perl/common/Status.xs --- a/libpurple/plugins/perl/common/Status.xs Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/plugins/perl/common/Status.xs Sat Dec 15 05:15:31 2007 +0000 @@ -90,6 +90,7 @@ t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(source_list), i, 0), t_sl)); } purple_presence_add_list(presence, t_GL); + g_list_free(t_GL); void purple_presence_add_status(presence, status) @@ -361,28 +362,6 @@ purple_status_type_destroy(status_type) Purple::StatusType status_type -Purple::StatusType -purple_status_type_find_with_id(status_types, id) - SV *status_types - const char *id -PREINIT: -/* XXX Check that this function actually works, I think it might need a */ -/* status_type as it's first argument to work as $status_type->find_with_id */ -/* properly. */ - GList *t_GL; - int i, t_len; -CODE: - t_GL = NULL; - t_len = av_len((AV *)SvRV(status_types)); - - for (i = 0; i < t_len; i++) { - STRLEN t_sl; - t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(status_types), i, 0), t_sl)); - } - RETVAL = (PurpleStatusType *)purple_status_type_find_with_id(t_GL, id); -OUTPUT: - RETVAL - Purple::StatusAttr purple_status_type_get_attr(status_type, id) Purple::StatusType status_type @@ -398,6 +377,26 @@ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::StatusAttr"))); } +Purple::StatusType +purple_status_type_find_with_id(status_types, id) + SV *status_types + const char *id +PREINIT: + GList *t_GL; + int i, t_len; +CODE: + t_GL = NULL; + t_len = av_len((AV *)SvRV(status_types)); + + for (i = 0; i < t_len; i++) { + STRLEN t_sl; + t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(status_types), i, 0), t_sl)); + } + RETVAL = (PurpleStatusType *)purple_status_type_find_with_id(t_GL, id); + g_list_free(t_GL); +OUTPUT: + RETVAL + const char * purple_status_type_get_id(status_type) Purple::StatusType status_type diff -r b9197011ddd6 -r e13759a83714 libpurple/pounce.c --- a/libpurple/pounce.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/pounce.c Sat Dec 15 05:15:31 2007 +0000 @@ -181,7 +181,8 @@ child = xmlnode_new_child(node, "account"); xmlnode_set_attrib(child, "protocol", pouncer->protocol_id); - xmlnode_insert_data(child, purple_account_get_username(pouncer), -1); + xmlnode_insert_data(child, + purple_normalize(pouncer, purple_account_get_username(pouncer)), -1); child = xmlnode_new_child(node, "pouncee"); xmlnode_insert_data(child, purple_pounce_get_pouncee(pounce), -1); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/Makefile.am --- a/libpurple/protocols/bonjour/Makefile.am Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.am Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/bonjour_ft.c --- a/libpurple/protocols/bonjour/bonjour_ft.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/buddy.c --- a/libpurple/protocols/bonjour/buddy.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/buddy.h --- a/libpurple/protocols/bonjour/buddy.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.h Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/issues.txt --- a/libpurple/protocols/bonjour/issues.txt Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/issues.txt Sat Dec 15 05:15:31 2007 +0000 @@ -2,5 +2,4 @@ ============= Known issues =============== ========================================== -* File transfers * Typing notifications diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/jabber.c --- a/libpurple/protocols/bonjour/jabber.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Sat Dec 15 05:15:31 2007 +0000 @@ -32,6 +32,12 @@ #include "libc_interface.h" #endif #include + +/* Solaris */ +#if defined (__SVR4) && defined (__sun) +#include +#endif + #include #include #include @@ -62,34 +68,15 @@ #define DOCTYPE "\n" \ "" +enum sent_stream_start_types { + NOT_SENT = 0, + PARTIALLY_SENT = 1, + FULLY_SENT = 2 +}; + static void xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb); -#if 0 /* this isn't used anywhere... */ -static const char * -_font_size_purple_to_ichat(int size) -{ - switch (size) { - case 1: - return "8"; - case 2: - return "10"; - case 3: - return "12"; - case 4: - return "14"; - case 5: - return "17"; - case 6: - return "21"; - case 7: - return "24"; - } - - return "12"; -} -#endif - static BonjourJabberConversation * bonjour_jabber_conv_new(PurpleBuddy *pb) { @@ -100,6 +87,8 @@ bconv->rx_handler = 0; bconv->pb = pb; + bonjour_parser_setup(bconv); + return bconv; } @@ -129,34 +118,48 @@ { xmlnode *body_node, *html_node, *events_node; PurpleConnection *gc = pb->account->gc; - char *body, *html_body = NULL; - const char *ichat_balloon_color = NULL; - const char *ichat_text_color = NULL; - const char *font_face = NULL; - const char *font_size = NULL; - const char *font_color = NULL; + gchar *body = NULL; gboolean composing_event = FALSE; body_node = xmlnode_get_child(message_node, "body"); - if (body_node == NULL) + html_node = xmlnode_get_child(message_node, "html"); + + if (body_node == NULL && html_node == NULL) { + purple_debug_error("bonjour", "No body or html node found, discarding message.\n"); return; - body = xmlnode_get_data(body_node); + } - html_node = xmlnode_get_child(message_node, "html"); - if (html_node != NULL) - { + events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event"); + if (events_node != NULL) { + if (xmlnode_get_child(events_node, "composing") != NULL) + composing_event = TRUE; + if (xmlnode_get_child(events_node, "id") != NULL) { + /* The user is just typing */ + /* TODO: Deal with typing notification */ + return; + } + } + + if (html_node != NULL) { xmlnode *html_body_node; html_body_node = xmlnode_get_child(html_node, "body"); - if (html_body_node != NULL) - { + if (html_body_node != NULL) { xmlnode *html_body_font_node; + const char *ichat_balloon_color = NULL; + const char *ichat_text_color = NULL; + const char *font_face = NULL; + const char *font_size = NULL; + const char *font_color = NULL; ichat_balloon_color = xmlnode_get_attrib(html_body_node, "ichatballooncolor"); ichat_text_color = xmlnode_get_attrib(html_body_node, "ichattextcolor"); html_body_font_node = xmlnode_get_child(html_body_node, "font"); - if (html_body_font_node != NULL) - { /* Types of messages sent by iChat */ + + /* Types of messages sent by iChat */ + if (html_body_font_node != NULL) { + gchar *html_body; + font_face = xmlnode_get_attrib(html_body_font_node, "face"); /* The absolute iChat font sizes should be converted to 1..7 range */ font_size = xmlnode_get_attrib(html_body_font_node, "ABSZ"); @@ -164,49 +167,41 @@ font_size = _font_size_ichat_to_purple(atoi(font_size)); font_color = xmlnode_get_attrib(html_body_font_node, "color"); html_body = xmlnode_get_data(html_body_font_node); + if (html_body == NULL) /* This is the kind of formated messages that Purple creates */ html_body = xmlnode_to_str(html_body_font_node, NULL); + + if (html_body != NULL) { + /* Use some sane defaults */ + if (font_face == NULL) font_face = "Helvetica"; + if (font_size == NULL) font_size = "3"; + if (ichat_text_color == NULL) ichat_text_color = "#000000"; + if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF"; + + body = g_strdup_printf("%s", + font_face, font_size, ichat_text_color, ichat_balloon_color, + html_body); + + g_free(html_body); + } } } } - events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event"); - if (events_node != NULL) - { - if (xmlnode_get_child(events_node, "composing") != NULL) - composing_event = TRUE; - if (xmlnode_get_child(events_node, "id") != NULL) - { - /* The user is just typing */ - /* TODO: Deal with typing notification */ - g_free(body); - g_free(html_body); - return; - } - } + /* Compose the message */ + if (body == NULL && body_node != NULL) + body = xmlnode_get_data(body_node); - /* Compose the message */ - if (html_body != NULL) - { - g_free(body); - - if (font_face == NULL) font_face = "Helvetica"; - if (font_size == NULL) font_size = "3"; - if (ichat_text_color == NULL) ichat_text_color = "#000000"; - if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF"; - body = g_strdup_printf("%s", - font_face, font_size, ichat_text_color, ichat_balloon_color, - html_body); + if (body == NULL) { + purple_debug_error("bonjour", "No html body or regular body found.\n"); + return; } - /* TODO: Should we do something with "composing_event" here? */ - /* Send the message to the UI */ serv_got_im(gc, pb->name, body, 0, time(NULL)); g_free(body); - g_free(html_body); } struct _check_buddy_by_address_t { @@ -225,13 +220,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; + } + } } } @@ -289,7 +295,7 @@ /* If we're not ready to actually send, append it to the buffer */ if (bconv->tx_handler != 0 || bconv->connect_data != NULL - || !bconv->sent_stream_start + || bconv->sent_stream_start != FULLY_SENT || !bconv->recv_stream_start || purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) { ret = -1; @@ -319,7 +325,8 @@ } if (ret < len) { - if (bconv->tx_handler == 0) + /* Don't interfere with the stream starting */ + if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && bconv->tx_handler == 0) bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE, _send_data_write_cb, pb); purple_circ_buffer_append(bconv->tx_buf, message + ret, len - ret); @@ -409,20 +416,6 @@ } } -void bonjour_jabber_stream_started(PurpleBuddy *pb) { - BonjourBuddy *bb = pb->proto_data; - BonjourJabberConversation *bconv = bb->conversation; - - /* If the stream has been completely started, we can start doing stuff */ - if (bconv->sent_stream_start && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) { - /* Watch for when we can write the buffered messages */ - bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE, - _send_data_write_cb, pb); - /* We can probably write the data right now. */ - _send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE); - } - -} struct _stream_start_data { char *msg; @@ -448,9 +441,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) @@ -478,14 +476,13 @@ /* Stream started; process the send buffer if there is one */ purple_input_remove(bconv->tx_handler); - bconv->tx_handler= 0; - bconv->sent_stream_start = TRUE; + bconv->tx_handler = 0; + bconv->sent_stream_start = FULLY_SENT; bonjour_jabber_stream_started(pb); - } -static gboolean bonjour_jabber_stream_init(PurpleBuddy *pb, int client_socket) +static gboolean bonjour_jabber_send_stream_init(PurpleBuddy *pb, int client_socket) { int ret, len; char *stream_start; @@ -495,6 +492,8 @@ purple_buddy_get_name(pb)); len = strlen(stream_start); + bb->conversation->sent_stream_start = PARTIALLY_SENT; + /* Start the stream */ ret = send(client_socket, stream_start, len, 0); @@ -502,9 +501,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); @@ -521,18 +525,60 @@ bb->conversation->tx_handler = purple_input_add(client_socket, PURPLE_INPUT_WRITE, _start_stream, pb); } else - bb->conversation->sent_stream_start = TRUE; + bb->conversation->sent_stream_start = FULLY_SENT; g_free(stream_start); - /* setup the parser fresh for each stream */ - bonjour_parser_setup(bb->conversation); + return TRUE; +} + +static gboolean +_async_bonjour_jabber_close_conversation(gpointer data) { + BonjourJabberConversation *bconv = data; + bonjour_jabber_close_conversation(bconv); + return FALSE; +} + +void bonjour_jabber_stream_started(PurpleBuddy *pb) { + BonjourBuddy *bb = pb->proto_data; + BonjourJabberConversation *bconv = bb->conversation; + + 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; - bb->conversation->socket = client_socket; - bb->conversation->rx_handler = purple_input_add(client_socket, - PURPLE_INPUT_READ, _client_socket_handler, pb); + purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n", + 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) + purple_conversation_write(conv, NULL, + _("Unable to send the message, the conversation couldn't be started."), + PURPLE_MESSAGE_SYSTEM, time(NULL)); - return TRUE; + close(bconv->socket); + /* This must be asynchronous because it destroys the parser and we + * may be in the middle of parsing. + */ + purple_timeout_add(0, _async_bonjour_jabber_close_conversation, bb->conversation); + bb->conversation = NULL; + return; + } + + /* If the stream has been completely started, we can start doing stuff */ + if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) { + /* Watch for when we can write the buffered messages */ + bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE, + _send_data_write_cb, pb); + /* We can probably write the data right now. */ + _send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE); + } + } static void @@ -576,14 +622,15 @@ bb = pb->proto_data; /* Check if the conversation has been previously started */ + /* This really shouldn't ever happen unless something weird is going on */ if (bb->conversation == NULL) { bb->conversation = bonjour_jabber_conv_new(pb); - if (!bonjour_jabber_stream_init(pb, client_socket)) { - close(client_socket); - return; - } + /* We wait for the stream start before doing anything else */ + bb->conversation->socket = client_socket; + bb->conversation->rx_handler = purple_input_add(client_socket, + PURPLE_INPUT_READ, _client_socket_handler, pb); } else { purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n"); @@ -620,7 +667,7 @@ } memset(&my_addr, 0, sizeof(struct sockaddr_in)); - my_addr.sin_family = PF_INET; + my_addr.sin_family = AF_INET; /* Attempt to find a free port */ bind_successful = FALSE; @@ -681,9 +728,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) @@ -696,12 +748,17 @@ return; } - if (!bonjour_jabber_stream_init(pb, source)) { + 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) @@ -714,6 +771,11 @@ bb->conversation = NULL; return; } + + /* Start listening for the stream acknowledgement */ + bb->conversation->socket = source; + bb->conversation->rx_handler = purple_input_add(source, + PURPLE_INPUT_READ, _client_socket_handler, pb); } static PurpleBuddy * @@ -737,6 +799,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); @@ -749,8 +816,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); @@ -843,7 +910,7 @@ /* Close the socket and remove the watcher */ if (bconv->socket >= 0) { /* Send the end of the stream to the other end of the conversation */ - if (bconv->sent_stream_start) + if (bconv->sent_stream_start == FULLY_SENT) send(bconv->socket, STREAM_END, strlen(STREAM_END), 0); /* TODO: We're really supposed to wait for "" before closing the socket */ close(bconv->socket); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/jabber.h --- a/libpurple/protocols/bonjour/jabber.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Sat Dec 15 05:15:31 2007 +0000 @@ -47,7 +47,7 @@ guint rx_handler; guint tx_handler; PurpleCircBuffer *tx_buf; - gboolean sent_stream_start; + int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */ gboolean recv_stream_start; PurpleProxyConnectData *connect_data; gpointer stream_data; diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/mdns_avahi.c --- a/libpurple/protocols/bonjour/mdns_avahi.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Sat Dec 15 05:15:31 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); @@ -155,7 +244,7 @@ /* Make sure it isn't us */ if (purple_utf8_strcasecmp(name, account->username) != 0) { if (!avahi_service_resolver_new(avahi_service_browser_get_client(b), - interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, + interface, protocol, name, type, domain, AVAHI_PROTO_INET, 0, _resolver_callback, account)) { purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); @@ -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; + } } /**************************** @@ -315,14 +443,14 @@ case PUBLISH_START: publish_result = avahi_entry_group_add_service_strlst( idata->group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, 0, + AVAHI_PROTO_INET, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, NULL, data->port_p2pj, lst); break; case PUBLISH_UPDATE: publish_result = avahi_entry_group_update_service_txt_strlst( idata->group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, 0, + AVAHI_PROTO_INET, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, lst); break; @@ -354,7 +482,7 @@ g_return_val_if_fail(idata != NULL, FALSE); - idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, ICHAT_SERVICE, NULL, 0, _browser_callback, data->account); + idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, ICHAT_SERVICE, NULL, 0, _browser_callback, data->account); if (!idata->sb) { purple_debug_error("bonjour", @@ -400,7 +528,7 @@ purple_account_get_username(data->account)); ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, flags, svc_name, + AVAHI_PROTO_INET, flags, svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len); g_free(svc_name); @@ -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); @@ -486,7 +617,7 @@ name = g_strdup_printf("%s." ICHAT_SERVICE "local", buddy->name); idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, + AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, _buddy_icon_record_cb, buddy); g_free(name); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/mdns_howl.c --- a/libpurple/protocols/bonjour/mdns_howl.c Sat Dec 15 05:12:24 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/bonjour/mdns_win32.c --- a/libpurple/protocols/bonjour/mdns_win32.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/irc/irc.c --- a/libpurple/protocols/irc/irc.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/irc/irc.c Sat Dec 15 05:15:31 2007 +0000 @@ -184,9 +184,14 @@ /* XXX I don't like messing directly with these buddies */ gboolean irc_blist_timeout(struct irc_conn *irc) { - GString *string = g_string_sized_new(512); + GString *string; char *list, *buf; + if (irc->ison_outstanding) + return TRUE; + + string = g_string_sized_new(512); + g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string); list = g_string_free(string, FALSE); @@ -200,6 +205,8 @@ irc_send(irc, buf); g_free(buf); + irc->ison_outstanding = TRUE; + return TRUE; } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/irc/irc.h --- a/libpurple/protocols/irc/irc.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/irc/irc.h Sat Dec 15 05:15:31 2007 +0000 @@ -56,6 +56,8 @@ guint timer; GHashTable *buddies; + gboolean ison_outstanding; + char *inbuf; int inbuflen; int inbufused; diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/irc/msgs.c --- a/libpurple/protocols/irc/msgs.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/irc/msgs.c Sat Dec 15 05:15:31 2007 +0000 @@ -707,6 +707,7 @@ g_strfreev(nicks); g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_status, (gpointer)irc); + irc->ison_outstanding = FALSE; } static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc) diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/irc/parse.c --- a/libpurple/protocols/irc/parse.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/irc/parse.c Sat Dec 15 05:15:31 2007 +0000 @@ -64,7 +64,7 @@ { "318", "nt:", irc_msg_endwhois }, /* End of WHOIS */ { "319", "nn:", irc_msg_whois }, /* Whois channels */ { "320", "nn:", irc_msg_whois }, /* Whois (fn ident) */ - { "314", "nnvvv:", irc_msg_whois }, /* Whowas user */ + { "314", "nnnvv:", irc_msg_whois }, /* Whowas user */ { "369", "nt:", irc_msg_endwhois }, /* End of WHOWAS */ { "321", "*", irc_msg_list }, /* Start of list */ { "322", "ncv:", irc_msg_list }, /* List. */ diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/jabber/caps.c --- a/libpurple/protocols/jabber/caps.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/jabber/caps.c Sat Dec 15 05:15:31 2007 +0000 @@ -257,18 +257,23 @@ /* this function assumes that all information is available locally */ static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const char *ver, GList *ext) { - JabberCapsClientInfo *result = g_new0(JabberCapsClientInfo, 1); + JabberCapsClientInfo *result; JabberCapsKey *key = g_new0(JabberCapsKey, 1); JabberCapsValue *caps; GList *iter; - + key->node = (char *)node; key->ver = (char *)ver; - + caps = g_hash_table_lookup(capstable,key); - + g_free(key); - + + if (caps == NULL) + return NULL; + + result = g_new0(JabberCapsClientInfo, 1); + /* join all information */ for(iter = caps->identities; iter; iter = g_list_next(iter)) { JabberCapsIdentity *id = iter->data; diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sat Dec 15 05:15:31 2007 +0000 @@ -537,12 +537,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) @@ -1280,6 +1281,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 b9197011ddd6 -r e13759a83714 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.h Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/jabber/presence.c Sat Dec 15 05:15:31 2007 +0000 @@ -376,23 +376,26 @@ static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) { JabberPresenceCapabilities *userdata = user_data; GList *iter; - + if(userdata->jbr->caps) jabber_caps_free_clientinfo(userdata->jbr->caps); userdata->jbr->caps = info; - - for(iter = info->features; iter; iter = g_list_next(iter)) { - if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) { - JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); - xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); - xmlnode_set_attrib(iq->node, "to", userdata->from); - xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); - - jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); - jabber_iq_send(iq); - break; + + if (info) { + for(iter = info->features; iter; iter = g_list_next(iter)) { + if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) { + JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); + xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); + xmlnode_set_attrib(iq->node, "to", userdata->from); + xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); + + jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); + jabber_iq_send(iq); + break; + } } } + g_free(userdata->from); g_free(userdata); } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/command.c --- a/libpurple/protocols/msn/command.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msn/command.c Sat Dec 15 05:15:31 2007 +0000 @@ -117,6 +117,7 @@ cmd->trId = 0; } + /* khc: Huh! */ /*add payload Length checking*/ msn_set_payload_len(cmd); purple_debug_info("MSNP14","get payload len:%d\n",cmd->payload_len); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/msn.c diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msn/notification.c Sat Dec 15 05:15:31 2007 +0000 @@ -1579,6 +1579,7 @@ { xmlnode * root; gchar * buf; + int xmllen; g_return_if_fail(cmd->payload != NULL); @@ -1588,10 +1589,10 @@ return; } - buf = xmlnode_to_formatted_str(root, NULL); + buf = xmlnode_to_formatted_str(root, &xmllen); /* get the payload content */ - purple_debug_info("MSNP14","GCF command payload:\n%s\n",buf); + purple_debug_info("MSNP14","GCF command payload:\n%.*s\n", xmllen, buf); g_free(buf); xmlnode_free(root); @@ -1777,7 +1778,7 @@ passport = msn_user_get_passport(session->user); url = session->passport_info.file; - purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL, + purple_notify_emails(gc, count, FALSE, NULL, NULL, &passport, &url, NULL, NULL); } } @@ -1850,7 +1851,7 @@ passport = msn_user_get_passport(session->user); url = session->passport_info.file; - purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL, + purple_notify_emails(gc, count, FALSE, NULL, NULL, &passport, &url, NULL, NULL); } } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/oim.c --- a/libpurple/protocols/msn/oim.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msn/oim.c Sat Dec 15 05:15:31 2007 +0000 @@ -488,10 +488,12 @@ char *unread = xmlnode_get_data(iu_node); const char *passport = msn_user_get_passport(session->user); const char *url = session->passport_info.file; + int count = atoi(unread); /* XXX/khc: pretty sure this is wrong */ - purple_notify_emails(session->account->gc, atoi(unread), FALSE, NULL, - NULL, &passport, &url, NULL, NULL); + if (count > 0) + purple_notify_emails(session->account->gc, count, FALSE, NULL, + NULL, &passport, &url, NULL, NULL); g_free(unread); } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msn/session.c Sat Dec 15 05:15:31 2007 +0000 @@ -74,6 +74,7 @@ msn_userlist_destroy(session->userlist); + g_free(session->psm); g_free(session->passport_info.t); g_free(session->passport_info.p); g_free(session->passport_info.kv); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/state.c --- a/libpurple/protocols/msn/state.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msn/state.c Sat Dec 15 05:15:31 2007 +0000 @@ -238,13 +238,15 @@ media = create_media_string(presence); g_free(session->psm); session->psm = msn_build_psm(statusline_stripped, media, NULL); - g_free(statusline_stripped); payload = session->psm; purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload); trans = msn_transaction_new(cmdproc, "UUX", "%d", strlen(payload)); msn_transaction_set_payload(trans, payload, strlen(payload)); msn_cmdproc_send_trans(cmdproc, trans); + + g_free(statusline_stripped); + g_free(media); } void diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msn/user.c Sat Dec 15 05:15:31 2007 +0000 @@ -83,6 +83,7 @@ g_free(user->media.artist); g_free(user->media.title); g_free(user->media.album); + g_free(user->statusline); g_free(user); } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msnp9/directconn.c --- a/libpurple/protocols/msnp9/directconn.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msnp9/directconn.c Sat Dec 15 05:15:31 2007 +0000 @@ -81,6 +81,7 @@ create_listener(int port) { int fd; + int flags; const int on = 1; #if 0 @@ -156,7 +157,8 @@ return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); return fd; } @@ -353,7 +355,7 @@ } static void -connect_cb(gpointer data, gint source, const gchar *error_message) +connect_cb(gpointer data, gint source, PurpleInputCondition cond) { MsnDirectConn* directconn; int fd; @@ -405,6 +407,15 @@ } } +static void +directconn_connect_cb(gpointer data, gint source, const gchar *error_message) +{ + if (error_message) + purple_debug_error("msn", "Error making direct connection: %s\n", error_message); + + connect_cb(data, source, PURPLE_INPUT_READ); +} + gboolean msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port) { @@ -424,14 +435,9 @@ #endif directconn->connect_data = purple_proxy_connect(NULL, session->account, - host, port, connect_cb, directconn); + host, port, directconn_connect_cb, directconn); - if (directconn->connect_data != NULL) - { - return TRUE; - } - else - return FALSE; + return (directconn->connect_data != NULL); } #if 0 diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msnp9/notification.c --- a/libpurple/protocols/msnp9/notification.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msnp9/notification.c Sat Dec 15 05:15:31 2007 +0000 @@ -235,6 +235,8 @@ /* OK */ const char *friendly = purple_url_decode(cmd->params[3]); + session->passport_info.verified = atoi(cmd->params[4]); + purple_connection_set_display_name(gc, friendly); msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msnp9/servconn.c --- a/libpurple/protocols/msnp9/servconn.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msnp9/servconn.c Sat Dec 15 05:15:31 2007 +0000 @@ -468,6 +468,7 @@ create_listener(int port) { int fd; + int flags; const int on = 1; #if 0 @@ -543,7 +544,8 @@ return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); return fd; } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msnp9/session.h --- a/libpurple/protocols/msnp9/session.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msnp9/session.h Sat Dec 15 05:15:31 2007 +0000 @@ -114,7 +114,7 @@ char *file; char *client_ip; int client_port; - + int verified; } passport_info; }; diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/msnp9/user.c --- a/libpurple/protocols/msnp9/user.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/msnp9/user.c Sat Dec 15 05:15:31 2007 +0000 @@ -126,16 +126,20 @@ msn_user_set_friendly_name(MsnUser *user, const char *name) { MsnCmdProc *cmdproc; + MsnSession *session; const char *encoded; g_return_if_fail(user != NULL); encoded = purple_url_encode(name); + session = user->userlist->session; - if (user->friendly_name && strcmp(user->friendly_name, name) && (strlen(encoded) < 387)) { + if (user->friendly_name && strcmp(user->friendly_name, name) + && (strlen(encoded) < 387) && session->passport_info.verified && + (user->list_op & MSN_LIST_FL_OP)) { /* copy the new name to the server list, but only when new */ /* should we check this more thoroughly? */ - cmdproc = user->userlist->session->notification->cmdproc; + cmdproc = session->notification->cmdproc; msn_cmdproc_send(cmdproc, "REA", "%s %s", user->passport, encoded); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/myspace/myspace.c --- a/libpurple/protocols/myspace/myspace.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.c Sat Dec 15 05:15:31 2007 +0000 @@ -1047,15 +1047,18 @@ msim_set_status(PurpleAccount *account, PurpleStatus *status) { PurpleStatusType *type; + PurplePresence *pres; MsimSession *session; guint status_code; - gchar *statstring; + const gchar *message; + gchar *stripped; session = (MsimSession *)account->gc->proto_data; g_return_if_fail(MSIM_SESSION_VALID(session)); type = purple_status_get_type(status); + pres = purple_status_get_presence(status); switch (purple_status_type_get_primitive(type)) { case PURPLE_STATUS_AVAILABLE: @@ -1083,16 +1086,20 @@ break; } - statstring = (gchar *)purple_status_get_attr_string(status, "message"); - - if (!statstring) { - statstring = ""; - } + message = purple_status_get_attr_string(status, "message"); /* Status strings are plain text. */ - statstring = purple_markup_strip_html(statstring); - - msim_set_status_code(session, status_code, statstring); + if (message != NULL) + stripped = purple_markup_strip_html(message); + else + stripped = g_strdup(""); + + msim_set_status_code(session, status_code, stripped); + + /* If we should be idle, set that status. Time is irrelevant here. */ + if (purple_presence_is_idle(pres) && status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN) + msim_set_idle(account->gc, 1); + } /** Go idle. */ @@ -1100,6 +1107,7 @@ msim_set_idle(PurpleConnection *gc, int time) { MsimSession *session; + PurpleStatus *status; g_return_if_fail(gc != NULL); @@ -1107,16 +1115,30 @@ g_return_if_fail(MSIM_SESSION_VALID(session)); + status = purple_account_get_active_status(session->account); + if (time == 0) { /* Going back from idle. In msim, idle is mutually exclusive * from the other states (you can only be away or idle, but not - * both, for example), so by going non-idle I go online. + * both, for example), so by going non-idle I go back to what + * libpurple says I should be. */ - /* TODO: find out how to keep old status string? */ - msim_set_status_code(session, MSIM_STATUS_CODE_ONLINE, g_strdup("")); + msim_set_status(session->account, status); } else { + const gchar *message; + gchar *stripped; + + /* Set the idle message to the status message from the real + * current status. + */ + message = purple_status_get_attr_string(status, "message"); + if (message != NULL) + stripped = purple_markup_strip_html(message); + else + stripped = g_strdup(""); + /* msim doesn't support idle time, so just go idle */ - msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, g_strdup("")); + msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, stripped); } } @@ -1347,7 +1369,9 @@ msim_msg_dump("msim_check_inbox_cb: reply=%s\n", reply); body = msim_msg_get_dictionary(reply, "body"); - g_return_if_fail(body != NULL); + + if (body == NULL) + return; old_inbox_status = session->inbox_status; @@ -1411,6 +1435,11 @@ session = (MsimSession *)data; + if (!MSIM_SESSION_VALID(session)) { + purple_debug_info("msim", "msim_check_inbox: session invalid, stopping the mail check.\n"); + return FALSE; + } + purple_debug_info("msim", "msim_check_inbox: checking mail\n"); g_return_val_if_fail(msim_send(session, "persist", MSIM_TYPE_INTEGER, 1, @@ -1623,7 +1652,7 @@ /* Check mail if they want to. */ if (purple_account_get_check_mail(session->account)) { - purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, + session->inbox_handle = purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, (GSourceFunc)msim_check_inbox, session); msim_check_inbox(session); } @@ -1894,7 +1923,7 @@ purple_debug_info("msim", "msim_status: found buddy %s\n", username); } - if (status_headline) { + if (status_headline && strcmp(status_headline, "") != 0) { /* The status headline is plaintext, but libpurple treats it as HTML, * so escape any HTML characters to their entity equivalents. */ status_headline_escaped = g_markup_escape_text(status_headline, strlen(status_headline)); @@ -1925,8 +1954,8 @@ break; case MSIM_STATUS_CODE_IDLE: - /* will be handled below */ - purple_status_code = -1; + /* Treat idle as an available status. */ + purple_status_code = PURPLE_STATUS_AVAILABLE; break; default: diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/myspace/session.c --- a/libpurple/protocols/myspace/session.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/myspace/session.c Sat Dec 15 05:15:31 2007 +0000 @@ -63,6 +63,7 @@ session->next_rid = 1; session->last_comm = time(NULL); session->inbox_status = 0; + session->inbox_handle = 0; return session; } @@ -90,6 +91,11 @@ msim_msg_free(session->server_info); } + /* Stop checking the inbox at the end of the session. */ + if (session->inbox_handle) { + purple_timeout_remove(session->inbox_handle); + } + g_free(session); } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/myspace/session.h --- a/libpurple/protocols/myspace/session.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/myspace/session.h Sat Dec 15 05:15:31 2007 +0000 @@ -45,6 +45,7 @@ guint next_rid; /**< Next request/response ID */ time_t last_comm; /**< Time received last communication */ guint inbox_status; /**< Bit field of inbox notifications */ + guint inbox_handle; /**< The handle for the mail check timer */ } MsimSession; /* Check if an MsimSession is valid */ diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/oscar/family_chatnav.c --- a/libpurple/protocols/oscar/family_chatnav.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/oscar/family_chatnav.c Sat Dec 15 05:15:31 2007 +0000 @@ -29,6 +29,49 @@ #include "oscar.h" +static int +error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) +{ + int ret = 0; + aim_snac_t *snac2; + guint16 error, chatnav_error; + GSList *tlvlist; + + if (!(snac2 = aim_remsnac(od, snac->id))) { + purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08lx)\n", snac->id); + return 0; + } + + if (snac2->family != 0x000d) { + purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family); + return 0; + } + + /* + * We now know what the original SNAC subtype was. + */ + if (snac2->type == 0x0008) /* create room */ + { + error = byte_stream_get16(bs); + tlvlist = aim_tlvlist_read(bs); + chatnav_error = aim_tlv_get16(tlvlist, 0x0008, 1); + + purple_debug_warning("oscar", + "Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n", + error, chatnav_error); + purple_notify_error(od->gc, NULL, _("Could not join chat room"), + chatnav_error == 0x0033 ? _("Invalid chat room name") : _("Unknown error")); + + ret = 1; + } + + if (snac2) + g_free(snac2->data); + g_free(snac2); + + return ret; +} + /* * Subtype 0x0002 * @@ -451,7 +494,9 @@ static int snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { - if (snac->subtype == 0x0009) + if (snac->subtype == 0x0001) + return error(od, conn, mod, frame, snac, bs); + else if (snac->subtype == 0x0009) return parseinfo(od, conn, mod, frame, snac, bs); return 0; diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/oscar/family_feedbag.c --- a/libpurple/protocols/oscar/family_feedbag.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/oscar/family_feedbag.c Sat Dec 15 05:15:31 2007 +0000 @@ -115,14 +115,10 @@ gboolean exists; struct aim_ssi_item *cur, *new; - new = (struct aim_ssi_item *)g_malloc(sizeof(struct aim_ssi_item)); + new = g_new(struct aim_ssi_item, 1); /* Set the name */ - if (name) { - new->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)); - strcpy(new->name, name); - } else - new->name = NULL; + new->name = g_strdup(name); /* Set the group ID# and buddy ID# */ new->gid = gid; @@ -345,13 +341,9 @@ */ struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn) { - struct aim_ssi_item *cur; - if (!list || !sn) + if (!sn) return NULL; - for (cur=list; cur; cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn))) - return cur; - return NULL; + return aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY); } /** @@ -510,7 +502,7 @@ for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) { if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) { n++; - new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp)); + new = g_new(struct aim_ssi_tmp, 1); new->action = SNAC_SUBTYPE_FEEDBAG_DEL; new->ack = 0xffff; new->name = NULL; @@ -530,7 +522,7 @@ for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) { if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) { n++; - new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp)); + new = g_new(struct aim_ssi_tmp, 1); new->action = SNAC_SUBTYPE_FEEDBAG_ADD; new->ack = 0xffff; new->name = NULL; @@ -551,7 +543,7 @@ cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid); if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) { n++; - new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp)); + new = g_new(struct aim_ssi_tmp, 1); new->action = SNAC_SUBTYPE_FEEDBAG_MOD; new->ack = 0xffff; new->name = NULL; @@ -1028,8 +1020,7 @@ return -EINVAL; g_free(group->name); - group->name = (char *)g_malloc((strlen(newgn)+1)*sizeof(char)); - strcpy(group->name, newgn); + group->name = g_strdup(newgn); /* Sync our local list with the server list */ return aim_ssi_sync(od); @@ -1461,11 +1452,7 @@ if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) { item->type = type; g_free(item->name); - if (name) { - item->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)); - strcpy(item->name, name); - } else - item->name = NULL; + item->name = g_strdup(name); aim_tlvlist_free(item->data); item->data = aim_tlvlist_copy(data); } @@ -1473,11 +1460,7 @@ if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) { item->type = type; g_free(item->name); - if (name) { - item->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)); - strcpy(item->name, name); - } else - item->name = NULL; + item->name = g_strdup(name); aim_tlvlist_free(item->data); item->data = aim_tlvlist_copy(data); } @@ -1555,10 +1538,7 @@ /* Remove the item from the local list */ /* Make sure cur->item is still valid memory */ if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) { - if (cur->item->name) { - cur->name = (char *)g_malloc((strlen(cur->item->name)+1)*sizeof(char)); - strcpy(cur->name, cur->item->name); - } + cur->name = g_strdup(cur->item->name); aim_ssi_itemlist_del(&od->ssi.local, cur->item); } cur->item = NULL; @@ -1569,11 +1549,7 @@ struct aim_ssi_item *cur1; if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) { g_free(cur->item->name); - if (cur1->name) { - cur->item->name = (char *)g_malloc((strlen(cur1->name)+1)*sizeof(char)); - strcpy(cur->item->name, cur1->name); - } else - cur->item->name = NULL; + cur->item->name = g_strdup(cur1->name); aim_tlvlist_free(cur->item->data); cur->item->data = aim_tlvlist_copy(cur1->data); } @@ -1603,11 +1579,7 @@ struct aim_ssi_item *cur1; if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) { g_free(cur1->name); - if (cur->item->name) { - cur1->name = (char *)g_malloc((strlen(cur->item->name)+1)*sizeof(char)); - strcpy(cur1->name, cur->item->name); - } else - cur1->name = NULL; + cur1->name = g_strdup(cur->item->name); aim_tlvlist_free(cur1->data); cur1->data = aim_tlvlist_copy(cur->item->data); } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/oscar/family_icbm.c --- a/libpurple/protocols/oscar/family_icbm.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Sat Dec 15 05:15:31 2007 +0000 @@ -213,7 +213,6 @@ */ static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { - aim_rxcallback_t userfunc; struct aim_icbmparameters params; params.maxchan = byte_stream_get16(bs); @@ -223,8 +222,11 @@ params.maxrecverwarn = byte_stream_get16(bs); params.minmsginterval = byte_stream_get32(bs); - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - return userfunc(od, conn, frame, ¶ms); + params.flags = 0x0000000b; + params.maxmsglen = 8000; + params.minmsginterval = 0; + + aim_im_setparams(od, ¶ms); return 0; } @@ -292,7 +294,7 @@ if (!args->msg || (args->msglen <= 0)) return -EINVAL; - if (args->msglen >= MAXMSGLEN) + if (args->msglen > MAXMSGLEN) return -E2BIG; } diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Sat Dec 15 05:15:31 2007 +0000 @@ -176,7 +176,6 @@ static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_parse_locerr (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_icbm_param_info (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_memrequest (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...); @@ -1249,7 +1248,6 @@ oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0); oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0); oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, 0x0005, purple_icbm_param_info, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0); @@ -1854,9 +1852,6 @@ signon = info->onlinesince; else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) signon = time(NULL) - info->sessionlen; - if (!aim_sncmp(purple_account_get_username(account), info->sn)) { - purple_connection_set_display_name(gc, info->sn); - } purple_prpl_got_user_login_time(account, info->sn, signon); /* Idle time stuff */ @@ -3463,6 +3458,8 @@ info = va_arg(ap, aim_userinfo_t *); va_end(ap); + purple_connection_set_display_name(od->gc, info->sn); + /* * What's with the + 0.5? * The 0.5 is basically poor-man's rounding. Normally @@ -3524,32 +3521,6 @@ return 1; } -static int purple_icbm_param_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - struct aim_icbmparameters *params; - va_list ap; - - va_start(ap, fr); - params = va_arg(ap, struct aim_icbmparameters *); - va_end(ap); - - /* XXX - evidently this crashes on solaris. i have no clue why - purple_debug_misc("oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, " - "max sender evil = %f, max receiver evil = %f, min msg interval = %u\n", - params->maxchan, params->flags, params->maxmsglen, - ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0, - params->minmsginterval); - */ - - /* Maybe senderwarn and recverwarn should be user preferences... */ - params->flags = 0x0000000b; - params->maxmsglen = 8000; - params->minmsginterval = 0; - - aim_im_setparams(od, params); - - return 1; -} - static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { PurpleConnection *gc = od->gc; @@ -5391,6 +5362,7 @@ if (chat_name != NULL) g_hash_table_insert(defaults, "room", g_strdup(chat_name)); + g_hash_table_insert(defaults, "exchange", g_strdup("4")); return defaults; } @@ -5407,26 +5379,29 @@ OscarData *od = (OscarData *)gc->proto_data; FlapConnection *conn; char *name, *exchange; + int exchange_int; name = g_hash_table_lookup(data, "room"); exchange = g_hash_table_lookup(data, "exchange"); - if ((name == NULL) || (*name == '\0')) { - purple_notify_error(gc, NULL, _("Invalid chat name specified."), NULL); - return; - } + g_return_if_fail(name != NULL && *name != '\0'); + g_return_if_fail(exchange != NULL); + + errno = 0; + exchange_int = strtol(exchange, NULL, 10); + g_return_if_fail(errno == 0); purple_debug_info("oscar", "Attempting to join chat room %s.\n", name); if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV))) { purple_debug_info("oscar", "chatnav exists, creating room\n"); - aim_chatnav_createroom(od, conn, name, atoi(exchange)); + aim_chatnav_createroom(od, conn, name, exchange_int); } else { /* this gets tricky */ struct create_room *cr = g_new0(struct create_room, 1); purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n"); - cr->exchange = atoi(exchange); + cr->exchange = exchange_int; cr->name = g_strdup(name); od->create_rooms = g_slist_prepend(od->create_rooms, cr); aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV); @@ -6752,7 +6727,7 @@ prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); option = purple_account_option_bool_new( - _("Always use ICQ proxy server for file transfers\n(slower, but does not reveal your IP address)"), "always_use_rv_proxy", + _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy", OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.h Sat Dec 15 05:15:31 2007 +0000 @@ -94,11 +94,8 @@ * for WinAIM clients (up through the latest (4.0.1957)) to * send any more than 1kb. Amaze all your windows friends * with utterly oversized instant messages! - * - * TODO: the real limit is the total SNAC size at 8192. Fix this. - * */ -#define MAXMSGLEN 7987 +#define MAXMSGLEN 2544 /* * Maximum size of a Buddy Icon. diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/qq/header_info.h --- a/libpurple/protocols/qq/header_info.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/qq/header_info.h Sat Dec 15 05:15:31 2007 +0000 @@ -33,7 +33,7 @@ #define QQ_PACKET_TAG 0x02 /* all QQ text packets starts with it */ #define QQ_PACKET_TAIL 0x03 /* all QQ text packets end with it */ -#define QQ_CLIENT 0x0E1B +#define QQ_CLIENT 0x0d55 /* list of known QQ commands */ enum { diff -r b9197011ddd6 -r e13759a83714 libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/yahoo/yahoo_filexfer.c --- a/libpurple/protocols/yahoo/yahoo_filexfer.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/yahoo/yahoo_filexfer.h --- a/libpurple/protocols/yahoo/yahoo_filexfer.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.h Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/protocols/yahoo/yahoo_packet.h --- a/libpurple/protocols/yahoo/yahoo_packet.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/prpl.h --- a/libpurple/prpl.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/prpl.h Sat Dec 15 05:15:31 2007 +0000 @@ -292,6 +292,13 @@ void (*chat_whisper)(PurpleConnection *, int id, const char *who, const char *message); int (*chat_send)(PurpleConnection *, int id, const char *message, PurpleMessageFlags flags); + + /** If implemented, this will be called regularly for this prpl's + * active connections. You'd want to do this if you need to repeatedly + * send some kind of keepalive packet to the server to avoid being + * disconnected. ("Regularly" is defined by + * KEEPALIVE_INTERVAL in libpurple/connection.c.) + */ void (*keepalive)(PurpleConnection *); /** new user registration */ diff -r b9197011ddd6 -r e13759a83714 libpurple/purple.h.in --- a/libpurple/purple.h.in Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/purple.h.in Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/savedstatuses.c --- a/libpurple/savedstatuses.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/savedstatuses.c Sat Dec 15 05:15:31 2007 +0000 @@ -243,7 +243,9 @@ child = xmlnode_new_child(node, "account"); xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(substatus->account)); - xmlnode_insert_data(child, purple_account_get_username(substatus->account), -1); + xmlnode_insert_data(child, + purple_normalize(substatus->account, + purple_account_get_username(substatus->account)), -1); child = xmlnode_new_child(node, "state"); xmlnode_insert_data(child, purple_status_type_get_id(substatus->type), -1); diff -r b9197011ddd6 -r e13759a83714 libpurple/tests/check_libpurple.c --- a/libpurple/tests/check_libpurple.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/tests/check_libpurple.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 libpurple/tests/tests.h --- a/libpurple/tests/tests.h Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/tests/tests.h Sat Dec 15 05:15:31 2007 +0000 @@ -1,7 +1,8 @@ #ifndef TESTS_H # define TESTS_H -#include +#include "../purple.h" + #include /* define the test suites here */ diff -r b9197011ddd6 -r e13759a83714 libpurple/util.c --- a/libpurple/util.c Sat Dec 15 05:12:24 2007 +0000 +++ b/libpurple/util.c Sat Dec 15 05:15:31 2007 +0000 @@ -921,6 +921,7 @@ { const char *pln; int len, pound; + char temp[2]; if (!text || *text != '&') return NULL; @@ -943,8 +944,9 @@ pln = "\302\256"; /* or use g_unichar_to_utf8(0xae); */ else if(IS_ENTITY("'")) pln = "\'"; - else if(*(text+1) == '#' && (sscanf(text, "&#%u;", £) == 1) && - pound != 0 && *(text+3+(gint)log10(pound)) == ';') { + else if(*(text+1) == '#' && + (sscanf(text, "&#%u%1[;]", £, temp) == 2 || sscanf(text, "&#x%x%1[;]", £, temp) == 2) && + pound != 0) { static char buf[7]; int buflen = g_unichar_to_utf8((gunichar)pound, buf); buf[buflen] = '\0'; diff -r b9197011ddd6 -r e13759a83714 pidgin.spec.in --- a/pidgin.spec.in Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin.spec.in Sat Dec 15 05:15:31 2007 +0000 @@ -14,6 +14,9 @@ %define pidginver @VERSION@ %endif +# define the minimum API version required, so we can use it for plugin deps +%define apiver %(echo "@VERSION@"|awk -F. '{print $1"."$2}') + Summary: A GTK+ based multiprotocol instant messaging client Name: @PACKAGE@ Version: %pidginver @@ -29,7 +32,7 @@ BuildRequires: gtk2-devel %{!?_without_startupnotification:BuildRequires: startup-notification-devel} -%{?_with_avahi:BuildRequires: avahi-compat-howl-devel} +%{?_with_avahi:BuildRequires: avahi-glib-devel} %{!?_without_gtkspell:BuildRequires: gtkspell-devel} %{?_with_howl:BuildRequires: howl-devel} %{?_with_meanwhile:BuildRequires: meanwhile-devel} @@ -118,21 +121,21 @@ %package -n libpurple-bonjour Summary: Bonjour plugin for Pidgin Group: Applications/Internet -Requires: libpurple = %{version} +Requires: libpurple >= %{apiver} %endif %if 0%{?_with_meanwhile:1} %package -n libpurple-meanwhile Summary: Lotus Sametime plugin for Pidgin using the Meanwhile library Group: Applications/Internet -Requires: libpurple = %{version} +Requires: libpurple >= %{apiver} %endif %if 0%{?_with_mono:1} %package -n libpurple-mono Summary: Mono .NET plugin support for Pidgin Group: Applications/Internet -Requires: libpurple = %{version} +Requires: libpurple >= %{apiver} %endif %if 0%{!?_without_text:1} @@ -462,6 +465,12 @@ %endif %changelog +* Wed Dec 5 2007 Stu Tomlinson +- When building with avahi, use native avahi instead of howl compatability + headers +- Make the split out plugins depend only on the minimum necessary API + version of libpurple + * Tue Oct 23 2007 Stu Tomlinson - Add finch.pc to finch-devel diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkblist.c Sat Dec 15 05:15:31 2007 +0000 @@ -1398,7 +1398,7 @@ pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, node); pidgin_append_blist_node_extended_menu(menu, node); - if (!contact_expanded) + if (!contact_expanded && contact != NULL) pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact); if (node->parent && node->parent->child->next && @@ -3170,6 +3170,7 @@ GList *cur; struct proto_chat_entry *pce; char *name, *value; + PidginBlistNode *bnode = node->ui_data; chat = (PurpleChat *)node; prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account)); @@ -3182,6 +3183,13 @@ g_free(tmp); } + if (bnode && bnode->conv.conv && + prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC) && + !purple_conv_chat_has_left(PURPLE_CONV_CHAT(bnode->conv.conv))) { + const char *topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(bnode->conv.conv)); + g_string_append_printf(str, _("\nTopic: %s"), topic ? topic : _("(no topic set)")); + } + if (prpl_info->chat_info != NULL) cur = prpl_info->chat_info(chat->account->gc); else @@ -3251,7 +3259,7 @@ /* Alias */ /* If there's not a contact alias, the node is being displayed with * this alias, so there's no point in showing it in the tooltip. */ - if (full && b->alias != NULL && b->alias[0] != '\0' && + if (full && c && b->alias != NULL && b->alias[0] != '\0' && (c->alias != NULL && c->alias[0] != '\0') && strcmp(c->alias, b->alias) != 0) { @@ -3302,13 +3310,13 @@ } /* Last Seen */ - if (full && !PURPLE_BUDDY_IS_ONLINE(b)) + if (full && c && !PURPLE_BUDDY_IS_ONLINE(b)) { struct _pidgin_blist_node *gtknode = ((PurpleBlistNode *)c)->ui_data; PurpleBlistNode *bnode; int lastseen = 0; - if (!gtknode->contact_expanded || PURPLE_BLIST_NODE_IS_CONTACT(node)) + if (gtknode && (!gtknode->contact_expanded || PURPLE_BLIST_NODE_IS_CONTACT(node))) { /* We're either looking at a buddy for a collapsed contact or * an expanded contact itself so we show the most recent @@ -3371,6 +3379,34 @@ return g_string_free(str, FALSE); } +static GHashTable *cached_emblems; + +static void _cleanup_cached_emblem(gpointer data, GObject *obj) { + g_hash_table_remove(cached_emblems, data); +} + +static GdkPixbuf * _pidgin_blist_get_cached_emblem(gchar *path) { + GdkPixbuf *pb = g_hash_table_lookup(cached_emblems, path); + + if (pb != NULL) { + /* The caller gets a reference */ + g_object_ref(pb); + g_free(path); + } else { + pb = gdk_pixbuf_new_from_file(path, NULL); + if (pb != NULL) { + /* We don't want to own a ref to the pixbuf, but we need to keep clean up. */ + /* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */ + g_object_weak_ref(G_OBJECT(pb), _cleanup_cached_emblem, path); + g_hash_table_insert(cached_emblems, path, pb); + } else + g_free(path); + } + + return pb; +} + + GdkPixbuf * pidgin_blist_get_emblem(PurpleBlistNode *node) { @@ -3381,7 +3417,6 @@ PurplePluginProtocolInfo *prpl_info; const char *name = NULL; char *filename, *path; - GdkPixbuf *ret; PurplePresence *p; if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { @@ -3394,11 +3429,9 @@ gtkbuddynode = node->ui_data; p = purple_buddy_get_presence(buddy); if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL); - ret = gdk_pixbuf_new_from_file(path, NULL); - g_free(path); - return ret; + return _pidgin_blist_get_cached_emblem(path); } if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) { @@ -3410,26 +3443,22 @@ return NULL; } + g_return_val_if_fail(buddy != NULL, NULL); + if (!purple_privacy_check(buddy->account, purple_buddy_get_name(buddy))) { path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "blocked.png", NULL); - ret = gdk_pixbuf_new_from_file(path, NULL); - g_free(path); - return ret; + return _pidgin_blist_get_cached_emblem(path); } p = purple_buddy_get_presence(buddy); if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL); - ret = gdk_pixbuf_new_from_file(path, NULL); - g_free(path); - return ret; + return _pidgin_blist_get_cached_emblem(path); } if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL); - ret = gdk_pixbuf_new_from_file(path, NULL); - g_free(path); - return ret; + return _pidgin_blist_get_cached_emblem(path); } prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account)); @@ -3446,12 +3475,10 @@ filename = g_strdup_printf("%s.png", name); path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL); - ret = gdk_pixbuf_new_from_file(path, NULL); - g_free(filename); - g_free(path); - - return ret; + + /* _pidgin_blist_get_cached_emblem() assumes ownership of path */ + return _pidgin_blist_get_cached_emblem(path); } @@ -4072,7 +4099,7 @@ PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node) { PidginBlistNode *ui = node->ui_data; - if (ui->conv.conv != conv || PIDGIN_CONVERSATION(conv) || + if (ui->conv.conv != conv || !pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)) || !(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))) return; ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE; @@ -4081,11 +4108,10 @@ } static void -displayed_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message, - PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node) +displayed_msg_update_ui_cb(PidginConversation *gtkconv, PurpleBlistNode *node) { PidginBlistNode *ui = node->ui_data; - if (ui->conv.conv != conv) + if (ui->conv.conv != gtkconv->active_conv) return; ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE; pidgin_blist_update(purple_get_blist(), node); @@ -4111,10 +4137,11 @@ ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui); purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy); - purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg", + purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy); } } + break; case PURPLE_CONV_TYPE_CHAT: { PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); @@ -4131,9 +4158,10 @@ ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui); purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat); - purple_signal_connect(pidgin_conversations_get_handle(), "displayed-chat-msg", + purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat); } + break; default: break; } @@ -5691,7 +5719,6 @@ static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded) { PurpleGroup *group; - GdkColor textcolor; gboolean selected; char group_count[12] = ""; char *mark, *esc; @@ -5699,7 +5726,6 @@ GtkTreeIter iter; group = (PurpleGroup*)gnode; - textcolor = gtkblist->treeview->style->fg[GTK_STATE_ACTIVE]; if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)), NULL, &iter)) { gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, @@ -5714,12 +5740,7 @@ } esc = g_markup_escape_text(group->name, -1); - if (selected) - mark = g_strdup_printf("%s%s", esc, group_count); - else - mark = g_strdup_printf("%s%s", - textcolor.red>>8, textcolor.green>>8, textcolor.blue>>8, - esc, group_count); + mark = g_strdup_printf("%s%s", esc ? esc : "", group_count); g_free(esc); return mark; @@ -5949,7 +5970,8 @@ ui = node->ui_data; conv = ui->conv.conv; - hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)); + hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE) && + pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv))); status = pidgin_blist_get_status_icon(node, biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL); @@ -6897,6 +6919,8 @@ { void *gtk_blist_handle = pidgin_blist_get_handle(); + cached_emblems = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-on", gtk_blist_handle, PURPLE_CALLBACK(account_signon_cb), NULL); @@ -6948,6 +6972,8 @@ void pidgin_blist_uninit(void) { + g_hash_table_destroy(cached_emblems); + purple_signals_unregister_by_instance(pidgin_blist_get_handle()); purple_signals_disconnect_by_handle(pidgin_blist_get_handle()); } diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkconv.c Sat Dec 15 05:15:31 2007 +0000 @@ -129,6 +129,7 @@ static GtkWidget *invite_dialog = NULL; static GtkWidget *warn_close_dialog = NULL; +static PidginWindow *hidden_convwin = NULL; static GList *window_list = NULL; /* Lists of status icons at all available sizes for use as window icons */ @@ -1379,7 +1380,13 @@ timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv); purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer)); } +#if 0 + /* I will miss you */ purple_conversation_set_ui_ops(conv, NULL); +#else + pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); + pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); +#endif } } @@ -2820,6 +2827,9 @@ if (gtkconv == NULL) { pidgin_conv_attach_to_conversation(conv); gtkconv = PIDGIN_CONVERSATION(conv); + } else if (gtkconv->win == hidden_convwin) { + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); } pidgin_conv_switch_active_conversation(conv); @@ -2852,20 +2862,15 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if (gtkconv != NULL && gtkconv->active_conv != conv) + if(gtkconv == NULL || gtkconv->active_conv != conv) continue; - if (gtkconv == NULL) { - if (!purple_conversation_get_data(conv, "unseen-count") || - !purple_conversation_get_data(conv, "unseen-state") || - GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-state"))unseen_state >= min_state + && (!hidden_only || + (hidden_only && gtkconv->win == hidden_convwin))) { + r = g_list_prepend(r, conv); c++; - } else { - if (gtkconv->unseen_state >= min_state && !hidden_only) { - r = g_list_prepend(r, conv); - c++; - } } } @@ -2908,8 +2913,8 @@ GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC); GtkWidget *item; gchar *text = g_strdup_printf("%s (%d)", - gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv), - gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))); + gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), + gtkconv->unseen_count); gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf); g_object_unref(pbuf); @@ -3183,7 +3188,7 @@ PurpleConversation *conv; GtkWidget *item; - if (win->window == NULL) + if (win->window == NULL || win == hidden_convwin) return; gtkconv = pidgin_conv_window_get_active_gtkconv(win); @@ -4423,7 +4428,7 @@ pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry)); pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(gtkconv->entry)); - height = (oneline.height + pad_top + pad_bottom) * lines; + height = (oneline.height + pad_top + pad_bottom) * MAX(lines, 2); height += (oneline.height + pad_inside) * (wrapped_lines - lines); gtkconv->auto_resize = TRUE; @@ -4455,7 +4460,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); @@ -4604,16 +4609,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(); @@ -5050,9 +5064,6 @@ GtkWidget *tab_cont; PurpleBlistNode *convnode; - if (hidden) - return; - if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) { conv->ui_data = gtkconv; if (!g_list_find(gtkconv->convs, conv)) @@ -5152,7 +5163,10 @@ G_CALLBACK(gtk_widget_grab_focus), gtkconv->entry); - pidgin_conv_placement_place(gtkconv); + if (hidden) + pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); + else + pidgin_conv_placement_place(gtkconv); if (nick_colors == NULL) { nbr_nick_colors = NUM_NICK_COLORS; @@ -5160,13 +5174,11 @@ } } -#if 0 static void pidgin_conv_new_hidden(PurpleConversation *conv) { private_gtkconv_new(conv, TRUE); } -#endif void pidgin_conv_new(PurpleConversation *conv) @@ -5182,24 +5194,30 @@ PurpleConversation *conv, PurpleMessageFlags flags) { PurpleConversationUiOps *ui_ops = pidgin_conversations_get_conv_ui_ops(); + gboolean hide = FALSE; /* create hidden conv if hide_new pref is always */ - /* or if hide_new pref is away and account is away */ - if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) || - (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && - !purple_status_is_available(purple_account_get_active_status(account)))) { - if (!conv) { - ui_ops->create_conversation = NULL; - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - purple_conversation_set_ui_ops(conv, NULL); - ui_ops->create_conversation = pidgin_conv_new; - } else { - /* TODO: update the unseen_state data on the conv here */ - } - } else { - /* new message for an IM */ - if (conv && conv->type == PURPLE_CONV_TYPE_IM) - pidgin_conv_attach_to_conversation(conv); + if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) + hide = TRUE; + + /* create hidden conv if hide_new pref is away and account is away */ + if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && + !purple_status_is_available(purple_account_get_active_status(account))) + hide = TRUE; + + if (conv && PIDGIN_IS_PIDGIN_CONVERSATION(conv) && !hide) { + PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + if (gtkconv->win == hidden_convwin) { + pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); + pidgin_conv_placement_place(gtkconv); + } + return; + } + + if (hide) { + ui_ops->create_conversation = pidgin_conv_new_hidden; + purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + ui_ops->create_conversation = pidgin_conv_new; } } @@ -5208,9 +5226,6 @@ { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if (!gtkconv) - return; - gtkconv->convs = g_list_remove(gtkconv->convs, conv); /* Don't destroy ourselves until all our convos are gone */ if (gtkconv->convs) { @@ -6553,7 +6568,6 @@ gboolean ellipsis = FALSE; /* I think this is a little longer than it needs to be but I'm lazy. */ char *style; - gboolean bold = FALSE; if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) im = PURPLE_CONV_IM(conv); @@ -6575,8 +6589,29 @@ markup = title; } } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { - const char *topic = gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text)); - char *esc = topic ? g_markup_escape_text(topic, -1) : NULL; + const char *topic = gtkconv->u.chat->topic_text ? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text)) : NULL; + char *esc = NULL; +#if GTK_CHECK_VERSION(2,6,0) + esc = topic ? g_markup_escape_text(topic, -1) : NULL; +#else + /* GTK < 2.6 doesn't have auto ellipsization, so we do a crude + * trucation to prevent forcing the window to be as wide as the topic */ + int len = 0; + char *c, *tmp = g_strdup(topic); + c = tmp; + while(*c && len < 72) { + c = g_utf8_next_char(c); + len++; + } + if (len == 72) { + *c = '\0'; + c = g_strdup_printf("%s...", tmp); + g_free(tmp); + tmp = c; + } + esc = tmp ? g_markup_escape_text(tmp, -1) : NULL; + g_free(tmp); +#endif markup = g_strdup_printf("%s%s%s", purple_conversation_get_title(conv), esc && *esc ? "\n" : "", @@ -6588,7 +6623,7 @@ CONV_TEXT_COLUMN, markup, -1); /* XXX seanegan Why do I have to do this? */ gtk_widget_queue_draw(gtkconv->infopane); - + if (title != markup) g_free(markup); @@ -6599,47 +6634,43 @@ if (im != NULL && purple_conv_im_get_typing_state(im) == PURPLE_TYPING) { atk_object_set_description(accessibility_obj, _("Typing")); - style = "color=\"#4e9a06\""; + style = "tab-label-typing"; } else if (im != NULL && purple_conv_im_get_typing_state(im) == PURPLE_TYPED) { atk_object_set_description(accessibility_obj, _("Stopped Typing")); - style = "color=\"#c4a000\""; + style = "tab-label-typed"; } else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK) { atk_object_set_description(accessibility_obj, _("Nick Said")); - style = "color=\"#204a87\""; + style = "tab-label-attention"; } else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT) { atk_object_set_description(accessibility_obj, _("Unread Messages")); if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_CHAT) - style = "color=\"#cc0000\""; + style = "tab-label-unreadchat"; else - style = "color=\"#204a87\""; + style = "tab-label-attention"; } else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) { atk_object_set_description(accessibility_obj, _("New Event")); - style = "color=\"#888a85\""; + style = "tab-label-event"; } else { style = NULL; } - + + gtk_widget_set_name(gtkconv->tab_label, style); + gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title); + gtk_widget_set_state(gtkconv->tab_label, GTK_STATE_ACTIVE); + if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT || gtkconv->unseen_state == PIDGIN_UNSEEN_NICK || - gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) - bold = TRUE; - - if (style || bold) - { - char *html_title,*label; - - html_title = g_markup_escape_text(title_tmp, -1); - label = g_strdup_printf("%s", - style ? style : "", - bold ? "weight=\"bold\"" : "", - html_title); - g_free(html_title); - gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label); - g_free(label); - } - else - gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title_tmp); + gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) { + PangoAttrList *list = pango_attr_list_new(); + PangoAttribute *attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert(list, attr); + gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), list); + pango_attr_list_unref(list); + } else + gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), NULL); if (pidgin_conv_window_is_active_conversation(conv)) update_typing_icon(gtkconv); @@ -6720,7 +6751,6 @@ unseen = PIDGIN_UNSEEN_TEXT; conv_set_unseen(conv, unseen); - purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); } } @@ -7244,7 +7274,6 @@ account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus, PurpleStatus *newstatus) { -#if 0 GList *l; PurpleConversation *conv = NULL; PidginConversation *gtkconv; @@ -7254,7 +7283,27 @@ if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus)) return; -#endif + + while ((l = hidden_convwin->gtkconvs) != NULL) + { + gtkconv = l->data; + + conv = gtkconv->active_conv; + + while(l && !purple_status_is_available( + purple_account_get_active_status( + purple_conversation_get_account(conv)))) + l = l->next; + if (!l) + break; + + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); + + /* TODO: do we need to do anything for any other conversations that are in the same gtkconv here? + * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/ + purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); + } } static void @@ -7262,25 +7311,35 @@ gconstpointer value, gpointer data) { GList *l; + PurpleConversation *conv = NULL; + PidginConversation *gtkconv; gboolean when_away = FALSE; + if(!hidden_convwin) + return; + if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0) return; if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0) when_away = TRUE; - for (l = purple_get_conversations(); l; l = l->next) + for (l = hidden_convwin->gtkconvs; l; ) { - PurpleConversation *conv = l->data; - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if (gtkconv) + gtkconv = l->data; + l = l->next; + + conv = gtkconv->active_conv; + + if (conv->type == PURPLE_CONV_TYPE_CHAT || + gtkconv->unseen_count == 0 || + (when_away && !purple_status_is_available( + purple_account_get_active_status( + purple_conversation_get_account(conv))))) continue; - if(when_away && !purple_status_is_available( - purple_account_get_active_status( - purple_conversation_get_account(conv)))) - continue; - pidgin_conv_attach_to_conversation(conv); + + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); } } @@ -7550,10 +7609,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) @@ -7561,8 +7623,22 @@ GList *list; PidginConversation *gtkconv; - if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) - return FALSE; + 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; + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + 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; + } pidgin_conv_attach(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -7783,17 +7859,17 @@ purple_value_new(PURPLE_TYPE_INT)); purple_signal_register(handle, "conversation-switched", - purple_marshal_VOID__POINTER_POINTER, NULL, 1, + purple_marshal_VOID__POINTER, NULL, 1, purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONVERSATION)); purple_signal_register(handle, "conversation-hiding", - purple_marshal_VOID__POINTER_POINTER, NULL, 1, + purple_marshal_VOID__POINTER, NULL, 1, purple_value_new(PURPLE_TYPE_BOXED, "PidginConversation *")); purple_signal_register(handle, "conversation-displayed", - purple_marshal_VOID__POINTER_POINTER, NULL, 1, + purple_marshal_VOID__POINTER, NULL, 1, purple_value_new(PURPLE_TYPE_BOXED, "PidginConversation *")); @@ -7835,6 +7911,9 @@ purple_conversations_set_ui_ops(&conversation_ui_ops); + hidden_convwin = pidgin_conv_window_new(); + window_list = g_list_remove(window_list, hidden_convwin); + purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", handle, PURPLE_CALLBACK(account_status_changed_cb), NULL); @@ -7874,6 +7953,41 @@ PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle, PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); + + { + /* Set default tab colors */ + GString *str = g_string_new(NULL); + GtkSettings *settings = gtk_settings_get_default(); + struct { + const char *stylename; + const char *labelname; + const char *color; + } styles[] = { + {"pidgin_tab_label_typing_default", "tab-label-typing", "#4e9a06"}, + {"pidgin_tab_label_typed_default", "tab-label-typed", "#c4a000"}, + {"pidgin_tab_label_attention_default", "tab-label-attention", "#006aff"}, + {"pidgin_tab_label_unreadchat_default", "tab-label-unreadchat", "#cc0000"}, + {"pidgin_tab_label_event_default", "tab-label-event", "#888a85"}, + {NULL, NULL, NULL} + }; + int iter; + for (iter = 0; styles[iter].stylename; iter++) { + if (!gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE)) + /* Apparently both ACTIVE and NORMAL are required */ + g_string_append_printf(str, "style \"%s\" {\n" + "fg[ACTIVE] = \"%s\"\n" + "}\n" + "widget \"*%s\" style \"%s\"\n", + styles[iter].stylename, + styles[iter].color, + styles[iter].labelname, styles[iter].stylename); + } + gtk_rc_parse_string(str->str); + g_string_free(str, TRUE); +#if GTK_CHECK_VERSION(2,4,0) + gtk_rc_reset_styles(settings); +#endif + } } void @@ -8708,7 +8822,7 @@ gtk_entry_get_text(entry)); } serv_alias_buddy(buddy); - } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { gtk_entry_set_text(GTK_ENTRY(gtkconv->u.chat->topic_text), gtk_entry_get_text(entry)); topic_callback(NULL, gtkconv); } @@ -8719,7 +8833,7 @@ infopane_entry_activate(PidginConversation *gtkconv) { GtkWidget *entry = NULL; - PurpleConversation *conv = gtkconv->active_conv; + PurpleConversation *conv = gtkconv->active_conv; const char *text = NULL; if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) { @@ -8735,9 +8849,21 @@ if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { PurpleBuddy *buddy = purple_find_buddy(gtkconv->active_conv->account, gtkconv->active_conv->name); if (!buddy) + /* This buddy isn't in your buddy list, so we can't alias him */ return FALSE; + text = purple_buddy_get_contact_alias(buddy); } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + PurpleConnection *gc; + PurplePluginProtocolInfo *prpl_info = NULL; + + gc = purple_conversation_get_gc(conv); + if (gc != NULL) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + if (prpl_info && prpl_info->set_chat_topic == NULL) + /* This protocol doesn't support setting the chat room topic */ + return FALSE; + text = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)); } @@ -8756,10 +8882,9 @@ g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(alias_cb), gtkconv); g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(alias_focus_cb), gtkconv); g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(alias_key_press_cb), gtkconv); - - - - gtk_entry_set_text(GTK_ENTRY(entry), text); + + if (text != NULL) + gtk_entry_set_text(GTK_ENTRY(entry), text); gtk_widget_show(entry); gtk_widget_hide(gtkconv->infopane); gtk_widget_grab_focus(entry); @@ -9049,8 +9174,6 @@ if (win->dialogs.search) gtk_widget_destroy(win->dialogs.search); - gtk_widget_hide_all(win->window); - if (win->gtkconvs) { while (win->gtkconvs) { gboolean last = (win->gtkconvs->next == NULL); @@ -9104,7 +9227,7 @@ ptr = gdk_cursor_new(GDK_LEFT_PTR); } - gtk_label_set_markup(label, "×"); + gtk_label_set_markup(label, "×"); gdk_window_set_cursor(event->window, ptr); return FALSE; } @@ -9117,7 +9240,7 @@ hand = gdk_cursor_new(GDK_HAND2); } - gtk_label_set_markup(label, "×"); + gtk_label_set_markup(label, "×"); gdk_window_set_cursor(event->window, hand); return FALSE; } @@ -9147,7 +9270,7 @@ gtk_event_box_set_visible_window(GTK_EVENT_BOX(gtkconv->close), FALSE); #endif gtk_widget_set_events(gtkconv->close, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); - close_image = gtk_label_new("×"); + close_image = gtk_label_new("×"); g_signal_connect(G_OBJECT(gtkconv->close), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_image); g_signal_connect(G_OBJECT(gtkconv->close), "leave-notify-event", G_CALLBACK(close_button_left_cb), close_image); gtk_widget_show(close_image); @@ -9176,6 +9299,7 @@ /* Tab label. */ gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv)); + gtk_widget_set_name(gtkconv->tab_label, "tab-label"); gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtkconv->menu_label = gtk_label_new(tmp_lab); @@ -9335,7 +9459,7 @@ if (win->gtkconvs && win->gtkconvs->next == NULL) pidgin_conv_tab_pack(win, win->gtkconvs->data); - if (!win->gtkconvs) + if (!win->gtkconvs && win != hidden_convwin) pidgin_conv_window_destroy(win); } @@ -9874,7 +9998,9 @@ gboolean pidgin_conv_is_hidden(PidginConversation *gtkconv) { - return (gtkconv == NULL); + g_return_val_if_fail(gtkconv != NULL, FALSE); + + return (gtkconv->win == hidden_convwin); } @@ -9975,5 +10101,3 @@ return colors; } - - diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkdebug.c --- a/pidgin/gtkdebug.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkdebug.c Sat Dec 15 05:15:31 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 b9197011ddd6 -r e13759a83714 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkimhtml.c Sat Dec 15 05:15:31 2007 +0000 @@ -846,28 +846,6 @@ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 5); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(clear_formatting_cb), imhtml); - - mi = gtk_menu_item_new(); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); - - img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_label(_("_Smile!")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); - - img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_label(_("_Insert")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); - - img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_label(_("_Font")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); } static char * @@ -4298,19 +4276,19 @@ gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_label(_("_Font")); + mi = gtk_image_menu_item_new_with_mnemonic(_("_Font")); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); gtk_widget_show(mi); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_label(_("_Insert")); + mi = gtk_image_menu_item_new_with_mnemonic(_("_Insert")); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); gtk_widget_show(mi); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_label(_("_Smile!")); + mi = gtk_image_menu_item_new_with_mnemonic(_("S_mile!")); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); gtk_widget_show(mi); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Sat Dec 15 05:15:31 2007 +0000 @@ -1315,7 +1315,9 @@ purple_prefs_connect_callback(toolbar, PIDGIN_PREFS_ROOT "/conversations/toolbar/wide", imhtmltoolbar_view_pref_changed, toolbar); - purple_prefs_trigger_callback(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide"); + g_signal_connect_data(G_OBJECT(toolbar), "realize", + G_CALLBACK(purple_prefs_trigger_callback), PIDGIN_PREFS_ROOT "/conversations/toolbar/wide", + NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED); #if GTK_CHECK_VERSION(2,4,0) gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE); diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkmain.c Sat Dec 15 05:15:31 2007 +0000 @@ -611,7 +611,7 @@ #ifndef _WIN32 "c:dhmnl::s:v", #else - "c:dhnl::v", + "c:dhmnl::v", #endif long_options, NULL)) != -1) { switch (opt) { diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkprefs.c Sat Dec 15 05:15:31 2007 +0000 @@ -1214,6 +1214,9 @@ vbox = pidgin_make_frame (ret, _("Ports")); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + pidgin_prefs_checkbox(_("_Enable automatic router port forwarding"), + "/purple/network/map_ports", vbox); + ports_checkbox = pidgin_prefs_checkbox(_("_Manually specify range of ports to listen on"), "/purple/network/ports_range_use", vbox); diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkroomlist.c --- a/pidgin/gtkroomlist.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkroomlist.c Sat Dec 15 05:15:31 2007 +0000 @@ -963,8 +963,10 @@ g_signal_connect(G_OBJECT(tree), "button-press-event", G_CALLBACK(room_click_cb), list); g_signal_connect(G_OBJECT(tree), "row-expanded", G_CALLBACK(row_expanded_cb), list); g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(row_activated_cb), list); +#if 0 /* uncomment this when the tooltips are slightly less annoying and more well behaved */ g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), list); g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(row_leave_cb), list); +#endif /* Enable CTRL+F searching */ gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), NAME_COLUMN); diff -r b9197011ddd6 -r e13759a83714 pidgin/gtkutils.c --- a/pidgin/gtkutils.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/gtkutils.c Sat Dec 15 05:15:31 2007 +0000 @@ -2451,7 +2451,13 @@ g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy", G_CALLBACK(icon_filesel_delete_cb), dialog); #endif /* FILECHOOSER */ - return dialog->icon_filesel; + +#ifdef _WIN32 + g_signal_connect(G_OBJECT(dialog->icon_filesel), "show", + G_CALLBACK(winpidgin_ensure_onscreen), dialog->icon_filesel); +#endif + + return dialog->icon_filesel; } diff -r b9197011ddd6 -r e13759a83714 pidgin/pidginstock.c --- a/pidgin/pidginstock.c Sat Dec 15 05:12:24 2007 +0000 +++ b/pidgin/pidginstock.c Sat Dec 15 05:15:31 2007 +0000 @@ -347,7 +347,7 @@ size_t i; GtkWidget *win; GtkIconSize microscopic, extra_small, small, medium, large, huge; - + if (stock_initted) return; @@ -372,7 +372,7 @@ { /* GTK+ Stock icon */ iconset = gtk_style_lookup_icon_set(gtk_widget_get_style(win), - stock_icons[i].filename); + stock_icons[i].filename); } else { @@ -386,11 +386,11 @@ gtk_icon_source_set_direction_wildcarded(source, TRUE); gtk_icon_source_set_size_wildcarded(source, TRUE); gtk_icon_source_set_state_wildcarded(source, TRUE); - + iconset = gtk_icon_set_new(); gtk_icon_set_add_source(iconset, source); - + gtk_icon_source_free(source); g_free(filename); } @@ -401,7 +401,7 @@ } /* register custom icon sizes */ - + microscopic = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11); extra_small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16); small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22); @@ -414,61 +414,41 @@ GtkIconSet *iconset; iconset = gtk_icon_set_new(); - if (sized_stock_icons[i].microscopic) - add_sized_icon(iconset, microscopic, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "11", sized_stock_icons[i].filename); - if (sized_stock_icons[i].extra_small) - add_sized_icon(iconset, extra_small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "16", sized_stock_icons[i].filename); - if (sized_stock_icons[i].small) - add_sized_icon(iconset, small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "22", sized_stock_icons[i].filename); - if (sized_stock_icons[i].medium) - add_sized_icon(iconset, medium, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "32", sized_stock_icons[i].filename); - if (sized_stock_icons[i].large) - add_sized_icon(iconset, large, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "48", sized_stock_icons[i].filename); - if (sized_stock_icons[i].huge) - add_sized_icon(iconset, huge, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "64", sized_stock_icons[i].filename); + +#define ADD_SIZED_ICON(name, size) do { \ + if (sized_stock_icons[i].name) \ + add_sized_icon(iconset, name, \ + sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \ + size, sized_stock_icons[i].filename); \ + } while (0) + ADD_SIZED_ICON(microscopic, "11"); + ADD_SIZED_ICON(extra_small, "16"); + ADD_SIZED_ICON(small, "22"); + ADD_SIZED_ICON(medium, "32"); + ADD_SIZED_ICON(large, "48"); + ADD_SIZED_ICON(huge, "64"); +#undef ADD_SIZED_ICON gtk_icon_factory_add(icon_factory, sized_stock_icons[i].name, iconset); gtk_icon_set_unref(iconset); if (sized_stock_icons[i].translucent_name) { iconset = gtk_icon_set_new(); - if (sized_stock_icons[i].microscopic) - add_translucent_sized_icon(iconset, microscopic, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "11", sized_stock_icons[i].filename); - if (sized_stock_icons[i].extra_small) - add_translucent_sized_icon(iconset, extra_small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "16", sized_stock_icons[i].filename); - if (sized_stock_icons[i].small) - add_translucent_sized_icon(iconset, small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "22", sized_stock_icons[i].filename); - if (sized_stock_icons[i].medium) - add_translucent_sized_icon(iconset, medium, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "32", sized_stock_icons[i].filename); - if (sized_stock_icons[i].large) - add_translucent_sized_icon(iconset, large, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "48", sized_stock_icons[i].filename); - if (sized_stock_icons[i].huge) - add_translucent_sized_icon(iconset, huge, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "64", sized_stock_icons[i].filename); - + +#define ADD_TRANS_ICON(name, size) do { \ + if (sized_stock_icons[i].name) \ + add_translucent_sized_icon(iconset, name, \ + sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \ + size, sized_stock_icons[i].filename); \ + } while (0) + ADD_TRANS_ICON(microscopic, "11"); + ADD_TRANS_ICON(extra_small, "16"); + ADD_TRANS_ICON(small, "22"); + ADD_TRANS_ICON(medium, "32"); + ADD_TRANS_ICON(large, "48"); + ADD_TRANS_ICON(huge, "64"); +#undef ADD_TRANS_ICON + gtk_icon_factory_add(icon_factory, sized_stock_icons[i].translucent_name, iconset); gtk_icon_set_unref(iconset); } diff -r b9197011ddd6 -r e13759a83714 po/ChangeLog --- a/po/ChangeLog Sat Dec 15 05:12:24 2007 +0000 +++ b/po/ChangeLog Sat Dec 15 05:15:31 2007 +0000 @@ -1,5 +1,8 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.3.1 + * German translation updated (Jochen Kemnade, Björn Voigt) + version 2.3.0 * Afrikaans translation added (Friedel Wolff) * Belarusian Latin translation updated (Ihar Hrachyshka) diff -r b9197011ddd6 -r e13759a83714 po/POTFILES.in --- a/po/POTFILES.in Sat Dec 15 05:12:24 2007 +0000 +++ b/po/POTFILES.in Sat Dec 15 05:15:31 2007 +0000 @@ -118,6 +118,7 @@ libpurple/protocols/myspace/zap.c libpurple/protocols/novell/nmuser.c libpurple/protocols/novell/novell.c +libpurple/protocols/oscar/family_chatnav.c libpurple/protocols/oscar/flap_connection.c libpurple/protocols/oscar/libaim.c libpurple/protocols/oscar/libicq.c diff -r b9197011ddd6 -r e13759a83714 po/de.po --- a/po/de.po Sat Dec 15 05:12:24 2007 +0000 +++ b/po/de.po Sat Dec 15 05:15:31 2007 +0000 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-28 10:03+0100\n" -"PO-Revision-Date: 2007-11-28 10:03+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" @@ -2336,6 +2336,12 @@ msgid "Loads .NET plugins with Mono." msgstr "Lädt .NET-Plugins mit Mono." +msgid "Add new line in IMs" +msgstr "Neue Zeile in IMs einfügen" + +msgid "Add new line in Chats" +msgstr "Neue Zeile in Chats einfügen" + #. *< magic #. *< major version #. *< minor version @@ -5760,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." @@ -6563,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 " @@ -6701,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." @@ -9989,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" @@ -11049,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" @@ -11124,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" @@ -11250,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 " @@ -11693,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"