summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2008-12-01 09:24:00 +0000
committerAlexander Larsson <alexl@src.gnome.org>2008-12-01 09:24:00 +0000
commitf8875b2649a0f85624e0da7df96fa6d47840c4a9 (patch)
treedf7f32b8849652f03b597aead9f1b56d697d806a
parent6e89574f1656ff71f5aba339ff38e8e0417b172b (diff)
downloadgvfs-f8875b2649a0f85624e0da7df96fa6d47840c4a9.tar.gz
Added. Added. Added. Added.
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) svn path=/trunk/; revision=2112
-rw-r--r--ChangeLog20
-rw-r--r--common/Makefile.am17
-rw-r--r--common/gvfsdnssdresolver.c1253
-rw-r--r--common/gvfsdnssdresolver.h80
-rw-r--r--common/gvfsdnssdutils.c323
-rw-r--r--common/gvfsdnssdutils.h50
-rw-r--r--daemon/Makefile.am18
-rw-r--r--daemon/dav+sd.mount.in4
-rw-r--r--daemon/dav.mount.in2
-rw-r--r--daemon/gvfsbackenddav.c152
-rw-r--r--daemon/gvfsbackenddnssd.c218
-rw-r--r--daemon/gvfsbackendnetwork.c2
12 files changed, 2050 insertions, 89 deletions
diff --git a/ChangeLog b/ChangeLog
index 098b5975..d28192ad 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+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)
+
2008-11-18 Alexander Larsson <alexl@redhat.com>
* daemon/gvfsbackendsftp.c:
diff --git a/common/Makefile.am b/common/Makefile.am
index 12dd9e83..4e10cf8b 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -22,3 +22,20 @@ libgvfscommon_la_SOURCES = \
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
+
diff --git a/common/gvfsdnssdresolver.c b/common/gvfsdnssdresolver.c
new file mode 100644
index 00000000..38327502
--- /dev/null
+++ b/common/gvfsdnssdresolver.c
@@ -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;
+}
diff --git a/common/gvfsdnssdresolver.h b/common/gvfsdnssdresolver.h
new file mode 100644
index 00000000..a694ff93
--- /dev/null
+++ b/common/gvfsdnssdresolver.h
@@ -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__ */
diff --git a/common/gvfsdnssdutils.c b/common/gvfsdnssdutils.c
new file mode 100644
index 00000000..f6592e1a
--- /dev/null
+++ b/common/gvfsdnssdutils.c
@@ -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;
+}
+
diff --git a/common/gvfsdnssdutils.h b/common/gvfsdnssdutils.h
new file mode 100644
index 00000000..21fe8a54
--- /dev/null
+++ b/common/gvfsdnssdutils.h
@@ -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__ */
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 653018b8..035be2c1 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -42,6 +42,9 @@ mount_DATA = sftp.mount trash.mount computer.mount burn.mount localtest.mount
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 @@ gvfsd_dnssd_CPPFLAGS = \
$(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 @@ gvfsd_dav_CPPFLAGS = \
-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
diff --git a/daemon/dav+sd.mount.in b/daemon/dav+sd.mount.in
new file mode 100644
index 00000000..45703270
--- /dev/null
+++ b/daemon/dav+sd.mount.in
@@ -0,0 +1,4 @@
+[Mount]
+Type=dav+sd;davs+sd
+Exec=@libexecdir@/gvfsd-dav
+AutoMount=false
diff --git a/daemon/dav.mount.in b/daemon/dav.mount.in
index 6e75bce7..6324625c 100644
--- a/daemon/dav.mount.in
+++ b/daemon/dav.mount.in
@@ -1,4 +1,4 @@
[Mount]
-Type=dav
+Type=dav;davs
Exec=@libexecdir@/gvfsd-dav
AutoMount=false
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c
index 24c2b34c..a693ac90 100644
--- a/daemon/gvfsbackenddav.c
+++ b/daemon/gvfsbackenddav.c
@@ -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 @@ struct _GVfsBackendDav
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 @@ g_vfs_backend_dav_finalize (GObject *object)
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 @@ path_equal (const char *a, const char *b, gboolean relax)
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 @@ g_mount_spec_to_dav_uri (GMountSpec *spec)
}
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 @@ g_mount_spec_from_dav_uri (SoupURI *uri)
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 @@ do_mount (GVfsBackend *backend,
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 @@ do_mount (GVfsBackend *backend,
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 @@ do_mount (GVfsBackend *backend,
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);
diff --git a/daemon/gvfsbackenddnssd.c b/daemon/gvfsbackenddnssd.c
index 3a224a50..9ef21eaa 100644
--- a/daemon/gvfsbackenddnssd.c
+++ b/daemon/gvfsbackenddnssd.c
@@ -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 gboolean avahi_initialized = FALSE;
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 @@ get_icon_for_type (const char *type)
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 @@ get_method_for_type (const char *type)
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 @@ link_file_new (const char *name,
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 @@ link_file_free (LinkFile *file)
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 @@ file_info_from_file (LinkFile *file,
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 @@ file_info_from_file (LinkFile *file,
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 @@ try_query_info (GVfsBackend *backend,
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_dns_sd_init (GVfsBackendDnsSd *network_backend)
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
diff --git a/daemon/gvfsbackendnetwork.c b/daemon/gvfsbackendnetwork.c
index d23438e7..939461e3 100644
--- a/daemon/gvfsbackendnetwork.c
+++ b/daemon/gvfsbackendnetwork.c
@@ -411,7 +411,7 @@ recompute_files (GVfsBackendNetwork *backend)
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);