gvfs r2112 - in trunk: . common daemon



Author: alexl
Date: Mon Dec  1 09:24:00 2008
New Revision: 2112
URL: http://svn.gnome.org/viewvc/gvfs?rev=2112&view=rev

Log:
2008-12-01  Alexander Larsson  <alexl redhat com>

        * common/Makefile.am:
        * common/gvfsdnssdresolver.c: Added.
        * common/gvfsdnssdresolver.h: Added.
        * common/gvfsdnssdutils.c: Added.
        * common/gvfsdnssdutils.h: Added.
        * daemon/Makefile.am:
        * daemon/dav+sd.mount.in: Added.
        * daemon/dav.mount.in:
        * daemon/gvfsbackenddav.c:
        * daemon/gvfsbackenddnssd.c:
        * daemon/gvfsbackendnetwork.c:
	For references to dns-sd dav services, use a
	dav+sd: uri, since this is stable over e.g.
	port changes and as such work better in e.g.
	bookmarks.
	
	Patch from David Zeuthen (#555436)



Added:
   trunk/common/gvfsdnssdresolver.c
   trunk/common/gvfsdnssdresolver.h
   trunk/common/gvfsdnssdutils.c
   trunk/common/gvfsdnssdutils.h
   trunk/daemon/dav+sd.mount.in
Modified:
   trunk/ChangeLog
   trunk/common/Makefile.am
   trunk/daemon/Makefile.am
   trunk/daemon/dav.mount.in
   trunk/daemon/gvfsbackenddav.c
   trunk/daemon/gvfsbackenddnssd.c
   trunk/daemon/gvfsbackendnetwork.c

Modified: trunk/common/Makefile.am
==============================================================================
--- trunk/common/Makefile.am	(original)
+++ trunk/common/Makefile.am	Mon Dec  1 09:24:00 2008
@@ -22,3 +22,20 @@
 libgvfscommon_la_LIBADD =	\
 	$(DBUS_LIBS) 	\
 	$(GLIB_LIBS)
+
+if HAVE_AVAHI
+lib_LTLIBRARIES += libgvfscommon-dnssd.la
+
+libgvfscommon_dnssd_la_SOURCES = 	\
+	gvfsdnssdutils.c gvfsdnssdutils.h \
+	gvfsdnssdresolver.c gvfsdnssdresolver.h \
+	$(NULL)
+
+libgvfscommon_dnssd_la_CFLAGS =	\
+	$(AVAHI_CFLAGS)
+
+libgvfscommon_dnssd_la_LIBADD =	\
+	$(top_builddir)/common/libgvfscommon.la \
+	$(AVAHI_LIBS)
+endif
+

Added: trunk/common/gvfsdnssdresolver.c
==============================================================================
--- (empty file)
+++ trunk/common/gvfsdnssdresolver.c	Mon Dec  1 09:24:00 2008
@@ -0,0 +1,1253 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+/*
+ * TODO: - locking
+ *       - cancellation
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/error.h>
+#include <avahi-common/timeval.h>
+#include <avahi-glib/glib-watch.h>
+#include <avahi-glib/glib-malloc.h>
+
+#include "gvfsdnssdutils.h"
+#include "gvfsdnssdresolver.h"
+
+enum
+{
+  PROP_0,
+  PROP_ENCODED_TRIPLE,
+  PROP_REQUIRED_TXT_KEYS,
+  PROP_SERVICE_NAME,
+  PROP_SERVICE_TYPE,
+  PROP_DOMAIN,
+  PROP_TIMEOUT_MSEC,
+
+  PROP_IS_RESOLVED,
+  PROP_ADDRESS,
+  PROP_PORT,
+  PROP_TXT_RECORDS,
+};
+
+enum {
+    CHANGED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GVfsDnsSdResolver
+{
+  GObject parent_instance;
+
+  char *encoded_triple;
+  char *service_name;
+  char *service_type;
+  char *domain;
+  char *required_txt_keys;
+  char **required_txt_keys_broken_out;
+  guint timeout_msec;
+
+  gboolean is_resolved;
+  char *address;
+  guint port;
+  char **txt_records;
+
+  AvahiServiceResolver *avahi_resolver;
+};
+
+
+struct _GVfsDnsSdResolverClass
+{
+  GObjectClass parent_class;
+
+  /* signals */
+  void (*changed) (GVfsDnsSdResolver *resolver);
+};
+
+G_DEFINE_TYPE (GVfsDnsSdResolver, g_vfs_dns_sd_resolver, G_TYPE_OBJECT);
+
+static gboolean resolver_supports_mdns = FALSE;
+static AvahiClient *global_client = NULL;
+static gboolean avahi_initialized = FALSE;
+static void free_global_avahi_client (void);
+static AvahiClient *get_global_avahi_client (GError **error);
+
+static gboolean ensure_avahi_resolver (GVfsDnsSdResolver  *resolver,
+                                       GError            **error);
+
+static void service_resolver_cb (AvahiServiceResolver   *resolver,
+                                 AvahiIfIndex            interface,
+                                 AvahiProtocol           protocol,
+                                 AvahiResolverEvent      event,
+                                 const char             *name,
+                                 const char             *type,
+                                 const char             *domain,
+                                 const char             *host_name,
+                                 const AvahiAddress     *a,
+                                 uint16_t                port,
+                                 AvahiStringList        *txt,
+                                 AvahiLookupResultFlags  flags,
+                                 void                   *user_data);
+
+
+
+static GList *resolvers = NULL;
+
+static void
+clear_avahi_data (GVfsDnsSdResolver *resolver);
+
+static void
+remove_client_from_resolver (GVfsDnsSdResolver *resolver)
+{
+  if (resolver->avahi_resolver != NULL)
+    {
+      avahi_service_resolver_free (resolver->avahi_resolver);
+      resolver->avahi_resolver = NULL;
+    }
+
+  clear_avahi_data (resolver);
+}
+
+static void
+add_client_to_resolver (GVfsDnsSdResolver *resolver)
+{
+  ensure_avahi_resolver (resolver, NULL);
+}
+
+/* Callback for state changes on the Client */
+static void
+avahi_client_callback (AvahiClient *client, AvahiClientState state, void *userdata)
+{
+  if (global_client == NULL)
+    global_client = client;
+
+  if (state == AVAHI_CLIENT_FAILURE)
+    {
+      if (avahi_client_errno (client) == AVAHI_ERR_DISCONNECTED)
+        {
+          free_global_avahi_client ();
+
+          /* Attempt to reconnect */
+          get_global_avahi_client (NULL);
+        }
+    }
+  else if (state == AVAHI_CLIENT_S_RUNNING)
+    {
+      /* Start resolving again */
+      g_list_foreach (resolvers, (GFunc) add_client_to_resolver, NULL);
+    }
+}
+
+static void
+free_global_avahi_client (void)
+{
+  /* Remove current resolvers */
+  g_list_foreach (resolvers, (GFunc) remove_client_from_resolver, NULL);
+
+  /* Destroy client */
+  avahi_client_free (global_client);
+  global_client = NULL;
+  avahi_initialized = FALSE;
+}
+
+static AvahiClient *
+get_global_avahi_client (GError **error)
+{
+  static AvahiGLibPoll *glib_poll = NULL;
+  int avahi_error;
+
+  if (!avahi_initialized)
+    {
+      avahi_initialized = TRUE;
+
+      if (glib_poll == NULL)
+        {
+          avahi_set_allocator (avahi_glib_allocator ());
+          glib_poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT);
+        }
+
+      /* Create a new AvahiClient instance */
+      global_client = avahi_client_new (avahi_glib_poll_get (glib_poll),
+                                        AVAHI_CLIENT_NO_FAIL,
+                                        avahi_client_callback,
+                                        glib_poll,
+                                        &avahi_error);
+
+      if (global_client == NULL)
+        {
+          g_set_error (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_FAILED,
+                       _("Error initializing Avahi: %s"),
+                       avahi_strerror (avahi_error));
+          goto out;
+        }
+    }
+
+ out:
+
+  return global_client;
+}
+
+
+static gboolean
+ensure_avahi_resolver (GVfsDnsSdResolver  *resolver,
+                       GError            **error)
+{
+  AvahiClient *avahi_client;
+  gboolean ret;
+
+  ret = FALSE;
+
+  if (resolver->avahi_resolver != NULL)
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  avahi_client = get_global_avahi_client (error);
+  if (avahi_client == NULL)
+    goto out;
+
+  resolver->avahi_resolver = avahi_service_resolver_new (avahi_client,
+                                                         AVAHI_IF_UNSPEC,
+                                                         AVAHI_PROTO_UNSPEC,
+                                                         resolver->service_name,
+                                                         resolver->service_type,
+                                                         resolver->domain,
+                                                         AVAHI_PROTO_UNSPEC,
+                                                         0, /* AvahiLookupFlags */
+                                                         service_resolver_cb,
+                                                         resolver);
+  if (resolver->avahi_resolver == NULL)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   _("Error creating Avahi resolver: %s"),
+                   avahi_strerror (avahi_client_errno (avahi_client)));
+      goto out;
+    }
+
+  ret = TRUE;
+
+out:
+  return ret;
+}
+
+static void
+g_vfs_dns_sd_resolver_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENCODED_TRIPLE:
+      g_value_set_string (value, resolver->encoded_triple);
+      break;
+
+    case PROP_REQUIRED_TXT_KEYS:
+      g_value_set_string (value, resolver->required_txt_keys);
+      break;
+
+    case PROP_SERVICE_NAME:
+      g_value_set_string (value, resolver->service_name);
+      break;
+
+    case PROP_SERVICE_TYPE:
+      g_value_set_string (value, resolver->service_type);
+      break;
+
+    case PROP_DOMAIN:
+      g_value_set_string (value, resolver->domain);
+      break;
+
+    case PROP_TIMEOUT_MSEC:
+      g_value_set_uint (value, resolver->timeout_msec);
+      break;
+
+    case PROP_IS_RESOLVED:
+      g_value_set_boolean (value, resolver->is_resolved);
+      break;
+
+    case PROP_ADDRESS:
+      g_value_set_string (value, resolver->address);
+      break;
+
+    case PROP_PORT:
+      g_value_set_uint (value, resolver->port);
+      break;
+
+    case PROP_TXT_RECORDS:
+      g_value_set_boxed (value, resolver->txt_records);
+      break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_vfs_dns_sd_resolver_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENCODED_TRIPLE:
+      resolver->encoded_triple = g_strdup (g_value_get_string (value));
+      break;
+
+    case PROP_REQUIRED_TXT_KEYS:
+      resolver->required_txt_keys = g_strdup (g_value_get_string (value));
+      if (resolver->required_txt_keys != NULL)
+        {
+          /* TODO: maybe support escaping ',' */
+          resolver->required_txt_keys_broken_out = g_strsplit (resolver->required_txt_keys, ",", 0);
+        }
+      break;
+
+    case PROP_SERVICE_NAME:
+      resolver->service_name = g_strdup (g_value_get_string (value));
+      break;
+
+    case PROP_SERVICE_TYPE:
+      resolver->service_type = g_strdup (g_value_get_string (value));
+      break;
+
+    case PROP_DOMAIN:
+      resolver->domain = g_strdup (g_value_get_string (value));
+      break;
+
+    case PROP_TIMEOUT_MSEC:
+      resolver->timeout_msec = g_value_get_uint (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_vfs_dns_sd_resolver_finalize (GObject *object)
+{
+  GVfsDnsSdResolver *resolver;
+
+  resolver = G_VFS_DNS_SD_RESOLVER (object);
+
+  g_free (resolver->encoded_triple);
+  g_free (resolver->service_name);
+  g_free (resolver->service_type);
+  g_free (resolver->domain);
+  g_free (resolver->required_txt_keys);
+  g_strfreev (resolver->required_txt_keys_broken_out);
+
+  g_free (resolver->address);
+  g_strfreev (resolver->txt_records);
+
+  if (resolver->avahi_resolver != NULL)
+    avahi_service_resolver_free (resolver->avahi_resolver);
+
+
+  resolvers = g_list_remove (resolvers, resolver);
+
+  /* free the global avahi client for the last resolver */
+  if (resolvers == NULL)
+    {
+      free_global_avahi_client ();
+    }
+
+  G_OBJECT_CLASS (g_vfs_dns_sd_resolver_parent_class)->finalize (object);
+}
+
+static void
+g_vfs_dns_sd_resolver_constructed (GObject *object)
+{
+  GVfsDnsSdResolver *resolver;
+
+  resolver = G_VFS_DNS_SD_RESOLVER (object);
+
+  if (resolver->encoded_triple != NULL)
+    {
+      GError *error;
+
+      if (resolver->service_name != NULL)
+        {
+          g_warning ("Ignoring service-name since encoded-triple is already set");
+          g_free (resolver->service_name);
+          resolver->service_name = NULL;
+        }
+
+      if (resolver->service_type != NULL)
+        {
+          g_warning ("Ignoring service-type since encoded-triple is already set");
+          g_free (resolver->service_type);
+          resolver->service_type = NULL;
+        }
+
+      if (resolver->domain != NULL)
+        {
+          g_warning ("Ignoring domain since encoded-triple is already set");
+          g_free (resolver->domain);
+          resolver->domain = NULL;
+        }
+
+
+      error = NULL;
+      if (!g_vfs_decode_dns_sd_triple (resolver->encoded_triple,
+                                       &(resolver->service_name),
+                                       &(resolver->service_type),
+                                       &(resolver->domain),
+                                       &error))
+        {
+          /* GObject construction can't fail. So whine if the triple isn't valid. */
+          g_warning ("Malformed construction data passed: %s", error->message);
+          g_error_free (error);
+
+          g_free (resolver->encoded_triple);
+          g_free (resolver->service_name);
+          g_free (resolver->service_type);
+          g_free (resolver->domain);
+          resolver->encoded_triple = NULL;
+          resolver->service_name = NULL;
+          resolver->service_type = NULL;
+          resolver->domain = NULL;
+          goto out;
+        }
+    }
+
+  /* Always set encoded triple to what we encode; this is because we can decode
+   * an encoded triple that isn't 100% properly URI encoded, e.g.
+   *
+   *  "davidz's public files on quad.fubar.dk._webdav._tcp.local"
+   *
+   * will be properly decoded. But we want to return a properly URI encoded triple
+   *
+   *  "davidz%27s%20public%20files%20on%20quad%2efubar%2edk._webdav._tcp.local"
+   *
+   * for e.g. setting the GMountSpec. This is useful because the use can
+   * put the former into the pathbar in a file manager and then it will
+   * be properly rewritten on mount.
+   */
+  g_free (resolver->encoded_triple);
+  resolver->encoded_triple = g_vfs_encode_dns_sd_triple (resolver->service_name,
+                                                         resolver->service_type,
+                                                         resolver->domain);
+
+  /* start resolving immediately */
+  ensure_avahi_resolver (resolver, NULL);
+
+  resolvers = g_list_prepend (resolvers, resolver);
+
+ out:
+
+  if (G_OBJECT_CLASS (g_vfs_dns_sd_resolver_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (g_vfs_dns_sd_resolver_parent_class)->constructed (object);
+}
+
+static void
+g_vfs_dns_sd_resolver_class_init (GVfsDnsSdResolverClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  resolver_supports_mdns = (avahi_nss_support () > 0);
+
+  gobject_class->get_property = g_vfs_dns_sd_resolver_get_property;
+  gobject_class->set_property = g_vfs_dns_sd_resolver_set_property;
+  gobject_class->finalize = g_vfs_dns_sd_resolver_finalize;
+  gobject_class->constructed = g_vfs_dns_sd_resolver_constructed;
+
+  /**
+   * GVfsDnsSdResolver::changed:
+   * @resolver: The resolver emitting the signal.
+   *
+   * Emitted when resolved data changes.
+   */
+  signals[CHANGED] = g_signal_new ("changed",
+                                   G_VFS_TYPE_DNS_SD_RESOLVER,
+                                   G_SIGNAL_RUN_LAST,
+                                   G_STRUCT_OFFSET (GVfsDnsSdResolverClass, changed),
+                                   NULL,
+                                   NULL,
+                                   g_cclosure_marshal_VOID__VOID,
+                                   G_TYPE_NONE,
+                                   0);
+
+
+  /**
+   * GVfsDnsSdResolver:encoded-triple:
+   *
+   * The encoded DNS-SD triple for the resolver.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ENCODED_TRIPLE,
+                                   g_param_spec_string ("encoded-triple",
+                                                        "Encoded triple",
+                                                        "Encoded triple",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:required-txt-keys:
+   *
+   * A comma separated list of keys that must appear in the TXT
+   * records in order to consider the service being resolved.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_REQUIRED_TXT_KEYS,
+                                   g_param_spec_string ("required-txt-keys",
+                                                        "Required TXT keys",
+                                                        "Required TXT keys",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:service-name:
+   *
+   * The name of the service for the resolver.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_SERVICE_NAME,
+                                   g_param_spec_string ("service-name",
+                                                        "Service Name",
+                                                        "Service Name",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:service-type:
+   *
+   * The type of the service for the resolver.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_SERVICE_TYPE,
+                                   g_param_spec_string ("service-type",
+                                                        "Service Type",
+                                                        "Service Type",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:domain:
+   *
+   * The domain for the resolver.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DOMAIN,
+                                   g_param_spec_string ("domain",
+                                                        "Domain",
+                                                        "Domain",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:timeout-msec:
+   *
+   * Timeout in milliseconds to use when resolving.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_TIMEOUT_MSEC,
+                                   g_param_spec_uint ("timeout-msec",
+                                                      "Timeout in milliseconds",
+                                                      "Timeout in milliseconds",
+                                                      0,
+                                                      G_MAXUINT,
+                                                      5000,
+                                                      G_PARAM_CONSTRUCT |
+                                                      G_PARAM_READWRITE |
+                                                      G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_BLURB |
+                                                      G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:is-resolved:
+   *
+   * Whether the service is resolved.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_IS_RESOLVED,
+                                   g_param_spec_boolean ("is-resolved",
+                                                         "Is resolved",
+                                                         "Is resolved",
+                                                         FALSE,
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_STATIC_NAME |
+                                                         G_PARAM_STATIC_BLURB |
+                                                         G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:address:
+   *
+   * The resolved address.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DOMAIN,
+                                   g_param_spec_string ("address",
+                                                        "Address",
+                                                        "Address",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:port:
+   *
+   * The resolved port.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PORT,
+                                   g_param_spec_uint ("port",
+                                                      "Port",
+                                                      "Port",
+                                                      0,
+                                                      65536,
+                                                      0,
+                                                      G_PARAM_READABLE |
+                                                      G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_BLURB |
+                                                      G_PARAM_STATIC_NICK));
+
+  /**
+   * GVfsDnsSdResolver:txt-records:
+   *
+   * The resolved TXT records.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_TXT_RECORDS,
+                                   g_param_spec_boxed ("txt-records",
+                                                       "TXT Records",
+                                                       "TXT Records",
+                                                       G_TYPE_STRV,
+                                                       G_PARAM_READABLE |
+                                                       G_PARAM_STATIC_NAME |
+                                                       G_PARAM_STATIC_BLURB |
+                                                       G_PARAM_STATIC_NICK));
+}
+
+static void
+g_vfs_dns_sd_resolver_init (GVfsDnsSdResolver *resolver)
+{
+}
+
+gboolean
+g_vfs_dns_sd_resolver_is_resolved (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), FALSE);
+  return resolver->is_resolved;
+}
+
+const gchar *
+g_vfs_dns_sd_resolver_get_encoded_triple (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return resolver->encoded_triple;
+}
+
+const gchar *
+g_vfs_dns_sd_resolver_get_required_txt_keys (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return resolver->required_txt_keys;
+}
+
+const gchar *
+g_vfs_dns_sd_resolver_get_service_name (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return resolver->service_name;
+}
+
+const gchar *
+g_vfs_dns_sd_resolver_get_service_type (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return resolver->service_type;
+}
+
+const gchar *
+g_vfs_dns_sd_resolver_get_domain (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return resolver->domain;
+}
+
+gchar *
+g_vfs_dns_sd_resolver_get_address (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return g_strdup (resolver->address);
+}
+
+guint
+g_vfs_dns_sd_resolver_get_port (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), (guint) -1);
+  return resolver->port;
+}
+
+gchar **
+g_vfs_dns_sd_resolver_get_txt_records (GVfsDnsSdResolver *resolver)
+{
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  return g_strdupv (resolver->txt_records);
+}
+
+gchar *
+g_vfs_dns_sd_resolver_lookup_txt_record (GVfsDnsSdResolver *resolver,
+                                         const gchar       *key)
+{
+  gint n;
+  gchar *result;
+  gsize key_len;
+
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  result = NULL;
+
+
+  if (resolver->txt_records == NULL)
+    goto out;
+
+  key_len = strlen (key);
+
+  for (n = 0; resolver->txt_records[n] != NULL; n++)
+    {
+      const gchar *s = resolver->txt_records[n];
+      const gchar *p;
+
+      p = strchr (s, '=');
+      if (p != NULL && (p - s) == key_len)
+        {
+          if (g_ascii_strncasecmp (s,
+                                   key,
+                                   p - s) == 0)
+            {
+              result = g_strdup (p + 1);
+              goto out;
+            }
+        }
+    }
+
+ out:
+  return result;
+}
+
+GVfsDnsSdResolver *
+g_vfs_dns_sd_resolver_new_for_encoded_triple (const gchar *encoded_triple,
+                                              const gchar *required_txt_keys)
+
+{
+  g_return_val_if_fail (encoded_triple != NULL, NULL);
+
+  return G_VFS_DNS_SD_RESOLVER (g_object_new (G_VFS_TYPE_DNS_SD_RESOLVER,
+                                              "encoded-triple", encoded_triple,
+                                              "required-txt-keys", required_txt_keys,
+                                              NULL));
+}
+
+GVfsDnsSdResolver *
+g_vfs_dns_sd_resolver_new_for_service (const gchar *service_name,
+                                       const gchar *service_type,
+                                       const gchar *domain,
+                                       const gchar *required_txt_keys)
+{
+  g_return_val_if_fail (service_name != NULL, NULL);
+  g_return_val_if_fail (service_type != NULL, NULL);
+  g_return_val_if_fail (domain != NULL, NULL);
+
+  return G_VFS_DNS_SD_RESOLVER (g_object_new (G_VFS_TYPE_DNS_SD_RESOLVER,
+                                              "service-name", service_name,
+                                              "service-type", service_type,
+                                              "domain", domain,
+                                              "required-txt-keys", required_txt_keys,
+                                              NULL));
+}
+
+static int
+safe_strcmp (const char *a, const char *b)
+{
+  if (a == NULL)
+    a = "";
+  if (b == NULL)
+    b = "";
+  return strcmp (a, b);
+}
+
+static gboolean
+strv_equal (char **a, char **b)
+{
+  static char *dummy[1] = {NULL};
+  int n;
+  gboolean ret;
+
+  if (a == NULL)
+    a = dummy;
+  if (b == NULL)
+    b = dummy;
+
+  ret = FALSE;
+
+  if (g_strv_length (a) != g_strv_length (b))
+    goto out;
+
+  for (n = 0; a[n] != NULL && b[n] != NULL; n++)
+    {
+      if (strcmp (a[n], b[n]) != 0)
+        goto out;
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+
+}
+
+static gboolean
+has_required_txt_keys (GVfsDnsSdResolver *resolver)
+{
+  gboolean ret;
+  int n;
+  char *value;
+
+  ret = FALSE;
+
+  if (resolver->required_txt_keys_broken_out != NULL)
+    {
+      for (n = 0; resolver->required_txt_keys_broken_out[n] != NULL; n++)
+        {
+          value = g_vfs_dns_sd_resolver_lookup_txt_record (resolver,
+                                                           resolver->required_txt_keys_broken_out[n]);
+          if (value == NULL)
+            {
+              /* key is missing */
+              goto out;
+            }
+          g_free (value);
+        }
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+static void
+set_avahi_data (GVfsDnsSdResolver    *resolver,
+                const char           *host_name,
+                AvahiProtocol         protocol,
+                const AvahiAddress   *a,
+                uint16_t              port,
+                AvahiStringList      *txt)
+{
+  char *address;
+  gboolean changed;
+  AvahiStringList *l;
+  GPtrArray *p;
+  char **txt_records;
+  gboolean is_resolved;
+
+  changed = FALSE;
+
+  if (resolver_supports_mdns)
+    {
+      address = g_strdup (host_name);
+    }
+  else
+    {
+      char aa[128];
+
+      avahi_address_snprint (aa, sizeof(aa), a);
+      if (protocol == AVAHI_PROTO_INET6)
+        {
+          /* an ipv6 address, follow RFC 2732 */
+          address = g_strdup_printf ("[%s]", aa);
+        }
+      else
+        {
+          address = g_strdup (aa);
+        }
+    }
+
+  if (safe_strcmp (resolver->address, address) != 0)
+    {
+      g_free (resolver->address);
+      resolver->address = g_strdup (address);
+      g_object_notify (G_OBJECT (resolver), "address");
+      changed = TRUE;
+    }
+
+  g_free (address);
+
+  if (resolver->port != port)
+    {
+      resolver->port = port;
+      g_object_notify (G_OBJECT (resolver), "port");
+      changed = TRUE;
+    }
+
+  p = g_ptr_array_new ();
+  for (l = txt; l != NULL; l = avahi_string_list_get_next (l))
+    {
+      g_ptr_array_add (p, g_strdup ((const char *) l->text));
+    }
+  g_ptr_array_add (p, NULL);
+  txt_records = (char **) g_ptr_array_free (p, FALSE);
+
+  if (strv_equal (resolver->txt_records, txt_records))
+    {
+      g_strfreev (txt_records);
+    }
+  else
+    {
+      g_strfreev (resolver->txt_records);
+      resolver->txt_records = txt_records;
+      g_object_notify (G_OBJECT (resolver), "txt-records");
+      changed = TRUE;
+    }
+
+  is_resolved = has_required_txt_keys (resolver);
+
+  if (is_resolved != resolver->is_resolved)
+    {
+      resolver->is_resolved = is_resolved;
+      g_object_notify (G_OBJECT (resolver), "is-resolved");
+      changed = TRUE;
+    }
+
+  if (changed)
+    g_signal_emit (resolver, signals[CHANGED], 0);
+}
+
+static void
+clear_avahi_data (GVfsDnsSdResolver *resolver)
+{
+  gboolean changed;
+
+  changed = FALSE;
+
+  if (resolver->is_resolved)
+    {
+      resolver->is_resolved = FALSE;
+      g_object_notify (G_OBJECT (resolver), "is-resolved");
+      changed = TRUE;
+    }
+
+  if (resolver->address != NULL)
+    {
+      g_free (resolver->address);
+      resolver->address = NULL;
+      g_object_notify (G_OBJECT (resolver), "address");
+      changed = TRUE;
+    }
+
+  if (resolver->port != 0)
+    {
+      resolver->port = 0;
+      g_object_notify (G_OBJECT (resolver), "port");
+      changed = TRUE;
+    }
+
+  if (resolver->txt_records != NULL)
+    {
+      resolver->txt_records = NULL;
+      g_object_notify (G_OBJECT (resolver), "txt-records");
+      changed = TRUE;
+    }
+
+  if (changed)
+    g_signal_emit (resolver, signals[CHANGED], 0);
+}
+
+static void
+service_resolver_cb (AvahiServiceResolver   *avahi_resolver,
+                     AvahiIfIndex            interface,
+                     AvahiProtocol           protocol,
+                     AvahiResolverEvent      event,
+                     const char             *name,
+                     const char             *type,
+                     const char             *domain,
+                     const char             *host_name,
+                     const AvahiAddress     *a,
+                     uint16_t                port,
+                     AvahiStringList        *txt,
+                     AvahiLookupResultFlags  flags,
+                     void                   *user_data)
+{
+  GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (user_data);
+
+  switch (event)
+    {
+    case AVAHI_RESOLVER_FOUND:
+      set_avahi_data (resolver,
+                      host_name,
+                      protocol,
+                      a,
+                      port,
+                      txt);
+      break;
+
+    case AVAHI_RESOLVER_FAILURE:
+      clear_avahi_data (resolver);
+      break;
+    }
+}
+
+
+typedef struct
+{
+  GVfsDnsSdResolver *resolver;
+  GSimpleAsyncResult *simple;
+  guint timeout_id;
+} ResolveData;
+
+static void service_resolver_changed (GVfsDnsSdResolver *resolver, ResolveData *data);
+
+static void
+resolve_data_free (ResolveData *data)
+{
+  if (data->timeout_id > 0)
+    g_source_remove (data->timeout_id);
+  g_signal_handlers_disconnect_by_func (data->resolver, service_resolver_changed, data);
+  g_object_unref (data->simple);
+  g_free (data);
+}
+
+static void
+service_resolver_changed (GVfsDnsSdResolver *resolver,
+                          ResolveData       *data)
+{
+  if (resolver->is_resolved)
+    {
+      g_simple_async_result_set_op_res_gboolean (data->simple, TRUE);
+      g_simple_async_result_complete (data->simple);
+      resolve_data_free (data);
+    }
+  else
+    {
+      if (data->resolver->address != NULL)
+        {
+          /* keep going until timeout if we're missing TXT records */
+        }
+      else
+        {
+          g_simple_async_result_set_error (data->simple,
+                                           G_IO_ERROR,
+                                           G_IO_ERROR_FAILED,
+                                           _("Error resolving \"%s\" service \"%s\" on domain \"%s\""),
+                                           data->resolver->service_type,
+                                           data->resolver->service_name,
+                                           data->resolver->domain);
+          g_simple_async_result_complete (data->simple);
+          resolve_data_free (data);
+        }
+    }
+}
+
+static gboolean
+service_resolver_timed_out (ResolveData *data)
+{
+
+  if (data->resolver->address != NULL)
+    {
+      /* special case if one of the required TXT records are missing */
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED,
+                                       _("Error resolving \"%s\" service \"%s\" on domain \"%s\". "
+                                         "One or more TXT records are missing. Keys required: \"%s\"."),
+                                       data->resolver->service_type,
+                                       data->resolver->service_name,
+                                       data->resolver->domain,
+                                       data->resolver->required_txt_keys);
+    }
+  else
+    {
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_TIMED_OUT,
+                                       _("Timed out resolving \"%s\" service \"%s\" on domain \"%s\""),
+                                       data->resolver->service_type,
+                                       data->resolver->service_name,
+                                       data->resolver->domain);
+    }
+
+  g_simple_async_result_complete (data->simple);
+  data->timeout_id = 0;
+  resolve_data_free (data);
+  return FALSE;
+}
+
+gboolean
+g_vfs_dns_sd_resolver_resolve_finish (GVfsDnsSdResolver  *resolver,
+                                      GAsyncResult       *res,
+                                      GError            **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), FALSE);
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_vfs_dns_sd_resolver_resolve);
+  g_simple_async_result_propagate_error (simple, error);
+
+  return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+void
+g_vfs_dns_sd_resolver_resolve (GVfsDnsSdResolver  *resolver,
+                               GCancellable       *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer            user_data)
+{
+  ResolveData *data;
+  GSimpleAsyncResult *simple;
+  GError *error;
+
+  g_return_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver));
+
+  simple = g_simple_async_result_new (G_OBJECT (resolver),
+                                      callback,
+                                      user_data,
+                                      g_vfs_dns_sd_resolver_resolve);
+
+
+  if (resolver->is_resolved)
+    {
+      g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  error = NULL;
+  if (!ensure_avahi_resolver (resolver, &error))
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      g_error_free (error);
+      goto out;
+    }
+
+  data = g_new0 (ResolveData, 1);
+  data->resolver = resolver;
+  data->simple = simple;
+  data->timeout_id = g_timeout_add (resolver->timeout_msec,
+                                    (GSourceFunc) service_resolver_timed_out,
+                                    data);
+
+  g_signal_connect (resolver,
+                    "changed",
+                    (GCallback) service_resolver_changed,
+                    data);
+
+ out:
+  ;
+}
+
+
+typedef struct
+{
+  GMainLoop *loop;
+  GError *error;
+  gboolean ret;
+} ResolveDataSync;
+
+static void
+resolve_sync_cb (GVfsDnsSdResolver *resolver,
+                 GAsyncResult      *res,
+                 ResolveDataSync   *data)
+{
+  data->ret = g_vfs_dns_sd_resolver_resolve_finish (resolver,
+                                                    res,
+                                                    &(data->error));
+  g_main_loop_quit (data->loop);
+}
+
+
+gboolean
+g_vfs_dns_sd_resolver_resolve_sync (GVfsDnsSdResolver  *resolver,
+                                    GCancellable       *cancellable,
+                                    GError            **error)
+{
+  ResolveDataSync *data;
+  gboolean ret;
+
+  g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), FALSE);
+
+  data = g_new0 (ResolveDataSync, 1);
+  data->loop = g_main_loop_new (NULL, FALSE);
+
+  g_vfs_dns_sd_resolver_resolve (resolver,
+                                 cancellable,
+                                 (GAsyncReadyCallback) resolve_sync_cb,
+                                 data);
+
+  g_main_loop_run (data->loop);
+
+  ret = data->ret;
+  if (data->error != NULL)
+    g_propagate_error (error, data->error);
+
+  g_main_loop_unref (data->loop);
+  g_free (data);
+
+  return ret;
+}

Added: trunk/common/gvfsdnssdresolver.h
==============================================================================
--- (empty file)
+++ trunk/common/gvfsdnssdresolver.h	Mon Dec  1 09:24:00 2008
@@ -0,0 +1,80 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_VFS_DNS_SD_RESOLVER_H__
+#define __G_VFS_DNS_SD_RESOLVER_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_DNS_SD_RESOLVER         (g_vfs_dns_sd_resolver_get_type ())
+#define G_VFS_DNS_SD_RESOLVER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_DNS_SD_RESOLVER, GVfsDnsSdResolver))
+#define G_VFS_DNS_SD_RESOLVER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_DNS_SD_RESOLVER, GVfsDnsSdResolverClass))
+#define G_VFS_IS_DNS_SD_RESOLVER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_DNS_SD_RESOLVER))
+#define G_VFS_IS_DNS_SD_RESOLVER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_DNS_SD_RESOLVER))
+#define G_VFS_DNS_SD_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_DNS_SD_RESOLVER, GVfsDnsSdResolverClass))
+
+/**
+ * GVfsDnsSdResolver:
+ *
+ * Resolves DNS-SD triples.
+ */
+typedef struct _GVfsDnsSdResolver        GVfsDnsSdResolver;
+typedef struct _GVfsDnsSdResolverClass   GVfsDnsSdResolverClass;
+
+GType                g_vfs_dns_sd_resolver_get_type               (void) G_GNUC_CONST;
+GVfsDnsSdResolver   *g_vfs_dns_sd_resolver_new_for_encoded_triple (const gchar        *encoded_triple,
+                                                                   const gchar        *required_txt_keys);
+GVfsDnsSdResolver   *g_vfs_dns_sd_resolver_new_for_service        (const gchar        *service_name,
+                                                                   const gchar        *service_type,
+                                                                   const gchar        *domain,
+                                                                   const gchar        *required_txt_keys);
+const gchar         *g_vfs_dns_sd_resolver_get_encoded_triple     (GVfsDnsSdResolver  *resolver);
+const gchar         *g_vfs_dns_sd_resolver_get_service_name       (GVfsDnsSdResolver  *resolver);
+const gchar         *g_vfs_dns_sd_resolver_get_service_type       (GVfsDnsSdResolver  *resolver);
+const gchar         *g_vfs_dns_sd_resolver_get_domain             (GVfsDnsSdResolver  *resolver);
+const gchar         *g_vfs_dns_sd_resolver_get_required_txt_keys  (GVfsDnsSdResolver  *resolver);
+
+void                 g_vfs_dns_sd_resolver_resolve                (GVfsDnsSdResolver  *resolver,
+                                                                   GCancellable       *cancellable,
+                                                                   GAsyncReadyCallback callback,
+                                                                   gpointer            user_data);
+
+gboolean             g_vfs_dns_sd_resolver_resolve_finish         (GVfsDnsSdResolver  *resolver,
+                                                                   GAsyncResult       *res,
+                                                                   GError            **error);
+
+gboolean             g_vfs_dns_sd_resolver_resolve_sync           (GVfsDnsSdResolver  *resolver,
+                                                                   GCancellable       *cancellable,
+                                                                   GError            **error);
+
+gboolean             g_vfs_dns_sd_resolver_is_resolved            (GVfsDnsSdResolver  *resolver);
+gchar               *g_vfs_dns_sd_resolver_get_address            (GVfsDnsSdResolver  *resolver);
+guint                g_vfs_dns_sd_resolver_get_port               (GVfsDnsSdResolver  *resolver);
+gchar              **g_vfs_dns_sd_resolver_get_txt_records        (GVfsDnsSdResolver  *resolver);
+gchar               *g_vfs_dns_sd_resolver_lookup_txt_record      (GVfsDnsSdResolver  *resolver,
+                                                                   const gchar        *key);
+
+G_END_DECLS
+
+#endif /* __G_VFS_DNS_SD_RESOLVER_H__ */

Added: trunk/common/gvfsdnssdutils.c
==============================================================================
--- (empty file)
+++ trunk/common/gvfsdnssdutils.c	Mon Dec  1 09:24:00 2008
@@ -0,0 +1,323 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/error.h>
+#include <avahi-common/timeval.h>
+#include <avahi-glib/glib-watch.h>
+#include <avahi-glib/glib-malloc.h>
+
+#include "gvfsdnssdutils.h"
+
+static gchar *
+escape_service_name (const gchar *service_name)
+{
+  GString *s;
+  char *res;
+  const gchar *p;
+
+  g_return_val_if_fail (service_name != NULL, NULL);
+
+  s = g_string_new (NULL);
+
+  p = service_name;
+  while (*p != '\0')
+    {
+      if (*p == '\\')
+        g_string_append (s, "\\\\");
+      else if (*p == '.')
+        g_string_append (s, "\\.");
+      else if (*p == '/')
+        g_string_append (s, "\\s");
+      else
+        g_string_append_c (s, *p);
+      p++;
+    }
+
+  res = g_uri_escape_string (s->str, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
+  g_string_free (s, TRUE);
+  return res;
+}
+
+static gchar *
+escape_service_name2 (const gchar *service_name)
+{
+  GString *s;
+  const gchar *p;
+
+  g_return_val_if_fail (service_name != NULL, NULL);
+
+  s = g_string_new (NULL);
+
+  p = service_name;
+  while (*p != '\0')
+    {
+      if (*p == '.')
+        g_string_append (s, "%2e");
+      else
+        g_string_append_c (s, *p);
+      p++;
+    }
+
+  return g_string_free (s, FALSE);
+}
+
+/**
+ * g_vfs_get_dns_sd_uri_for_triple:
+ * @service_name: DNS-SD service name.
+ * @service_type: DNS-SD service type.
+ * @domain: DNS-SD domain.
+ *
+ * Creates an URI for a file on the GVfs <literal>dns-sd</literal>
+ * virtual file system that provides live data for resolving the given
+ * DNS-SD service.
+ *
+ * The URI is of the form
+ * <literal>dns-sd://domain/service_name.service_type<literal> with
+ * suitable encoding added.
+ *
+ * Note that there may not exist a file at the returned URI, the
+ * resource providing the DNS-SD service will have to be available for
+ * the file to exist.
+ *
+ * Returns: An URI. Free with g_free().
+ **/
+gchar *
+g_vfs_get_dns_sd_uri_for_triple (const gchar *service_name,
+                                 const gchar *service_type,
+                                 const gchar *domain)
+{
+  gchar *escaped_service_name;
+  gchar *ret;
+
+  g_return_val_if_fail (service_name != NULL, NULL);
+  g_return_val_if_fail (service_type != NULL, NULL);
+  g_return_val_if_fail (domain != NULL, NULL);
+
+  escaped_service_name = escape_service_name (service_name);
+  
+  ret = g_strdup_printf ("dns-sd://%s/%s.%s",
+                         domain,
+                         escaped_service_name,
+                         service_type);
+  g_free (escaped_service_name);
+
+  return ret;
+}
+
+/**
+ * g_vfs_encode_dns_sd_triple:
+ * @service_name: DNS-SD service name.
+ * @service_type: DNS-SD service type.
+ * @domain: DNS-SD domain.
+ *
+ * Creates an encoded triple representing a DNS-SD service. The triple
+ * will be of the form
+ * <literal>service_name.service_type.domain</literal> with suitable
+ * encoding.
+ *
+ * Use g_vfs_decode_dns_sd_triple() to decode the returned string.
+ *
+ * Returns: A string representing the triple, free with g_free().
+ **/
+gchar *
+g_vfs_encode_dns_sd_triple (const gchar *service_name,
+                            const gchar *service_type,
+                            const gchar *domain)
+{
+  char *dot_escaped_service_name;
+  char *escaped_service_name;
+  char *escaped_service_type;
+  char *escaped_domain;
+  char *s;
+
+  escaped_service_name = g_uri_escape_string (service_name, NULL, FALSE);
+  dot_escaped_service_name = escape_service_name2 (escaped_service_name);
+  escaped_service_type = g_uri_escape_string (service_type, NULL, FALSE);
+  escaped_domain = g_uri_escape_string (domain, NULL, FALSE);
+  s = g_strdup_printf ("%s.%s.%s",
+                       dot_escaped_service_name,
+                       escaped_service_type,
+                       escaped_domain);
+  g_free (dot_escaped_service_name);
+  g_free (escaped_service_name);
+  g_free (escaped_service_type);
+  g_free (escaped_domain);
+  return s;
+}
+
+/**
+ * g_vfs_decode_dns_sd_triple:
+ * @encoded_triple: A string obtained from g_vfs_encode_dns_sd_triple().
+ * @out_service_name: %NULL or return location for the service name.
+ * @out_service_type: %NULL or return location for the service type.
+ * @out_domain: %NULL or return location for the domain.
+ * @error: Return location for error or %NULL.
+ *
+ * Constructs a DNS-SD triple by decoding a string generated from
+ * g_vfs_encode_dns_sd_triple(). This can fail if @encoded_triple is
+ * malformed.
+ *
+ * Returns: %TRUE unless @error is set.
+ **/
+gboolean
+g_vfs_decode_dns_sd_triple (const gchar *encoded_triple,
+                            gchar      **out_service_name,
+                            gchar      **out_service_type,
+                            gchar      **out_domain,
+                            GError     **error)
+{
+  gboolean ret;
+  int n;
+  int m;
+  int service_type_pos;
+  char *escaped_service_name;
+  char *escaped_service_type;
+  char *escaped_domain;
+
+  g_return_val_if_fail (encoded_triple != NULL, FALSE);
+
+
+  escaped_service_name = NULL;
+  escaped_service_type = NULL;
+  escaped_domain = NULL;
+  ret = FALSE;
+
+  if (out_service_name != NULL)
+    *out_service_name = NULL;
+
+  if (out_service_type != NULL)
+    *out_service_type = NULL;
+
+  if (out_domain != NULL)
+    *out_domain = NULL;
+
+  /* Find first '.' followed by an underscore. */
+  for (n = 0; encoded_triple[n] != '\0'; n++)
+    {
+      if (encoded_triple[n] == '.')
+        {
+          if (encoded_triple[n + 1] == '_')
+            break;
+        }
+    }
+  if (encoded_triple[n] == '\0')
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Malformed dns-sd encoded_triple '%s'"),
+                   encoded_triple);
+      goto out;
+    }
+
+  escaped_service_name = g_strndup (encoded_triple, n);
+  if (escaped_service_name == NULL)
+    goto out;
+
+  if (out_service_name != NULL)
+    *out_service_name = g_uri_unescape_string (escaped_service_name, NULL);
+
+  /* skip dot between service name and service type */
+  n += 1;
+
+  service_type_pos = n;
+
+  /* skip next two dots */
+  for (m = 0; m < 2; m++)
+    {
+      for (; encoded_triple[n] != '\0'; n++)
+        {
+          if (encoded_triple[n] == '.')
+            break;
+        }
+      if (encoded_triple[n] == '\0')
+        {
+          g_set_error (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_INVALID_ARGUMENT,
+                       _("Malformed dns-sd encoded_triple '%s'"),
+                       encoded_triple);
+          goto out;
+        }
+      n++;
+    }
+
+  escaped_service_type = g_strndup (encoded_triple + service_type_pos, n - service_type_pos - 1);
+  if (out_service_type != NULL)
+    *out_service_type = g_uri_unescape_string (escaped_service_type, NULL);
+
+  /* the domain is the rest */
+  if (encoded_triple[n] == '\0')
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Malformed dns-sd encoded_triple '%s'"),
+                   encoded_triple);
+      goto out;
+    }
+
+  escaped_domain = g_strdup (encoded_triple + n);
+  if (out_domain != NULL)
+    *out_domain = g_uri_unescape_string (escaped_domain, NULL);
+
+  ret = TRUE;
+
+ out:
+  g_free (escaped_service_name);
+  g_free (escaped_service_type);
+  g_free (escaped_domain);
+  return ret;
+}
+
+gchar *
+g_vfs_normalize_encoded_dns_sd_triple (const gchar *encoded_triple)
+{
+  char *service_name;
+  char *service_type;
+  char *domain;
+  char *ret;
+
+  ret = NULL;
+
+  if (!g_vfs_decode_dns_sd_triple (encoded_triple,
+                                   &service_name,
+                                   &service_type,
+                                   &domain,
+                                   NULL))
+    goto out;
+
+  ret = g_vfs_encode_dns_sd_triple (service_name, service_type, domain);
+  g_free (service_name);
+  g_free (service_type);
+  g_free (domain);
+
+ out:
+  return ret;
+}
+

Added: trunk/common/gvfsdnssdutils.h
==============================================================================
--- (empty file)
+++ trunk/common/gvfsdnssdutils.h	Mon Dec  1 09:24:00 2008
@@ -0,0 +1,50 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_VFS_DNS_SD_UTILS_H__
+#define __G_VFS_DNS_SD_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gchar *g_vfs_encode_dns_sd_triple (const gchar *service_name,
+                                   const gchar *service_type,
+                                   const gchar *domain);
+
+gchar *g_vfs_normalize_encoded_dns_sd_triple (const gchar *encoded_triple);
+
+gboolean
+g_vfs_decode_dns_sd_triple (const gchar *encoded_triple,
+                            gchar      **out_service_name,
+                            gchar      **out_service_type,
+                            gchar      **out_domain,
+                            GError     **error);
+
+gchar *
+g_vfs_get_dns_sd_uri_for_triple (const gchar *service_name,
+                                 const gchar *service_type,
+                                 const gchar *domain);
+
+G_END_DECLS
+
+#endif /* __G_VFS_DNS_SD_UTILS_H__ */

Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am	(original)
+++ trunk/daemon/Makefile.am	Mon Dec  1 09:24:00 2008
@@ -42,6 +42,9 @@
 mount_in_files += http.mount.in dav.mount.in ftp.mount.in 
 if HAVE_HTTP
 mount_DATA += http.mount dav.mount ftp.mount
+if HAVE_AVAHI
+mount_DATA += dav+sd.mount
+endif
 libexec_PROGRAMS += gvfsd-http gvfsd-dav gvfsd-ftp
 endif
 
@@ -304,7 +307,7 @@
 	$(AVAHI_CFLAGS) \
 	-DBACKEND_TYPES='"dns-sd", G_VFS_TYPE_BACKEND_DNS_SD,'
 
-gvfsd_dnssd_LDADD = $(libraries) $(AVAHI_LIBS)
+gvfsd_dnssd_LDADD = $(libraries) $(AVAHI_LIBS) $(top_builddir)/common/libgvfscommon-dnssd.la
 
 
 gvfsd_archive_SOURCES = \
@@ -391,8 +394,15 @@
 	-DBACKEND_HEADER=gvfsbackenddav.h \
 	-DDEFAULT_BACKEND_TYPE=dav \
 	-DMAX_JOB_THREADS=1 \
-	$(HTTP_CFLAGS) \
-	-DBACKEND_TYPES='"dav", G_VFS_TYPE_BACKEND_DAV,'
+	$(HTTP_CFLAGS)
 
-gvfsd_dav_LDADD = $(libraries) $(HTTP_LIBS)
+if HAVE_AVAHI
+gvfsd_dav_CPPFLAGS += -DBACKEND_TYPES='"dav", G_VFS_TYPE_BACKEND_DAV, "dav+sd", G_VFS_TYPE_BACKEND_DAV, "davs+sd", G_VFS_TYPE_BACKEND_DAV,'
+else
+gvfsd_dav_CPPFLAGS += -DBACKEND_TYPES='"dav", G_VFS_TYPE_BACKEND_DAV,'
+endif
 
+gvfsd_dav_LDADD = $(libraries) $(HTTP_LIBS)
+if HAVE_AVAHI
+gvfsd_dav_LDADD += $(top_builddir)/common/libgvfscommon-dnssd.la
+endif

Added: trunk/daemon/dav+sd.mount.in
==============================================================================
--- (empty file)
+++ trunk/daemon/dav+sd.mount.in	Mon Dec  1 09:24:00 2008
@@ -0,0 +1,4 @@
+[Mount]
+Type=dav+sd;davs+sd
+Exec= libexecdir@/gvfsd-dav
+AutoMount=false

Modified: trunk/daemon/dav.mount.in
==============================================================================
--- trunk/daemon/dav.mount.in	(original)
+++ trunk/daemon/dav.mount.in	Mon Dec  1 09:24:00 2008
@@ -1,4 +1,4 @@
 [Mount]
-Type=dav
+Type=dav;davs
 Exec= libexecdir@/gvfsd-dav
 AutoMount=false

Modified: trunk/daemon/gvfsbackenddav.c
==============================================================================
--- trunk/daemon/gvfsbackenddav.c	(original)
+++ trunk/daemon/gvfsbackenddav.c	Mon Dec  1 09:24:00 2008
@@ -61,10 +61,20 @@
 #include "soup-input-stream.h"
 #include "soup-output-stream.h"
 
+#ifdef HAVE_AVAHI
+#include "gvfsdnssdutils.h"
+#include "gvfsdnssdresolver.h"
+#endif
+
 typedef struct _MountAuthData MountAuthData;
 
 static void mount_auth_info_free (MountAuthData *info);
 
+
+#ifdef HAVE_AVAHI
+static void dns_sd_resolver_changed  (GVfsDnsSdResolver *resolver, GVfsBackendDav *dav_backend);
+#endif
+
 typedef struct _AuthInfo {
 
    /* for server authentication */
@@ -91,6 +101,11 @@
   GVfsBackendHttp parent_instance;
 
   MountAuthData auth_info;
+
+#ifdef HAVE_AVAHI
+  /* only set if we're handling a [dav|davs]+sd:// mounts */
+  GVfsDnsSdResolver *resolver;
+#endif
 };
 
 G_DEFINE_TYPE (GVfsBackendDav, g_vfs_backend_dav, G_VFS_TYPE_BACKEND_HTTP);
@@ -102,6 +117,14 @@
 
   dav_backend = G_VFS_BACKEND_DAV (object);
 
+#ifdef HAVE_AVAHI
+  if (dav_backend->resolver != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (dav_backend->resolver, dns_sd_resolver_changed, dav_backend);
+      g_object_unref (dav_backend->resolver);
+    }
+#endif
+
   mount_auth_info_free (&(dav_backend->auth_info));
   
   if (G_OBJECT_CLASS (g_vfs_backend_dav_parent_class)->finalize)
@@ -209,10 +232,10 @@
   a_len = strlen (a);
   b_len = strlen (b);
 
-  while (a[a_len - 1] == '/')
+  while (a_len > 0 && a[a_len - 1] == '/')
     a_len--;
 
-  while (b[b_len - 1] == '/')
+  while (b_len > 0 && b[b_len - 1] == '/')
     b_len--;
 
   if (a_len == b_len)
@@ -1392,11 +1415,32 @@
 }
 
 static GMountSpec *
-g_mount_spec_from_dav_uri (SoupURI *uri)
+g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend,
+                           SoupURI *uri)
 {
   GMountSpec *spec;
   const char *ssl;
 
+#ifdef HAVE_AVAHI
+  if (dav_backend->resolver != NULL)
+    {
+      const char *type;
+      const char *service_type;
+
+      service_type = g_vfs_dns_sd_resolver_get_service_type (dav_backend->resolver);
+      if (strcmp (service_type, "_webdavs._tcp") == 0)
+        type = "davs+sd";
+      else
+        type = "dav+sd";
+
+      spec = g_mount_spec_new (type);
+      g_mount_spec_set (spec,
+                        "host",
+                        g_vfs_dns_sd_resolver_get_encoded_triple (dav_backend->resolver));
+      return spec;
+    }
+#endif
+
   spec = g_mount_spec_new ("dav");
 
   g_mount_spec_set (spec, "host", uri->host);
@@ -1423,6 +1467,63 @@
   return spec;
 }
 
+#ifdef HAVE_AVAHI
+static SoupURI *
+dav_uri_from_dns_sd_resolver (GVfsBackendDav *dav_backend)
+{
+  SoupURI    *uri;
+  char       *user;
+  char       *path;
+  char       *address;
+  const char *service_type;
+  guint       port;
+
+  service_type = g_vfs_dns_sd_resolver_get_service_type (dav_backend->resolver);
+  address = g_vfs_dns_sd_resolver_get_address (dav_backend->resolver);
+  port = g_vfs_dns_sd_resolver_get_port (dav_backend->resolver);
+  user = g_vfs_dns_sd_resolver_lookup_txt_record (dav_backend->resolver, "u"); /* mandatory */
+  path = g_vfs_dns_sd_resolver_lookup_txt_record (dav_backend->resolver, "path"); /* optional */
+
+  /* TODO: According to http://www.dns-sd.org/ServiceTypes.html
+   * there's also a TXT record "p" for password. Handle this.
+   */
+
+  uri = soup_uri_new (NULL);
+
+  if (strcmp (service_type, "_webdavs._tcp") == 0)
+    soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
+  else
+    soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
+
+  soup_uri_set_user (uri, user);
+
+  soup_uri_set_port (uri, port);
+
+  soup_uri_set_host (uri, address);
+
+  if (path != NULL)
+    soup_uri_set_path (uri, path);
+  else
+    soup_uri_set_path (uri, "/");
+
+
+  g_free (address);
+  g_free (user);
+  g_free (path);
+
+  return uri;
+}
+#endif
+
+#ifdef HAVE_AVAHI
+static void
+dns_sd_resolver_changed (GVfsDnsSdResolver *resolver,
+                         GVfsBackendDav    *dav_backend)
+{
+  /* TODO: handle when DNS-SD data changes */
+}
+#endif
+
 /* ************************************************************************* */
 /* Backend Functions */
 static void
@@ -1432,6 +1533,7 @@
           GMountSource *mount_source,
           gboolean      is_automount)
 {
+  GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend);
   MountAuthData  *data;
   SoupSession    *session;
   SoupMessage    *msg_opts;
@@ -1444,10 +1546,43 @@
   gboolean        res;
   char           *last_good_path;
   char           *display_name;
+  const char     *host;
+  const char     *type;
 
   g_print ("+ mount\n");
 
-  mount_base = g_mount_spec_to_dav_uri (mount_spec);
+  host = g_mount_spec_get (mount_spec, "host");
+  type = g_mount_spec_get (mount_spec, "type");
+
+#ifdef HAVE_AVAHI
+  /* resolve DNS-SD style URIs */
+  if ((strcmp (type, "dav+sd") == 0 || strcmp (type, "davs+sd") == 0) && host != NULL)
+    {
+      GError *error;
+
+      dav_backend->resolver = g_vfs_dns_sd_resolver_new_for_encoded_triple (host, "u");
+
+      error = NULL;
+      if (!g_vfs_dns_sd_resolver_resolve_sync (dav_backend->resolver,
+                                               NULL,
+                                               &error))
+        {
+          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+          g_error_free (error);
+          return;
+        }
+      g_signal_connect (dav_backend->resolver,
+                        "changed",
+                        (GCallback) dns_sd_resolver_changed,
+                        dav_backend);
+
+      mount_base = dav_uri_from_dns_sd_resolver (dav_backend);
+    }
+  else
+#endif
+    {
+      mount_base = g_mount_spec_to_dav_uri (mount_spec);
+    }
 
   if (mount_base == NULL)
     {
@@ -1546,12 +1681,17 @@
   mount_base->path = last_good_path;
 
   /* dup the mountspec, but only copy known fields */
-  mount_spec = g_mount_spec_from_dav_uri (mount_base);
+  mount_spec = g_mount_spec_from_dav_uri (dav_backend, mount_base);
 
   g_vfs_backend_set_mount_spec (backend, mount_spec);
   g_vfs_backend_set_icon_name (backend, "folder-remote");
   
-  display_name = g_strdup_printf (_("WebDAV on %s"), mount_base->host);
+#ifdef HAVE_AVAHI
+  if (dav_backend->resolver != NULL)
+    display_name = g_strdup (g_vfs_dns_sd_resolver_get_service_name (dav_backend->resolver));
+  else
+#endif
+    display_name = g_strdup_printf (_("WebDAV on %s"), mount_base->host);
   g_vfs_backend_set_display_name (backend, display_name);
   g_free (display_name);
   

Modified: trunk/daemon/gvfsbackenddnssd.c
==============================================================================
--- trunk/daemon/gvfsbackenddnssd.c	(original)
+++ trunk/daemon/gvfsbackenddnssd.c	Mon Dec  1 09:24:00 2008
@@ -37,6 +37,7 @@
 #include <avahi-glib/glib-malloc.h>
 
 #include "gvfsbackenddnssd.h"
+#include "gvfsdnssdutils.h"
 
 #include "gvfsdaemonprotocol.h"
 #include "gvfsjobcreatemonitor.h"
@@ -45,15 +46,40 @@
 #include "gvfsmonitor.h"
 
 static struct {
-	char *type;
-	char *method;
-	char *icon;
-	gpointer handle;
+  char *type;
+  char *method;
+  gboolean use_dns_sd_uri;
+  char *icon;
 } dns_sd_types[] = {
-	{"_ftp._tcp", "ftp", "folder-remote-ftp"},
-	{"_webdav._tcp", "dav", "folder-remote"},
-	{"_webdavs._tcp", "davs", "folder-remote"},
-	{"_sftp-ssh._tcp", "sftp", "folder-remote-ssh"},
+	{
+          "_ftp._tcp",
+          "ftp",
+          FALSE,
+          "folder-remote-ftp"
+        },
+	{
+          "_webdav._tcp",
+          "dav+sd",
+          TRUE,
+          "folder-remote-dav"
+        },
+	{
+          "_webdavs._tcp",
+          "davs+sd",
+          TRUE,
+          "folder-remote-davs"},
+	{
+          "_sftp-ssh._tcp",
+          "sftp",
+          FALSE,
+          "folder-remote-ssh"
+        },
+	{
+          "_ssh._tcp",
+          "sftp",
+          FALSE,
+          "folder-remote-ssh"
+        },
 };
 
 static AvahiClient *global_client = NULL;
@@ -62,15 +88,19 @@
 static GList *dnssd_backends = NULL;
 
 typedef struct {
-  char *file_name; 
+  char *file_name;
   char *name;
   char *type;
+  char *domain;
   char *target_uri;
+
   GIcon *icon;
 } LinkFile;
 
 static LinkFile root = { "/" };
 
+static gboolean resolver_supports_mdns = FALSE;
+
 struct _GVfsBackendDnsSd
 {
   GVfsBackend parent_instance;
@@ -164,7 +194,7 @@
   for (i = 0; i < G_N_ELEMENTS (dns_sd_types); i++)
     {
       if (strcmp (type, dns_sd_types[i].type) == 0)
-	return g_themed_icon_new (dns_sd_types[i].icon);
+	return g_themed_icon_new_with_default_fallbacks (dns_sd_types[i].icon);
     }
   
   return g_themed_icon_new ("text-x-generic");
@@ -184,34 +214,18 @@
   return NULL;
 }
 
-static char *
-encode_filename (const char *service,
-		 const char *type)
+static gboolean
+use_dns_sd_uri_for_type (const char *type)
 {
-  GString *string;
-  const char *p;
-  
-  string = g_string_new (NULL);
-  
-  p = service;
-  
-  while (*p)
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (dns_sd_types); i++)
     {
-      if (*p == '\\') 
-	g_string_append (string, "\\\\");
-      else if (*p == '.') 
-	g_string_append (string, "\\.");
-      else if (*p == '/') 
-	g_string_append (string, "\\s");
-      else
-	g_string_append_c (string, *p);
-      p++;
+      if (strcmp (type, dns_sd_types[i].type) == 0)
+	return dns_sd_types[i].use_dns_sd_uri;
     }
-  
-  g_string_append_c (string, '.');
-  g_string_append (string, type);
-  
-  return g_string_free (string, FALSE);
+
+  return FALSE;
 }
 
 static LinkFile *
@@ -225,56 +239,89 @@
 	       AvahiStringList *txt)
 {
   LinkFile *file;
-  char *path, *user, *user_str;
-  AvahiStringList *path_l, *user_l;
   char a[128];
   const char *method;
-  
+  char *uri;
+
   file = g_slice_new0 (LinkFile);
 
   file->name = g_strdup (name);
   file->type = g_strdup (type);
-  file->file_name = encode_filename (name, type);
+  file->domain = g_strdup (domain);
   file->icon = get_icon_for_type (type);
-	
 
-  path = NULL;
-  user_str = NULL;
-  if (txt != NULL)
-    {
-      path_l = avahi_string_list_find (txt, "path");
-      if (path_l != NULL)
-	avahi_string_list_get_pair (path_l, NULL, &path, NULL);
-      
-      user_l = avahi_string_list_find (txt, "u");
-      if (user_l != NULL)
-	{
-	  avahi_string_list_get_pair (user_l, NULL, &user, NULL);
-	  
-	  user_str = g_strconcat (user, "@", NULL);
-	}
-    }
-  
-  if (path == NULL)
-    path = g_strdup ("/");
-      
+  uri = g_vfs_get_dns_sd_uri_for_triple (name, type, domain);
+  file->file_name = g_path_get_basename (uri);
+  g_free (uri);
+
   avahi_address_snprint (a, sizeof(a), address);
 
   method = get_method_for_type (type);
 
-  if (protocol == AVAHI_PROTO_INET6)
-	/* an ipv6 address, follow rfc2732 */
-    file->target_uri = g_strdup_printf ("%s://%s[%s]:%d%s",
-					method,
-					user_str?user_str:"",
-					a, port, path);
+  if (use_dns_sd_uri_for_type (type))
+    {
+      char *encoded_triple;
+      encoded_triple = g_vfs_encode_dns_sd_triple (name, type, domain);
+      file->target_uri = g_strdup_printf ("%s://%s",
+                                          method,
+                                          encoded_triple);
+      g_free (encoded_triple);
+    }
   else
-    file->target_uri = g_strdup_printf ("%s://%s%s:%d%s",
-					method,
-					user_str?user_str:"",
-					a, port, path);
-  g_free (user_str);
-  g_free (path);
+    {
+      char *path, *user, *user_str;
+      AvahiStringList *path_l, *user_l;
+
+      path = NULL;
+      user_str = NULL;
+      if (txt != NULL)
+        {
+          path_l = avahi_string_list_find (txt, "path");
+          if (path_l != NULL)
+            avahi_string_list_get_pair (path_l, NULL, &path, NULL);
+          user_l = avahi_string_list_find (txt, "u");
+          if (user_l != NULL)
+            {
+              avahi_string_list_get_pair (user_l, NULL, &user, NULL);
+              user_str = g_strconcat (user, "@", NULL);
+            }
+        }
+      if (path == NULL)
+        path = g_strdup ("/");
+
+      if (resolver_supports_mdns)
+        {
+          file->target_uri = g_strdup_printf ("%s://%s%s:%d%s",
+                                              method,
+                                              user_str != NULL ? user_str : "",
+                                              host_name, port, path);
+        }
+      else
+        {
+          if (protocol == AVAHI_PROTO_INET6)
+            {
+              /* an ipv6 address, follow rfc2732 */
+              file->target_uri = g_strdup_printf ("%s://%s[%s]:%d%s",
+                                                  method,
+                                                  user_str?user_str:"",
+                                                  a,
+                                                  port,
+                                                  path);
+            }
+          else
+            {
+              file->target_uri = g_strdup_printf ("%s://%s%s:%d%s",
+                                                  method,
+                                                  user_str?user_str:"",
+                                                  a,
+                                                  port,
+                                                  path);
+            }
+        }
+
+      g_free (user_str);
+      g_free (path);
+    }
 
   return file;
 }
@@ -285,6 +332,7 @@
   g_free (file->file_name);
   g_free (file->name);
   g_free (file->type);
+  g_free (file->domain);
   g_free (file->target_uri);
  
   if (file->icon)
@@ -357,7 +405,7 @@
   g_file_info_set_name (info, file->file_name);
   g_file_info_set_display_name (info, file->name);
 
-  if (file->icon) 
+  if (file->icon)
     g_file_info_set_icon (info, file->icon);
 
   g_file_info_set_file_type (info, G_FILE_TYPE_SHORTCUT);
@@ -366,8 +414,7 @@
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
-  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, 
-                                    file->target_uri);
+  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, file->target_uri);
 }
 
 /* Backend Functions */
@@ -427,13 +474,28 @@
   if (file == &root)
     {
       GIcon *icon;
+      char *display_name;
+      char *s;
+
+      s = g_strdup (job->uri);
+      if (s[strlen(s) - 1] == '/') /* job->uri is guranteed to be longer than 1 byte */
+        s[strlen(s) - 1] = '\0';
+      display_name = g_path_get_basename (s);
+      if (strcmp (display_name, "local") == 0)
+        {
+          g_free (display_name);
+          display_name = g_strdup (_("Local Network"));
+        }
+      g_free (s);
+
       g_file_info_set_name (info, "/");
       g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
-      /* TODO: Name */
-      g_file_info_set_display_name (info, _("dns-sd"));
+      g_file_info_set_display_name (info, display_name);
       icon = g_themed_icon_new ("network-workgroup");
       g_file_info_set_icon (info, icon);
       g_object_unref (icon);
+      g_free (display_name);
+
       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
@@ -689,6 +751,8 @@
   g_vfs_backend_set_icon_name (backend, "network-workgroup");
   g_vfs_backend_set_user_visible (backend, FALSE);
 
+  resolver_supports_mdns = (avahi_nss_support () > 0);
+
 }
 
 static void

Modified: trunk/daemon/gvfsbackendnetwork.c
==============================================================================
--- trunk/daemon/gvfsbackendnetwork.c	(original)
+++ trunk/daemon/gvfsbackendnetwork.c	Mon Dec  1 09:24:00 2008
@@ -411,7 +411,7 @@
 	      file = network_file_new (file_name,
 				       domains[i],
 				       link_uri,
-				       backend->server_icon);
+				       backend->workgroup_icon);
 	      files = g_list_prepend (files, file);   
 	      g_free (link_uri);
 	      g_free (file_name);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]