gvfs r1116 - trunk/daemon



Author: gicmo
Date: Sun Jan 13 21:50:47 2008
New Revision: 1116
URL: http://svn.gnome.org/viewvc/gvfs?rev=1116&view=rev

Log:
2008-01-13  Christian Kellner <gicmo gnome org>

	* client/httpuri.c: 
	Also handle dav uris in the http mapper.

	* daemon/dav.mount.in:
	* daemon/Makefile.am:
	Add the new dav backend.

	* daemon/gvfsbackendhttp.c: 
	* daemon/gvfsbackendhttp.h:
	Implement a utility function so dav and http
	backend can share uri from filename creation.

	* daemon/gvfsbackenddav.c: 
	First attempt to write the dav backend as a subclass of http.
	Mount, QueryInfo, Enumerate should work and Read is provided
	by the base class.


Added:
   trunk/daemon/dav.mount.in
   trunk/daemon/gvfsbackenddav.c
   trunk/daemon/gvfsbackenddav.h
Modified:
   trunk/daemon/Makefile.am
   trunk/daemon/gvfsbackendhttp.c
   trunk/daemon/gvfsbackendhttp.h

Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am	(original)
+++ trunk/daemon/Makefile.am	Sun Jan 13 21:50:47 2008
@@ -30,10 +30,10 @@
 %.mount: %.mount.in ../config.log
 	sed -e "s|\ libexecdir\@|$(libexecdir)|" $< > $@
 
-libexec_PROGRAMS=gvfsd gvfsd-ftp gvfsd-sftp gvfsd-http gvfsd-trash gvfsd-computer gvfsd-localtest
+libexec_PROGRAMS=gvfsd gvfsd-ftp gvfsd-sftp gvfsd-http gvfsd-dav gvfsd-trash gvfsd-computer gvfsd-localtest
 
-mount_in_files = ftp.mount.in sftp.mount.in http.mount.in trash.mount.in computer.mount.in localtest.mount.in
-mount_DATA = ftp.mount sftp.mount trash.mount http.mount computer.mount localtest.mount
+mount_in_files = ftp.mount.in sftp.mount.in http.mount.in dav.mount.in trash.mount.in computer.mount.in localtest.mount.in
+mount_DATA = ftp.mount sftp.mount trash.mount http.mount dav.mount computer.mount localtest.mount
 
 if HAVE_SAMBA
 mount_in_files += smb.mount.in smb-browse.mount.in
@@ -235,3 +235,20 @@
 	-DBACKEND_TYPES='"http", G_VFS_TYPE_BACKEND_HTTP,'
 
 gvfsd_http_LDADD = $(libraries) $(HTTP_LIBS)
+
+gvfsd_dav_SOURCES = \
+	soup-input-stream.c soup-input-stream.h \
+	gvfsbackendhttp.c gvfsbackendhttp.h \
+	gvfsbackenddav.c gvfsbackenddav.h \
+	daemon-main.c daemon-main.h \
+	daemon-main-generic.c 
+
+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,'
+
+gvfsd_dav_LDADD = $(libraries) $(HTTP_LIBS)
+

Added: trunk/daemon/dav.mount.in
==============================================================================
--- (empty file)
+++ trunk/daemon/dav.mount.in	Sun Jan 13 21:50:47 2008
@@ -0,0 +1,4 @@
+[Mount]
+Type=dav
+Exec= libexecdir@/gvfsd-dav
+AutoMount=false

Added: trunk/daemon/gvfsbackenddav.c
==============================================================================
--- (empty file)
+++ trunk/daemon/gvfsbackenddav.c	Sun Jan 13 21:50:47 2008
@@ -0,0 +1,943 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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: Christian Kellner <gicmo gnome org>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-headers.h>
+#include <libsoup/soup-uri.h>
+
+/* LibXML2 includes */
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "gvfsbackenddav.h"
+#include "gvfsjobmount.h"
+#include "gvfsjobopenforread.h"
+#include "gvfsjobread.h"
+#include "gvfsjobseekread.h"
+#include "gvfsjobopenforwrite.h"
+#include "gvfsjobwrite.h"
+#include "gvfsjobseekwrite.h"
+#include "gvfsjobsetdisplayname.h"
+#include "gvfsjobqueryinfo.h"
+#include "gvfsjobqueryfsinfo.h"
+#include "gvfsjobqueryattributes.h"
+#include "gvfsjobenumerate.h"
+#include "gvfsdaemonprotocol.h"
+
+#include "soup-input-stream.h"
+
+
+struct _GVfsBackendDav
+{
+  GVfsBackendHttp parent_instance;
+
+  /* Only used during mount: */
+  char         *last_good_path;
+  GMountSource *mount_source;
+};
+
+G_DEFINE_TYPE (GVfsBackendDav, g_vfs_backend_dav, G_VFS_TYPE_BACKEND_HTTP);
+
+static void
+g_vfs_backend_dav_finalize (GObject *object)
+{
+  GVfsBackendDav *backend;
+
+  backend = G_VFS_BACKEND_DAV (object);
+
+  if (G_OBJECT_CLASS (g_vfs_backend_dav_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_vfs_backend_dav_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_backend_dav_init (GVfsBackendDav *backend)
+{
+}
+
+/* ************************************************************************* */
+/*  */
+
+static inline gboolean
+sm_has_header (SoupMessage *msg, const char *header)
+{
+  return soup_message_get_header (msg->response_headers, header) != NULL;
+}
+
+static inline guint
+soup_protocol_default_port (SoupProtocol proto)
+{
+	if (proto == SOUP_PROTOCOL_HTTP)
+		return 80;
+	else if (proto == SOUP_PROTOCOL_HTTPS)
+		return 443;
+	else
+		return 0;
+}
+
+/* ************************************************************************* */
+/*  */
+
+static inline void
+send_message (GVfsBackend           *backend,
+              SoupMessage           *message, 
+              SoupMessageCallbackFn  callback,
+              gpointer               user_data)
+{
+
+  soup_session_queue_message (G_VFS_BACKEND_HTTP (backend)->session,
+                              message,
+                              callback, user_data);
+}
+
+static inline SoupMessage *
+message_new_from_filename (GVfsBackend *backend,
+                           const char  *method,
+                           const char  *filename)
+{
+  SoupMessage *msg;
+  SoupUri     *uri;
+
+  uri = g_vfs_backend_uri_for_filename (backend, filename);
+  msg = soup_message_new_from_uri (method, uri);
+  soup_uri_free (uri);
+
+  soup_message_add_header (msg->request_headers,
+                           "User-Agent", "gvfs/" VERSION);
+  return msg;
+}
+
+/* ************************************************************************* */
+/*  */
+static xmlDocPtr
+multistatus_parse_xml (SoupMessage *msg, xmlNodePtr *root, GError **error)
+{
+  xmlDocPtr  doc;
+
+  if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+    {
+      
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   _("HTTP Error: %s"), msg->reason_phrase);
+      return NULL;
+    }
+
+  doc = xmlReadMemory (msg->response.body,
+                       msg->response.length,
+                       "response.xml",
+                       NULL,
+                       0);
+  if (doc == NULL)
+    { 
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   _("Could not parse response"));
+      return NULL;
+    }
+
+  *root = xmlDocGetRootElement (doc);
+
+  if (doc == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   _("Empty response"));
+      return NULL;
+    }
+
+  if (strcmp ((char *) (*root)->name, "multistatus"))
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                     _("Unexpected reply from server"));
+      return NULL;
+    }
+
+  return doc;
+}
+
+static GFileType
+parse_resourcetype (xmlNodePtr rt)
+{
+  xmlNodePtr node;
+  GFileType  type;
+
+  for (node = rt->children; node; node = node->next)
+    { 
+      if (node->type == XML_ELEMENT_NODE &&
+          node->name != NULL)
+          break;
+    }
+
+  if (node == NULL)
+    return G_FILE_TYPE_REGULAR;
+
+  if (! strcmp ((char *) node->name, "collection"))
+    type = G_FILE_TYPE_DIRECTORY;
+  else if (! strcmp ((char *) node->name, "redirectref"))
+    type = G_FILE_TYPE_SYMBOLIC_LINK;
+  else
+    type = G_FILE_TYPE_UNKNOWN;
+
+  return type;
+}
+
+static GFileInfo *
+mulitstatus_parse_prop_node (xmlDocPtr doc, xmlNodePtr prop)
+{
+  GFileInfo  *info;
+  xmlNodePtr  node;
+
+  info = g_file_info_new ();
+
+  for (node = prop->children; node; node = node->next)
+    {
+     if (node->type != XML_ELEMENT_NODE ||
+          node->name == NULL)
+        {
+          continue;
+        }
+     else if (!strcmp ((char *) node->name, "resourcetype"))
+        {
+           GFileType type = parse_resourcetype (node);
+           g_file_info_set_file_type (info, type);
+        }
+     else if (!strcmp ((char *) node->name, "displayname"))
+        {
+          xmlChar *text;
+          text = xmlNodeGetContent (node);
+          g_file_info_set_display_name (info, (char *) text);
+          xmlFree (text);
+        }
+     else if (!strcmp ((char *) node->name, "getetag"))
+        {
+
+        }
+     else if (!strcmp ((char *) node->name, "creationdate"))
+        {
+          
+        }
+     else if (!strcmp ((char *) node->name, "getcontenttype"))
+        {
+          xmlChar *text;
+          text = xmlNodeGetContent (node);
+          g_file_info_set_content_type (info, (char *) text);
+          xmlFree (text);
+        }
+     else if (!strcmp ((char *) node->name, "getcontentlength"))
+        {
+          gint64 size; 
+          xmlChar *text;
+          text = xmlNodeGetContent (node);
+          size = g_ascii_strtoll ((char *) text, NULL, 10);
+          xmlFree (text);
+          g_file_info_set_size (info, size);
+        }
+    }
+  return info;
+}
+
+static GFileInfo *
+multistatus_parse_response (xmlDocPtr    doc,
+                            xmlNodePtr   resp,
+                            SoupUri     *base)
+{
+  GFileInfo  *info;
+  xmlNodePtr  node;
+  char       *name;
+  gboolean    res;
+ 
+  info = NULL;
+  name = NULL;
+
+  for (node = resp->children; node; node = node->next)
+    {
+      if (node->type != XML_ELEMENT_NODE ||
+          node->name == NULL)
+        {
+          continue;
+        }
+      else if (! strcmp ((char *) node->name, "href"))
+        {
+          xmlChar *text;
+          char    *path;
+          char    *p;
+          size_t   len;
+
+          /* FIXME: redo this (quickly hacked together) */
+          text = xmlNodeGetContent (node);
+          path = (char *) text;
+
+          if ((len = strlen (path)) > 1)
+            {
+              if (path[len - 1] == '/')
+                len--;
+            }
+
+          p = g_strrstr_len (path, len, "/");
+
+          name = g_strndup (p + 1, (len - (p - path + 1)));
+        }
+      else if (! strcmp ((char *) node->name, "propstat"))
+        {
+          xmlNodePtr  iter;
+          xmlNodePtr  prop;
+          xmlChar    *status_text;
+          guint       code;
+
+          status_text = NULL;
+          prop = NULL;
+        
+          for (iter = node->children; iter; iter = iter->next)
+            {
+                if (node->type != XML_ELEMENT_NODE ||
+                    node->name == NULL)
+                    continue;
+                else if (! strcmp ((char *) iter->name, "status"))
+                  {
+                    status_text = xmlNodeGetContent (iter);
+                  }
+                else if (! strcmp ((char *) iter->name, "prop"))
+                  {
+                    prop = iter;
+                  }
+
+                if (status_text && prop)
+                  break;
+            }
+
+          if (status_text == NULL || prop == NULL)
+            {
+              if (status_text)
+                xmlFree (status_text);
+
+              continue;
+            } 
+
+          res = soup_headers_parse_status_line ((char *) status_text,
+                                                NULL,
+                                                &code,
+                                                NULL);
+          xmlFree (status_text);
+
+          if (res == FALSE || !SOUP_STATUS_IS_SUCCESSFUL (code))
+            continue;
+
+          info = mulitstatus_parse_prop_node (doc, prop); 
+        }
+    }
+
+  /* after this loop we should have a non-null info object
+   * and the right name */
+  
+  if (info && name)
+    {
+      g_file_info_set_name (info, name);
+      g_file_info_set_edit_name (info, name);
+    }
+  else
+    {
+      if (info)
+        g_object_unref (info);
+      
+      g_free (name);
+      info = NULL;
+    }
+
+  return info;
+}
+
+static char *
+create_propfind_request (GFileAttributeMatcher *matcher, gulong *size)
+{
+    xmlOutputBufferPtr   buf;
+    xmlNodePtr           node;
+    xmlNodePtr           root;
+    xmlDocPtr            doc;
+    xmlNsPtr             nsdav;
+    char                *res;
+
+    doc = xmlNewDoc ((xmlChar *) "1.0");
+    root = xmlNewNode (NULL, (xmlChar *) "propfind");
+    nsdav = xmlNewNs (root, (xmlChar *) "DAV:", (xmlChar *) "D");
+    xmlSetNs (root, nsdav);
+
+    node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
+
+    /* FIXME: we should just ask for properties that 
+     * the matcher tells us to ask for
+     * nota bene: <D:reftarget/>  */
+    xmlNewTextChild (node, nsdav, (xmlChar *) "resourcetype", NULL);
+    xmlNewTextChild (node, nsdav, (xmlChar *) "displayname", NULL);
+    xmlNewTextChild (node, nsdav, (xmlChar *) "getetag", NULL);
+    xmlNewTextChild (node, nsdav, (xmlChar *) "getlastmodified", NULL);
+    xmlNewTextChild (node, nsdav, (xmlChar *) "creationdate", NULL);
+    xmlNewTextChild (node, nsdav, (xmlChar *) "getcontenttype", NULL);
+    xmlNewTextChild (node, nsdav, (xmlChar *) "getcontentlength", NULL);
+
+    buf = xmlAllocOutputBuffer (NULL);
+    xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
+    xmlOutputBufferFlush (buf);
+
+    res = g_strndup ((char *) buf->buffer->content, buf->buffer->use);
+    *size = buf->buffer->use;
+
+    xmlOutputBufferClose (buf);
+    xmlFreeDoc (doc);
+    return res;
+}
+
+/* ************************************************************************* */
+/*  */
+
+static void
+do_authentication (SoupSession *session,
+                   SoupMessage *msg,
+                   gchar       *auth_type,
+                   gchar       *auth_realm,
+                   char       **username,
+                   char       **password,
+                   gboolean     is_reprompt,
+                   gpointer     user_data)
+{
+  GVfsBackendDav *backend;
+  GVfsJobMount   *job;
+  SoupUri        *mount_base;
+  gboolean        res;
+  gboolean        aborted;
+  char           *prompt;
+  char           *new_password;
+  char           *new_user;
+
+  job = G_VFS_JOB_MOUNT (user_data);
+  backend = G_VFS_BACKEND_DAV (job->backend);
+  mount_base = G_VFS_BACKEND_HTTP (backend)->mount_base;
+
+  new_user = new_password = NULL;
+  prompt = g_strdup_printf (_("Enter password for %s"), auth_realm);
+
+  g_print ("+ authenticate \n");
+
+  res = g_mount_source_ask_password (backend->mount_source,
+                                     prompt,
+                                     mount_base->user,
+                                     NULL,
+                                     G_ASK_PASSWORD_NEED_PASSWORD |
+                                     G_ASK_PASSWORD_NEED_USERNAME,
+                                     &aborted,
+                                     &new_password,
+                                     &new_user,
+                                     NULL);
+
+  if (res && !aborted)
+    {
+      g_print ("Setting u/p\n");
+      *username = new_user;
+      *password = new_password;
+    }
+  else
+   {
+     g_free (new_user);
+     g_free (new_password);
+   }
+
+  g_free (prompt);
+
+  g_print ("- authenticate \n");
+}
+
+static void 
+authenticate (SoupSession *session,
+              SoupMessage *msg,
+              gchar       *auth_type,
+              gchar       *auth_realm,
+              char       **username,
+              char       **password,
+              gpointer     user_data)
+{
+  do_authentication (session,
+                     msg,
+                     auth_type,
+                     auth_realm,
+                     username,
+                     password,
+                     FALSE,
+                     user_data);
+}
+
+static void 
+reauthenticate (SoupSession *session,
+                SoupMessage *msg,
+                gchar       *auth_type,
+                gchar       *auth_realm,
+                gpointer     username,
+                gpointer     password,
+                gpointer     user_data)
+{
+  do_authentication (session,
+                     msg,
+                     auth_type,
+                     auth_realm,
+                     username,
+                     password,
+                     TRUE,
+                     user_data);
+}
+
+static void discover_mount_root_ready (SoupMessage *msg,
+                                       gpointer     user_data);
+static void
+discover_mount_root (GVfsBackendDav *backend, GVfsJobMount *job)
+{
+  GVfsBackendHttp *http_backend;
+  SoupMessage     *msg;
+  SoupSession     *session;
+  SoupUri         *mount_base;
+
+  http_backend = G_VFS_BACKEND_HTTP (backend);
+  mount_base = http_backend->mount_base;
+  session = http_backend->session;
+
+  msg = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base);
+  soup_message_add_header (msg->request_headers, "User-Agent", "gvfs/" VERSION);
+  soup_session_queue_message (session, msg, discover_mount_root_ready, job);
+}
+
+static void
+discover_mount_root_ready (SoupMessage *msg,
+                           gpointer     user_data)
+{
+  GVfsBackendDav *backend;
+  GVfsJobMount   *job;
+  GMountSpec     *mount_spec;
+  SoupUri        *mount_base;
+  gboolean        is_success;
+  gboolean        is_dav;
+
+  job = G_VFS_JOB_MOUNT (user_data);
+  backend = G_VFS_BACKEND_DAV (job->backend);
+  mount_base = G_VFS_BACKEND_HTTP (backend)->mount_base;
+
+  is_success = SOUP_STATUS_IS_SUCCESSFUL (msg->status_code);
+  is_dav = sm_has_header (msg, "DAV");
+  
+  g_print ("+ discover_mount_root_ready \n");
+
+  if (is_success && is_dav)
+    {
+      char *parent, *path;
+      size_t len;
+
+      path = backend->last_good_path = mount_base->path;
+      mount_base->path = NULL;
+
+      if ((len = strlen (path)) > 1)
+        {
+          if (path[len - 1] == '/')
+           len--;
+
+          parent = g_strrstr_len (path, len, "/");
+          mount_base->path = g_strndup (path, (parent - path) + 1);
+          discover_mount_root (backend, job);
+          return;
+        }
+      
+    }
+
+  /* we have reached the end of paths we are allowed to
+   * chdir up to */
+  
+  /* check if we at all have a good path */
+  if (backend->last_good_path == NULL)
+    {
+      if (!is_success) 
+        g_vfs_job_failed (G_VFS_JOB (job),
+                          G_IO_ERROR,G_IO_ERROR_FAILED,
+                          _("HTTP Error: %s"), msg->reason_phrase);
+      else
+        g_vfs_job_failed (G_VFS_JOB (job),
+                          G_IO_ERROR,G_IO_ERROR_FAILED,
+                          _("Not a WebDAV enabled share"));
+
+      return;
+    }
+
+  mount_spec = g_mount_spec_new ("dav"); 
+  g_mount_spec_set (mount_spec, "host", mount_base->host);
+
+  if (mount_base->user)
+    g_mount_spec_set (mount_spec, "user", mount_base->user);
+
+  if (mount_base->protocol == SOUP_PROTOCOL_HTTP)
+    g_mount_spec_set (mount_spec, "ssl", "false");
+  else if (mount_base->protocol == SOUP_PROTOCOL_HTTPS)
+    g_mount_spec_set (mount_spec, "ssl", "true");
+
+  g_free (mount_base->path);
+  mount_base->path = backend->last_good_path;
+
+  g_mount_spec_set_mount_prefix (mount_spec, mount_base->path);
+  g_vfs_backend_set_mount_spec (G_VFS_BACKEND (backend), mount_spec);
+  g_vfs_backend_set_icon_name (G_VFS_BACKEND (backend), "folder-remote");
+
+  g_mount_spec_unref (mount_spec);
+  g_print ("- discover_mount_root_ready success: %s \n", mount_base->path);
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+
+
+
+static void
+mount (GVfsBackend  *backend,
+       GVfsJobMount *job,
+       GMountSpec   *mount_spec,
+       GMountSource *mount_source,
+       gboolean      is_automount)
+{
+  GVfsBackendDav *op_backend;
+  SoupSession    *session;
+
+  g_print ("+ mount\n");
+
+  op_backend = G_VFS_BACKEND_DAV (backend);
+  session = G_VFS_BACKEND_HTTP (backend)->session;
+
+  g_signal_connect (session, "authenticate",
+                    G_CALLBACK (authenticate), job);
+
+  g_signal_connect (session, "reauthenticate",
+                    G_CALLBACK (reauthenticate), job);
+
+  op_backend->mount_source = mount_source;
+  discover_mount_root (op_backend, job);
+  g_print ("- mount\n");
+}
+
+static gboolean
+try_mount (GVfsBackend  *backend,
+           GVfsJobMount *job,
+           GMountSpec   *mount_spec,
+           GMountSource *mount_source,
+           gboolean      is_automount)
+{
+  GVfsBackendDav *op_backend;
+  SoupUri        *uri;
+  const char     *host;
+  const char     *user;
+  const char     *port;
+  const char     *ssl;
+  guint           port_num;
+
+  /* We have to override this method since the http backend
+   * has its own try_mount and we don't want that to be used.
+   * We also need to do the dav mounting stuff inside a
+   * thread since the auth callback from soup is getting
+   * called in the main thread otherwise and we would block */
+
+  op_backend = G_VFS_BACKEND_DAV (backend);
+
+  host = g_mount_spec_get (mount_spec, "host");
+  user = g_mount_spec_get (mount_spec, "user");
+  port = g_mount_spec_get (mount_spec, "port");
+  ssl  = g_mount_spec_get (mount_spec, "ssl");
+  
+  if (host == NULL || *host == 0)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                        _("Invalid mount spec"));
+      
+      return TRUE;
+    }
+
+  uri = g_new0 (SoupUri, 1);
+
+  if (ssl != NULL && (strcmp (ssl, "true") == 0))
+    uri->protocol = SOUP_PROTOCOL_HTTPS;
+  else
+    uri->protocol = SOUP_PROTOCOL_HTTP;
+  
+  uri->user = g_strdup (user);
+
+  if (port && (port_num = atoi (port)))
+    uri->port = port_num;
+  else
+    uri->port = soup_protocol_default_port (uri->protocol);
+
+  uri->host = g_strdup (host);
+  uri->path = g_strdup (mount_spec->mount_prefix);
+
+  G_VFS_BACKEND_HTTP (backend)->mount_base = uri; 
+
+  return FALSE;
+}
+
+static void
+query_info_ready (SoupMessage *msg,
+                  gpointer     user_data)
+{
+  GVfsBackendDav    *backend;
+  GVfsJobQueryInfo  *job;
+  SoupUri           *base;
+  GFileInfo         *info;
+  GError            *error;
+  xmlDocPtr          doc;
+  xmlNodePtr         root;
+  xmlNodePtr         node;
+ 
+  job     = G_VFS_JOB_QUERY_INFO (user_data);
+  backend = G_VFS_BACKEND_DAV (job->backend);
+  base    = G_VFS_BACKEND_HTTP (backend)->mount_base;
+  error   = NULL;
+  info    = NULL;
+
+  doc = multistatus_parse_xml (msg, &root, &error);
+
+  if (doc == NULL)
+    {
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_error_free (error);
+      return;
+    }
+
+  for (node = root->children; node; node = node->next)
+    {
+      if (node->type != XML_ELEMENT_NODE ||
+          node->name == NULL ||
+          strcmp ((char *) node->name, "response"))
+        continue;
+
+      info = multistatus_parse_response (doc, node, base);
+    }
+
+  if (info)
+    {
+        g_file_info_copy_into (info, job->file_info);
+        g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+  else
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR,G_IO_ERROR_FAILED,
+                        _("Response invalid"));
+    }
+
+  xmlFreeDoc (doc);
+}
+
+/* *** query_info () *** */
+static gboolean
+try_query_info (GVfsBackend           *backend,
+                GVfsJobQueryInfo      *job,
+                const char            *filename,
+                GFileQueryInfoFlags    flags,
+                GFileInfo             *info,
+                GFileAttributeMatcher *attribute_matcher)
+{
+  SoupMessage *msg;
+  gulong       len;
+  char        *request;
+  char        *redirect_header;
+
+  len = 0;
+  msg = message_new_from_filename (backend, "PROPFIND", filename);
+
+  request = create_propfind_request (attribute_matcher, &len);
+
+  if (request == NULL || msg == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR,G_IO_ERROR_FAILED,
+                        _("Could not create request"));
+      
+
+      if (msg)
+          g_object_unref (msg);
+
+      g_free (request);
+      return TRUE;
+    }
+
+  soup_message_add_header (msg->request_headers, "Depth", "0");
+
+  /* RFC 4437 */
+  if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
+      redirect_header = "F";
+  else
+      redirect_header = "T";
+
+  soup_message_add_header (msg->request_headers,
+                           "Apply-To-Redirect-Ref", redirect_header);
+
+  soup_message_set_request (msg, "application/xml",
+                            SOUP_BUFFER_SYSTEM_OWNED,
+                            request,
+                            len);
+
+  send_message (backend, msg, query_info_ready, job);
+  return TRUE;
+}
+
+/* *** enumerate *** */
+
+
+
+static void
+enumerate_ready (SoupMessage *msg,
+                 gpointer     user_data)
+{
+  GVfsBackendDav    *backend;
+  GVfsJobEnumerate  *job;
+  SoupUri           *base;
+  GFileInfo         *info;
+  GError            *error;
+  xmlDocPtr          doc;
+  xmlNodePtr         root;
+  xmlNodePtr         node;
+ 
+  job     = G_VFS_JOB_ENUMERATE (user_data);
+  backend = G_VFS_BACKEND_DAV (job->backend);
+  base    = G_VFS_BACKEND_HTTP (backend)->mount_base;
+  error   = NULL;
+  info    = NULL;
+
+  doc = multistatus_parse_xml (msg, &root, &error);
+
+  if (doc == NULL)
+    {
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_error_free (error);
+      return;
+    }
+
+  for (node = root->children; node; node = node->next)
+    {
+      GFileInfo *info;
+      const char *fn;
+
+      if (node->type != XML_ELEMENT_NODE ||
+          node->name == NULL ||
+          strcmp ((char *) node->name, "response"))
+        continue;
+
+      info = multistatus_parse_response (doc, node, base);
+
+      if (info == NULL)
+        continue;
+      
+      fn = g_file_info_get_name (info);
+
+      if (fn == NULL || g_str_equal (job->filename, fn))
+        {
+          g_object_unref (info);
+          continue;
+        }
+
+        g_vfs_job_enumerate_add_info (job, info);
+    }
+
+  g_vfs_job_succeeded (G_VFS_JOB (job)); /* should that be called earlier? */
+  g_vfs_job_enumerate_done (G_VFS_JOB_ENUMERATE (job));
+  xmlFreeDoc (doc);
+}
+
+static gboolean
+try_enumerate (GVfsBackend           *backend,
+               GVfsJobEnumerate      *job,
+               const char            *filename,
+               GFileAttributeMatcher *attribute_matcher,
+               GFileQueryInfoFlags    flags)
+{
+  SoupMessage *msg;
+  gulong       len;
+  char        *request;
+  char        *redirect_header;
+
+  len = 0;
+  msg = message_new_from_filename (backend, "PROPFIND", filename);
+
+  request = create_propfind_request (attribute_matcher, &len);
+
+  if (request == NULL || msg == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR,G_IO_ERROR_FAILED,
+                        _("Could not create request"));
+      
+
+      if (msg)
+          g_object_unref (msg);
+
+      g_free (request);
+      return TRUE;
+    }
+
+  soup_message_add_header (msg->request_headers, "Depth", "1");
+
+  /* RFC 4437 */
+  if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
+      redirect_header = "F";
+  else
+      redirect_header = "T";
+
+  soup_message_add_header (msg->request_headers,
+                           "Apply-To-Redirect-Ref", redirect_header);
+
+  soup_message_set_request (msg, "application/xml",
+                            SOUP_BUFFER_SYSTEM_OWNED,
+                            request,
+                            len);
+
+  send_message (backend, msg, enumerate_ready, job);
+  return TRUE;
+}
+
+/* ************************************************************************* */
+/*  */
+
+static void
+g_vfs_backend_dav_class_init (GVfsBackendDavClass *klass)
+{
+  GObjectClass     *gobject_class;
+  GVfsBackendClass *backend_class;
+  
+  gobject_class = G_OBJECT_CLASS (klass); 
+  gobject_class->finalize  = g_vfs_backend_dav_finalize;
+
+  backend_class = G_VFS_BACKEND_CLASS (klass); 
+  backend_class->mount             = mount;
+  backend_class->try_mount         = try_mount;
+  backend_class->try_query_info    = try_query_info;
+  backend_class->try_enumerate     = try_enumerate;
+}

Added: trunk/daemon/gvfsbackenddav.h
==============================================================================
--- (empty file)
+++ trunk/daemon/gvfsbackenddav.h	Sun Jan 13 21:50:47 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: Christian Kellner <gicmo gnome org>
+ */
+
+#ifndef __G_VFS_BACKEND_DAV_H__
+#define __G_VFS_BACKEND_DAV_H__
+
+#include <gvfsbackendhttp.h>
+#include <gmountspec.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_BACKEND_DAV         (g_vfs_backend_dav_get_type ())
+#define G_VFS_BACKEND_DAV(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_DAV, GVfsBackendDav))
+#define G_VFS_BACKEND_DAV_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_DAV, GVfsBackendDavClass))
+#define G_VFS_IS_BACKEND_DAV(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_DAV))
+#define G_VFS_IS_BACKEND_DAV_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_DAV))
+#define G_VFS_BACKEND_DAV_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_DAV, GVfsBackendDavClass))
+
+typedef struct _GVfsBackendDav        GVfsBackendDav;
+typedef struct _GVfsBackendDavClass   GVfsBackendDavClass;
+
+struct _GVfsBackendDavClass
+{
+  GVfsBackendHttpClass parent_class;
+};
+
+GType g_vfs_backend_dav_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_VFS_BACKEND_DAV_H__ */

Modified: trunk/daemon/gvfsbackendhttp.c
==============================================================================
--- trunk/daemon/gvfsbackendhttp.c	(original)
+++ trunk/daemon/gvfsbackendhttp.c	Sun Jan 13 21:50:47 2008
@@ -53,13 +53,7 @@
 #include "soup-input-stream.h"
 
 
-struct _GVfsBackendHttp
-{
-  GVfsBackend parent_instance;
 
-  SoupUri     *mount_base;
-  SoupSession *session;
-};
 
 G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND);
 
@@ -88,6 +82,31 @@
   backend->session = soup_session_async_new ();
 }
 
+SoupUri *
+g_vfs_backend_uri_for_filename (GVfsBackend *backend, const char *filename)
+{
+  GVfsBackendHttp *op_backend;
+  SoupUri         *uri;
+  char            *path;
+
+  op_backend = G_VFS_BACKEND_HTTP (backend);
+  uri = soup_uri_copy (op_backend->mount_base);
+
+  /* "/" means "whatever mount_base is" */
+  if (!strcmp (filename, "/"))
+    return uri;
+
+  /* Otherwise, we append filename to mount_base (which is assumed to
+   * be a directory in this case).
+   */
+  path = g_build_path ("/", uri->path, filename, NULL);
+  g_free (uri->path);
+  uri->path = path;
+
+  return uri;
+}
+
+
 static gboolean
 try_mount (GVfsBackend  *backend,
            GVfsJobMount *job,
@@ -120,7 +139,7 @@
 
   real_mount_spec = g_mount_spec_new ("http");
   g_mount_spec_set (real_mount_spec, "uri", uri_str);
-  g_vfs_backend_set_mount_spec (backend, mount_spec);
+  g_vfs_backend_set_mount_spec (backend, real_mount_spec);
   
   op_backend->mount_base = uri;
 
@@ -166,28 +185,6 @@
   g_vfs_job_succeeded (job);
 }
 
-static SoupUri *
-uri_for_filename (GVfsBackendHttp *op_backend, const char *filename)
-{
-  SoupUri *uri;
-  char *path;
-
-  uri = soup_uri_copy (op_backend->mount_base);
-
-  /* "/" means "whatever mount_base is" */
-  if (!strcmp (filename, "/"))
-    return uri;
-
-  /* Otherwise, we append filename to mount_base (which is assumed to
-   * be a directory in this case).
-   */
-  path = g_build_filename (uri->path, filename, NULL);
-  g_free (uri->path);
-  uri->path = path;
-
-  return uri;
-}
-
 static gboolean 
 try_open_for_read (GVfsBackend        *backend,
                    GVfsJobOpenForRead *job,
@@ -199,7 +196,7 @@
   SoupMessage     *msg;
 
   op_backend = G_VFS_BACKEND_HTTP (backend);
-  uri = uri_for_filename (op_backend, filename);
+  uri = g_vfs_backend_uri_for_filename (backend, filename);
 
   msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
   soup_uri_free (uri);

Modified: trunk/daemon/gvfsbackendhttp.h
==============================================================================
--- trunk/daemon/gvfsbackendhttp.h	(original)
+++ trunk/daemon/gvfsbackendhttp.h	Sun Jan 13 21:50:47 2008
@@ -25,6 +25,7 @@
 
 #include <gvfsbackend.h>
 #include <gmountspec.h>
+#include <libsoup/soup.h>
 
 G_BEGIN_DECLS
 
@@ -43,8 +44,16 @@
   GVfsBackendClass parent_class;
 };
 
-GType g_vfs_backend_http_get_type (void) G_GNUC_CONST;
+struct _GVfsBackendHttp
+{
+  GVfsBackend parent_instance;
+
+  SoupUri     *mount_base;
+  SoupSession *session;
+};
 
+GType        g_vfs_backend_http_get_type    (void) G_GNUC_CONST;
+SoupUri *    g_vfs_backend_uri_for_filename (GVfsBackend *backend, const char *filename);
 G_END_DECLS
 
 #endif /* __G_VFS_BACKEND_HTTP_H__ */



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