Index: configure.in
===================================================================
RCS file: /cvs/gnome/gnome-vfs/configure.in,v
retrieving revision 1.384
diff -u -p -r1.384 configure.in
--- configure.in	25 Jul 2005 21:20:20 -0000	1.384
+++ configure.in	28 Jul 2005 00:33:57 -0000
@@ -665,20 +665,68 @@ fi
 dnl ****************************
 dnl Howl
 dnl ****************************
-msg_howl=no
+msg_zeroconf=no
 AM_CONDITIONAL(HAVE_HOWL, false)
 AC_ARG_ENABLE(howl, [  --disable-howl     build without howl support])
 if test "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 ****************************
 msg_hal=no
@@ -980,8 +1028,8 @@ dnl End of IPv6 checks
 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 bonobo-activation-2.0 >= $BONOBO_ACTIVATION_REQUIRED libbonobo-2.0 >= $BONOBO_REQUIRED ORBit-2.0 >= $ORBIT_REQUIRED libxml-2.0 >= $XML_REQUIRED gnome-mime-data-2.0)
-LIBGNOMEVFS_CFLAGS="$LIBGNOMEVFS_CFLAGS $OPENSSL_CFLAGS $LIBGNUTLS_CFLAGS $HOWL_CFLAGS"
-LIBGNOMEVFS_LIBS="$LIBGNOMEVFS_LIBS $OPENSSL_LIBS $LIBGNUTLS_LIBS $HOWL_LIBS $RESOLVER_LIBS"
+LIBGNOMEVFS_CFLAGS="$LIBGNOMEVFS_CFLAGS $OPENSSL_CFLAGS $LIBGNUTLS_CFLAGS $HOWL_CFLAGS $BONJOUR_CFLAGS"
+LIBGNOMEVFS_LIBS="$LIBGNOMEVFS_LIBS $OPENSSL_LIBS $LIBGNUTLS_LIBS $HOWL_LIBS $BONJOUR_LIBS $RESOLVER_LIBS"
 AC_SUBST(LIBGNOMEVFS_CFLAGS)
 AC_SUBST(LIBGNOMEVFS_LIBS)
 
@@ -1145,7 +1193,7 @@ echo 
 echo "Gnome VFS configuration summary:"
 echo "
 	IPv6 support:                $have_ipv6
-      	Howl support:		     $msg_howl
+      	Zeroconf support:	     $msg_zeroconf
 	HAL  support:                $msg_hal
 	Gtk Doc:                     $enable_gtk_doc
 	FS monitor backends:         $fs_monitor_backends
Index: modules/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-vfs/modules/Makefile.am,v
retrieving revision 1.112
diff -u -p -r1.112 Makefile.am
--- modules/Makefile.am	14 Apr 2005 18:48:29 -0000	1.112
+++ modules/Makefile.am	28 Jul 2005 00:33:57 -0000
@@ -7,6 +7,7 @@ INCLUDES =					\
 	$(MODULES_XML_GCONF_CFLAGS)		\
 	$(MODULES_FILE_CFLAGS) 			\
 	$(HOWL_CFLAGS)				\
+	$(BONJOUR_CFLAGS)			\
 	$(LIBEFS_CFLAGS)			\
 	$(SAMBA_CFLAGS)				\
 	$(GSSAPI_CFLAGS)			\
@@ -103,7 +104,7 @@ libcomputer_la_LIBADD = $(MODULES_FILE_L
 
 libdns_sd_la_SOURCES = dns-sd-method.c
 libdns_sd_la_LDFLAGS = $(module_flags)
-libdns_sd_la_LIBADD = $(MODULES_LIBS) $(HOWL_LIBS) ../libgnomevfs/libgnomevfs-2.la
+libdns_sd_la_LIBADD = $(MODULES_LIBS) $(HOWL_LIBS) $(BONJOUR_LIBS) ../libgnomevfs/libgnomevfs-2.la
 
 ###  `file' method
 
Index: modules/dns-sd-method.c
===================================================================
RCS file: /cvs/gnome/gnome-vfs/modules/dns-sd-method.c,v
retrieving revision 1.5
diff -u -p -r1.5 dns-sd-method.c
--- modules/dns-sd-method.c	8 May 2005 13:04:05 -0000	1.5
+++ modules/dns-sd-method.c	28 Jul 2005 00:33:58 -0000
@@ -36,6 +36,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>
@@ -61,12 +66,12 @@ static struct {
 	{"_sftp-ssh._tcp", "sftp", "gnome-fs-ssh"},
 };
 
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || 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 */
+#endif /* HAVE_HOWL || HAVE_BONJOUR */
 
 typedef struct {
 	char *data;
@@ -216,7 +221,7 @@ encode_filename (const char *service,
 	return g_string_free (string, FALSE);
 }
 
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 
 static void
 call_monitors (gboolean add, char *filename)
@@ -291,6 +296,24 @@ local_browse (gboolean add,
 	}
 }
 
+static void
+local_browse_callback (GnomeVFSDNSSDBrowseHandle *handle,
+		       GnomeVFSDNSSDServiceStatus status,
+		       const GnomeVFSDNSSDService *service,
+		       gpointer callback_data)
+{
+	G_LOCK (local);
+
+	local_browse (status == GNOME_VFS_DNS_SD_SERVICE_ADDED,
+		      service->name, service->type, service->domain);
+	
+	G_UNLOCK (local);
+}
+
+#endif /* HAVE_HOWL || HAVE_BONJOUR */
+
+#ifdef HAVE_HOWL
+
 static sw_result
 local_browse_callback_sync (sw_discovery                 discovery,
 			    sw_discovery_oid             id,
@@ -308,20 +331,6 @@ local_browse_callback_sync (sw_discovery
 
 	return SW_OKAY;
 }
-			    
-static void
-local_browse_callback (GnomeVFSDNSSDBrowseHandle *handle,
-		       GnomeVFSDNSSDServiceStatus status,
-		       const GnomeVFSDNSSDService *service,
-		       gpointer callback_data)
-{
-	G_LOCK (local);
-
-	local_browse (status == GNOME_VFS_DNS_SD_SERVICE_ADDED,
-		      service->name, service->type, service->domain);
-	
-	G_UNLOCK (local);
-}
 
 static void
 init_local (void)
@@ -402,10 +411,130 @@ init_local (void)
 		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,
@@ -694,7 +823,7 @@ directory_handle_add_filename (Directory
 	}
 }
 
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 static void
 directory_handle_add_filenames (DirectoryHandle *dir_handle, GList *files)
 {
@@ -733,14 +862,14 @@ do_open_directory (GnomeVFSMethod *metho
 	dir_handle = directory_handle_new (options);
 	
 	if (strcmp (domain, "local") == 0) {
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 		G_LOCK (local);
 		init_local ();
 
 		directory_handle_add_filenames (dir_handle, local_files);
 		
 		G_UNLOCK (local);
-#endif /* HAVE_HOWL */
+#endif /* HAVE_HOWL || HAVE_BONJOUR */
 	} else 	{
 		for (i=0; i < G_N_ELEMENTS (dns_sd_types); i++) {
 			int n_services;
@@ -1007,7 +1136,7 @@ do_monitor_add (GnomeVFSMethod *method,
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 	}
 	
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 	if (strcmp (uri->text, "") == 0 ||
 	    strcmp (uri->text, "/") == 0) {
 		int *handle;
@@ -1025,7 +1154,7 @@ do_monitor_add (GnomeVFSMethod *method,
 
 		return GNOME_VFS_OK;
 	} else 
-#endif /* HAVE_HOWL */
+#endif /* HAVE_HOWL || HAVE_BONJOUR */
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 }
 
@@ -1033,7 +1162,7 @@ static GnomeVFSResult
 do_monitor_cancel (GnomeVFSMethod *method,
 		   GnomeVFSMethodHandle *method_handle)
 {
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 	G_LOCK (local);
 	
 	local_monitors = g_list_remove (local_monitors, method_handle);
@@ -1044,7 +1173,7 @@ do_monitor_cancel (GnomeVFSMethod *metho
 	return GNOME_VFS_OK;
 #else
 	return GNOME_VFS_ERROR_NOT_SUPPORTED;
-#endif /* HAVE_HOWL */
+#endif /* HAVE_HOWL || HAVE_BONJOUR */
 }
 
 
Index: libgnomevfs/gnome-vfs-dns-sd.c
===================================================================
RCS file: /cvs/gnome/gnome-vfs/libgnomevfs/gnome-vfs-dns-sd.c,v
retrieving revision 1.12
diff -u -p -r1.12 gnome-vfs-dns-sd.c
--- libgnomevfs/gnome-vfs-dns-sd.c	14 Jul 2005 07:47:53 -0000	1.12
+++ libgnomevfs/gnome-vfs-dns-sd.c	28 Jul 2005 00:33:58 -0000
@@ -43,6 +43,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"
 
@@ -776,6 +781,40 @@ get_global_howl_session (void) {
 
 #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;
@@ -791,6 +830,12 @@ struct GnomeVFSDNSSDBrowseHandle {
 #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;
@@ -818,6 +863,14 @@ free_browse_handle (GnomeVFSDNSSDBrowseH
 
 	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);
 }
@@ -864,9 +917,9 @@ unicast_browse_thread (gpointer data)
 	return NULL;
 }
 
-#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;
@@ -875,9 +928,9 @@ struct howl_browse_idle_data {
 
 
 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;
@@ -895,7 +948,7 @@ howl_browse_idle (gpointer data)
 }
 
 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);
@@ -910,6 +963,10 @@ free_browse_handle_idle (gpointer data)
 	return FALSE;
 }
 
+#endif
+
+#ifdef HAVE_HOWL
+
 static sw_result
 howl_browse_reply (sw_discovery                 discovery,
 		   sw_discovery_oid             oid,
@@ -921,7 +978,7 @@ howl_browse_reply (sw_discovery         
 		   sw_opaque                    extra)
 {
 	GnomeVFSDNSSDBrowseHandle *handle;
-	struct howl_browse_idle_data *idle_data;
+	struct zc_browse_idle_data *idle_data;
 	int len;
 
 	handle = extra;
@@ -936,7 +993,7 @@ howl_browse_reply (sw_discovery         
 	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) {
@@ -962,7 +1019,7 @@ howl_browse_reply (sw_discovery         
 		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;
@@ -970,6 +1027,63 @@ howl_browse_reply (sw_discovery         
 
 #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: A pointer to a pointer to a GnomeVFSDNSSDBrowseHandle object
@@ -1034,8 +1148,32 @@ gnome_vfs_dns_sd_browse (GnomeVFSDNSSDBr
 		}
 		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;
@@ -1067,6 +1205,12 @@ gnome_vfs_dns_sd_stop_browse (GnomeVFSDN
 		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)
@@ -1097,6 +1241,12 @@ struct GnomeVFSDNSSDResolveHandle {
 	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;
@@ -1117,7 +1267,15 @@ free_resolve_handle (GnomeVFSDNSSDResolv
 
 	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);
 }
 
@@ -1174,10 +1332,10 @@ unicast_resolve_thread (gpointer data)
 	return NULL;
 }
 
+#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;
@@ -1211,7 +1369,9 @@ howl_resolve_idle (gpointer data)
 	return FALSE;
 }
 
+#endif
 
+#ifdef HAVE_HOWL
 
 static sw_result
 howl_resolve_reply (sw_discovery                   discovery,
@@ -1244,7 +1404,7 @@ howl_resolve_reply (sw_discovery        
 	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;
@@ -1282,6 +1442,98 @@ howl_resolve_timeout (gpointer data)
 
 #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: A pointer to a pointer to a GnomeVFSDNSSDResolveHandle object
@@ -1358,7 +1610,38 @@ gnome_vfs_dns_sd_resolve (GnomeVFSDNSSDR
 		}
 		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;
@@ -1399,7 +1682,14 @@ gnome_vfs_dns_sd_cancel_resolve (GnomeVF
 					     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 {
@@ -1409,7 +1699,7 @@ gnome_vfs_dns_sd_cancel_resolve (GnomeVF
 }
 
 
-#ifdef HAVE_HOWL
+#if defined (HAVE_HOWL) || defined (HAVE_BONJOUR)
 static int
 find_existing_service (GArray *array,
 		       const char *name,
@@ -1430,7 +1720,9 @@ find_existing_service (GArray *array,
 	return -1;
 		    
 }
+#endif
 
+#ifdef HAVE_HOWL
 
 static sw_result
 howl_browse_reply_sync (sw_discovery                  discovery,
@@ -1495,8 +1787,116 @@ howl_browse_reply_sync (sw_discovery    
 	}	
 	return SW_OKAY;
 }
+
 #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
@@ -1590,7 +1990,32 @@ gnome_vfs_dns_sd_browse_sync (const char
 		
 		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,
@@ -1599,14 +2024,18 @@ gnome_vfs_dns_sd_browse_sync (const char
 	}
 }
 
-#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,
@@ -1633,8 +2062,59 @@ howl_resolve_reply_sync (sw_discovery   
 	
 	return SW_OKAY;
 }
+
 #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
@@ -1741,7 +2221,48 @@ gnome_vfs_dns_sd_resolve_sync (const cha
 		
 		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,
