im.pidgin.pidgin: 46aa0a2d9a76892a9fd9aef671bef46f129e1e61
datallah at pidgin.im
datallah at pidgin.im
Sun Dec 9 23:00:43 EST 2007
-----------------------------------------------------------------
Revision: 46aa0a2d9a76892a9fd9aef671bef46f129e1e61
Ancestor: 0f1bc6944f478b162c2c539eeede8b0b03496657
Author: datallah at pidgin.im
Date: 2007-12-10T03:50:58
Branch: im.pidgin.pidgin
Deleted entries:
libpurple/protocols/bonjour/mdns_howl.c
Modified files:
ChangeLog configure.ac
libpurple/protocols/bonjour/Makefile.am
libpurple/protocols/bonjour/bonjour_ft.c
libpurple/protocols/bonjour/buddy.c
libpurple/protocols/bonjour/buddy.h
libpurple/protocols/bonjour/jabber.c
libpurple/protocols/bonjour/mdns_avahi.c
libpurple/protocols/bonjour/mdns_win32.c
ChangeLog:
Update Bonjour prpl to support multiple presence records for the same buddy. Eliminate the Howl backend to avoid having to maintain yet another set of code. References #4187 (more to come to fix the rest of the ticket).
-------------- next part --------------
============================================================
--- ChangeLog c8386a4a6bc4ccdb2e3a96fda7f932590a7d3dae
+++ ChangeLog f615fef8bc7053da6f1f066438357ae9ac95d476
@@ -1,9 +1,12 @@ version 2.3.2 (??/??/????):
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
version 2.3.2 (??/??/????):
libpurple:
* Fixed various problems with loss of status messages when going
or returning from idle on MySpaceIM.
+ * Eliminated unmaintained Howl backend implementation for the
+ Bonjour protocol. Avahi (or Apple's Bonjour runtime on win32) is
+ now required to use Bonjour.
Finch:
* Color is used in the buddylist to indicate status, and the conversation
============================================================
--- configure.ac 617f95f36ac89823b33c1849acfb11bbdd3a7c52
+++ configure.ac 2343fb13efa34daeffdb8de0a31e3c248adb7f6e
@@ -722,61 +722,8 @@ AC_SUBST(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"])
-AC_ARG_WITH(howl-libs, [AC_HELP_STRING([--with-howl-libs=DIR], [compile the Bonjour plugin against the Howl libs in DIR])], [ac_howl_libs="$withval"], [ac_howl_libs="no"])
-HOWL_CFLAGS=""
-HOWL_LIBS=""
-
-dnl Attempt to autodetect avahi-compat-howl
-dnl TODO: (This should be removed when the native avahi stuff is stable)
-PKG_CHECK_MODULES(HOWL, avahi-compat-howl, [
- howlincludes="yes"
- howllibs="yes"
-], [
- AC_MSG_RESULT(no)
- howlincludes="no"
- howllibs="no"
-])
-
-dnl Attempt to autodetect Howl
-if test "x$howlincludes" = "xno"; then
- PKG_CHECK_MODULES(HOWL, howl, [
- howlincludes="yes"
- howllibs="yes"
- ], [
- AC_MSG_RESULT(no)
- howlincludes="no"
- howllibs="no"
- ])
-fi
-
-dnl Override HOWL_CFLAGS if the user specified an include dir
-if test "$ac_howl_includes" != "no"; then
- HOWL_CFLAGS="-I$ac_howl_includes"
-fi
-CPPFLAGS_save="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $HOWL_CFLAGS"
-AC_CHECK_HEADER(howl.h, [howlincludes=yes], [howlincludes=no])
-CPPFLAGS="$CPPFLAGS_save"
-
-dnl Override HOWL_LIBS if the user specified a libs dir
-if test "$ac_howl_libs" != "no"; then
- HOWL_LIBS="-L$ac_howl_libs -lhowl"
-fi
-AC_CHECK_LIB(howl, sw_discovery_init, [howllibs=yes], [howllibs=no], $HOWL_LIBS)
-
-AC_SUBST(HOWL_CFLAGS)
-AC_SUBST(HOWL_LIBS)
-
-AM_CONDITIONAL(MDNS_HOWL, test "x$howlincludes" = "xyes" -a "x$howllibs" = "xyes")
-
-
-dnl #######################################################################
dnl # Check for SILC client includes and libraries
dnl #######################################################################
AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"])
@@ -960,9 +907,7 @@ if test "x$avahiincludes" != "xyes" -o "
STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'`
fi
if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
- if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then
- STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
- fi
+ STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
fi
if test "x$enable_msnp14" != "xyes" ; then
STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/msn/msnp9/'`
@@ -1049,9 +994,7 @@ if test "x$avahiincludes" != "xyes" -o "
DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'`
fi
if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
- if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then
- DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
- fi
+ DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
fi
if test "x$enable_msnp14" != "xyes" ; then
DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/msn/msnp9/'`
@@ -1087,7 +1030,7 @@ done
*) echo "Invalid dynamic protocol $i!!" ; exit ;;
esac
done
-AM_CONDITIONAL(DYNAMIC_BONJOUR, test "x$dynamic_bonjour" = "xyes" -a [ [ "x$avahiincludes" = "xyes" -a "x$avahilibs " = "xyes" ] -o [ "x$howlincludes" = "xyes" -a "x$howllibs" = "xyes" ] ] )
+AM_CONDITIONAL(DYNAMIC_BONJOUR, test "x$dynamic_bonjour" = "xyes" -a [ "x$avahiincludes" = "xyes" -a "x$avahilibs " = "xyes" ] )
AM_CONDITIONAL(DYNAMIC_GG, test "x$dynamic_gg" = "xyes")
AM_CONDITIONAL(DYNAMIC_IRC, test "x$dynamic_irc" = "xyes")
AM_CONDITIONAL(DYNAMIC_JABBER, test "x$dynamic_jabber" = "xyes")
============================================================
--- libpurple/protocols/bonjour/Makefile.am e32ff6a3141f800ae6e996d5235e4509e399554d
+++ libpurple/protocols/bonjour/Makefile.am 65cf96aa0a7ff7ed33cb588864d5b5ff05632b2c
@@ -13,6 +13,7 @@ BONJOURSOURCES = \
buddy.h \
jabber.c \
jabber.h \
+ mdns_avahi.c \
mdns_common.c \
mdns_common.h \
mdns_interface.h \
@@ -22,14 +23,6 @@ BONJOURSOURCES = \
bonjour_ft.c \
bonjour_ft.h
-if MDNS_AVAHI
- BONJOURSOURCES += mdns_avahi.c
-else
-if MDNS_HOWL
- BONJOURSOURCES += mdns_howl.c
-endif
-endif
-
AM_CFLAGS = $(st)
libbonjour_la_LDFLAGS = -module -avoid-version
@@ -40,29 +33,14 @@ libbonjour_a_CFLAGS = $(AM_CFLAGS)
noinst_LIBRARIES = libbonjour.a
libbonjour_a_SOURCES = $(BONJOURSOURCES)
libbonjour_a_CFLAGS = $(AM_CFLAGS)
-libbonjour_a_LIBADD =
+libbonjour_a_LIBADD = $(AVAHI_LIBS)
-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) $(LIBXML_LIBS)
-if MDNS_AVAHI
- libbonjour_la_LIBADD += $(AVAHI_LIBS)
-else
-if MDNS_HOWL
- libbonjour_la_LIBADD += $(HOWL_LIBS)
-endif
-endif
+libbonjour_la_LIBADD = $(GLIB_LIBS) $(LIBXML_LIBS) $(AVAHI_LIBS)
endif
@@ -72,13 +50,6 @@ AM_CPPFLAGS = \
-I$(top_builddir)/libpurple \
$(GLIB_CFLAGS) \
$(DEBUG_CFLAGS) \
- $(LIBXML_CFLAGS)
+ $(LIBXML_CFLAGS) \
+ $(AVAHI_CFLAGS)
-if MDNS_AVAHI
- AM_CPPFLAGS += $(AVAHI_CFLAGS)
-else
-if MDNS_HOWL
- AM_CPPFLAGS += $(HOWL_CFLAGS)
-endif
-endif
-
============================================================
--- libpurple/protocols/bonjour/bonjour_ft.c bc6a526b517d70641f27b6172e6cdd590c87e4d6
+++ libpurple/protocols/bonjour/bonjour_ft.c 6a6a99b6d7d4593a49b8e7bd0c146acccb5c0938
@@ -387,7 +387,7 @@ bonjour_xfer_init(PurpleXfer *xfer)
bonjour_xfer_init(PurpleXfer *xfer)
{
PurpleBuddy *buddy = NULL;
- BonjourBuddy *bd = NULL;
+ BonjourBuddy *bb = NULL;
XepXfer *xf = NULL;
xf = (XepXfer*)xfer->data;
@@ -401,8 +401,10 @@ bonjour_xfer_init(PurpleXfer *xfer)
if (buddy == NULL)
return;
- bd = (BonjourBuddy *)buddy->proto_data;
- xf->buddy_ip = g_strdup(bd->ip);
+ bb = (BonjourBuddy *)buddy->proto_data;
+ /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
+ if (bb->ips)
+ xf->buddy_ip = g_strdup(bb->ips->data);
if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
/* initiate file transfer, send SI offer. */
purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
============================================================
--- libpurple/protocols/bonjour/buddy.c 9c6a665becc68873fddb59ebdc6d45038066f5cf
+++ libpurple/protocols/bonjour/buddy.c f82aa1b7dc08a09471bba9385e18212cb9db74d5
@@ -237,7 +237,10 @@ bonjour_buddy_delete(BonjourBuddy *buddy
bonjour_buddy_delete(BonjourBuddy *buddy)
{
g_free(buddy->name);
- g_free(buddy->ip);
+ while (buddy->ips != NULL) {
+ g_free(buddy->ips->data);
+ buddy->ips = g_slist_delete_link(buddy->ips, buddy->ips);
+ }
g_free(buddy->first);
g_free(buddy->phsh);
g_free(buddy->status);
============================================================
--- libpurple/protocols/bonjour/buddy.h 085ae98d1521773a6534498ceb1afefb66057ba2
+++ libpurple/protocols/bonjour/buddy.h d9693b016b1c83c4f3d0491b91a266121bb8eedf
@@ -27,8 +27,7 @@ typedef struct _BonjourBuddy
PurpleAccount *account;
gchar *name;
- /* TODO: Remove and just use the hostname */
- gchar *ip;
+ GSList *ips;
gint port_p2pj;
gchar *first;
============================================================
--- libpurple/protocols/bonjour/jabber.c d77b3b25f67116db47df388798914fe3ec865ee7
+++ libpurple/protocols/bonjour/jabber.c 89961ad1a2c2bf3d008d7d7f7c54bce6d7a7f7e7
@@ -239,13 +239,24 @@ _check_buddy_by_address(gpointer key, gp
/*
* If the current PurpleBuddy's data is not null and the PurpleBuddy's account
* is the same as the account requesting the check then continue to determine
- * whether the buddies IP matches the target IP.
+ * whether one of the buddies IPs matches the target IP.
*/
if (cbba->bj->account == pb->account)
{
bb = pb->proto_data;
- if ((bb != NULL) && (g_ascii_strcasecmp(bb->ip, cbba->address) == 0))
- *(cbba->pb) = pb;
+ if (bb != NULL) {
+ const char *ip;
+ GSList *tmp = bb->ips;
+
+ while(tmp) {
+ ip = tmp->data;
+ if (ip != NULL && g_ascii_strcasecmp(ip, cbba->address) == 0) {
+ *(cbba->pb) = pb;
+ break;
+ }
+ tmp = tmp->next;
+ }
+ }
}
}
@@ -449,9 +460,14 @@ _start_stream(gpointer data, gint source
else if (ret <= 0) {
const char *err = g_strerror(errno);
PurpleConversation *conv;
+ const char *ip = NULL;
+ /* For better or worse, use the first IP*/
+ if (bb->ips)
+ ip = bb->ips->data;
+
purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
- purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+ purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
if (conv != NULL)
@@ -504,9 +520,14 @@ static gboolean bonjour_jabber_send_stre
ret = 0;
else if (ret <= 0) {
const char *err = g_strerror(errno);
+ const char *ip = NULL;
+ /* For better or worse, use the first IP*/
+ if (bb->ips)
+ ip = bb->ips->data;
+
purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
- purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+ purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
close(client_socket);
g_free(stream_start);
@@ -544,9 +565,14 @@ void bonjour_jabber_stream_started(Purpl
if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(pb, bconv->socket)) {
const char *err = g_strerror(errno);
PurpleConversation *conv;
+ const char *ip = NULL;
+ /* For better or worse, use the first IP*/
+ if (bb->ips)
+ ip = bb->ips->data;
+
purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
- purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+ purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
if (conv != NULL)
@@ -721,9 +747,14 @@ _connected_to_buddy(gpointer data, gint
if (source < 0) {
PurpleConversation *conv;
+ const char *ip = NULL;
+ /* For better or worse, use the first IP*/
+ if (bb->ips)
+ ip = bb->ips->data;
+
purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
- purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, error ? error : "(null)");
+ purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, error ? error : "(null)");
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
if (conv != NULL)
@@ -739,9 +770,14 @@ _connected_to_buddy(gpointer data, gint
if (!bonjour_jabber_send_stream_init(pb, source)) {
const char *err = g_strerror(errno);
PurpleConversation *conv;
+ const char *ip = NULL;
+ /* For better or worse, use the first IP*/
+ if (bb->ips)
+ ip = bb->ips->data;
+
purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
- purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+ purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
if (conv != NULL)
@@ -782,7 +818,12 @@ _find_or_start_conversation(BonjourJabbe
{
PurpleProxyConnectData *connect_data;
PurpleProxyInfo *proxy_info;
+ const char *ip = NULL;
+ /* For better or worse, use the first IP*/
+ if (bb->ips)
+ ip = bb->ips->data;
+
purple_debug_info("bonjour", "Starting conversation with %s\n", to);
/* Make sure that the account always has a proxy of "none".
@@ -795,7 +836,7 @@ _find_or_start_conversation(BonjourJabbe
purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
connect_data = purple_proxy_connect(NULL, data->account,
- bb->ip, bb->port_p2pj, _connected_to_buddy, pb);
+ ip, bb->port_p2pj, _connected_to_buddy, pb);
if (connect_data == NULL) {
purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
============================================================
--- libpurple/protocols/bonjour/mdns_avahi.c 03c3c9bf354d71b0c4f82b07d072e2256f7f43e9
+++ libpurple/protocols/bonjour/mdns_avahi.c ba48df8bff0c528b45ecbc0e3c5f88bb93f5b157
@@ -47,12 +47,63 @@ typedef struct _avahi_session_impl_data
AvahiEntryGroup *buddy_icon_group;
} AvahiSessionImplData;
-typedef struct _avahi_buddy_impl_data {
+typedef struct _avahi_buddy_service_resolver_data {
AvahiServiceResolver *resolver;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ gchar *name;
+ gchar *type;
+ gchar *domain;
+ /* This is a reference to the entry in BonjourBuddy->ips */
+ const char *ip;
+} AvahiSvcResolverData;
+
+typedef struct _avahi_buddy_impl_data {
+ GSList *resolvers;
AvahiRecordBrowser *buddy_icon_rec_browser;
} AvahiBuddyImplData;
+static gint
+_find_resolver_data(gconstpointer a, gconstpointer b) {
+ const AvahiSvcResolverData *rd_a = a;
+ const AvahiSvcResolverData *rd_b = b;
+ gint ret = 1;
+
+ if(rd_a->interface == rd_b->interface
+ && rd_a->protocol == rd_b->protocol
+ && !strcmp(rd_a->name, rd_b->name)
+ && !strcmp(rd_a->type, rd_b->type)
+ && !strcmp(rd_a->domain, rd_b->domain)) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static gint
+_find_resolver_data_by_resolver(gconstpointer a, gconstpointer b) {
+ const AvahiSvcResolverData *rd_a = a;
+ const AvahiServiceResolver *resolver = b;
+ gint ret = 1;
+
+ if(rd_a->resolver == resolver)
+ ret = 0;
+
+ return ret;
+}
+
static void
+_cleanup_resolver_data(AvahiSvcResolverData *rd) {
+ if (rd->resolver)
+ avahi_service_resolver_free(rd->resolver);
+ g_free(rd->name);
+ g_free(rd->type);
+ g_free(rd->domain);
+ g_free(rd);
+}
+
+
+static void
_resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
AvahiResolverEvent event, const char *name, const char *type, const char *domain,
const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt,
@@ -65,6 +116,10 @@ _resolver_callback(AvahiServiceResolver
size_t size;
char *key, *value;
int ret;
+ char ip[AVAHI_ADDRESS_STR_MAX];
+ AvahiBuddyImplData *b_impl;
+ AvahiSvcResolverData *rd;
+ GSList *res;
g_return_if_fail(r != NULL);
@@ -78,31 +133,60 @@ _resolver_callback(AvahiServiceResolver
avahi_service_resolver_free(r);
if (bb != NULL) {
- /* We've already freed the resolver */
- if (r == ((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver)
- ((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver = NULL;
- purple_account_remove_buddy(account, pb, NULL);
- purple_blist_remove_buddy(pb);
+ b_impl = bb->mdns_impl_data;
+ res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
+ if (res != NULL) {
+ rd = res->data;
+ b_impl->resolvers = g_slist_remove_link(b_impl->resolvers, res);
+
+ /* We've already freed the resolver */
+ rd->resolver = NULL;
+ _cleanup_resolver_data(rd);
+
+ /* If this was the last resolver, remove the buddy */
+ if (b_impl->resolvers == NULL) {
+ purple_account_remove_buddy(account, pb, NULL);
+ purple_blist_remove_buddy(pb);
+ }
+ }
}
break;
case AVAHI_RESOLVER_FOUND:
/* create a buddy record */
if (bb == NULL)
bb = bonjour_buddy_new(name, account);
+ b_impl = bb->mdns_impl_data;
- /* If we're reusing an existing buddy, make sure if it is a different resolver to clean up the old one.
- * I don't think this should ever happen, but I'm afraid we might get events out of sequence. */
- if (((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver != NULL
- && ((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver != r) {
- avahi_service_resolver_free(((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver);
+ /* If we're reusing an existing buddy, it may be a new resolver or an existing one. */
+ res = g_slist_find_custom(b_impl->resolvers, r, _find_resolver_data_by_resolver);
+ if (res != NULL)
+ rd = res->data;
+ else {
+ rd = g_new0(AvahiSvcResolverData, 1);
+ rd->resolver = r;
+ rd->interface = interface;
+ rd->protocol = protocol;
+ rd->name = g_strdup(name);
+ rd->type = g_strdup(type);
+ rd->domain = g_strdup(domain);
+
+ b_impl->resolvers = g_slist_prepend(b_impl->resolvers, rd);
}
- ((AvahiBuddyImplData *)bb->mdns_impl_data)->resolver = r;
- g_free(bb->ip);
+
/* Get the ip as a string */
- bb->ip = g_malloc(AVAHI_ADDRESS_STR_MAX);
- avahi_address_snprint(bb->ip, AVAHI_ADDRESS_STR_MAX, a);
+ avahi_address_snprint(ip, AVAHI_ADDRESS_STR_MAX, a);
+ if (rd->ip == NULL || strcmp(rd->ip, ip) != 0) {
+ /* We store duplicates in bb->ips, so we always remove the one */
+ if (rd->ip != NULL) {
+ bb->ips = g_slist_remove(bb->ips, rd->ip);
+ g_free((gchar *) rd->ip);
+ }
+ bb->ips = g_slist_prepend(bb->ips, g_strdup(ip));
+ rd->ip = bb->ips->data;
+ }
+
bb->port_p2pj = port;
/* Obtain the parameters from the text_record */
@@ -118,11 +202,16 @@ _resolver_callback(AvahiServiceResolver
}
if (!bonjour_buddy_check(bb)) {
- if (pb != NULL) {
- purple_account_remove_buddy(account, pb, NULL);
- purple_blist_remove_buddy(pb);
- } else
- bonjour_buddy_delete(bb);
+ _cleanup_resolver_data(rd);
+ b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
+ /* If this was the last resolver, remove the buddy */
+ if (b_impl->resolvers == NULL) {
+ if (pb != NULL) {
+ purple_account_remove_buddy(account, pb, NULL);
+ purple_blist_remove_buddy(pb);
+ } else
+ bonjour_buddy_delete(bb);
+ }
} else
/* Add or update the buddy in our buddy list */
bonjour_buddy_add_to_purple(bb, pb);
@@ -166,8 +255,44 @@ _browser_callback(AvahiServiceBrowser *b
purple_debug_info("bonjour", "_browser_callback - Remove service\n");
pb = purple_find_buddy(account, name);
if (pb != NULL) {
- purple_account_remove_buddy(account, pb, NULL);
- purple_blist_remove_buddy(pb);
+ BonjourBuddy *bb = pb->proto_data;
+ AvahiBuddyImplData *b_impl;
+ GSList *l;
+ AvahiSvcResolverData *rd_search;
+
+ g_return_if_fail(bb != NULL);
+
+ b_impl = bb->mdns_impl_data;
+
+ /* There may be multiple presences, we should only get rid of this one */
+
+ rd_search = g_new0(AvahiSvcResolverData, 1);
+ rd_search->interface = interface;
+ rd_search->protocol = protocol;
+ rd_search->name = (gchar *) name;
+ rd_search->type = (gchar *) type;
+ rd_search->domain = (gchar *) domain;
+
+ l = g_slist_find_custom(b_impl->resolvers, rd_search, _find_resolver_data);
+
+ g_free(rd_search);
+
+ if (l != NULL) {
+ AvahiSvcResolverData *rd = l->data;
+ b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
+ /* This IP is no longer available */
+ if (rd->ip != NULL) {
+ bb->ips = g_slist_remove(bb->ips, rd->ip);
+ g_free((gchar *) rd->ip);
+ }
+ _cleanup_resolver_data(rd);
+
+ /* If this was the last resolver, remove the buddy */
+ if (b_impl->resolvers == NULL) {
+ purple_account_remove_buddy(account, pb, NULL);
+ purple_blist_remove_buddy(pb);
+ }
+ }
}
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
@@ -188,6 +313,7 @@ _buddy_icon_group_cb(AvahiEntryGroup *g,
switch(state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED:
purple_debug_info("bonjour", "Successfully registered buddy icon data.\n");
+ break;
case AVAHI_ENTRY_GROUP_COLLISION:
purple_debug_error("bonjour", "Collision registering buddy icon data.\n");
break;
@@ -249,8 +375,10 @@ _buddy_icon_record_cb(AvahiRecordBrowser
}
/* Stop listening */
- avahi_record_browser_free(idata->buddy_icon_rec_browser);
- idata->buddy_icon_rec_browser = NULL;
+ avahi_record_browser_free(b);
+ if (idata->buddy_icon_rec_browser == b) {
+ idata->buddy_icon_rec_browser = NULL;
+ }
}
/****************************
@@ -462,8 +590,11 @@ void _mdns_delete_buddy(BonjourBuddy *bu
if (idata->buddy_icon_rec_browser != NULL)
avahi_record_browser_free(idata->buddy_icon_rec_browser);
- if (idata->resolver != NULL)
- avahi_service_resolver_free(idata->resolver);
+ while(idata->resolvers != NULL) {
+ AvahiSvcResolverData *rd = idata->resolvers->data;
+ _cleanup_resolver_data(rd);
+ idata->resolvers = g_slist_delete_link(idata->resolvers, idata->resolvers);
+ }
g_free(idata);
============================================================
--- libpurple/protocols/bonjour/mdns_win32.c ce1562e7608efd912c35189431c839366b8c269d
+++ libpurple/protocols/bonjour/mdns_win32.c 77eec4e5a1c2351925e46ffc2cbd10fae58631a8
@@ -28,18 +28,8 @@
#include "dnsquery.h"
#include "mdns_common.h"
+static GSList *pending_buddies = NULL;
-/* data structure for the resolve callback */
-typedef struct _ResolveCallbackArgs {
- DNSServiceRef resolver;
- guint resolver_handler;
- gchar *full_service_name;
-
- PurpleDnsQueryData *query;
-
- BonjourBuddy* buddy;
-} ResolveCallbackArgs;
-
/* data used by win32 bonjour implementation */
typedef struct _win32_session_impl_data {
DNSServiceRef presence_svc;
@@ -50,20 +40,69 @@ typedef struct _win32_session_impl_data
guint browser_handler;
} Win32SessionImplData;
-typedef struct _win32_buddy_impl_data {
+typedef struct _win32_buddy_service_resolver_data {
DNSServiceRef txt_query;
guint txt_query_handler;
+ uint32_t if_idx;
+ gchar *name;
+ gchar *type;
+ gchar *domain;
+ /* This is a reference to the entry in BonjourBuddy->ips */
+ const char *ip;
+} Win32SvcResolverData;
+
+typedef struct _win32_buddy_impl_data {
+ GSList *resolvers;
DNSServiceRef null_query;
guint null_query_handler;
} Win32BuddyImplData;
+/* data structure for the resolve callback */
+typedef struct _ResolveCallbackArgs {
+ DNSServiceRef resolver;
+ guint resolver_handler;
+ PurpleAccount *account;
+ BonjourBuddy *bb;
+ Win32SvcResolverData *res_data;
+ gchar *full_service_name;
+ PurpleDnsQueryData *query;
+} ResolveCallbackArgs;
+
+static gint
+_find_resolver_data(gconstpointer a, gconstpointer b) {
+ const Win32SvcResolverData *rd_a = a;
+ const Win32SvcResolverData *rd_b = b;
+ gint ret = 1;
+
+ if(rd_a->if_idx == rd_b->if_idx
+ && !strcmp(rd_a->name, rd_b->name)
+ && !strcmp(rd_a->type, rd_b->type)
+ && !strcmp(rd_a->domain, rd_b->domain)) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
static void
+_cleanup_resolver_data(Win32SvcResolverData *rd) {
+ if (rd->txt_query != NULL) {
+ purple_input_remove(rd->txt_query_handler);
+ DNSServiceRefDeallocate(rd->txt_query);
+ }
+ g_free(rd->name);
+ g_free(rd->type);
+ g_free(rd->domain);
+ g_free(rd);
+}
+
+static void
_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
DNSServiceProcessResult((DNSServiceRef) data);
}
static void
-_mdns_parse_text_record(BonjourBuddy* buddy, const char* record, uint16_t record_len)
+_mdns_parse_text_record(BonjourBuddy *buddy, const char *record, uint16_t record_len)
{
const char *txt_entry;
uint8_t txt_len;
@@ -114,36 +153,45 @@ _mdns_resolve_host_callback(GSList *host
static void
_mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message)
{
- ResolveCallbackArgs* args = (ResolveCallbackArgs*)data;
- BonjourBuddy* bb = args->buddy;
+ ResolveCallbackArgs* args = (ResolveCallbackArgs*) data;
+ Win32BuddyImplData *idata = args->bb->mdns_impl_data;
+ gboolean delete_buddy = FALSE;
+ PurpleBuddy *pb;
+ if ((pb = purple_find_buddy(args->account, args->bb->name)))
+ if (pb->proto_data != args->bb)
+ purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record. "
+ "This is going to be ugly!.\n", args->bb->name);
+
if (!hosts || !hosts->data) {
purple_debug_error("bonjour", "host resolution - callback error.\n");
- bonjour_buddy_delete(bb);
+ delete_buddy = TRUE;
} else {
- struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1);
- Win32BuddyImplData *idata = bb->mdns_impl_data;
+ struct sockaddr_in *addr = g_slist_nth_data(hosts, 1);
- g_return_if_fail(idata != NULL);
+ /* finally, set up the continuous txt record watcher, and add the buddy to purple */
- g_free(bb->ip);
- bb->ip = g_strdup(inet_ntoa(addr->sin_addr));
+ if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&args->res_data->txt_query, kDNSServiceFlagsLongLivedQuery,
+ kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
+ kDNSServiceClass_IN, _mdns_record_query_callback, args->bb)) {
- /* finally, set up the continuous txt record watcher, and add the buddy to purple */
+ const char *ip = inet_ntoa(addr->sin_addr);
- if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->txt_query, kDNSServiceFlagsLongLivedQuery,
- kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
- kDNSServiceClass_IN, _mdns_record_query_callback, bb)) {
+ purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
- purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", bb->name, bb->ip, bb->port_p2pj);
- idata->txt_query_handler = purple_input_add(DNSServiceRefSockFD(idata->txt_query),
- PURPLE_INPUT_READ, _mdns_handle_event, idata->txt_query);
+ args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip));
+ args->res_data->ip = args->bb->ips->data;
- bonjour_buddy_add_to_purple(bb, NULL);
- } else
- bonjour_buddy_delete(bb);
+ args->res_data->txt_query_handler = purple_input_add(DNSServiceRefSockFD(args->res_data->txt_query),
+ PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query);
+ bonjour_buddy_add_to_purple(args->bb, NULL);
+ } else {
+ purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s\n", args->bb->name);
+ delete_buddy = TRUE;
+ }
+
}
/* free the hosts list*/
@@ -153,6 +201,26 @@ _mdns_resolve_host_callback(GSList *host
hosts = g_slist_remove(hosts, hosts->data);
}
+ if (delete_buddy) {
+ idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
+ _cleanup_resolver_data(args->res_data);
+
+ /* If this was the last resolver, remove the buddy */
+ if (idata->resolvers == NULL) {
+ if (pb) {
+ purple_account_remove_buddy(args->account, pb, NULL);
+ purple_blist_remove_buddy(pb);
+ } else
+ bonjour_buddy_delete(args->bb);
+
+ /* Remove from the pending list */
+ pending_buddies = g_slist_remove(pending_buddies, args->bb);
+ }
+ } else {
+ /* Remove from the pending list */
+ pending_buddies = g_slist_remove(pending_buddies, args->bb);
+ }
+
/* free the remaining args memory */
g_free(args->full_service_name);
g_free(args);
@@ -163,37 +231,54 @@ _mdns_service_resolve_callback(DNSServic
const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
{
ResolveCallbackArgs *args = (ResolveCallbackArgs*)context;
+ Win32BuddyImplData *idata = args->bb->mdns_impl_data;
/* remove the input fd and destroy the service ref */
purple_input_remove(args->resolver_handler);
+ args->resolver_handler = 0;
DNSServiceRefDeallocate(args->resolver);
+ args->resolver = NULL;
if (kDNSServiceErr_NoError != errorCode)
- {
purple_debug_error("bonjour", "service resolver - callback error.\n");
- bonjour_buddy_delete(args->buddy);
- g_free(args);
+ else {
+ /* set more arguments, and start the host resolver */
+
+ if ((args->query =
+ purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) {
+
+ args->full_service_name = g_strdup(fullname);
+
+ /* TODO: Should this be per resolver? */
+ args->bb->port_p2pj = ntohs(port);
+
+ /* We don't want to hit the cleanup code */
+ return;
+ } else
+ purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
}
- else
- {
- args->buddy->port_p2pj = ntohs(port);
- /* parse the text record */
- _mdns_parse_text_record(args->buddy, txtRecord, txtLen);
+ /* If we get this far, clean up */
- /* set more arguments, and start the host resolver */
- args->full_service_name = g_strdup(fullname);
+ idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
+ _cleanup_resolver_data(args->res_data);
- if (!(args->query =
- purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)))
- {
- purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
- bonjour_buddy_delete(args->buddy);
- g_free(args->full_service_name);
- g_free(args);
+ /* If this was the last resolver, remove the buddy */
+ if (idata->resolvers == NULL) {
+ PurpleBuddy *pb;
+ /* See if this is now attached to a PurpleBuddy */
+ if ((pb = purple_find_buddy(args->account, args->bb->name))) {
+ purple_account_remove_buddy(args->account, pb, NULL);
+ purple_blist_remove_buddy(pb);
+ } else {
+ /* Remove from the pending list */
+ pending_buddies = g_slist_remove(pending_buddies, args->bb);
+ bonjour_buddy_delete(args->bb);
}
}
+ g_free(args);
+
}
static void DNSSD_API
@@ -212,7 +297,6 @@ _mdns_service_browse_callback(DNSService
DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context)
{
PurpleAccount *account = (PurpleAccount*)context;
- PurpleBuddy *pb = NULL;
if (kDNSServiceErr_NoError != errorCode)
purple_debug_error("bonjour", "service browser - callback error\n");
@@ -221,28 +305,118 @@ _mdns_service_browse_callback(DNSService
if (purple_utf8_strcasecmp(serviceName, account->username) != 0) {
/* OK, lets go ahead and resolve it to add to the buddy list */
ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
- args->buddy = bonjour_buddy_new(serviceName, account);
- if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype,
+ purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n",
+ serviceName, interfaceIndex, regtype ? regtype : "",
+ replyDomain ? replyDomain : "");
+
+ if (kDNSServiceErr_NoError == DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype,
replyDomain, _mdns_service_resolve_callback, args)) {
- bonjour_buddy_delete(args->buddy);
- g_free(args);
- purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
- } else {
+ GSList *tmp = pending_buddies;
+ PurpleBuddy *pb;
+ BonjourBuddy* bb = NULL;
+ Win32SvcResolverData *rd;
+ Win32BuddyImplData *idata;
+ gint fd;
+
+ /* Is there an existing buddy? */
+ if ((pb = purple_find_buddy(account, serviceName)))
+ bb = pb->proto_data;
+ /* Is there a pending buddy? */
+ else {
+ while (tmp) {
+ BonjourBuddy *bb_tmp = tmp->data;
+ if (!strcmp(bb_tmp->name, serviceName)) {
+ bb = bb_tmp;
+ break;
+ }
+ tmp = tmp->next;
+ }
+ }
+
+ if (bb == NULL) {
+ bb = bonjour_buddy_new(serviceName, account);
+
+ /* This is only necessary for the wacky case where someone previously manually added a buddy. */
+ if (pb == NULL)
+ pending_buddies = g_slist_prepend(pending_buddies, bb);
+ else
+ pb->proto_data = bb;
+ }
+
+
+ rd = g_new0(Win32SvcResolverData, 1);
+ rd->if_idx = interfaceIndex;
+ rd->name = g_strdup(serviceName);
+ rd->type = g_strdup(regtype);
+ rd->domain = g_strdup(replyDomain);
+
+ idata = bb->mdns_impl_data;
+ idata->resolvers = g_slist_prepend(idata->resolvers, rd);
+
+ args->bb = bb;
+ args->res_data = rd;
+ args->account = account;
+
/* get a file descriptor for this service ref, and add it to the input list */
- gint fd = DNSServiceRefSockFD(args->resolver);
+ fd = DNSServiceRefSockFD(args->resolver);
args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
+ } else {
+ purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
+ g_free(args);
}
}
} else {
+ PurpleBuddy *pb = NULL;
+
/* A peer has sent a goodbye packet, remove them from the buddy list */
- purple_debug_info("bonjour", "service browser - remove notification\n");
+ purple_debug_info("bonjour", "Received remove notification for '%s' on iface %u (%s, %s)\n",
+ serviceName, interfaceIndex, regtype ? regtype : "",
+ replyDomain ? replyDomain : "");
+
pb = purple_find_buddy(account, serviceName);
if (pb != NULL) {
- purple_account_remove_buddy(account, pb, NULL);
- purple_blist_remove_buddy(pb);
- } else
+ GSList *l;
+ /* There may be multiple presences, we should only get rid of this one */
+ Win32SvcResolverData *rd_search;
+ BonjourBuddy *bb = pb->proto_data;
+ Win32BuddyImplData *idata;
+
+ g_return_if_fail(bb != NULL);
+
+ idata = bb->mdns_impl_data;
+
+ rd_search = g_new0(Win32SvcResolverData, 1);
+ rd_search->if_idx = interfaceIndex;
+ rd_search->name = (gchar *) serviceName;
+ rd_search->type = (gchar *) regtype;
+ rd_search->domain = (gchar *) replyDomain;
+
+ l = g_slist_find_custom(idata->resolvers, rd_search, _find_resolver_data);
+
+ g_free(rd_search);
+
+ if (l != NULL) {
+ Win32SvcResolverData *rd = l->data;
+ idata->resolvers = g_slist_delete_link(idata->resolvers, l);
+ /* This IP is no longer available */
+ if (rd->ip != NULL) {
+ bb->ips = g_slist_remove(bb->ips, rd->ip);
+ g_free((gchar *) rd->ip);
+ }
+ _cleanup_resolver_data(rd);
+
+ /* If this was the last resolver, remove the buddy */
+ if (idata->resolvers == NULL) {
+ purple_debug_info("bonjour", "Removed last presence for buddy '%s'; removing buddy.\n", serviceName);
+ purple_account_remove_buddy(account, pb, NULL);
+ purple_blist_remove_buddy(pb);
+ }
+ }
+ } else {
purple_debug_warning("bonjour", "Unable to find buddy (%s) to remove\n", serviceName ? serviceName : "(null)");
+ /* TODO: Should we look in the pending buddies list? */
+ }
}
}
@@ -385,9 +559,10 @@ void _mdns_delete_buddy(BonjourBuddy *bu
g_return_if_fail(idata != NULL);
- if (idata->txt_query != NULL) {
- purple_input_remove(idata->txt_query_handler);
- DNSServiceRefDeallocate(idata->txt_query);
+ while (idata->resolvers) {
+ Win32SvcResolverData *rd = idata->resolvers->data;
+ _cleanup_resolver_data(rd);
+ idata->resolvers = g_slist_delete_link(idata->resolvers, idata->resolvers);
}
if (idata->null_query != NULL) {
More information about the Commits
mailing list