# HG changeset patch # User Sean Egan # Date 1186433432 0 # Node ID 25c2af20affe900116511b019478281fc04ff472 # Parent 8007c107054b482aea5144f4fa81389ea163e04f# Parent 0622a7f08ddec297ba1a0f1f385e9a37c5c1632f merge of '5cd317ad290e840fa8a8624d17dd5f23f99330ac' and '5f2f745cd67ec501c85a6a740626a581415614f5' diff -r 0622a7f08dde -r 25c2af20affe configure.ac --- a/configure.ac Mon Aug 06 20:49:41 2007 +0000 +++ b/configure.ac Mon Aug 06 20:50:32 2007 +0000 @@ -593,6 +593,46 @@ AC_SUBST(MEANWHILE_LIBS) dnl ####################################################################### +dnl # Check for Native Avahi headers (for Bonjour) +dnl ####################################################################### +AC_ARG_WITH(avahi-client-includes, [AC_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"]) +AC_ARG_WITH(avahi-client-libs, [AC_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"]) +AVAHI_CFLAGS="" +AVAHI_LIBS="" + +dnl Attempt to autodetect Avahi +PKG_CHECK_MODULES(AVAHI, [avahi-client avahi-glib], [ + avahiincludes="yes" + avahilibs="yes" +], [ + AC_MSG_RESULT(no) + avahiincludes="no" + avahilibs="no" +]) + +dnl Override AVAHI_CFLAGS if the user specified an include dir +if test "$ac_avahi_client_includes" != "no"; then + AVAHI_CFLAGS="-I$ac_avahi_client_includes" +fi +CPPFLAGS_save="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $AVAHI_CFLAGS" +AC_CHECK_HEADER(avahi-client/client.h, [avahiincludes=yes], [avahiincludes=no]) +CPPFLAGS="$CPPFLAGS $AVAHI_CFLAGS $GLIB_CFLAGS" +AC_CHECK_HEADER(avahi-glib/glib-malloc.h, [avahiincludes=yes], [avahiincludes=no]) +CPPFLAGS="$CPPFLAGS_save" + +dnl Override AVAHI_LIBS if the user specified a libs dir +if test "$ac_avahi_client_libs" != "no"; then + AVAHI_LIBS="-L$ac_avahi_client_libs -lavahi-common -lavahi-client -lavahi-glib " +fi +AC_CHECK_LIB(avahi-client, avahi_client_new, [avahilibs=yes], [avahilibs=no], $AVAHI_LIBS) + +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"]) @@ -601,6 +641,7 @@ 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" @@ -640,6 +681,9 @@ 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 dnl ####################################################################### @@ -819,8 +863,10 @@ if test "x$have_meanwhile" != "xyes" ; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'` fi -if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then - STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'` +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 fi if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'` @@ -873,7 +919,7 @@ *) echo "Invalid static protocol $i!!" ; exit ;; esac done -AM_CONDITIONAL(STATIC_BONJOUR, test "x$static_bonjour" = "xyes" -a "x$howlincludes" = "xyes" -a "x$howllibs" = "xyes") +AM_CONDITIONAL(STATIC_BONJOUR, test "x$static_bonjour" = "xyes") AM_CONDITIONAL(STATIC_GG, test "x$static_gg" = "xyes") AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes") AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes") @@ -898,8 +944,10 @@ if test "x$have_meanwhile" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'` fi -if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then - DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'` +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 fi if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'` @@ -930,7 +978,7 @@ *) echo "Invalid dynamic protocol $i!!" ; exit ;; esac done -AM_CONDITIONAL(DYNAMIC_BONJOUR, test "x$dynamic_bonjour" = "xyes" -a "x$bonjourincludes" = "xyes" -a "x$bonjourclient" = "xyes") +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_GG, test "x$dynamic_gg" = "xyes") AM_CONDITIONAL(DYNAMIC_IRC, test "x$dynamic_irc" = "xyes") AM_CONDITIONAL(DYNAMIC_JABBER, test "x$dynamic_jabber" = "xyes") diff -r 0622a7f08dde -r 25c2af20affe finch/libgnt/gnttree.c --- a/finch/libgnt/gnttree.c Mon Aug 06 20:49:41 2007 +0000 +++ b/finch/libgnt/gnttree.c Mon Aug 06 20:50:32 2007 +0000 @@ -1094,7 +1094,7 @@ free_tree_col(gpointer data) { GntTreeCol *col = data; - if (col->isbinary) + if (!col->isbinary) g_free(col->text); g_free(col); } diff -r 0622a7f08dde -r 25c2af20affe libpurple/Makefile.am --- a/libpurple/Makefile.am Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/Makefile.am Mon Aug 06 20:50:32 2007 +0000 @@ -148,7 +148,7 @@ # purple dbus server dbus_sources = dbus-server.c dbus-useful.c -dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h +dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus-types.h dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \ connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \ diff -r 0622a7f08dde -r 25c2af20affe libpurple/account.h --- a/libpurple/account.h Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/account.h Mon Aug 06 20:50:32 2007 +0000 @@ -51,20 +51,50 @@ PURPLE_ACCOUNT_REQUEST_AUTHORIZATION = 0 /* Account authorization request */ } PurpleAccountRequestType; + +/** Account UI operations, used to notify the user of status changes and when + * buddies add this account to their buddy lists. + */ struct _PurpleAccountUiOps { - /* A buddy we already have added us to their buddy list. */ - void (*notify_added)(PurpleAccount *account, const char *remote_user, - const char *id, const char *alias, + /** A buddy who is already on this account's buddy list added this account + * to their buddy list. + */ + void (*notify_added)(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, const char *message); - void (*status_changed)(PurpleAccount *account, PurpleStatus *status); - /* Someone we don't have on our list added us. Will prompt to add them. */ - void (*request_add)(PurpleAccount *account, const char *remote_user, - const char *id, const char *alias, + + /** This account's status changed. */ + void (*status_changed)(PurpleAccount *account, + PurpleStatus *status); + + /** Someone we don't have on our list added us; prompt to add them. */ + void (*request_add)(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, const char *message); - void *(*request_authorize)(PurpleAccount *account, const char *remote_user, const char *id, - const char *alias, const char *message, gboolean on_list, - GCallback authorize_cb, GCallback deny_cb, void *user_data); + + /** Prompt for authorization when someone adds this account to their buddy + * list. To authorize them to see this account's presence, call \a + * authorize_cb (\a user_data); otherwise call \a deny_cb (\a user_data); + * @return a UI-specific handle, as passed to #close_account_request. + */ + void *(*request_authorize)(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message, + gboolean on_list, + GCallback authorize_cb, + GCallback deny_cb, + void *user_data); + + /** Close a pending request for authorization. \a ui_handle is a handle + * as returned by #request_authorize. + */ void (*close_account_request)(void *ui_handle); void (*_purple_reserved1)(void); @@ -193,7 +223,7 @@ /** * Notifies the user that a remote user has wants to add the local user - * to his or her buddy list and requires authorization to d oso. + * to his or her buddy list and requires authorization to do so. * * This will present a dialog informing the user of this and ask if the * user authorizes or denies the remote user from adding him. diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/Makefile.am --- a/libpurple/protocols/bonjour/Makefile.am Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.am Mon Aug 06 20:50:32 2007 +0000 @@ -1,7 +1,7 @@ EXTRA_DIST = \ - mdns_win32.c \ - mdns_win32.h \ - Makefile.mingw + mdns_win32.c \ + dns_sd_proxy.h \ + Makefile.mingw pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -10,18 +10,24 @@ bonjour.h \ buddy.c \ buddy.h \ - dns_sd_proxy.h \ jabber.c \ jabber.h \ mdns_common.c \ mdns_common.h \ - mdns_howl.c \ - mdns_howl.h \ + mdns_interface.h \ mdns_types.h \ parser.c \ parser.h -AM_CFLAGS = $(st) -DUSE_BONJOUR_HOWL +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 @@ -31,14 +37,29 @@ noinst_LIBRARIES = libbonjour.a libbonjour_a_SOURCES = $(BONJOURSOURCES) libbonjour_a_CFLAGS = $(AM_CFLAGS) -libbonjour_a_LIBADD = $(HOWL_LIBS) +libbonjour_a_LIBADD = + +if MDNS_AVAHI + libbonjour_a_LIBADD += $(AVAHI_LIBS) +else +if MDNS_HOWL + libbonjour_a_LIBADD += $(HOWL_LIBS) +endif +endif else st = pkg_LTLIBRARIES = libbonjour.la libbonjour_la_SOURCES = $(BONJOURSOURCES) -libbonjour_la_LIBADD = $(GLIB_LIBS) $(HOWL_LIBS) $(LIBXML_LIBS) +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 endif @@ -48,5 +69,13 @@ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ $(DEBUG_CFLAGS) \ - $(HOWL_CFLAGS) \ $(LIBXML_CFLAGS) + +if MDNS_AVAHI + AM_CPPFLAGS += $(AVAHI_CFLAGS) +else +if MDNS_HOWL + AM_CPPFLAGS += $(HOWL_CFLAGS) +endif +endif + diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/buddy.c --- a/libpurple/protocols/bonjour/buddy.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.c Mon Aug 06 20:50:32 2007 +0000 @@ -22,6 +22,7 @@ #include "account.h" #include "blist.h" #include "bonjour.h" +#include "mdns_interface.h" #include "debug.h" /** @@ -35,6 +36,8 @@ buddy->account = account; buddy->name = g_strdup(name); + _mdns_init_buddy(buddy); + return buddy; } @@ -102,8 +105,9 @@ { PurpleBuddy *buddy; PurpleGroup *group; - const char *status_id, *first, *last; - gchar *alias; + PurpleAccount *account = bonjour_buddy->account; + const char *status_id, *first, *last, *old_hash, *new_hash; + gchar *alias = NULL; /* Translate between the Bonjour status and the Purple status */ if (g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0) @@ -116,44 +120,55 @@ * field from the DNS SD. */ - /* Create the alias for the buddy using the first and the last name */ - first = bonjour_buddy->first; - last = bonjour_buddy->last; - alias = g_strdup_printf("%s%s%s", - (first && *first ? first : ""), - (first && *first && last && *last ? " " : ""), - (last && *last ? last : "")); - /* Make sure the Bonjour group exists in our buddy list */ group = purple_find_group(BONJOUR_GROUP_NAME); /* Use the buddy's domain, instead? */ - if (group == NULL) - { + if (group == NULL) { group = purple_group_new(BONJOUR_GROUP_NAME); purple_blist_add_group(group, NULL); } /* Make sure the buddy exists in our buddy list */ - buddy = purple_find_buddy(bonjour_buddy->account, bonjour_buddy->name); + buddy = purple_find_buddy(account, bonjour_buddy->name); - if (buddy == NULL) - { - buddy = purple_buddy_new(bonjour_buddy->account, bonjour_buddy->name, alias); + if (buddy == NULL) { + buddy = purple_buddy_new(account, bonjour_buddy->name, NULL); buddy->proto_data = bonjour_buddy; purple_blist_node_set_flags((PurpleBlistNode *)buddy, PURPLE_BLIST_NODE_FLAG_NO_SAVE); purple_blist_add_buddy(buddy, NULL, group, NULL); } + /* Create the alias for the buddy using the first and the last name */ + first = bonjour_buddy->first; + last = bonjour_buddy->last; + if ((first && *first) || (last && *last)) + alias = g_strdup_printf("%s%s%s", + (first && *first ? first : ""), + (first && *first && last && *last ? " " : ""), + (last && *last ? last : "")); + serv_got_alias(purple_account_get_connection(account), buddy->name, alias); + g_free(alias); + /* Set the user's status */ if (bonjour_buddy->msg != NULL) - purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id, - "message", bonjour_buddy->msg, - NULL); + purple_prpl_got_user_status(account, buddy->name, status_id, + "message", bonjour_buddy->msg, NULL); else - purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id, - NULL); - purple_prpl_got_user_idle(bonjour_buddy->account, buddy->name, FALSE, 0); + purple_prpl_got_user_status(account, buddy->name, status_id, NULL); + + purple_prpl_got_user_idle(account, buddy->name, FALSE, 0); + + /* TODO: Because we don't save Bonjour buddies in blist.xml, + * we will always have to look up the buddy icon at login time. + * I think we should figure out a way to do something about this. */ - g_free(alias); + /* Deal with the buddy icon */ + old_hash = purple_buddy_icons_get_checksum_for_user(buddy); + new_hash = (bonjour_buddy->phsh && *(bonjour_buddy->phsh)) ? bonjour_buddy->phsh : NULL; + if (new_hash && (!old_hash || strcmp(old_hash, new_hash) != 0)) { + /* Look up the new icon data */ + bonjour_dns_sd_retrieve_buddy_icon(bonjour_buddy); + } else + purple_buddy_icons_set_for_user(account, buddy->name, NULL, 0, NULL); } /** @@ -164,6 +179,7 @@ { g_free(buddy->name); g_free(buddy->ip); + g_free(buddy->full_service_name); g_free(buddy->first); g_free(buddy->phsh); @@ -182,13 +198,8 @@ bonjour_jabber_close_conversation(buddy->conversation); buddy->conversation = NULL; -#ifdef USE_BONJOUR_APPLE - if (buddy->txt_query != NULL) - { - purple_input_remove(buddy->txt_query_fd); - DNSServiceRefDeallocate(buddy->txt_query); - } -#endif + /* Clean up any mdns implementation data */ + _mdns_delete_buddy(buddy); g_free(buddy); } diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/buddy.h --- a/libpurple/protocols/bonjour/buddy.h Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.h Mon Aug 06 20:50:32 2007 +0000 @@ -19,22 +19,17 @@ #include -#include "config.h" #include "account.h" #include "jabber.h" -#ifdef USE_BONJOUR_APPLE -#include "dns_sd_proxy.h" -#else /* USE_BONJOUR_HOWL */ -#include -#endif - typedef struct _BonjourBuddy { PurpleAccount *account; gchar *name; + /* TODO: Remove and just use the hostname */ gchar *ip; + gchar *full_service_name; gint port_p2pj; gchar *first; @@ -53,11 +48,7 @@ BonjourJabberConversation *conversation; -#ifdef USE_BONJOUR_APPLE - DNSServiceRef txt_query; - int txt_query_fd; -#endif - + gpointer mdns_impl_data; } BonjourBuddy; static const char *const buddy_TXT_records[] = { diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_avahi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Mon Aug 06 20:50:32 2007 +0000 @@ -0,0 +1,349 @@ +/* + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "internal.h" + +#include "mdns_interface.h" +#include "debug.h" +#include "buddy.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* data used by avahi bonjour implementation */ +typedef struct _avahi_session_impl_data { + AvahiClient *client; + AvahiGLibPoll *glib_poll; + AvahiServiceBrowser *sb; + AvahiEntryGroup *group; +} AvahiSessionImplData; + +static void +_resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, + AvahiResolverEvent event, const char *name, const char *type, const char *domain, + const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt, + AvahiLookupResultFlags flags, void *userdata) { + + BonjourBuddy *buddy; + PurpleAccount *account = userdata; + AvahiStringList *l; + size_t size; + char *key, *value; + int ret; + + g_return_if_fail(r != NULL); + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + purple_debug_error("bonjour", "_resolve_callback - Failure: %s\n", + avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + case AVAHI_RESOLVER_FOUND: + /* create a buddy record */ + buddy = bonjour_buddy_new(name, account); + + /* Get the ip as a string */ + buddy->ip = g_malloc(AVAHI_ADDRESS_STR_MAX); + avahi_address_snprint(buddy->ip, AVAHI_ADDRESS_STR_MAX, a); + + buddy->port_p2pj = port; + + /* Obtain the parameters from the text_record */ + l = txt; + while (l != NULL) { + ret = avahi_string_list_get_pair(l, &key, &value, &size); + l = l->next; + if (ret < 0) + continue; + set_bonjour_buddy_value(buddy, key, value, size); + /* TODO: Since we're using the glib allocator, I think we + * can use the values instead of re-copying them */ + avahi_free(key); + avahi_free(value); + } + + if (!bonjour_buddy_check(buddy)) + bonjour_buddy_delete(buddy); + else + /* Add or update the buddy in our buddy list */ + bonjour_buddy_add_to_purple(buddy); + + break; + default: + purple_debug_info("bonjour", "Unrecognized Service Resolver event: %d.\n", event); + } + + avahi_service_resolver_free(r); +} + +static void +_browser_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, + AvahiProtocol protocol, AvahiBrowserEvent event, + const char *name, const char *type, const char *domain, + AvahiLookupResultFlags flags, void *userdata) { + + PurpleAccount *account = userdata; + PurpleBuddy *gb = NULL; + + switch (event) { + case AVAHI_BROWSER_FAILURE: + purple_debug_error("bonjour", "_browser_callback - Failure: %s\n", + avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); + /* TODO: This is an error that should be handled. */ + break; + case AVAHI_BROWSER_NEW: + /* A new peer has joined the network and uses iChat bonjour */ + purple_debug_info("bonjour", "_browser_callback - new service\n"); + /* Make sure it isn't us */ + if (g_ascii_strcasecmp(name, account->username) != 0) { + if (!avahi_service_resolver_new(avahi_service_browser_get_client(b), + interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, + 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)))); + } + } + break; + case AVAHI_BROWSER_REMOVE: + purple_debug_info("bonjour", "_browser_callback - Remove service\n"); + gb = purple_find_buddy(account, name); + if (gb != NULL) { + bonjour_buddy_delete(gb->proto_data); + purple_blist_remove_buddy(gb); + } + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + purple_debug_warning("bonjour", "(Browser) %s\n", + event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + break; + default: + purple_debug_info("bonjour", "Unrecognized Service browser event: %d.\n", event); + } +} + +static void +_entry_group_cb(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { + AvahiSessionImplData *idata = userdata; + + g_return_if_fail(g == idata->group || idata->group == NULL); + + switch(state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + purple_debug_info("bonjour", "Successfully registered service.\n"); + break; + case AVAHI_ENTRY_GROUP_COLLISION: + purple_debug_error("bonjour", "Collision registering entry group.\n"); + /* TODO: Handle error - this should log out the account. (Possibly with "wants to die")*/ + break; + case AVAHI_ENTRY_GROUP_FAILURE: + purple_debug_error("bonjour", "Error registering entry group: %s\n.", + avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); + /* TODO: Handle error - this should log out the account.*/ + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + break; + } + +} + +/**************************** + * mdns_interface functions * + ****************************/ + +gboolean _mdns_init_session(BonjourDnsSd *data) { + AvahiSessionImplData *idata = g_new0(AvahiSessionImplData, 1); + const AvahiPoll *poll_api; + int error; + + /* Tell avahi to use g_malloc and g_free */ + avahi_set_allocator (avahi_glib_allocator ()); + + /* This currently depends on the glib mainloop, + * we should make it use the libpurple abstraction */ + + idata->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT); + + poll_api = avahi_glib_poll_get(idata->glib_poll); + + idata->client = avahi_client_new(poll_api, 0, NULL, data, &error); + + if (idata->client == NULL) { + purple_debug_error("bonjour", "Error initializing Avahi: %s", avahi_strerror(error)); + avahi_glib_poll_free(idata->glib_poll); + g_free(idata); + return FALSE; + } + + data->mdns_impl_data = idata; + + return TRUE; +} + +gboolean _mdns_publish(BonjourDnsSd *data, PublishType type) { + int publish_result = 0; + char portstring[6]; + const char *jid, *aim, *email; + AvahiSessionImplData *idata = data->mdns_impl_data; + AvahiStringList *lst = NULL; + + g_return_val_if_fail(idata != NULL, FALSE); + + if (!idata->group) { + idata->group = avahi_entry_group_new(idata->client, + _entry_group_cb, idata); + if (!idata->group) { + purple_debug_error("bonjour", + "Unable to initialize the data for the mDNS (%s).\n", + avahi_strerror(avahi_client_errno(idata->client))); + return FALSE; + } + } + + /* Convert the port to a string */ + snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj); + + jid = purple_account_get_string(data->account, "jid", NULL); + aim = purple_account_get_string(data->account, "AIM", NULL); + email = purple_account_get_string(data->account, "email", NULL); + + /* We should try to follow XEP-0174, but some clients have "issues", so we humor them. + * See http://telepathy.freedesktop.org/wiki/SalutInteroperability + */ + + /* Needed by iChat */ + lst = avahi_string_list_add_pair(lst,"txtvers", "1"); + /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */ + lst = avahi_string_list_add_pair(lst, "1st", data->first); + /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */ + lst = avahi_string_list_add_pair(lst, "last", data->last); + /* Needed by Adium */ + lst = avahi_string_list_add_pair(lst, "port.p2pj", portstring); + /* Needed by iChat, Gaim/Pidgin <= 2.0.1 */ + lst = avahi_string_list_add_pair(lst, "status", data->status); + /* Currently always set to "!" since we don't support AV and wont ever be in a conference */ + lst = avahi_string_list_add_pair(lst, "vc", data->vc); + lst = avahi_string_list_add_pair(lst, "ver", VERSION); + if (email != NULL && *email != '\0') + lst = avahi_string_list_add_pair(lst, "email", email); + if (jid != NULL && *jid != '\0') + lst = avahi_string_list_add_pair(lst, "jid", jid); + /* Nonstandard, but used by iChat */ + if (aim != NULL && *aim != '\0') + lst = avahi_string_list_add_pair(lst, "AIM", aim); + if (data->msg != NULL && *data->msg != '\0') + lst = avahi_string_list_add_pair(lst, "msg", data->msg); + if (data->phsh != NULL && *data->phsh != '\0') + lst = avahi_string_list_add_pair(lst, "phsh", data->phsh); + + /* TODO: ext, nick, node */ + + + /* Publish the service */ + switch (type) { + case PUBLISH_START: + publish_result = avahi_entry_group_add_service_strlst( + idata->group, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, 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, + purple_account_get_username(data->account), + ICHAT_SERVICE, NULL, lst); + break; + } + + /* Free the memory used by temp data */ + avahi_string_list_free(lst); + + if (publish_result < 0) { + purple_debug_error("bonjour", + "Failed to add the " ICHAT_SERVICE " service. Error: %s\n", + avahi_strerror(publish_result)); + return FALSE; + } + + if ((publish_result = avahi_entry_group_commit(idata->group)) < 0) { + purple_debug_error("bonjour", + "Failed to commit " ICHAT_SERVICE " service. Error: %s\n", + avahi_strerror(publish_result)); + return FALSE; + } + + return TRUE; +} + +gboolean _mdns_browse(BonjourDnsSd *data) { + AvahiSessionImplData *idata = data->mdns_impl_data; + + 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); + if (!idata->sb) { + + purple_debug_error("bonjour", + "Unable to initialize service browser. Error: %s\n.", + avahi_strerror(avahi_client_errno(idata->client))); + return FALSE; + } + + return TRUE; +} + +/* This is done differently than with Howl/Apple Bonjour */ +guint _mdns_register_to_mainloop(BonjourDnsSd *data) { + return 0; +} + +void _mdns_stop(BonjourDnsSd *data) { + AvahiSessionImplData *idata = data->mdns_impl_data; + + if (idata == NULL || idata->client == NULL) + return; + + if (idata->sb != NULL) + avahi_service_browser_free(idata->sb); + + avahi_client_free(idata->client); + avahi_glib_poll_free(idata->glib_poll); + + g_free(idata); + + data->mdns_impl_data = NULL; +} + +void _mdns_init_buddy(BonjourBuddy *buddy) { +} + +void _mdns_delete_buddy(BonjourBuddy *buddy) { +} + +void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { +} diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_common.c --- a/libpurple/protocols/bonjour/mdns_common.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_common.c Mon Aug 06 20:50:32 2007 +0000 @@ -17,8 +17,8 @@ #include #include "internal.h" -#include "config.h" #include "mdns_common.h" +#include "mdns_interface.h" #include "bonjour.h" #include "buddy.h" #include "debug.h" @@ -73,65 +73,28 @@ gboolean bonjour_dns_sd_start(BonjourDnsSd *data) { - PurpleAccount *account; PurpleConnection *gc; - gint dns_sd_socket; - gpointer opaque_data; -#ifdef USE_BONJOUR_HOWL - sw_discovery_oid session_id; -#endif - - account = data->account; - gc = purple_account_get_connection(account); + gc = purple_account_get_connection(data->account); /* Initialize the dns-sd data and session */ -#ifndef USE_BONJOUR_APPLE - if (sw_discovery_init(&data->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 */ - data->session = NULL; - + if (!_mdns_init_session(data)) return FALSE; - } -#endif /* Publish our bonjour IM client at the mDNS daemon */ - - if (0 != _mdns_publish(data, PUBLISH_START)) - { + if (!_mdns_publish(data, PUBLISH_START)) return FALSE; - } /* Advise the daemon that we are waiting for connections */ - -#ifdef USE_BONJOUR_APPLE - if (DNSServiceBrowse(&data->browser, 0, 0, ICHAT_SERVICE, NULL, _mdns_service_browse_callback, account) - != kDNSServiceErr_NoError) -#else /* USE_BONJOUR_HOWL */ - if (sw_discovery_browse(data->session, 0, ICHAT_SERVICE, NULL, _browser_reply, - account, &session_id) != SW_OKAY) -#endif - { + if (!_mdns_browse(data)) { purple_debug_error("bonjour", "Unable to get service."); return FALSE; } + /* Get the socket that communicates with the mDNS daemon and bind it to a */ /* callback that will handle the dns_sd packets */ - -#ifdef USE_BONJOUR_APPLE - dns_sd_socket = DNSServiceRefSockFD(data->browser); - opaque_data = data->browser; -#else /* USE_BONJOUR_HOWL */ - dns_sd_socket = sw_discovery_socket(data->session); - opaque_data = data->session; -#endif - - gc->inpa = purple_input_add(dns_sd_socket, PURPLE_INPUT_READ, - _mdns_handle_event, opaque_data); + gc->inpa = _mdns_register_to_mainloop(data); return TRUE; } @@ -143,34 +106,11 @@ void bonjour_dns_sd_stop(BonjourDnsSd *data) { - PurpleAccount *account; PurpleConnection *gc; -#ifdef USE_BONJOUR_APPLE - if (data->advertisement == NULL || data->browser == NULL) -#else /* USE_BONJOUR_HOWL */ - if (data->session == NULL) -#endif - return; - -#ifdef USE_BONJOUR_HOWL - sw_discovery_cancel(data->session, data->session_id); -#endif + _mdns_stop(data); - account = data->account; - gc = purple_account_get_connection(account); - purple_input_remove(gc->inpa); - -#ifdef USE_BONJOUR_APPLE - /* hack: for win32, we need to stop listening to the advertisement pipe too */ - purple_input_remove(data->advertisement_handler); - - DNSServiceRefDeallocate(data->advertisement); - DNSServiceRefDeallocate(data->browser); - data->advertisement = NULL; - data->browser = NULL; -#else /* USE_BONJOUR_HOWL */ - g_free(data->session); - data->session = NULL; -#endif + gc = purple_account_get_connection(data->account); + if (gc->inpa > 0) + purple_input_remove(gc->inpa); } diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_common.h --- a/libpurple/protocols/bonjour/mdns_common.h Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_common.h Mon Aug 06 20:50:32 2007 +0000 @@ -19,11 +19,7 @@ #include "mdns_types.h" -#ifdef USE_BONJOUR_APPLE -#include "mdns_win32.h" -#elif defined USE_BONJOUR_HOWL -#include "mdns_howl.h" -#endif +#include "buddy.h" /** * Allocate space for the dns-sd data. @@ -41,6 +37,11 @@ void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message); /** + * Retrieve the buddy icon blob + */ +void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy); + +/** * Advertise our presence within the dns-sd daemon and start * browsing for other bonjour peers. */ diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_howl.c --- a/libpurple/protocols/bonjour/mdns_howl.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_howl.c Mon Aug 06 20:50:32 2007 +0000 @@ -15,12 +15,20 @@ */ #include "internal.h" -#include "mdns_howl.h" +#include "mdns_interface.h" #include "debug.h" #include "buddy.h" -sw_result HOWL_API +#include + +/* data used by howl bonjour implementation */ +typedef struct _howl_impl_data { + sw_discovery session; + sw_discovery_oid session_id; +} HowlSessionImplData; + +static sw_result HOWL_API _publish_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_publish_status status, sw_opaque extra) { @@ -46,7 +54,7 @@ return SW_OKAY; } -sw_result HOWL_API +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, @@ -95,7 +103,7 @@ return SW_OKAY; } -sw_result HOWL_API +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, @@ -137,7 +145,7 @@ break; case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: purple_debug_info("bonjour", "_browser_reply --> Remove service\n"); - gb = purple_find_buddy((PurpleAccount*)extra, name); + gb = purple_find_buddy(account, name); if (gb != NULL) { bonjour_buddy_delete(gb->proto_data); @@ -154,19 +162,49 @@ return SW_OKAY; } -int -_mdns_publish(BonjourDnsSd *data, PublishType type) +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) { sw_text_record dns_data; sw_result publish_result = SW_OKAY; char portstring[6]; const char *jid, *aim, *email; + 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) - { + if (sw_text_record_init(&dns_data) != SW_OKAY) { purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n"); - return -1; + return FALSE; } /* Convert the port to a string */ @@ -208,32 +246,70 @@ /* TODO: ext, nick, node */ /* Publish the service */ - switch (type) - { + switch (type) { case PUBLISH_START: - publish_result = sw_discovery_publish(data->session, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, + 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, &data->session_id); + _publish_reply, NULL, &idata->session_id); break; case PUBLISH_UPDATE: - publish_result = sw_discovery_publish_update(data->session, data->session_id, + publish_result = sw_discovery_publish_update(idata->session, idata->session_id, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data)); break; } - if (publish_result != SW_OKAY) - { - purple_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.\n"); - return -1; - } /* Free the memory used by temp data */ sw_text_record_fina(dns_data); - return 0; + 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); + + return (sw_discovery_browse(idata->session, 0, ICHAT_SERVICE, NULL, _browser_reply, + data->account, &session_id) == SW_OKAY); } -void -_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) -{ - sw_discovery_read_socket((sw_discovery)data); +guint _mdns_register_to_mainloop(BonjourDnsSd *data) { + HowlSessionImplData *idata = data->mdns_impl_data; + + g_return_val_if_fail(idata != NULL, 0); + + return purple_input_add(sw_discovery_socket(idata->session), + PURPLE_INPUT_READ, _mdns_handle_event, idata->session); } + +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); + + /* 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 bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { +} diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_howl.h --- a/libpurple/protocols/bonjour/mdns_howl.h Mon Aug 06 20:49:41 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _BONJOUR_MDNS_HOWL -#define _BONJOUR_MDNS_HOWL - -#include "config.h" - -#ifdef USE_BONJOUR_HOWL - -#include -#include -#include "mdns_types.h" - -/* callback functions */ - -sw_result HOWL_API _publish_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_publish_status status, sw_opaque extra); - -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); - -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); - - -/* interface functions */ - -int _mdns_publish(BonjourDnsSd *data, PublishType type); -void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition); - -#endif - -#endif diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_interface.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_interface.h Mon Aug 06 20:50:32 2007 +0000 @@ -0,0 +1,40 @@ +/* + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _BONJOUR_MDNS_INTERFACE +#define _BONJOUR_MDNS_INTERFACE + +#include "mdns_types.h" +#include "buddy.h" + +gboolean _mdns_init_session(BonjourDnsSd *data); + +gboolean _mdns_publish(BonjourDnsSd *data, PublishType type); + +gboolean _mdns_browse(BonjourDnsSd *data); + +guint _mdns_register_to_mainloop(BonjourDnsSd *data); + +void _mdns_stop(BonjourDnsSd *data); + +void _mdns_init_buddy(BonjourBuddy *buddy); + +void _mdns_delete_buddy(BonjourBuddy *buddy); + +/* This doesn't quite belong here, but there really isn't any shared functionality */ +void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy); + +#endif diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_types.h --- a/libpurple/protocols/bonjour/mdns_types.h Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_types.h Mon Aug 06 20:50:32 2007 +0000 @@ -19,31 +19,14 @@ #include #include "account.h" -#include "config.h" - -#ifdef USE_BONJOUR_APPLE -#include "dns_sd_proxy.h" -#else /* USE_BONJOUR_HOWL */ -#include -#endif #define ICHAT_SERVICE "_presence._tcp." /** * Data to be used by the dns-sd connection. */ -typedef struct _BonjourDnsSd -{ -#ifdef USE_BONJOUR_APPLE - DNSServiceRef advertisement; - DNSServiceRef browser; - - int advertisement_handler; /* hack... windows bonjour is broken, so we have to have this */ -#else /* USE_BONJOUR_HOWL */ - sw_discovery session; - sw_discovery_oid session_id; -#endif - +typedef struct _BonjourDnsSd { + gpointer mdns_impl_data; PurpleAccount *account; gchar *first; gchar *last; @@ -59,5 +42,4 @@ PUBLISH_UPDATE } PublishType; - #endif diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_win32.c --- a/libpurple/protocols/bonjour/mdns_win32.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Mon Aug 06 20:50:32 2007 +0000 @@ -15,22 +15,44 @@ */ #include "internal.h" -#include "mdns_win32.h" +#include "debug.h" -#include "debug.h" +#include "buddy.h" +#include "mdns_interface.h" +#include "dns_sd_proxy.h" +#include "dnsquery.h" + /* data structure for the resolve callback */ -typedef struct _ResolveCallbackArgs -{ +typedef struct _ResolveCallbackArgs { DNSServiceRef resolver; - int resolver_fd; + guint resolver_handler; PurpleDnsQueryData *query; - gchar *fqn; BonjourBuddy* buddy; } ResolveCallbackArgs; +/* data used by win32 bonjour implementation */ +typedef struct _win32_session_impl_data { + DNSServiceRef advertisement; + DNSServiceRef browser; + + guint advertisement_handler; /* hack... windows bonjour is broken, so we have to have this */ +} Win32SessionImplData; + +typedef struct _win32_buddy_impl_data { + DNSServiceRef txt_query; + guint txt_query_handler; + DNSServiceRef null_query; + guint null_query_handler; +} Win32BuddyImplData; + +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) { @@ -51,13 +73,32 @@ uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) { + if (kDNSServiceErr_NoError != errorCode) - purple_debug_error("bonjour", "text record query - callback error.\n"); + purple_debug_error("bonjour", "record query - callback error.\n"); else if (flags & kDNSServiceFlagsAdd) { - BonjourBuddy *buddy = (BonjourBuddy*)context; - _mdns_parse_text_record(buddy, rdata, rdlen); - bonjour_buddy_add_to_purple(buddy); + if (rrtype == kDNSServiceType_TXT) { + /* New Buddy */ + BonjourBuddy *buddy = (BonjourBuddy*) context; + _mdns_parse_text_record(buddy, rdata, rdlen); + bonjour_buddy_add_to_purple(buddy); + } else if (rrtype == kDNSServiceType_NULL) { + /* Buddy Icon response */ + BonjourBuddy *buddy = (BonjourBuddy*) context; + Win32BuddyImplData *idata = buddy->mdns_impl_data; + + g_return_if_fail(idata != NULL); + + purple_buddy_icons_set_for_user(buddy->account, buddy->name, + g_memdup(rdata, rdlen), rdlen, buddy->phsh); + + /* We've got what we need; stop listening */ + purple_input_remove(idata->null_query_handler); + idata->null_query_handler = -1; + DNSServiceRefDeallocate(idata->null_query); + idata->null_query = NULL; + } } } @@ -68,26 +109,26 @@ if (!hosts || !hosts->data) purple_debug_error("bonjour", "host resolution - callback error.\n"); - else - { + else { struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1); BonjourBuddy* buddy = args->buddy; + Win32BuddyImplData *idata = buddy->mdns_impl_data; + + g_return_if_fail(idata != NULL); buddy->ip = g_strdup(inet_ntoa(addr->sin_addr)); /* finally, set up the continuous txt record watcher, and add the buddy to purple */ - if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&buddy->txt_query, 0, 0, args->fqn, - kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) - { - gint fd = DNSServiceRefSockFD(buddy->txt_query); - buddy->txt_query_fd = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, buddy->txt_query); + if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->txt_query, 0, 0, buddy->full_service_name, + kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) { + int fd = DNSServiceRefSockFD(idata->txt_query); + idata->txt_query_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->txt_query); bonjour_buddy_add_to_purple(buddy); purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", buddy->name, buddy->ip, buddy->port_p2pj); - } - else + } else bonjour_buddy_delete(buddy); } @@ -97,7 +138,6 @@ /* free the remaining args memory */ purple_dnsquery_destroy(args->query); - g_free(args->fqn); g_free(args); } @@ -108,7 +148,7 @@ ResolveCallbackArgs *args = (ResolveCallbackArgs*)context; /* remove the input fd and destroy the service ref */ - purple_input_remove(args->resolver_fd); + purple_input_remove(args->resolver_handler); DNSServiceRefDeallocate(args->resolver); if (kDNSServiceErr_NoError != errorCode) @@ -125,14 +165,13 @@ _mdns_parse_text_record(args->buddy, txtRecord, txtLen); /* set more arguments, and start the host resolver */ - args->fqn = g_strdup(fullname); + args->buddy->full_service_name = g_strdup(fullname); 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->fqn); g_free(args); } } @@ -150,7 +189,7 @@ purple_debug_info("bonjour", "service advertisement - callback.\n"); } -void DNSSD_API +static void DNSSD_API _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) { @@ -159,50 +198,52 @@ if (kDNSServiceErr_NoError != errorCode) purple_debug_error("bonjour", "service browser - callback error"); - else if (flags & kDNSServiceFlagsAdd) - { + else if (flags & kDNSServiceFlagsAdd) { /* A presence service instance has been discovered... check it isn't us! */ - if (g_ascii_strcasecmp(serviceName, account->username) != 0) - { + if (g_ascii_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); - if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, replyDomain, _mdns_service_resolve_callback, args)) - { + if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, replyDomain, _mdns_service_resolve_callback, args)) { bonjour_buddy_delete(args->buddy); g_free(args); purple_debug_error("bonjour", "service browser - failed to resolve service.\n"); - } - else - { + } else { /* get a file descriptor for this service ref, and add it to the input list */ - gint resolver_fd = DNSServiceRefSockFD(args->resolver); - args->resolver_fd = purple_input_add(resolver_fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver); + gint fd = DNSServiceRefSockFD(args->resolver); + args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver); } } - } - else - { + } else { /* A peer has sent a goodbye packet, remove them from the buddy list */ purple_debug_info("bonjour", "service browser - remove notification\n"); gb = purple_find_buddy(account, serviceName); - if (gb != NULL) - { + if (gb != NULL) { bonjour_buddy_delete(gb->proto_data); purple_blist_remove_buddy(gb); } } } -int -_mdns_publish(BonjourDnsSd *data, PublishType type) -{ +/**************************** + * mdns_interface functions * + ****************************/ + +gboolean _mdns_init_session(BonjourDnsSd *data) { + data->mdns_impl_data = g_new0(Win32SessionImplData, 1); + return TRUE; +} + +gboolean _mdns_publish(BonjourDnsSd *data, PublishType type) { TXTRecordRef dns_data; char portstring[6]; - int ret = 0; + gboolean ret = TRUE; const char *jid, *aim, *email; DNSServiceErrorType set_ret; + Win32SessionImplData *idata = data->mdns_impl_data; + + g_return_val_if_fail(idata != NULL, FALSE); TXTRecordCreate(&dns_data, 256, NULL); @@ -250,41 +291,34 @@ /* TODO: ext, nick, node */ - if (set_ret != kDNSServiceErr_NoError) - { + if (set_ret != kDNSServiceErr_NoError) { purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); - ret = -1; - } - else - { + ret = FALSE; + } else { DNSServiceErrorType err = kDNSServiceErr_NoError; /* OK, we're done constructing the text record, (re)publish the service */ - switch (type) - { + switch (type) { case PUBLISH_START: purple_debug_info("bonjour", "Registering service on port %d\n", data->port_p2pj); - err = DNSServiceRegister(&data->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, + err = DNSServiceRegister(&idata->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), _mdns_service_register_callback, NULL); break; case PUBLISH_UPDATE: - err = DNSServiceUpdateRecord(data->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); + err = DNSServiceUpdateRecord(idata->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); break; } - if (kDNSServiceErr_NoError != err) - { + if (err != kDNSServiceErr_NoError) { purple_debug_error("bonjour", "Failed to publish presence service.\n"); - ret = -1; - } - else if (PUBLISH_START == type) - { + ret = FALSE; + } else if (type == PUBLISH_START) { /* hack: Bonjour on windows is broken. We don't care about the callback but we have to listen anyway */ - gint advertisement_fd = DNSServiceRefSockFD(data->advertisement); - data->advertisement_handler = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement); + gint fd = DNSServiceRefSockFD(idata->advertisement); + idata->advertisement_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->advertisement); } } @@ -293,8 +327,84 @@ return ret; } -void -_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) -{ - DNSServiceProcessResult((DNSServiceRef)data); +gboolean _mdns_browse(BonjourDnsSd *data) { + Win32SessionImplData *idata = data->mdns_impl_data; + + g_return_val_if_fail(idata != NULL, FALSE); + + return (DNSServiceBrowse(&idata->browser, 0, 0, ICHAT_SERVICE, NULL, + _mdns_service_browse_callback, data->account) + == kDNSServiceErr_NoError); +} + +guint _mdns_register_to_mainloop(BonjourDnsSd *data) { + Win32SessionImplData *idata = data->mdns_impl_data; + + g_return_val_if_fail(idata != NULL, 0); + + return purple_input_add(DNSServiceRefSockFD(idata->browser), + PURPLE_INPUT_READ, _mdns_handle_event, idata->browser); +} + +void _mdns_stop(BonjourDnsSd *data) { + Win32SessionImplData *idata = data->mdns_impl_data; + + if (idata == NULL || idata->advertisement == NULL || idata->browser == NULL) + return; + + /* hack: for win32, we need to stop listening to the advertisement pipe too */ + purple_input_remove(idata->advertisement_handler); + + DNSServiceRefDeallocate(idata->advertisement); + DNSServiceRefDeallocate(idata->browser); + + g_free(idata); + + data->mdns_impl_data = NULL; +} + +void _mdns_init_buddy(BonjourBuddy *buddy) { + buddy->mdns_impl_data = g_new0(Win32BuddyImplData, 1); } + +void _mdns_delete_buddy(BonjourBuddy *buddy) { + Win32BuddyImplData *idata = buddy->mdns_impl_data; + + g_return_if_fail(idata != NULL); + + if (idata->txt_query != NULL) { + purple_input_remove(idata->txt_query_handler); + DNSServiceRefDeallocate(idata->txt_query); + } + + if (idata->null_query != NULL) { + purple_input_remove(idata->null_query_handler); + DNSServiceRefDeallocate(idata->null_query); + } + + g_free(idata); + + buddy->mdns_impl_data = NULL; +} + +void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { + Win32BuddyImplData *idata = buddy->mdns_impl_data; + + g_return_if_fail(idata != NULL); + + /* Cancel any existing query */ + if (idata->null_query != NULL) { + purple_input_remove(idata->null_query_handler); + idata->null_query_handler = 0; + DNSServiceRefDeallocate(idata->null_query); + idata->null_query = NULL; + } + + if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, 0, buddy->full_service_name, + kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) { + int fd = DNSServiceRefSockFD(idata->null_query); + idata->null_query_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query); + } + +} + diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/bonjour/mdns_win32.h --- a/libpurple/protocols/bonjour/mdns_win32.h Mon Aug 06 20:49:41 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _BONJOUR_MDNS_WIN32 -#define _BONJOUR_MDNS_WIN32 - -#ifdef USE_BONJOUR_APPLE - -#include -#include "mdns_types.h" -#include "buddy.h" -#include "dnsquery.h" -#include "dns_sd_proxy.h" - -/* Bonjour async callbacks */ - -void DNSSD_API _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context); - -/* interface functions */ - -int _mdns_publish(BonjourDnsSd *data, PublishType type); -void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition); - -#endif - -#endif diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/oscar/flap_connection.c --- a/libpurple/protocols/oscar/flap_connection.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Mon Aug 06 20:50:32 2007 +0000 @@ -129,8 +129,8 @@ new_current = rateclass_get_new_current(conn, rateclass, &now); + /* (Add 100ms padding to account for inaccuracies in the calculation) */ if (new_current < rateclass->alert + 100) - /* (Add 100ms padding to account for inaccuracies in the calculation) */ /* Not ready to send this SNAC yet--keep waiting. */ return TRUE; @@ -186,9 +186,9 @@ gettimeofday(&now, NULL); new_current = rateclass_get_new_current(conn, rateclass, &now); + /* (Add 100ms padding to account for inaccuracies in the calculation) */ if (new_current < rateclass->alert + 100) { - /* (Add 100ms padding to account for inaccuracies in the calculation) */ enqueue = TRUE; } else diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Mon Aug 06 20:50:32 2007 +0000 @@ -161,7 +161,6 @@ static int purple_conv_chat_info_update (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_icon_error (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...); static int oscar_icon_req (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_parse_msgack (OscarData *, FlapConnection *, FlapFrame *, ...); @@ -195,7 +194,7 @@ static int purple_ssi_authreply (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_ssi_gotadded (OscarData *, FlapConnection *, FlapFrame *, ...); -static gboolean purple_icon_timerfunc(gpointer data); +static void purple_icons_fetch(PurpleConnection *gc); static void recent_buddies_cb(const char *name, PurplePrefType type, gconstpointer value, gpointer data); void oscar_set_info(PurpleConnection *gc, const char *info); @@ -1156,8 +1155,7 @@ od->iconconnecting = FALSE; - if (od->icontimer == 0) - od->icontimer = purple_timeout_add(100, purple_icon_timerfunc, gc); + purple_icons_fetch(gc); } static int @@ -1203,7 +1201,6 @@ oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0); oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0007, purple_parse_login, 0); oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_ERROR, purple_icon_error, 0); oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0); oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, purple_parse_genericerr, 0); oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, purple_bosrights, 0); @@ -1873,13 +1870,12 @@ saved_b16 = purple_buddy_icons_get_checksum_for_user(b); if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) { - GSList *cur = od->requesticon; - while (cur && aim_sncmp((char *)cur->data, info->sn)) - cur = cur->next; - if (!cur) { - od->requesticon = g_slist_append(od->requesticon, g_strdup(purple_normalize(account, info->sn))); - if (od->icontimer == 0) - od->icontimer = purple_timeout_add(500, purple_icon_timerfunc, gc); + if (g_slist_find_custom(od->requesticon, info->sn, + (GCompareFunc)aim_sncmp) == NULL) + { + od->requesticon = g_slist_prepend(od->requesticon, + g_strdup(purple_normalize(account, info->sn))); + purple_icons_fetch(gc); } } g_free(b16); @@ -3237,24 +3233,8 @@ return 1; } -static int purple_icon_error(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - char *sn; - - sn = od->requesticon->data; - purple_debug_misc("oscar", "removing %s from hash table\n", sn); - od->requesticon = g_slist_remove(od->requesticon, sn); - g_free(sn); - - if (od->icontimer == 0) - od->icontimer = purple_timeout_add(500, purple_icon_timerfunc, gc); - - return 1; -} - static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { PurpleConnection *gc = od->gc; - GSList *cur; va_list ap; char *sn; guint8 iconcsumtype, *iconcsum, *icon; @@ -3280,38 +3260,23 @@ g_free(b16); } - cur = od->requesticon; - while (cur) { - char *cursn = cur->data; - if (!aim_sncmp(cursn, sn)) { - od->requesticon = g_slist_remove(od->requesticon, cursn); - g_free(cursn); - cur = od->requesticon; - } else - cur = cur->next; - } - - if (od->icontimer == 0) - od->icontimer = purple_timeout_add(250, purple_icon_timerfunc, gc); - return 1; } -static gboolean purple_icon_timerfunc(gpointer data) { - PurpleConnection *gc = data; +static void +purple_icons_fetch(PurpleConnection *gc) +{ OscarData *od = gc->proto_data; aim_userinfo_t *userinfo; FlapConnection *conn; - od->icontimer = 0; - conn = flap_connection_getbytype(od, SNAC_FAMILY_BART); if (!conn) { if (!od->iconconnecting) { aim_srv_requestnew(od, SNAC_FAMILY_BART); od->iconconnecting = TRUE; } - return FALSE; + return; } if (od->set_icon) { @@ -3322,32 +3287,24 @@ } else { purple_debug_info("oscar", "Uploading icon to icon server\n"); - aim_bart_upload(od, purple_imgstore_get_data(img), + aim_bart_upload(od, purple_imgstore_get_data(img), purple_imgstore_get_size(img)); purple_imgstore_unref(img); } od->set_icon = FALSE; } - if (!od->requesticon) { - purple_debug_misc("oscar", - "no more icons to request\n"); - return FALSE; - } - - userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data); - if ((userinfo != NULL) && (userinfo->iconcsumlen > 0)) { - aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen); - return FALSE; - } else { - gchar *sn = od->requesticon->data; - od->requesticon = g_slist_remove(od->requesticon, sn); - g_free(sn); - } - - od->icontimer = purple_timeout_add(100, purple_icon_timerfunc, gc); - - return FALSE; + while (od->requesticon != NULL) + { + userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data); + if ((userinfo != NULL) && (userinfo->iconcsumlen > 0)) + aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen); + + g_free(od->requesticon->data); + od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon); + } + + purple_debug_misc("oscar", "no more icons to request\n"); } /* diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.h Mon Aug 06 20:50:32 2007 +0000 @@ -449,7 +449,6 @@ GSList *requesticon; gboolean icq; - guint icontimer; guint getblisttimer; guint getinfotimer; diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/oscar/oscar_data.c --- a/libpurple/protocols/oscar/oscar_data.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/oscar/oscar_data.c Mon Aug 06 20:50:32 2007 +0000 @@ -95,8 +95,6 @@ g_free(od->email); g_free(od->newp); g_free(od->oldp); - if (od->icontimer > 0) - purple_timeout_remove(od->icontimer); if (od->getblisttimer > 0) purple_timeout_remove(od->getblisttimer); if (od->getinfotimer > 0) diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/buddy_opt.c --- a/libpurple/protocols/qq/buddy_opt.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Mon Aug 06 20:50:32 2007 +0000 @@ -270,11 +270,11 @@ if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { read_packet_b(data, &cursor, len, &reply); if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request fails\n"); + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n"); if (NULL == (segments = split_data(data, len, "\x1f", 2))) return; msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - purple_notify_error(gc, NULL, _("Add buddy with auth request fails"), msg_utf8); + purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8); g_free(msg_utf8); } else { purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n"); @@ -305,6 +305,7 @@ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n"); } else { /* if reply */ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n"); + /* TODO: We don't really need to notify the user about this, do we? */ purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL); } } else { @@ -333,7 +334,8 @@ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n"); else { /* if reply */ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n"); - purple_notify_info(gc, NULL, _("You have successfully removed yourself from a buddy"), NULL); + /* TODO: Does the user really need to be notified about this? */ + purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL); } } else { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n"); @@ -413,7 +415,7 @@ g_free(nombre); } else { /* add OK */ qq_add_buddy_by_recv_packet(gc, for_uid, TRUE, TRUE); - msg = g_strdup_printf(_("You have added %d in buddy list"), for_uid); + msg = g_strdup_printf(_("You have added %d to buddy list"), for_uid); purple_notify_info(gc, NULL, msg, NULL); g_free(msg); } diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/group.c --- a/libpurple/protocols/qq/group.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/group.c Mon Aug 06 20:50:32 2007 +0000 @@ -117,7 +117,7 @@ purple_roomlist_set_in_progress(qd->roomlist, TRUE); purple_request_input(gc, _("QQ Qun"), - _("Please input external group ID"), + _("Please enter external group ID"), _("You can only search for permanent QQ groups\n"), NULL, FALSE, FALSE, NULL, _("Search"), G_CALLBACK(_qq_group_search_callback), diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/group_im.c --- a/libpurple/protocols/qq/group_im.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/group_im.c Mon Aug 06 20:50:32 2007 +0000 @@ -123,7 +123,7 @@ convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); - msg = g_strdup_printf(_("User %d applied to join group %d"), user_uid, external_group_id); + msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, external_group_id); reason = g_strdup_printf(_("Reason: %s"), reason_utf8); g = g_new0(group_member_opt, 1); @@ -177,7 +177,7 @@ convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf - (_("You request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); + (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); reason = g_strdup_printf(_("Reason: %s"), reason_utf8); purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason); @@ -218,7 +218,7 @@ convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf - (_("You request to join group %d has been approved by admin %d"), external_group_id, admin_uid); + (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid); purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL); @@ -254,7 +254,7 @@ g_return_if_fail(external_group_id > 0 && uid > 0); - msg = g_strdup_printf(_("You [%d] has exit group \"%d\""), uid, external_group_id); + msg = g_strdup_printf(_("You [%d] have left group \"%d\""), uid, external_group_id); purple_notify_info(gc, _("QQ Qun Operation"), msg, NULL); group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); @@ -288,7 +288,7 @@ g_return_if_fail(external_group_id > 0 && uid > 0); - msg = g_strdup_printf(_("You [%d] has been added by group \"%d\""), uid, external_group_id); + msg = g_strdup_printf(_("You [%d] have been added to group \"%d\""), uid, external_group_id); purple_notify_info(gc, _("QQ Qun Operation"), msg, _("This group has been added to your buddy list")); group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/group_internal.c --- a/libpurple/protocols/qq/group_internal.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/group_internal.c Mon Aug 06 20:50:32 2007 +0000 @@ -38,7 +38,7 @@ switch (group->my_status) { case QQ_GROUP_MEMBER_STATUS_NOT_MEMBER: - status_desc = _("I am not member"); + status_desc = _("I am not a member"); break; case QQ_GROUP_MEMBER_STATUS_IS_MEMBER: status_desc = _("I am a member"); diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/group_join.c --- a/libpurple/protocols/qq/group_join.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/group_join.c Mon Aug 06 20:50:32 2007 +0000 @@ -230,7 +230,7 @@ purple_blist_remove_chat(chat); qq_group_delete_internal_record(qd, internal_group_id); } - purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully exited the group"), NULL); + purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL); } else { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid exit group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); @@ -254,8 +254,8 @@ if (bytes == expected_bytes) purple_notify_info - (gc, _("QQ Group Auth"), - _("Your authorization operation has been accepted by the QQ server"), NULL); + (gc, _("QQ Group Auth"), + _("Your authorization request has been accepted by the QQ server"), NULL); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); @@ -325,8 +325,8 @@ errno = 0; external_group_id = strtol(external_group_id_ptr, NULL, 10); if (errno != 0) { - purple_notify_error(gc, _("Error"), - _("You inputted a group id outside the acceptable range"), NULL); + purple_notify_error(gc, _("Error"), + _("You entered a group ID outside the acceptable range"), NULL); return; } @@ -357,12 +357,12 @@ g->uid = internal_group_id; purple_request_action(gc, _("QQ Qun Operation"), - _("Are you sure to exit this Qun?"), + _("Are you sure you want to leave this Qun?"), _ ("Note, if you are the creator, \nthis operation will eventually remove this Qun."), 1, purple_connection_get_account(gc), NULL, NULL, g, 2, _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), - _("Go ahead"), G_CALLBACK(_qq_group_exit_with_gc_and_id)); + _("Continue"), G_CALLBACK(_qq_group_exit_with_gc_and_id)); } diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/group_opt.c --- a/libpurple/protocols/qq/group_opt.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/group_opt.c Mon Aug 06 20:50:32 2007 +0000 @@ -120,8 +120,8 @@ { g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0); - qq_send_packet_get_info(g->gc, g->member, TRUE); /* we wanna see window */ - purple_request_action(g->gc, NULL, _("Do you wanna approve the request?"), "", 2, + qq_send_packet_get_info(g->gc, g->member, TRUE); /* we want to see window */ + purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "", 2, purple_connection_get_account(g->gc), NULL, NULL, g, 2, _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct), @@ -134,7 +134,7 @@ g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0); msg1 = g_strdup_printf(_("You rejected %d's request"), g->member); - msg2 = g_strdup(_("Input your reason:")); + msg2 = g_strdup(_("Enter your reason:")); nombre = uid_to_purple_name(g->member); purple_request_input(g->gc, /* title */ NULL, msg1, msg2, @@ -232,7 +232,7 @@ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in modify members for Qun %d\n", group->external_group_id); - purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modify Qun member"), NULL); + purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modified Qun member"), NULL); } void qq_group_modify_info(PurpleConnection *gc, qq_group *group) @@ -302,7 +302,7 @@ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in modify info for Qun %d\n", group->external_group_id); qq_group_refresh(gc, group); - purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modify Qun information"), NULL); + purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modified Qun information"), NULL); } /* we create a very simple group first, and then let the user to modify */ diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/im.c --- a/libpurple/protocols/qq/im.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/im.c Mon Aug 06 20:50:32 2007 +0000 @@ -573,7 +573,7 @@ read_packet_b(data, &cursor, len, &reply); if (reply != QQ_SEND_IM_REPLY_OK) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n"); - purple_notify_error(gc, _("Server ACK"), _("Failed to send IM."), NULL); + purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL); } else purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM ACK OK\n"); diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/keep_alive.c --- a/libpurple/protocols/qq/keep_alive.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/keep_alive.c Mon Aug 06 20:50:32 2007 +0000 @@ -84,7 +84,7 @@ /* segments[0] and segment[1] are all 0x30 ("0") */ qd->all_online = strtol(segments[2], NULL, 10); if(0 == qd->all_online) - purple_connection_error(gc, _("Keep alive error, seems connection lost!")); + purple_connection_error(gc, _("Keep alive error")); g_free(qd->my_ip); qd->my_ip = g_strdup(segments[3]); qd->my_port = strtol(segments[4], NULL, 10); diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/login_logout.c --- a/libpurple/protocols/qq/login_logout.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/login_logout.c Mon Aug 06 20:50:32 2007 +0000 @@ -405,7 +405,7 @@ ">>> %d bytes -> [default] decrypt and dump\n%s", buf_len, hex_dump); try_dump_as_gbk(buf, buf_len); - purple_connection_error(gc, _("Request login token error!")); + purple_connection_error(gc, _("Error requesting login token")); } g_free(hex_dump); } diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/qq.c --- a/libpurple/protocols/qq/qq.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/qq.c Mon Aug 06 20:50:32 2007 +0000 @@ -195,7 +195,8 @@ { qq_buddy *q_bud; gchar *ip_str; - char *tmp, *tmp2; + char *tmp; + const char *tmp2; g_return_if_fail(b != NULL); @@ -206,11 +207,12 @@ { ip_str = gen_ip_str(q_bud->ip); if (strlen(ip_str) != 0) { - tmp = g_strdup_printf(_("%s Address"), - ((q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) ? "TCP" : "UDP")); - tmp2 = g_strdup_printf("%s:%d", ip_str, q_bud->port); - purple_notify_user_info_add_pair(user_info, tmp, tmp2); - g_free(tmp2); + if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) + tmp2 = _("TCP Address"); + else + tmp2 = _("UDP Address"); + tmp = g_strdup_printf("%s:%d", ip_str, q_bud->port); + purple_notify_user_info_add_pair(user_info, tmp2, tmp); g_free(tmp); } g_free(ip_str); @@ -238,7 +240,7 @@ if (q_bud->level) { tmp = g_strdup_printf("%d", q_bud->level); purple_notify_user_info_add_pair(user_info, _("Level"), tmp); - g_free(tmp); + g_free(tmp); } /* For debugging */ /* @@ -275,19 +277,19 @@ GList *types = NULL; status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, - "available", _("QQ: Available"), FALSE, TRUE, FALSE); + "available", _("Available"), FALSE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "away", _("QQ: Away"), FALSE, TRUE, FALSE); + "away", _("Away"), FALSE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - "invisible", _("QQ: Invisible"), FALSE, TRUE, FALSE); + "invisible", _("Invisible"), FALSE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - "offline", _("QQ: Offline"), FALSE, TRUE, FALSE); + "offline", _("Offline"), FALSE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, @@ -416,7 +418,7 @@ g->uid = uid; purple_request_action(gc, _("Block Buddy"), - _("Are you sure to block this buddy?"), NULL, + _("Are you sure you want to block this buddy?"), NULL, 1, g, 2, _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), @@ -470,7 +472,7 @@ PurpleConnection *gc = (PurpleConnection *) action->context; purple_request_input(gc, _("Create QQ Qun"), _("Input Qun name here"), - _("Only QQ member can create permanent Qun"), + _("Only QQ members can create permanent Qun"), "OpenQ", FALSE, FALSE, NULL, _("Create"), G_CALLBACK(qq_group_create_with_name), _("Cancel"), NULL, gc); } @@ -528,7 +530,7 @@ PurplePluginAction *act; m = NULL; - act = purple_plugin_action_new(_("Modify My Information"), _qq_menu_modify_my_info); + act = purple_plugin_action_new(_("Set My Information"), _qq_menu_modify_my_info); m = g_list_append(m, act); act = purple_plugin_action_new(_("Change Password"), _qq_menu_change_password); @@ -555,7 +557,7 @@ PurpleMenuAction *act; m = NULL; - act = purple_menu_action_new(_("Exit this QQ Qun"), PURPLE_CALLBACK(_qq_menu_unsubscribe_group), NULL, NULL); + act = purple_menu_action_new(_("Leave this QQ Qun"), PURPLE_CALLBACK(_qq_menu_unsubscribe_group), NULL, NULL); m = g_list_append(m, act); /* TODO: enable this diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/qq_proxy.c --- a/libpurple/protocols/qq/qq_proxy.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/qq_proxy.c Mon Aug 06 20:50:32 2007 +0000 @@ -493,13 +493,8 @@ errno = 0; ret = send(qd->fd, data, len, 0); } - if (ret == -1) { - purple_connection_error(qd->gc, _("Socket send error")); - return ret; - } else if (errno == ECONNREFUSED) { - purple_connection_error(qd->gc, _("Connection refused")); - return ret; - } + if (ret == -1) + purple_connection_error(qd->gc, strerror(errno)); return ret; } diff -r 0622a7f08dde -r 25c2af20affe libpurple/protocols/qq/sys_msg.c --- a/libpurple/protocols/qq/sys_msg.c Mon Aug 06 20:49:41 2007 +0000 +++ b/libpurple/protocols/qq/sys_msg.c Mon Aug 06 20:50:32 2007 +0000 @@ -80,12 +80,11 @@ uid = g->uid; g_return_if_fail(gc != 0 && uid != 0); - qq_send_packet_get_info(gc, uid, TRUE); /* we wanna see window */ + qq_send_packet_get_info(gc, uid, TRUE); /* we want to see window */ nombre = uid_to_purple_name(uid); - /* TODO: 'wanna' is not an appropriate word for this string. Fix after string freeze. */ purple_request_action - (gc, NULL, _("Do you wanna approve the request?"), "", 2, + (gc, NULL, _("Do you want to approve the request?"), "", 2, purple_connection_get_account(gc), nombre, NULL, g, 2, _("Reject"), G_CALLBACK(qq_reject_add_request_with_gc_and_uid), @@ -105,11 +104,10 @@ uid = g->uid; g_return_if_fail(gc != 0 && uid != 0); - qq_send_packet_get_info(gc, uid, TRUE); /* we wanna see window */ - /* TODO: 'wanna' is not an appropriate word for this string. Fix after string freeze. */ + qq_send_packet_get_info(gc, uid, TRUE); /* we want to see window */ nombre = uid_to_purple_name(uid); purple_request_action - (gc, NULL, _("Do you wanna add this buddy?"), "", 2, + (gc, NULL, _("Do you want to add this buddy?"), "", 2, purple_connection_get_account(gc), nombre, NULL, g, 2, _("Cancel"), NULL, @@ -175,7 +173,7 @@ _("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid), _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid)); } else { - message = g_strdup_printf(_("%s has added you [%s]"), from, to); + message = g_strdup_printf(_("%s has added you [%s] to his or her buddy list"), from, to); _qq_sys_msg_log_write(gc, message, from); purple_notify_info(gc, NULL, message, NULL); } @@ -211,7 +209,7 @@ qd = (qq_data *) gc->proto_data; qq_add_buddy_by_recv_packet(gc, strtol(from, NULL, 10), TRUE, TRUE); - message = g_strdup_printf(_("User %s has approved your request"), from); + message = g_strdup_printf(_("User %s approved your request"), from); _qq_sys_msg_log_write(gc, message, from); purple_notify_info(gc, NULL, message, NULL); @@ -236,9 +234,8 @@ name = uid_to_purple_name(uid); - /* TODO: 'wanna' is not an appropriate word for this string. Fix after string freeze */ /* TODO: this should go through purple_account_request_authorization() */ - message = g_strdup_printf(_("%s wanna add you [%s] as friends"), from, to); + message = g_strdup_printf(_("%s wants to add you [%s] as a friend"), from, to); reason = g_strdup_printf(_("Message: %s"), msg_utf8); _qq_sys_msg_log_write(gc, message, from); diff -r 0622a7f08dde -r 25c2af20affe pidgin/gtkconv.c --- a/pidgin/gtkconv.c Mon Aug 06 20:49:41 2007 +0000 +++ b/pidgin/gtkconv.c Mon Aug 06 20:50:32 2007 +0000 @@ -187,6 +187,9 @@ static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv); static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); +static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y, + int width, int height); + static GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) { static GdkColor col; GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml); @@ -7170,25 +7173,28 @@ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/tabs", TRUE); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/tab_side", GTK_POS_TOP); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/scrollback_lines", 4000); - purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/x", 0); - purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/y", 0); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font", TRUE); purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", ""); /* Conversations -> Chat */ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/chat"); - purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", 410); - purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height", 160); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", 50); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/userlist_width", 80); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/x", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/y", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/width", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/height", 0); + /* Conversations -> IM */ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/im"); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/x", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/y", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/width", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/height", 0); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons", TRUE); - purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width", 410); - purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height", 160); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", 50); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE); @@ -8332,10 +8338,8 @@ return FALSE; /* don't save if nothing changed */ - if (x == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x") && - y == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y") && - event->width == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width") && - event->height == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height")) + if (x == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/x") && + y == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/y")) return FALSE; /* carry on normally */ /* don't save off-screen positioning */ @@ -8345,9 +8349,9 @@ y > gdk_screen_height()) return FALSE; /* carry on normally */ - /* store the position */ - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/x", x); - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/y", y); + /* store the position */ + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/x", x); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/y", y); purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width", event->width); purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height); @@ -8357,35 +8361,38 @@ } static void -pidgin_conv_restore_position(PidginWindow *win) { - int conv_x, conv_y, conv_width, conv_height; - - conv_width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"); - +pidgin_conv_set_position_size(PidginWindow *win, int conv_x, int conv_y, + int conv_width, int conv_height) +{ /* if the window exists, is hidden, we're saving positions, and the * position is sane... */ - if (win && win->window && - !GTK_WIDGET_VISIBLE(win->window) && conv_width != 0) { - - conv_x = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x"); - conv_y = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y"); - conv_height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height"); - - /* ...check position is on screen... */ - if (conv_x >= gdk_screen_width()) - conv_x = gdk_screen_width() - 100; - else if (conv_x + conv_width < 0) - conv_x = 100; - - if (conv_y >= gdk_screen_height()) - conv_y = gdk_screen_height() - 100; - else if (conv_y + conv_height < 0) - conv_y = 100; - - /* ...and move it back. */ - gtk_window_move(GTK_WINDOW(win->window), conv_x, conv_y); - gtk_window_resize(GTK_WINDOW(win->window), conv_width, conv_height); - } + if (win && win->window && + !GTK_WIDGET_VISIBLE(win->window) && conv_width != 0) { + + /* ...check position is on screen... */ + if (conv_x >= gdk_screen_width()) + conv_x = gdk_screen_width() - 100; + else if (conv_x + conv_width < 0) + conv_x = 100; + + if (conv_y >= gdk_screen_height()) + conv_y = gdk_screen_height() - 100; + else if (conv_y + conv_height < 0) + conv_y = 100; + + /* ...and move it back. */ + gtk_window_move(GTK_WINDOW(win->window), conv_x, conv_y); + gtk_window_resize(GTK_WINDOW(win->window), conv_width, conv_height); + } +} + +static void +pidgin_conv_restore_position(PidginWindow *win) { + pidgin_conv_set_position_size(win, + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/width"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/height")); } PidginWindow * @@ -8410,8 +8417,6 @@ g_signal_connect(G_OBJECT(win->window), "delete_event", G_CALLBACK(close_win_cb), win); - g_signal_connect(G_OBJECT(win->window), "configure_event", - G_CALLBACK(gtk_conv_configure_cb), NULL); g_signal_connect(G_OBJECT(win->window), "focus_in_event", G_CALLBACK(focus_win_cb), win); @@ -8926,6 +8931,9 @@ if (win == NULL) { win = pidgin_conv_window_new(); + g_signal_connect(G_OBJECT(win->window), "configure_event", + G_CALLBACK(gtk_conv_configure_cb), NULL); + pidgin_conv_window_add_gtkconv(win, conv); pidgin_conv_window_show(win); } else { @@ -8934,6 +8942,53 @@ } /* This one places conversations in the last made window of the same type. */ +static gboolean +conv_placement_last_created_win_type_configured_cb(GtkWidget *w, + GdkEventConfigure *event, PidginConversation *conv) +{ + int x, y; + PurpleConversationType type = purple_conversation_get_type(conv->active_conv); + GList *all; + + if (GTK_WIDGET_VISIBLE(w)) + gtk_window_get_position(GTK_WINDOW(w), &x, &y); + else + return FALSE; /* carry on normally */ + + /* Workaround for GTK+ bug # 169811 - "configure_event" is fired + * when the window is being maximized */ + if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED) + return FALSE; + + /* don't save off-screen positioning */ + if (x + event->width < 0 || + y + event->height < 0 || + x > gdk_screen_width() || + y > gdk_screen_height()) + return FALSE; /* carry on normally */ + + for (all = conv->convs; all != NULL; all = all->next) { + if (type != purple_conversation_get_type(all->data)) { + /* this window has different types of conversation, don't save */ + return FALSE; + } + } + + if (type == PURPLE_CONV_TYPE_IM) { + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/x", x); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/y", y); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width", event->width); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height); + } else if (type == PURPLE_CONV_TYPE_CHAT) { + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/x", x); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/y", y); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/width", event->width); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/height", event->height); + } + + return FALSE; +} + static void conv_placement_last_created_win_type(PidginConversation *conv) { @@ -8944,8 +8999,26 @@ if (win == NULL) { win = pidgin_conv_window_new(); + if (PURPLE_CONV_TYPE_IM == purple_conversation_get_type(conv->active_conv) || + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/width") == 0) { + pidgin_conv_set_position_size(win, + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/x"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/y"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height")); + } else if (PURPLE_CONV_TYPE_CHAT == purple_conversation_get_type(conv->active_conv)) { + pidgin_conv_set_position_size(win, + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/x"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/y"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/width"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/height")); + } + pidgin_conv_window_add_gtkconv(win, conv); pidgin_conv_window_show(win); + + g_signal_connect(G_OBJECT(win->window), "configure_event", + G_CALLBACK(conv_placement_last_created_win_type_configured_cb), conv); } else pidgin_conv_window_add_gtkconv(win, conv); } @@ -8957,6 +9030,8 @@ PidginWindow *win; win = pidgin_conv_window_new(); + g_signal_connect(G_OBJECT(win->window), "configure_event", + G_CALLBACK(gtk_conv_configure_cb), NULL); pidgin_conv_window_add_gtkconv(win, conv); diff -r 0622a7f08dde -r 25c2af20affe pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Mon Aug 06 20:49:41 2007 +0000 +++ b/pidgin/gtkimhtml.c Mon Aug 06 20:50:32 2007 +0000 @@ -1053,7 +1053,7 @@ if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml))) return; - if (selection_data->length < 0) { + if (imhtml->wbfo || selection_data->length < 0) { gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml); return; } else { diff -r 0622a7f08dde -r 25c2af20affe pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Mon Aug 06 20:49:41 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Mon Aug 06 20:50:32 2007 +0000 @@ -939,12 +939,10 @@ *y -= widget->allocation.height; } -static void pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu) +static void pidgin_menu_clicked(GtkWidget *button, GdkEventButton *event, GtkMenu *menu) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) { - gtk_widget_show_all(GTK_WIDGET(menu)); - gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time()); - } + gtk_widget_show_all(GTK_WIDGET(menu)); + gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time()); } static void pidgin_menu_deactivate(GtkWidget *menu, GtkToggleButton *button) @@ -1179,7 +1177,7 @@ G_CALLBACK(button_sensitiveness_changed), menuitem); } - g_signal_connect(G_OBJECT(font_button), "clicked", G_CALLBACK(pidgin_menu_clicked), font_menu); + g_signal_connect(G_OBJECT(font_button), "button-press-event", G_CALLBACK(pidgin_menu_clicked), font_menu); g_signal_connect(G_OBJECT(font_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), font_button); /* Sep */ @@ -1220,7 +1218,7 @@ g_signal_connect(G_OBJECT(toolbar->link), "notify::sensitive", G_CALLBACK(button_sensitiveness_changed), menuitem); - g_signal_connect(G_OBJECT(insert_button), "clicked", G_CALLBACK(pidgin_menu_clicked), insert_menu); + g_signal_connect(G_OBJECT(insert_button), "button-press-event", G_CALLBACK(pidgin_menu_clicked), insert_menu); g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button); toolbar->sml = NULL; } diff -r 0622a7f08dde -r 25c2af20affe pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Mon Aug 06 20:49:41 2007 +0000 +++ b/pidgin/gtkprefs.c Mon Aug 06 20:50:32 2007 +0000 @@ -2236,4 +2236,13 @@ purple_prefs_remove(PIDGIN_PREFS_ROOT "/away/queue_messages"); purple_prefs_remove(PIDGIN_PREFS_ROOT "/away"); purple_prefs_remove("/plugins/gtk/docklet/queue_messages"); + + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_height"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_width"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_height"); + purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/x", + PIDGIN_PREFS_ROOT "/conversations/im/x"); + purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y", + PIDGIN_PREFS_ROOT "/conversations/im/y"); } diff -r 0622a7f08dde -r 25c2af20affe pidgin/pixmaps/icons/22/Makefile.am --- a/pidgin/pixmaps/icons/22/Makefile.am Mon Aug 06 20:49:41 2007 +0000 +++ b/pidgin/pixmaps/icons/22/Makefile.am Mon Aug 06 20:50:32 2007 +0000 @@ -2,7 +2,7 @@ EXTRA_DIST = pidgin.png -pidginiconspixdir = $(datadir)/icons/hicolor/24x24/apps +pidginiconspixdir = $(datadir)/icons/hicolor/22x22/apps pidginiconspix_DATA = $(EXTRA_DIST)