File gnome-vfs2-bonjour-support.patch of Package gnome-vfs2

Index: configure.in
================================================================================
--- configure.in
+++ configure.in
@@ -705,17 +705,67 @@
 fi
 			  
 AC_ARG_ENABLE(howl, [  --disable-howl     build without howl support])
-msg_howl=no
+msg_zeroconf=no
 if test "x$msg_avahi" = "xno" -a "x$enable_howl" != "xno"; then
 	PKG_CHECK_MODULES(HOWL, howl >= 0.9.6,
 		  	[AM_CONDITIONAL(HAVE_HOWL, true)
 		   	AC_DEFINE(HAVE_HOWL, [], [Set if we can use howl])]
-                        msg_howl=yes,
+			msg_zeroconf=howl,
 	          	[AM_CONDITIONAL(HAVE_HOWL, false)])
 	AC_SUBST(HOWL_CFLAGS)
 	AC_SUBST(HOWL_LIBS)
 fi
 
+ dnl ****************************
+dnl Bonjour
+dnl ****************************
+
+AM_CONDITIONAL(HAVE_BONJOUR, false)
+AC_ARG_ENABLE(bonjour, [  --enable-bonjour     build with bonjour support])
+if test "x$enable_bonjour" = "xyes"; then
+       if test "x${HOWL_LIBS}" != "x"; then
+               AC_MSG_ERROR([*** Can't use both howl and bonjour at the same time. Please pick one only. ***])
+       else
+               CPPFLAGS_save="$CPPFLAGS"
+               LDFLAGS_save="$LDFLAGS"
+
+               AC_ARG_WITH(bonjour-includes, [  --with-bonjour-includes=PREFIX
+	           Location of Bonjour includes.],
+		   with_bonjour_includes="$withval",
+		   with_bonjour_includes="/usr/include")
+
+               CPPFLAGS="$CPPFLAGS -I$with_bonjour_includes"
+               AC_CHECK_HEADER(dns_sd.h, , AC_MSG_ERROR([Unable to find Bonjour includes]))
+               CPPFLAGS="$CPPFLAGS_save"
+
+               if test x$with_bonjour_includes != x/usr/include; then
+                       BONJOUR_CFLAGS="-I$with_bonjour_includes"
+               else
+                       BONJOUR_CFLAGS=""
+               fi
+
+               AC_ARG_WITH(bonjour-libs, [  --with-bonjour-libs=PREFIX
+					 Location of Bonjour libs.],
+                                         with_bonjour_libs="$withval", with_bonjour_libs="/usr/lib")
+
+               LDFLAGS="$LDFLAGS -L$with_bonjour_libs -ldns_sd"
+               AC_TRY_LINK_FUNC(DNSServiceResolve, , AC_MSG_ERROR([Unable to find Bonjour libs]))
+               LDFLAGS="$LDFLAGS_save"
+
+               if test x$with_bonjour_libs != x/usr/lib; then
+                       BONJOUR_LIBS="-L$with_bonjour_libs -ldns_sd"
+               else
+                       BONJOUR_LIBS="-ldns_sd"
+               fi
+
+               msg_zeroconf="bonjour"
+               AM_CONDITIONAL(HAVE_BONJOUR, true)
+               AC_DEFINE(HAVE_BONJOUR, 1, [Define to 1 if Bonjour is available and desired])
+               AC_SUBST(BONJOUR_CFLAGS)
+               AC_SUBST(BONJOUR_LIBS)
+       fi
+fi
+
 dnl ****************************
 dnl HAL stuff
 dnl ****************************
@@ -1019,8 +1069,8 @@
 dnl ==============================================================================
 
 PKG_CHECK_MODULES(LIBGNOMEVFS, glib-2.0 >= $GLIB_REQUIRED gmodule-no-export-2.0 >= $GLIB_REQUIRED gthread-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED gconf-2.0 >= $GCONF_REQUIRED libxml-2.0 >= $XML_REQUIRED gnome-mime-data-2.0 dbus-glib-1 >= 0.60)
-LIBGNOMEVFS_CFLAGS="$LIBGNOMEVFS_CFLAGS $OPENSSL_CFLAGS $LIBGNUTLS_CFLAGS $HOWL_CFLAGS $AVAHI_CFLAGS"
-LIBGNOMEVFS_LIBS="$LIBGNOMEVFS_LIBS $OPENSSL_LIBS $LIBGNUTLS_LIBS $HOWL_LIBS $AVAHI_LIBS $RESOLVER_LIBS"
+LIBGNOMEVFS_CFLAGS="$LIBGNOMEVFS_CFLAGS $OPENSSL_CFLAGS $LIBGNUTLS_CFLAGS $HOWL_CFLAGS $BONJOUR_CFLAGS $AVAHI_CFLAGS"
+LIBGNOMEVFS_LIBS="$LIBGNOMEVFS_LIBS $OPENSSL_LIBS $LIBGNUTLS_LIBS $HOWL_LIBS $BONJOUR_LIBS $AVAHI_LIBS $RESOLVER_LIBS"
 AC_SUBST(LIBGNOMEVFS_CFLAGS)
 AC_SUBST(LIBGNOMEVFS_LIBS)
 
@@ -1327,7 +1377,7 @@
 	IPv6 support:                $have_ipv6
 	SSL support:                 $msg_ssl
       	Avahi support:		     $msg_avahi
-      	Howl support:		     $msg_howl
+      	Zeroconf support:	     $msg_zeroconf
 	HAL  support:                $msg_hal
 	SELinux  support:            $msg_selinux
 	File ACL backends:           $acl_backends
--- libgnomevfs/gnome-vfs-dns-sd.c
+++ libgnomevfs/gnome-vfs-dns-sd.c
@@ -52,6 +52,11 @@
 #include <howl.h>
 #endif
 
+/* Only one of HAVE_HOWL and HAVE_BONJOUR can be defined */
+#ifdef HAVE_BONJOUR
+# include <dns_sd.h>
+#endif
+
 #define PATH_GCONF_GNOME_VFS_DNS_SD "/system/dns_sd"
 #define PATH_GCONF_GNOME_VFS_DNS_SD_EXTRA_DOMAINS "/system/dns_sd/extra_domains"
 
@@ -839,6 +844,40 @@
 
 #endif /* HAVE_HOWL */
 
+#ifdef HAVE_BONJOUR
+
+static gboolean
+bonjour_input (GIOChannel  *io_channel,
+	       GIOCondition cond,
+	       gpointer     callback_data)
+{
+	DNSServiceRef session;
+
+	session = callback_data;
+	DNSServiceProcessResult (session);
+
+	return TRUE;
+}
+
+static guint
+set_up_bonjour_session (DNSServiceRef session)
+{
+	int fd;
+	GIOChannel *channel;
+	guint watch_id;
+
+	fd = DNSServiceRefSockFD (session);
+
+	channel = g_io_channel_unix_new (fd);
+	watch_id = g_io_add_watch (channel,
+				   G_IO_IN,
+				   bonjour_input, session);
+	g_io_channel_unref (channel);
+	return watch_id;
+}
+
+#endif /* HAVE_BONJOUR */
+
 struct GnomeVFSDNSSDBrowseHandle {
 	char *domain;
 	char *type;
@@ -858,6 +897,12 @@
 #ifdef HAVE_HOWL
 	sw_discovery_oid howl_id;
 #endif
+
+#ifdef HAVE_BONJOUR
+	gboolean is_bonjour;
+	DNSServiceRef bonjour_session;
+	guint bonjour_input_id;
+#endif
 	
 	/* unicast data: */
 	int n_services;
@@ -885,6 +930,14 @@
 
 	if (handle->callback_data_destroy_func != NULL)
 		handle->callback_data_destroy_func (handle->callback_data);
+
+#ifdef HAVE_BONJOUR
+	if (handle->is_bonjour) {
+		if (handle->bonjour_input_id != 0)
+			g_source_remove (handle->bonjour_input_id);
+		DNSServiceRefDeallocate (handle->bonjour_session);
+	}
+#endif
 	
 	g_free (handle);
 }
@@ -966,9 +1019,9 @@
 }
 #endif
 
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 
-struct howl_browse_idle_data {
+struct zc_browse_idle_data {
 	GnomeVFSDNSSDBrowseHandle *handle;
 	GnomeVFSDNSSDServiceStatus status;
 	GnomeVFSDNSSDService service;
@@ -977,9 +1030,9 @@
 
 
 static gboolean
-howl_browse_idle (gpointer data)
+zc_browse_idle (gpointer data)
 {
-	struct howl_browse_idle_data *idle_data;
+	struct zc_browse_idle_data *idle_data;
 	GnomeVFSDNSSDBrowseHandle *handle;
 
 	idle_data = data;
@@ -997,7 +1050,7 @@
 }
 
 static void
-browse_idle_data_free (struct howl_browse_idle_data *idle_data)
+browse_idle_data_free (struct zc_browse_idle_data *idle_data)
 {
 	g_free (idle_data->service.name);
 	g_free (idle_data->service.type);
@@ -1012,6 +1065,10 @@
 	return FALSE;
 }
 
+#endif
+
+#ifdef HAVE_HOWL
+
 static sw_result
 howl_browse_reply (sw_discovery                 discovery,
 		   sw_discovery_oid             oid,
@@ -1023,7 +1080,7 @@
 		   sw_opaque                    extra)
 {
 	GnomeVFSDNSSDBrowseHandle *handle;
-	struct howl_browse_idle_data *idle_data;
+	struct zc_browse_idle_data *idle_data;
 	int len;
 
 	handle = extra;
@@ -1038,7 +1095,7 @@
 	if (handle->cancelled)
 		return SW_OKAY;
 
-	idle_data = g_new (struct howl_browse_idle_data, 1);
+	idle_data = g_new (struct zc_browse_idle_data, 1);
 	idle_data->handle = handle;
 	
 	if (status == SW_DISCOVERY_BROWSE_ADD_SERVICE) {
@@ -1064,7 +1121,7 @@
 		idle_data->service.domain[len-1] = 0;
 	
 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
-			 howl_browse_idle,
+			 zc_browse_idle,
 			 idle_data,
 			 (GDestroyNotify) browse_idle_data_free);
 	return SW_OKAY;
@@ -1072,6 +1129,63 @@
 
 #endif /* HAVE_HOWL */
 
+#ifdef HAVE_BONJOUR
+
+static void
+bonjour_browse_reply (DNSServiceRef        session,
+		      DNSServiceFlags      flags,
+		      uint32_t             interface_index,
+		      DNSServiceErrorType  error_code,
+		      const char          *name,
+		      const char          *type,
+		      const char          *domain,
+		      void                *context)
+{
+	GnomeVFSDNSSDBrowseHandle *handle;
+	struct zc_browse_idle_data *idle_data;
+	int len;
+
+	handle = context;
+	
+	if (error_code != kDNSServiceErr_NoError) {
+		/* free in an idle to make sure the other idles are done,
+		   and to give sane environment for destroy callback */
+		g_idle_add (free_browse_handle_idle, handle);
+		return;
+	}
+	
+	if (handle->cancelled)
+		return;
+
+	idle_data = g_new (struct zc_browse_idle_data, 1);
+	idle_data->handle = handle;
+	
+	if (flags & kDNSServiceFlagsAdd) {
+		idle_data->status = GNOME_VFS_DNS_SD_SERVICE_ADDED;
+	} else {
+		idle_data->status = GNOME_VFS_DNS_SD_SERVICE_REMOVED;
+	}
+
+	idle_data->service.name = g_strdup (name);
+	idle_data->service.type = g_strdup (type);
+	idle_data->service.domain = g_strdup (domain);
+
+	/* We don't want last dots in the domain or type */
+	len = strlen (idle_data->service.type);
+	if (len > 0 && idle_data->service.type[len-1] == '.')
+		idle_data->service.type[len-1] = 0;
+	len = strlen (idle_data->service.domain);
+	if (len > 0 && idle_data->service.domain[len-1] == '.')
+		idle_data->service.domain[len-1] = 0;
+	
+	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+			 zc_browse_idle,
+			 idle_data,
+			 (GDestroyNotify) browse_idle_data_free);
+}
+
+#endif /* HAVE_BONJOUR */
+
 /**
  * gnome_vfs_dns_sd_browse:
  * @handle: pointer to a pointer to a #GnomeVFSDNSSDBrowseHandle object.
@@ -1154,8 +1268,32 @@
 		}
 		return GNOME_VFS_ERROR_GENERIC;
 #else
+# ifdef HAVE_BONJOUR
+		DNSServiceErrorType res;
+
+		handle->is_local = TRUE;
+		handle->is_bonjour = TRUE;
+
+		res = DNSServiceBrowse (&handle->bonjour_session,
+					0 /* flags (none needed) */,
+					0 /* interface (-1 for local, 0 for all) */, 
+					type,
+					domain,
+					bonjour_browse_reply,
+					handle);
+
+		if (res != kDNSServiceErr_NoError) {
+			return GNOME_VFS_ERROR_GENERIC;
+		}
+
+		handle->bonjour_input_id = set_up_bonjour_session (handle->bonjour_session);
+
+		*handle_out = handle;
+		return GNOME_VFS_OK;
+# else
 		free_browse_handle (handle);
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
+# endif
 #endif
 	} else {
 		handle->is_local = FALSE;
@@ -1192,6 +1330,12 @@
 		handle->cancelled = TRUE;
 		sw_discovery_cancel (get_global_howl_session (), handle->howl_id);
 #endif
+#ifdef HAVE_BONJOUR
+		handle->cancelled = TRUE;
+		g_source_remove (handle->bonjour_input_id);
+		handle->bonjour_input_id = 0;
+		g_idle_add (free_browse_handle_idle, handle);
+#endif
 		return GNOME_VFS_OK;
 	} else {
 		if (handle->finished)
@@ -1226,6 +1370,12 @@
 	sw_discovery_oid howl_id;
 	guint timeout_tag;
 #endif
+#ifdef HAVE_BONJOUR
+	gboolean is_bonjour;
+	DNSServiceRef bonjour_session;
+	guint bonjour_input_id;
+	guint timeout_tag;
+#endif
 	
 	/* unicast data: */
 	gboolean cancelled;
@@ -1246,7 +1396,15 @@
 
 	if (handle->callback_data_destroy_func != NULL)
 		handle->callback_data_destroy_func (handle->callback_data);
-	
+
+#ifdef HAVE_BONJOUR
+	if (handle->is_bonjour) {
+		if (handle->bonjour_input_id != 0)
+			g_source_remove (handle->bonjour_input_id);
+		DNSServiceRefDeallocate (handle->bonjour_session);
+	}
+#endif
+
 	g_free (handle);
 }
 
@@ -1368,10 +1526,10 @@
 
 #endif
 
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 
-#ifdef HAVE_HOWL
 static gboolean
-howl_resolve_idle (gpointer data)
+zc_resolve_idle (gpointer data)
 {
 	GnomeVFSDNSSDResolveHandle *handle;
 	GnomeVFSDNSSDService service;
@@ -1405,7 +1563,9 @@
 	return FALSE;
 }
 
+#endif
 
+#ifdef HAVE_HOWL
 
 static sw_result
 howl_resolve_reply (sw_discovery                   discovery,
@@ -1438,7 +1598,7 @@
 	g_source_remove (handle->timeout_tag);
 	
 	handle->idle_tag = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
-					    howl_resolve_idle,
+					    zc_resolve_idle,
 					    handle,
 					    (GDestroyNotify) NULL);
 	return SW_OKAY;
@@ -1476,6 +1636,98 @@
 
 #endif
 
+#ifdef HAVE_BONJOUR
+
+static void
+bonjour_resolve_reply (DNSServiceRef session,
+		       DNSServiceFlags flags,
+		       uint32_t interface_index,
+		       DNSServiceErrorType error_code,
+		       const char *full_name,
+		       const char *host_name,
+		       uint16_t port,
+		       uint16_t text_record_len,
+		       const char *text_record,
+		       void *context)
+{
+	GnomeVFSDNSSDResolveHandle *handle;
+	gint host_len, domain_len;
+
+	handle = context;
+
+	g_assert (handle->idle_tag == 0);
+
+	handle->host = g_strdup (host_name);
+	handle->port = g_ntohs (port);
+	handle->text = g_memdup (text_record, text_record_len);
+	handle->text_len = text_record_len;
+
+	domain_len = strlen (handle->domain);
+	host_len = strlen (handle->host);
+
+	/* The hostname includes the .local domain and a trailing dot. Strip it. */
+
+	/* hostname.[domain.] */
+	if (host_len > domain_len && !strcmp (handle->host + host_len - domain_len, handle->domain) &&
+	    handle->host [host_len - domain_len - 1] == '.') {
+		handle->host [host_len - domain_len - 1] = 0;
+		host_len -= domain_len - 1;
+	} else if (host_len > 0 && handle->host [host_len - 1] == '.') {
+		handle->host [host_len - 1] = 0;
+		host_len--;
+
+		/* hostname.[domain] */
+		if (host_len > domain_len && !strcmp (handle->host + host_len - domain_len, handle->domain) &&
+		    handle->host [host_len - domain_len - 1] == '.') {
+			handle->host [host_len - domain_len - 1] = 0;
+			host_len -= domain_len - 1;
+		}
+	}
+
+	/* We want no more replies */
+	if (handle->is_bonjour) {
+		if (handle->bonjour_input_id != 0) {
+			g_source_remove (handle->bonjour_input_id);
+			handle->bonjour_input_id = 0;
+		}
+		DNSServiceRefDeallocate (handle->bonjour_session);
+		handle->is_bonjour = FALSE;
+	}
+
+	g_source_remove (handle->timeout_tag);
+	
+	handle->idle_tag = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+					    zc_resolve_idle,
+					    handle,
+					    (GDestroyNotify) NULL);
+}
+
+static gboolean
+bonjour_resolve_timeout (gpointer data)
+{
+	GnomeVFSDNSSDResolveHandle *handle;
+	
+	handle = data;
+
+	handle->callback (handle,
+			  GNOME_VFS_ERROR_HOST_NOT_FOUND,
+			  NULL,
+			  NULL, 0,
+			  NULL, 0, NULL,
+			  handle->callback_data);
+
+	if (handle->idle_tag) {
+		/* We already resolved, but the idle hasn't run yet */
+		g_source_remove (handle->idle_tag);
+	}
+	
+	free_resolve_handle (handle);
+	
+	return FALSE;
+}
+
+#endif /* HAVE_BONJOUR */
+
 /**
  * gnome_vfs_dns_sd_resolve:
  * @handle: pointer to a pointer to a #GnomeVFSDNSSDResolveHandle object.
@@ -1569,7 +1821,38 @@
 		}
 		return GNOME_VFS_ERROR_GENERIC;
 #else
+# ifdef HAVE_BONJOUR
+		DNSServiceErrorType res;
+
+		handle->is_local = TRUE;
+		handle->is_bonjour = TRUE;
+
+		res = DNSServiceResolve (&handle->bonjour_session,
+					 0 /* flags (none needed) */,
+					 0 /* interface (-1 for local, 0 for all) */,
+					 name,
+					 type,
+					 domain,
+					 bonjour_resolve_reply,
+					 handle);
+
+		if (res != kDNSServiceErr_NoError) {
+			return GNOME_VFS_ERROR_GENERIC;
+		}
+
+		handle->bonjour_input_id = set_up_bonjour_session (handle->bonjour_session);
+
+		if (timeout != 0) {
+			handle->timeout_tag = g_timeout_add (timeout,
+							     bonjour_resolve_timeout,
+							     handle);
+		}
+
+		*handle_out = handle;
+		return GNOME_VFS_OK;
+# else
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
+# endif
 #endif
 	} else {
 		handle->is_local = FALSE;
@@ -1614,7 +1897,14 @@
 					     handle->howl_id);
 		}
 		free_resolve_handle (handle);
-		
+#endif
+#ifdef HAVE_BONJOUR
+		g_source_remove (handle->timeout_tag);
+		if (handle->idle_tag) {
+			/* We already resolved, but the idle hasn't run yet */
+			g_source_remove (handle->idle_tag);
+		}
+		free_resolve_handle (handle);
 #endif
 		return GNOME_VFS_OK;
 	} else {
@@ -1623,7 +1913,7 @@
 	}
 }
 
-#if defined(HAVE_AVAHI) || defined(HAVE_HOWL)
+#if defined(HAVE_AVAHI) || defined(HAVE_HOWL)  || defined (HAVE_BONJOUR)
 static int
 find_existing_service (GArray *array,
 		       const char *name,
@@ -1794,6 +2084,115 @@
 }
 #endif /* HAVE_HOWL */
 
+#ifdef HAVE_BONJOUR
+
+static void
+bonjour_run_sync (DNSServiceRef session, gint timeout_msec, gboolean *stop_processing_flag)
+{
+       struct timeval duration_tv, end_tv, tv;
+
+       gettimeofday (&end_tv, NULL);
+       tv = end_tv;
+
+       end_tv.tv_sec += timeout_msec / 1000;
+       end_tv.tv_usec += (timeout_msec % 1000) * 1000;
+       end_tv.tv_sec += end_tv.tv_usec / 1000000;
+       end_tv.tv_usec %= 1000000;
+
+       while ((end_tv.tv_sec > tv.tv_sec ||
+              (end_tv.tv_sec == tv.tv_sec && end_tv.tv_usec > tv.tv_usec)) &&
+              !(stop_processing_flag && *stop_processing_flag)) {
+               gint n_ready;
+               fd_set read_fds;
+               gint fd;
+
+
+               duration_tv.tv_sec = end_tv.tv_sec - tv.tv_sec;
+               if (tv.tv_usec <= end_tv.tv_usec) {
+                       duration_tv.tv_usec = end_tv.tv_usec - tv.tv_usec;
+	       } else {
+                       duration_tv.tv_sec--;
+                       duration_tv.tv_usec = end_tv.tv_usec + 1000000 - tv.tv_usec;
+	       }
+
+               fd = DNSServiceRefSockFD (session);
+               if (fd < 0)
+                       break;
+
+               FD_ZERO (&read_fds);
+               FD_SET (fd, &read_fds);
+
+               n_ready = select (fd + 1, &read_fds, NULL, NULL, &duration_tv);
+               gettimeofday (&tv, NULL);
+
+               if (n_ready < 1)
+                       continue;
+
+               DNSServiceProcessResult (session);
+       }
+}
+
+static void
+bonjour_browse_reply_sync (DNSServiceRef        session,
+                          DNSServiceFlags      flags,
+                          uint32_t             interface_index,
+                          DNSServiceErrorType  error_code,
+                          const char          *name,
+                          const char          *type,
+                          const char          *domain,
+                          void                *context)
+{
+       GnomeVFSDNSSDService service, *existing;
+       GArray *array;
+       int i, len;
+       gboolean free_service;
+
+       array = context;
+       if (error_code != kDNSServiceErr_NoError)
+               return;
+
+       free_service = TRUE;
+       service.name = g_strdup (name);
+       service.type = g_strdup (type);
+       service.domain = g_strdup (domain);
+
+       /* We don't want last dots in the domain or type */
+       len = strlen (service.type);
+       if (len > 0 && service.type[len-1] == '.')
+               service.type[len-1] = 0;
+       len = strlen (service.domain);
+       if (len > 0 && service.domain[len-1] == '.')
+               service.domain[len-1] = 0;
+
+       if (flags & kDNSServiceFlagsAdd) {
+               if (find_existing_service (array, service.name, service.type,
+                                          service.domain) == -1) {
+                       free_service = FALSE;
+                       g_array_append_val (array, service);
+	       }
+       } else {
+               i = find_existing_service (array, service.name, service.type,
+                                          service.domain);
+               if (i != -1) {
+                       existing = &g_array_index (array, GnomeVFSDNSSDService,
+						  i);
+                       g_free (existing->name);
+                       g_free (existing->type);
+                       g_free (existing->domain);
+                       g_array_remove_index (array, i);
+	       }
+       }
+
+       if (free_service) {
+               g_free (service.name);
+               g_free (service.type);
+               g_free (service.domain);
+       }
+}
+
+#endif /* HAVE_BONJOUR */
+
+
 /**
  * gnome_vfs_dns_sd_browse_sync:
  * @domain: The dns domain to browse, or "local" for multicast DNS.
@@ -1946,7 +2345,32 @@
 		
 		return GNOME_VFS_OK;
 #else
+# ifdef HAVE_BONJOUR
+               DNSServiceRef session;
+               DNSServiceErrorType error_code;
+               GArray *array;
+
+               array = g_array_new (FALSE, FALSE, sizeof (GnomeVFSDNSSDService));
+               error_code = DNSServiceBrowse (&session,
+                                              0 /* flags (none needed) */,
+                                              0 /* interface (-1 for local, 0 for all) */,
+                                              type,
+                                              domain,
+                                              bonjour_browse_reply_sync,
+                                              array);
+               if (error_code != kDNSServiceErr_NoError) {
+                       g_warning ("gnome_vfs_dns_sd_browse_sync - bonjour browse failed\n");
+                       g_array_free (array, TRUE);
+                       return GNOME_VFS_ERROR_GENERIC;
+	       }
+
+               bonjour_run_sync (session, timeout_msec, NULL);
+
+               DNSServiceRefDeallocate (session);
+               return GNOME_VFS_OK;
+# else
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
+# endif
 #endif
 	} else {
 		return unicast_browse_sync (domain, type,
@@ -2012,14 +2436,18 @@
 
 #endif
 
-#ifdef HAVE_HOWL
+#if defined HAVE_HOWL  || defined (HAVE_BONJOUR)
 struct sync_resolve_data {
 	gboolean got_data;
+	const char *domain;
 	char *host;
 	int port;
 	char *text;
 	int text_len;
 };
+#endif
+
+#ifdef HAVE_HOWL
 
 static sw_result
 howl_resolve_reply_sync (sw_discovery                   discovery,
@@ -2048,6 +2476,55 @@
 }
 #endif
 
+#ifdef HAVE_BONJOUR
+
+static void
+bonjour_resolve_reply_sync (DNSServiceRef session,
+                           DNSServiceFlags flags,
+                           uint32_t interface_index,
+                           DNSServiceErrorType error_code,
+                           const char *full_name,
+                           const char *host_name,
+                           uint16_t port,
+                           uint16_t text_record_len,
+                           const char *text_record,
+                           void *context)
+{
+	struct sync_resolve_data *data;
+       gint host_len, domain_len;
+
+       data = context;
+       data->got_data = TRUE;
+
+       data->host = g_strdup (host_name);
+       data->port = g_ntohs (port);
+       data->text = g_memdup (text_record, text_record_len);
+       data->text_len = text_record_len;
+
+       domain_len = strlen (data->domain);
+       host_len = strlen (data->host);
+
+       /* The hostname includes the .local domain and a trailing dot. Strip it. */
+       /* hostname.[domain.] */
+       if (host_len > domain_len && !strcmp (data->host + host_len - domain_len, data->domain) &&
+           data->host [host_len - domain_len - 1] == '.') {
+               data->host [host_len - domain_len - 1] = 0;
+               host_len -= domain_len - 1;
+ } else if (host_len > 0 && data->host [host_len - 1] == '.') {
+               data->host [host_len - 1] = 0;
+               host_len--;
+
+               /* hostname.[domain] */
+               if (host_len > domain_len && !strcmp (data->host + host_len - domain_len, data->domain) &&
+                   data->host [host_len - domain_len - 1] == '.') {
+                       data->host [host_len - domain_len - 1] = 0;
+                       host_len -= domain_len - 1;
+	       }
+ }
+}
+
+#endif /* HAVE_BONJOUR */
+
 /**
  * gnome_vfs_dns_sd_resolve_sync:
  * @name: name of the service to resolve in UTF-8 encoding.
@@ -2211,7 +2688,49 @@
 		
 		return GNOME_VFS_ERROR_HOST_NOT_FOUND;
 #else
+# ifdef HAVE_BONJOUR
+               DNSServiceRef session;
+               DNSServiceErrorType error_code;
+               struct sync_resolve_data resolve_data = {0};
+
+               resolve_data.domain = domain;
+
+               error_code = DNSServiceResolve (&session,
+                                               0 /* flags (none needed) */,
+                                               0 /* interface (-1 for local, 0
+						    for all) */,
+                                               name,
+                                               type,
+                                               domain,
+                                               bonjour_resolve_reply_sync,
+                                               &resolve_data);
+               if (error_code != kDNSServiceErr_NoError) {
+                       g_warning ("gnome_vfs_dns_sd_resolve_sync - bonjour resolve failed\n");
+                       return GNOME_VFS_ERROR_GENERIC;
+	       }
+
+               bonjour_run_sync (session, timeout_msec, &resolve_data.got_data);
+
+               DNSServiceRefDeallocate (session);
+
+               if (resolve_data.got_data) {
+                       *host = resolve_data.host;
+                       *port = resolve_data.port;
+                       if (text != NULL)
+                               *text = decode_txt_record (resolve_data.text, resolve_data.text_len);
+                       if (text_raw_len_out != NULL && text_raw_out) {
+                               *text_raw_len_out = resolve_data.text_len;
+                               *text_raw_out = resolve_data.text;
+		       } else {
+                               g_free (resolve_data.text);
+		       }
+                       return GNOME_VFS_OK;
+	       }
+
+               return GNOME_VFS_ERROR_HOST_NOT_FOUND;
+# else
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
+# endif
 #endif
 	} else {
 		res = unicast_resolve_sync (name, type, domain,
--- modules/Makefile.am
+++ modules/Makefile.am
@@ -7,6 +7,7 @@
 	$(MODULES_XML_GCONF_CFLAGS)		\
 	$(MODULES_FILE_CFLAGS) 			\
 	$(HOWL_CFLAGS)				\
+	$(BONJOUR_CFLAGS)			\
 	$(AVAHI_CFLAGS)				\
 	$(LIBEFS_CFLAGS)			\
 	$(SAMBA_CFLAGS)				\
@@ -115,7 +116,7 @@
 
 libdns_sd_la_SOURCES = dns-sd-method.c
 libdns_sd_la_LDFLAGS = $(module_flags)
-libdns_sd_la_LIBADD = $(MODULES_LIBS) $(HOWL_LIBS) $(AVAHI_LIBS) ../libgnomevfs/libgnomevfs-2.la
+libdns_sd_la_LIBADD = $(MODULES_LIBS) $(HOWL_LIBS) $(BONJOUR_LIBS) $(AVAHI_LIBS) ../libgnomevfs/libgnomevfs-2.la
 
 ###  `file' method
 
--- modules/dns-sd-method.c
+++ modules/dns-sd-method.c
@@ -45,6 +45,11 @@
 #include <howl.h>
 #endif
 
+/* Only one of HAVE_HOWL and HAVE_BONJOUR can be defined */
+#ifdef HAVE_BONJOUR
+# include <dns_sd.h>
+#endif
+
 #include <libgnomevfs/gnome-vfs-ops.h>
 #include <libgnomevfs/gnome-vfs-directory.h>
 #include <libgnomevfs/gnome-vfs-module-shared.h>
@@ -71,12 +76,12 @@
 	{"_sftp-ssh._tcp", "sftp", "gnome-fs-ssh"},
 };
 
-#if defined (HAVE_HOWL) || defined (HAVE_AVAHI)
+#if defined (HAVE_HOWL) || defined (HAVE_AVAHI) || defined (HAVE_BONJOUR)
 G_LOCK_DEFINE_STATIC (local);
 static gboolean started_local = FALSE;
 static GList *local_files = NULL;
 static GList *local_monitors = NULL;
-#endif /* HAVE_HOWL || HAVE_AVAHI */
+#endif /* HAVE_HOWL || HAVE_AVAHI || HAVE_BONJOUR*/
 
 typedef struct {
 	char *data;
@@ -226,7 +231,7 @@
 	return g_string_free (string, FALSE);
 }
 
-#if defined (HAVE_HOWL) || defined (HAVE_AVAHI)
+#if defined (HAVE_HOWL) || defined (HAVE_AVAHI) || defined (HAVE_BONJOUR)
 static void
 call_monitors (gboolean add, char *filename)
 {
@@ -312,7 +317,7 @@
 	
 	G_UNLOCK (local);
 }
-#endif /* HAVE_HOWL || HAVE_AVAHI */
+#endif /* HAVE_HOWL || HAVE_AVAHI || HAVE_BONJOUR*/
 
 
 #ifdef HAVE_AVAHI
@@ -535,10 +540,130 @@
 		sw_discovery_fina (session);
 	}
 }
+
 #endif /* HAVE_HOWL */
 
+#ifdef HAVE_BONJOUR
+
+static void
+local_browse_callback_sync (DNSServiceRef        session,
+			    DNSServiceFlags      flags,
+			    uint32_t             interface_index,
+			    DNSServiceErrorType  error_code,
+			    const char          *name,
+			    const char          *type,
+			    const char          *domain,
+			    void                *context)
+{
+	if (flags & kDNSServiceFlagsAdd)
+		local_browse (TRUE, name, type, domain);
+	else
+		local_browse (FALSE, name, type, domain);
+}
+
+static void
+bonjour_run_multi_sync (DNSServiceRef *session_handles, gint n_sessions, gint timeout_msec)
+{
+	struct timeval duration_tv, end_tv, tv;
+
+	gettimeofday (&end_tv, NULL);
+	tv = end_tv;
+
+	end_tv.tv_sec += timeout_msec / 1000;
+	end_tv.tv_usec += (timeout_msec % 1000) * 1000;
+	end_tv.tv_sec += end_tv.tv_usec / 1000000;
+	end_tv.tv_usec %= 1000000;
+
+	while (end_tv.tv_sec > tv.tv_sec ||
+	       (end_tv.tv_sec == tv.tv_sec && end_tv.tv_usec > tv.tv_usec)) {
+		gint n_ready;
+		fd_set read_fds;
+		gint high_fd = 0;
+		gint i;
+
+		duration_tv.tv_sec = end_tv.tv_sec - tv.tv_sec;
+		if (tv.tv_usec <= end_tv.tv_usec) {
+			duration_tv.tv_usec = end_tv.tv_usec - tv.tv_usec;
+		} else {
+			duration_tv.tv_sec--;
+			duration_tv.tv_usec = end_tv.tv_usec + 1000000 - tv.tv_usec;
+		}
+
+		FD_ZERO (&read_fds);
+
+		for (i = 0; i < n_sessions; i++) {
+			gint fd;
+
+			fd = DNSServiceRefSockFD (session_handles [i]);
+			if (fd < 0)
+				continue;
+
+			FD_SET (fd, &read_fds);
+			high_fd = MAX (fd, high_fd);
+		}
+
+		n_ready = select (high_fd + 1, &read_fds, NULL, NULL, &duration_tv);
+		gettimeofday (&tv, NULL);
+
+		if (n_ready < 1)
+			continue;
 
+		for (i = 0; i < n_sessions; i++) {
+			gint fd;
 
+			fd = DNSServiceRefSockFD (session_handles [i]);
+
+			if (FD_ISSET (fd, &read_fds))
+				DNSServiceProcessResult (session_handles [i]);
+		}
+	}
+}
+
+static void
+init_local (void)
+{
+	int i;
+	GnomeVFSResult res;
+	
+	if (!started_local) {
+		DNSServiceRef *session_handles;
+
+		started_local = TRUE;
+		
+		for (i = 0; i < G_N_ELEMENTS (dns_sd_types); i++) {
+			GnomeVFSDNSSDBrowseHandle *handle;
+			res = gnome_vfs_dns_sd_browse (&handle,
+						       "local",
+						       dns_sd_types [i].type,
+						       local_browse_callback,
+						       NULL, NULL);
+			if (res == GNOME_VFS_OK) {
+				dns_sd_types [i].handle = handle;
+			}
+		}
+
+		session_handles = g_new0 (DNSServiceRef, G_N_ELEMENTS (dns_sd_types));
+
+		for (i = 0; i < G_N_ELEMENTS (dns_sd_types); i++) {
+			DNSServiceBrowse (&session_handles [i],
+					  0 /* flags (none needed) */,
+					  0 /* interface (-1 for local, 0 for all) */,
+					  dns_sd_types [i].type,
+					  "local",
+					  local_browse_callback_sync,
+					  NULL);
+		}
+
+		bonjour_run_multi_sync (session_handles, G_N_ELEMENTS (dns_sd_types),
+					LOCAL_SYNC_BROWSE_DELAY_MSEC);
+
+		for (i = 0; i < G_N_ELEMENTS (dns_sd_types); i++) {
+			DNSServiceRefDeallocate (session_handles [i]);
+		}
+	}
+}
+
+#endif
 
 static GnomeVFSResult
 do_open (GnomeVFSMethod *method,
@@ -827,7 +952,7 @@
 	}
 }
 
-#if defined (HAVE_HOWL) || defined (HAVE_AVAHI)
+#if defined (HAVE_HOWL) || defined (HAVE_AVAHI) || defined (HAVE_BONJOUR)
 static void
 directory_handle_add_filenames (DirectoryHandle *dir_handle, GList *files)
 {
@@ -836,7 +961,7 @@
 		files = files->next;
 	}
 } 
-#endif /* HAVE_HOWL || HAVE_AVAHI */
+#endif /* HAVE_HOWL || HAVE_AVAHI || HAVE_BONJOUR*/
 
 static GnomeVFSResult
 do_open_directory (GnomeVFSMethod *method,
@@ -866,14 +991,14 @@
 	dir_handle = directory_handle_new (options);
 	
 	if (strcmp (domain, "local") == 0) {
-#if defined (HAVE_HOWL) || defined (HAVE_AVAHI)
+#if defined (HAVE_HOWL) || defined (HAVE_AVAHI) || defined (HAVE_BONJOUR)
 		G_LOCK (local);
 		init_local ();
 
 		directory_handle_add_filenames (dir_handle, local_files);
 		
 		G_UNLOCK (local);
-#endif /* HAVE_HOWL || HAVE_AVAHI */
+#endif /* HAVE_HOWL || HAVE_AVAHI || HAVE_BONJOUR */
 	} else 	{
 		for (i=0; i < G_N_ELEMENTS (dns_sd_types); i++) {
 			int n_services;
@@ -1140,7 +1265,7 @@
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 	}
 	
-#if defined (HAVE_HOWL) || defined (HAVE_AVAHI)
+#if defined (HAVE_HOWL) || defined (HAVE_AVAHI) || defined (HAVE_BONJOUR)
 	if (strcmp (uri->text, "") == 0 ||
 	    strcmp (uri->text, "/") == 0) {
 		int *handle;
@@ -1158,7 +1283,7 @@
 
 		return GNOME_VFS_OK;
 	} else 
-#endif /* HAVE_HOWL || HAVE_AVAHI */
+#endif /* HAVE_HOWL || HAVE_AVAHI || HAVE_BONJOUR */
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 }
 
@@ -1166,7 +1291,7 @@
 do_monitor_cancel (GnomeVFSMethod *method,
 		   GnomeVFSMethodHandle *method_handle)
 {
-#if defined (HAVE_HOWL) || defined (HAVE_AVAHI)
+#if defined (HAVE_HOWL) || defined (HAVE_AVAHI) || defined (HAVE_BONJOUR)
 	G_LOCK (local);
 	
 	local_monitors = g_list_remove (local_monitors, method_handle);
@@ -1177,7 +1302,7 @@
 	return GNOME_VFS_OK;
 #else
 	return GNOME_VFS_ERROR_NOT_SUPPORTED;
-#endif /* HAVE_HOWL || HAVE_AVAHI */
+#endif /* HAVE_HOWL || HAVE_AVAHI || HAVE_BONJOUR */
 }