diff options
author | Christian Kellner <gicmo@src.gnome.org> | 2008-09-24 12:46:09 +0000 |
---|---|---|
committer | Christian Kellner <gicmo@src.gnome.org> | 2008-09-24 12:46:09 +0000 |
commit | d01d71d8f36809983b174f0b7d62d20bedf33a74 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /daemon/gvfsbackenddav.c | |
parent | 953132a87aed73507042a6279b343523741a1800 (diff) | |
download | gvfs-d01d71d8f36809983b174f0b7d62d20bedf33a74.tar.gz |
Wrong branchGVFS_1_0_1_svn2027
svn path=/tags/GVFS_1_0_1; revision=2026
Diffstat (limited to 'daemon/gvfsbackenddav.c')
-rw-r--r-- | daemon/gvfsbackenddav.c | 2160 |
1 files changed, 0 insertions, 2160 deletions
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c deleted file mode 100644 index 24c2b34c..00000000 --- a/daemon/gvfsbackenddav.c +++ /dev/null @@ -1,2160 +0,0 @@ -/* 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> - -/* LibXML2 includes */ -#include <libxml/parser.h> -#include <libxml/tree.h> -#include <libxml/xpath.h> -#include <libxml/xpathInternals.h> - -#include "gvfsbackenddav.h" -#include "gvfskeyring.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" -#include "soup-output-stream.h" - -typedef struct _MountAuthData MountAuthData; - -static void mount_auth_info_free (MountAuthData *info); - -typedef struct _AuthInfo { - - /* for server authentication */ - char *username; - char *password; - char *realm; - - GPasswordSave pw_save; - -} AuthInfo; - -struct _MountAuthData { - - SoupSession *session; - GMountSource *mount_source; - - AuthInfo server_auth; - AuthInfo proxy_auth; - -}; - -struct _GVfsBackendDav -{ - GVfsBackendHttp parent_instance; - - MountAuthData auth_info; -}; - -G_DEFINE_TYPE (GVfsBackendDav, g_vfs_backend_dav, G_VFS_TYPE_BACKEND_HTTP); - -static void -g_vfs_backend_dav_finalize (GObject *object) -{ - GVfsBackendDav *dav_backend; - - dav_backend = G_VFS_BACKEND_DAV (object); - - mount_auth_info_free (&(dav_backend->auth_info)); - - 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) -{ - g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), TRUE); -} - -/* ************************************************************************* */ -/* Small utility functions */ - -static inline gboolean -sm_has_header (SoupMessage *msg, const char *header) -{ - return soup_message_headers_get (msg->response_headers, header) != NULL; -} - -static char * -path_get_parent_dir (const char *path) -{ - char *parent; - size_t len; - - len = strlen (path); - - while (len > 0 && path[len - 1] == '/') - len--; - - if (len == 0) - return NULL; - - parent = g_strrstr_len (path, len, "/"); - - if (parent == NULL) - return NULL; - - return g_strndup (path, (parent - path) + 1); -} - -/* message utility functions */ - -static void -message_add_destination_header (SoupMessage *msg, - SoupURI *uri) -{ - char *string; - - string = soup_uri_to_string (uri, FALSE); - soup_message_headers_append (msg->request_headers, - "Destination", - string); - g_free (string); -} -static void -message_add_overwrite_header (SoupMessage *msg, - gboolean overwrite) -{ - soup_message_headers_append (msg->request_headers, - "Overwrite", - overwrite ? "T" : "F"); -} - -static void -message_add_redirect_header (SoupMessage *msg, - GFileQueryInfoFlags flags) -{ - const char *header_redirect; - - /* RFC 4437 */ - if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) - header_redirect = "F"; - else - header_redirect = "T"; - - soup_message_headers_append (msg->request_headers, - "Apply-To-Redirect-Ref", - header_redirect); -} - -static inline gboolean -str_equal (const char *a, const char *b, gboolean insensitive) -{ - if (a == NULL || b == NULL) - return a == b; - - return insensitive ? !g_ascii_strcasecmp (a, b) : !strcmp (a, b); -} - -static gboolean -path_equal (const char *a, const char *b, gboolean relax) -{ - gboolean res; - size_t a_len, b_len; - - if (relax == FALSE) - return str_equal (a, b, FALSE); - - if (a == NULL || b == NULL) - return a == b; - - a_len = strlen (a); - b_len = strlen (b); - - while (a[a_len - 1] == '/') - a_len--; - - while (b[b_len - 1] == '/') - b_len--; - - if (a_len == b_len) - res = ! strncmp (a, b, a_len); - else - res = FALSE; - - return res; -} - -/* Like soup_uri_equal */ -static gboolean -dav_uri_match (SoupURI *a, SoupURI *b, gboolean relax) -{ - if (a->scheme != b->scheme || - a->port != b->port || - ! str_equal (a->user, b->user, FALSE) || - ! str_equal (a->password, b->password, FALSE) || - ! str_equal (a->host, b->host, TRUE) || - ! path_equal (a->path, b->path, relax) || - ! str_equal (a->query, b->query, FALSE) || - ! str_equal (a->fragment, b->fragment, FALSE)) - return FALSE; - return TRUE; -} - -static gboolean -message_should_apply_redir_ref (SoupMessage *msg) -{ - const char *header; - - header = soup_message_headers_get (msg->request_headers, - "Apply-To-Redirect-Ref"); - - if (header == NULL || g_ascii_strcasecmp (header, "T")) - return FALSE; - - return TRUE; -} - -/* redirection */ -static void -redirect_handler (SoupMessage *msg, gpointer user_data) -{ - SoupSession *session = user_data; - const char *new_loc; - SoupURI *new_uri; - SoupURI *old_uri; - guint status; - gboolean redirect; - - status = msg->status_code; - new_loc = soup_message_headers_get (msg->response_headers, "Location"); - - /* If we don't have a location to redirect to, just fail */ - if (new_loc == NULL) - return; - - new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc); - if (new_uri == NULL) - { - soup_message_set_status_full (msg, - SOUP_STATUS_MALFORMED, - "Invalid Redirect URL"); - return; - } - - old_uri = soup_message_get_uri (msg); - - /* Check if this is a trailing slash redirect (i.e. /a/b to /a/b/), - * redirect it right away - */ - redirect = dav_uri_match (new_uri, old_uri, TRUE); - - if (redirect == TRUE) - { - const char *dest; - - dest = soup_message_headers_get (msg->request_headers, - "Destination"); - - if (dest && g_str_has_suffix (dest, "/") == FALSE) - { - char *new_dest = g_strconcat (dest, "/", NULL); - soup_message_headers_replace (msg->request_headers, - "Destination", - new_dest); - g_free (new_dest); - } - } - else if (message_should_apply_redir_ref (msg)) - { - - - if (status == SOUP_STATUS_MOVED_PERMANENTLY || - status == SOUP_STATUS_TEMPORARY_REDIRECT) - { - - /* Only corss-site redirect safe methods */ - if (msg->method == SOUP_METHOD_GET && - msg->method == SOUP_METHOD_HEAD && - msg->method == SOUP_METHOD_OPTIONS && - msg->method == SOUP_METHOD_PROPFIND) - redirect = TRUE; - } - -#if 0 - else if (msg->status_code == SOUP_STATUS_SEE_OTHER || - msg->status_code == SOUP_STATUS_FOUND) - { - /* Redirect using a GET */ - g_object_set (msg, - SOUP_MESSAGE_METHOD, SOUP_METHOD_GET, - NULL); - soup_message_set_request (msg, NULL, - SOUP_MEMORY_STATIC, NULL, 0); - soup_message_headers_set_encoding (msg->request_headers, - SOUP_ENCODING_NONE); - } -#endif - /* ELSE: - * - * Three possibilities: - * - * 1) This was a non-3xx response that happened to - * have a "Location" header - * 2) It's a non-redirecty 3xx response (300, 304, - * 305, 306) - * 3) It's some newly-defined 3xx response (308+) - * - * We ignore all of these cases. In the first two, - * redirecting would be explicitly wrong, and in the - * last case, we have no clue if the 3xx response is - * supposed to be redirecty or non-redirecty. Plus, - * 2616 says unrecognized status codes should be - * treated as the equivalent to the x00 code, and we - * don't redirect on 300, so therefore we shouldn't - * redirect on 308+ either. - */ - } - - if (redirect) - { - soup_message_set_uri (msg, new_uri); - soup_session_requeue_message (session, msg); - } - - soup_uri_free (new_uri); -} - -static guint -g_vfs_backend_dav_send_message (GVfsBackend *backend, SoupMessage *message) -{ - GVfsBackendHttp *http_backend; - SoupSession *session; - - http_backend = G_VFS_BACKEND_HTTP (backend); - session = http_backend->session; - - /* We have our own custom redirect handler */ - soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT); - - soup_message_add_header_handler (message, "got_body", "Location", - G_CALLBACK (redirect_handler), session); - - return http_backend_send_message (backend, message); -} - -/* ************************************************************************* */ -/* generic xml parsing functions */ - -static inline gboolean -node_has_name (xmlNodePtr node, const char *name) -{ - g_return_val_if_fail (node != NULL, FALSE); - - return ! strcmp ((char *) node->name, name); -} - -static inline gboolean -node_has_name_ns (xmlNodePtr node, const char *name, const char *ns_href) -{ - gboolean has_name; - gboolean has_ns; - - g_return_val_if_fail (node != NULL, FALSE); - - has_name = has_ns = TRUE; - - if (name) - has_name = node->name && ! strcmp ((char *) node->name, name); - - if (ns_href) - has_ns = node->ns && node->ns->href && - ! g_ascii_strcasecmp ((char *) node->ns->href, ns_href); - - return has_name && has_ns; -} - -static inline gboolean -node_is_element (xmlNodePtr node) -{ - return node->type == XML_ELEMENT_NODE && node->name != NULL; -} - - -static inline gboolean -node_is_element_with_name (xmlNodePtr node, const char *name) -{ - return node->type == XML_ELEMENT_NODE && - node->name != NULL && - ! strcmp ((char *) node->name, name); -} - -static const char * -node_get_content (xmlNodePtr node) -{ - if (node == NULL) - return NULL; - - switch (node->type) - { - case XML_ELEMENT_NODE: - return node_get_content (node->children); - break; - case XML_TEXT_NODE: - return (const char *) node->content; - break; - default: - return NULL; - } -} - -typedef struct _xmlNodeIter { - - xmlNodePtr cur_node; - xmlNodePtr next_node; - - const char *name; - const char *ns_href; - - void *user_data; - -} xmlNodeIter; - -static xmlNodePtr -xml_node_iter_next (xmlNodeIter *iter) -{ - xmlNodePtr node; - - while ((node = iter->next_node)) - { - iter->next_node = node->next; - - if (node->type == XML_ELEMENT_NODE) { - if (node_has_name_ns (node, iter->name, iter->ns_href)) - break; - } - } - - iter->cur_node = node; - return node; -} - -static void * -xml_node_iter_get_user_data (xmlNodeIter *iter) -{ - return iter->user_data; -} - -static xmlNodePtr -xml_node_iter_get_current (xmlNodeIter *iter) -{ - return iter->cur_node; -} - -static xmlDocPtr -parse_xml (SoupMessage *msg, - xmlNodePtr *root, - const char *name, - 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->data, - msg->response_body->length, - "response.xml", - NULL, - XML_PARSE_NONET | - XML_PARSE_NOWARNING | - XML_PARSE_NOBLANKS | - XML_PARSE_NSCLEAN | - XML_PARSE_NOCDATA | - XML_PARSE_COMPACT); - if (doc == NULL) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Could not parse response")); - return NULL; - } - - *root = xmlDocGetRootElement (doc); - - if (*root == NULL || (*root)->children == NULL) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Empty response")); - xmlFreeDoc (doc); - return NULL; - } - - if (strcmp ((char *) (*root)->name, name)) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Unexpected reply from server")); - xmlFreeDoc (doc); - return NULL; - } - - return doc; -} - -/* ************************************************************************* */ -/* Multistatus parsing code */ - -typedef struct _Multistatus Multistatus; -typedef struct _MsResponse MsResponse; -typedef struct _MsPropstat MsPropstat; - -struct _Multistatus { - - xmlDocPtr doc; - xmlNodePtr root; - - SoupURI *target; - -}; - -struct _MsResponse { - - Multistatus *multistatus; - - xmlNodePtr href; - xmlNodePtr first_propstat; -}; - -struct _MsPropstat { - - Multistatus *multistatus; - - xmlNodePtr prop_node; - guint status_code; - -}; - - -static gboolean -multistatus_parse (SoupMessage *msg, Multistatus *multistatus, GError **error) -{ - xmlDocPtr doc; - xmlNodePtr root; - - doc = parse_xml (msg, &root, "multistatus", error); - - if (doc == NULL) - return FALSE; - - multistatus->doc = doc; - multistatus->root = root; - multistatus->target = soup_message_get_uri (msg); - - return TRUE; -} - -static void -multistatus_free (Multistatus *multistatus) -{ - xmlFreeDoc (multistatus->doc); -} - -static void -multistatus_get_response_iter (Multistatus *multistatus, xmlNodeIter *iter) -{ - iter->cur_node = multistatus->root->children; - iter->next_node = multistatus->root->children; - iter->name = "response"; - iter->ns_href = "DAV:"; - iter->user_data = multistatus; -} - -static gboolean -multistatus_get_response (xmlNodeIter *resp_iter, MsResponse *response) -{ - Multistatus *multistatus; - xmlNodePtr resp_node; - xmlNodePtr iter; - xmlNodePtr href; - xmlNodePtr propstat; - - multistatus = xml_node_iter_get_user_data (resp_iter); - resp_node = xml_node_iter_get_current (resp_iter); - - if (resp_node == NULL) - return FALSE; - - propstat = NULL; - href = NULL; - - for (iter = resp_node->children; iter; iter = iter->next) - { - if (! node_is_element (iter)) - { - continue; - } - else if (node_has_name_ns (iter, "href", "DAV:")) - { - href = iter; - } - else if (node_has_name_ns (iter, "propstat", "DAV:")) - { - if (propstat == NULL) - propstat = iter; - } - - if (href && propstat) - break; - } - - if (href == NULL) - return FALSE; - - response->href = href; - response->multistatus = multistatus; - response->first_propstat = propstat; - - return resp_node != NULL; -} - -static char * -ms_response_get_basename (MsResponse *response) -{ - const char *text; - text = node_get_content (response->href); - - return http_uri_get_basename (text); - -} - -static gboolean -ms_response_is_target (MsResponse *response) -{ - const char *text; - const char *path; - SoupURI *target; - SoupURI *uri; - gboolean res; - - uri = NULL; - path = NULL; - target = response->multistatus->target; - text = node_get_content (response->href); - - if (text == NULL) - return FALSE; - - uri = soup_uri_new_with_base (target, text); - - if (uri == NULL) - return FALSE; - - res = dav_uri_match (uri, target, TRUE); - - soup_uri_free (uri); - - return res; -} - -static void -ms_response_get_propstat_iter (MsResponse *response, xmlNodeIter *iter) -{ - iter->cur_node = response->first_propstat; - iter->next_node = response->first_propstat; - iter->name = "propstat"; - iter->ns_href = "DAV:"; - iter->user_data = response; -} - -static guint -ms_response_get_propstat (xmlNodeIter *cur_node, MsPropstat *propstat) -{ - MsResponse *response; - xmlNodePtr pstat_node; - xmlNodePtr iter; - xmlNodePtr prop; - xmlNodePtr status; - const char *status_text; - gboolean res; - guint code; - - response = xml_node_iter_get_user_data (cur_node); - pstat_node = xml_node_iter_get_current (cur_node); - - if (pstat_node == NULL) - return 0; - - status = NULL; - prop = NULL; - - for (iter = pstat_node->children; iter; iter = iter->next) - { - if (!node_is_element (iter)) - { - continue; - } - else if (node_has_name_ns (iter, "status", "DAV:")) - { - status = iter; - } - else if (node_has_name_ns (iter, "prop", "DAV:")) - { - prop = iter; - } - - if (status && prop) - break; - } - - status_text = node_get_content (status); - - if (status_text == NULL || prop == NULL) - return 0; - - res = soup_headers_parse_status_line ((char *) status_text, - NULL, - &code, - NULL); - - if (res == FALSE) - return 0; - - propstat->prop_node = prop; - propstat->status_code = code; - propstat->multistatus = response->multistatus; - - return code; -} - -static GFileType -parse_resourcetype (xmlNodePtr rt) -{ - xmlNodePtr node; - GFileType type; - - for (node = rt->children; node; node = node->next) - { - if (node_is_element (node)) - 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 inline void -file_info_set_content_type (GFileInfo *info, const char *type) -{ - g_file_info_set_content_type (info, type); - g_file_info_set_attribute_string (info, - G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, - type); - -} - -static void -ms_response_to_file_info (MsResponse *response, - GFileInfo *info) -{ - xmlNodeIter iter; - MsPropstat propstat; - xmlNodePtr node; - guint status; - char *basename; - const char *text; - GTimeVal tv; - GFileType file_type; - char *mime_type; - GIcon *icon; - gboolean have_display_name; - - basename = ms_response_get_basename (response); - g_file_info_set_name (info, basename); - g_file_info_set_edit_name (info, basename); - - file_type = G_FILE_TYPE_UNKNOWN; - mime_type = NULL; - - have_display_name = FALSE; - ms_response_get_propstat_iter (response, &iter); - while (xml_node_iter_next (&iter)) - { - status = ms_response_get_propstat (&iter, &propstat); - - if (! SOUP_STATUS_IS_SUCCESSFUL (status)) - continue; - - for (node = propstat.prop_node->children; node; node = node->next) - { - if (! node_is_element (node)) - continue; /* TODO: check namespace, parse user data nodes*/ - - text = node_get_content (node); - - if (node_has_name (node, "resourcetype")) - { - file_type = parse_resourcetype (node); - g_file_info_set_file_type (info, file_type); - } - else if (node_has_name (node, "displayname")) - { - g_file_info_set_display_name (info, text); - have_display_name = TRUE; - } - else if (node_has_name (node, "getetag")) - { - g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, - text); - } - else if (node_has_name (node, "creationdate")) - { - if (! g_time_val_from_iso8601 (text, &tv)) - continue; - - g_file_info_set_attribute_uint64 (info, - G_FILE_ATTRIBUTE_TIME_CREATED, - tv.tv_sec); - } - else if (node_has_name (node, "getcontenttype")) - { - mime_type = g_strdup (text); - } - else if (node_has_name (node, "getcontentlength")) - { - gint64 size; - size = g_ascii_strtoll (text, NULL, 10); - g_file_info_set_size (info, size); - } - else if (node_has_name (node, "getlastmodified")) - { - SoupDate *sd; - GTimeVal tv; - sd = soup_date_new_from_string(text); - if (sd) - { - soup_date_to_timeval (sd, &tv); - g_file_info_set_modification_time (info, &tv); - soup_date_free (sd); - } - } - } - } - - if (file_type == G_FILE_TYPE_DIRECTORY) - { - icon = g_themed_icon_new ("folder"); - file_info_set_content_type (info, "inode/directory"); - } - else - { - if (mime_type == NULL) - mime_type = g_content_type_guess (basename, NULL, 0, NULL); - - icon = g_content_type_get_icon (mime_type); - - if (G_IS_THEMED_ICON (icon)) - g_themed_icon_append_name (G_THEMED_ICON (icon), "text-x-generic"); - - file_info_set_content_type (info, mime_type); - } - - if (have_display_name == FALSE) - g_file_info_set_display_name (info, basename); - - g_file_info_set_icon (info, icon); - g_object_unref (icon); - g_free (mime_type); - g_free (basename); - -} - -static GFileType -ms_response_to_file_type (MsResponse *response) -{ - xmlNodeIter prop_iter; - MsPropstat propstat; - GFileType file_type; - guint status; - - file_type = G_FILE_TYPE_UNKNOWN; - - ms_response_get_propstat_iter (response, &prop_iter); - while (xml_node_iter_next (&prop_iter)) - { - xmlNodePtr iter; - - status = ms_response_get_propstat (&prop_iter, &propstat); - - if (! SOUP_STATUS_IS_SUCCESSFUL (status)) - continue; - - for (iter = propstat.prop_node->children; iter; iter = iter->next) - { - if (node_is_element (iter) && - node_has_name_ns (iter, "resourcetype", "DAV:")) - break; - } - - if (iter) - { - file_type = parse_resourcetype (iter); - break; - } - } - - return file_type; -} - -#define PROPSTAT_XML_BEGIN \ - "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" \ - " <D:propfind xmlns:D=\"DAV:\">\n" - -#define PROPSTAT_XML_ALLPROP " <D:allprop/>\n" -#define PROPSTAT_XML_PROP_BEGIN " <D:prop>\n" -#define PROPSTAT_XML_PROP_END " </D:prop>\n" - -#define PROPSTAT_XML_END \ - " </D:propfind>" - -typedef struct _PropName { - - const char *name; - const char *namespace; - -} PropName; - - -static SoupMessage * -propfind_request_new (GVfsBackend *backend, - const char *filename, - guint depth, - const PropName *properties) -{ - SoupMessage *msg; - SoupURI *uri; - const char *header_depth; - GString *body; - - uri = http_backend_uri_for_filename (backend, filename, depth > 0); - msg = soup_message_new_from_uri (SOUP_METHOD_PROPFIND, uri); - soup_uri_free (uri); - - if (msg == NULL) - return NULL; - - if (depth == 0) - header_depth = "0"; - else if (depth == 1) - header_depth = "1"; - else - header_depth = "infinity"; - - soup_message_headers_append (msg->request_headers, "Depth", header_depth); - - body = g_string_new (PROPSTAT_XML_BEGIN); - - if (properties != NULL) - { - const PropName *prop; - g_string_append (body, PROPSTAT_XML_PROP_BEGIN); - - for (prop = properties; prop->name; prop++) - { - if (prop->namespace != NULL) - g_string_append_printf (body, "<%s xmlns=\"%s\"/>\n", - prop->name, - prop->namespace); - else - g_string_append_printf (body, "<D:%s/>\n", prop->name); - } - g_string_append (body, PROPSTAT_XML_PROP_END); - } - else - g_string_append (body, PROPSTAT_XML_ALLPROP); - - - g_string_append (body, PROPSTAT_XML_END); - - soup_message_set_request (msg, "application/xml", - SOUP_MEMORY_TAKE, - body->str, - body->len); - - g_string_free (body, FALSE); - - return msg; -} - -static SoupMessage * -stat_location_begin (SoupURI *uri, - gboolean count_children) -{ - SoupMessage *msg; - const char *depth; - static const char *stat_profind_body = - PROPSTAT_XML_BEGIN - PROPSTAT_XML_PROP_BEGIN - "<D:resourcetype/>\n" - PROPSTAT_XML_PROP_END - PROPSTAT_XML_END; - - msg = soup_message_new_from_uri (SOUP_METHOD_PROPFIND, uri); - - if (count_children) - depth = "1"; - else - depth = "0"; - - soup_message_headers_append (msg->request_headers, "Depth", depth); - - soup_message_set_request (msg, "application/xml", - SOUP_MEMORY_STATIC, - stat_profind_body, - strlen (stat_profind_body)); - return msg; -} - -static gboolean -stat_location_finish (SoupMessage *msg, - GFileType *target_type, - guint *num_children) -{ - Multistatus ms; - xmlNodeIter iter; - gboolean res; - GError *error; - guint child_count; - GFileType file_type; - - if (msg->status_code != 207) - return FALSE; - - res = multistatus_parse (msg, &ms, &error); - - if (res == FALSE) - return FALSE; - - res = FALSE; - child_count = 0; - file_type = G_FILE_TYPE_UNKNOWN; - - multistatus_get_response_iter (&ms, &iter); - while (xml_node_iter_next (&iter)) - { - MsResponse response; - - if (! multistatus_get_response (&iter, &response)) - continue; - - if (ms_response_is_target (&response)) - { - file_type = ms_response_to_file_type (&response); - res = TRUE; - } - else - child_count++; - } - - if (res) - { - if (target_type) - *target_type = file_type; - - if (num_children) - *num_children = child_count; - } - - multistatus_free (&ms); - return res; -} - -static gboolean -stat_location (GVfsBackend *backend, - SoupURI *uri, - GFileType *target_type, - guint *num_children, - GError **error) -{ - SoupMessage *msg; - guint status; - gboolean count_children; - gboolean res; - - count_children = num_children != NULL; - msg = stat_location_begin (uri, count_children); - - if (msg == NULL) - return FALSE; - - status = g_vfs_backend_dav_send_message (backend, msg); - - if (status != 207) - { - g_set_error_literal (error, - G_IO_ERROR, - http_error_code_from_status (status), - msg->reason_phrase); - - return FALSE; - } - - res = stat_location_finish (msg, target_type, num_children); - - if (res == FALSE) - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Response invalid")); - - return res; -} - - -/* ************************************************************************* */ -/* Authentication */ - -static void -mount_auth_info_free (MountAuthData *data) -{ - if (data->mount_source) - g_object_unref (data->mount_source); - - g_free (data->server_auth.username); - g_free (data->server_auth.password); - g_free (data->server_auth.realm); - - g_free (data->proxy_auth.username); - g_free (data->proxy_auth.password); - -} - -static void -soup_authenticate_from_data (SoupSession *session, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer user_data) -{ - MountAuthData *data; - AuthInfo *info; - - g_print ("+ soup_authenticate_from_data (%s) \n", - retrying ? "retrying" : "first auth"); - - if (retrying) - return; - - data = (MountAuthData *) user_data; - - if (soup_auth_is_for_proxy (auth)) - info = &data->proxy_auth; - else - info = &data->server_auth; - - soup_auth_authenticate (auth, info->username, info->password); -} - -static void -soup_authenticate_interactive (SoupSession *session, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer user_data) -{ - MountAuthData *data; - AuthInfo *info; - GAskPasswordFlags pw_ask_flags; - GPasswordSave pw_save; - const char *realm; - gboolean res; - gboolean aborted; - gboolean is_proxy; - gboolean have_auth; - char *new_username; - char *new_password; - char *prompt; - - g_print ("+ soup_authenticate_interactive (%s) \n", - retrying ? "retrying" : "first auth"); - - data = (MountAuthData *) user_data; - - new_username = NULL; - new_password = NULL; - realm = NULL; - pw_ask_flags = G_ASK_PASSWORD_NEED_PASSWORD; - - is_proxy = soup_auth_is_for_proxy (auth); - realm = soup_auth_get_realm (auth); - - if (is_proxy) - info = &(data->proxy_auth); - else - info = &(data->server_auth); - - if (realm && info->realm == NULL) - info->realm = g_strdup (realm); - else if (realm && info->realm && !g_str_equal (realm, info->realm)) - return; - - have_auth = info->username && info->password; - - if (have_auth == FALSE && g_vfs_keyring_is_available ()) - { - SoupURI *uri; - SoupURI *uri_free = NULL; - - pw_ask_flags |= G_ASK_PASSWORD_SAVING_SUPPORTED; - - if (is_proxy) - { - g_object_get (session, SOUP_SESSION_PROXY_URI, &uri_free, NULL); - uri = uri_free; - } - else - uri = soup_message_get_uri (msg); - - res = g_vfs_keyring_lookup_password (info->username, - uri->host, - NULL, - "http", - realm, - is_proxy ? "proxy" : "basic", - uri->port, - &new_username, - NULL, - &new_password); - - if (res == TRUE) - { - have_auth = TRUE; - g_free (info->username); - g_free (info->password); - info->username = new_username; - info->password = new_password; - } - - if (uri_free) - soup_uri_free (uri_free); - } - - if (retrying == FALSE && have_auth) - { - soup_auth_authenticate (auth, info->username, info->password); - return; - } - - if (is_proxy == FALSE) - { - if (realm == NULL) - realm = _("WebDAV share"); - - prompt = g_strdup_printf (_("Enter password for %s"), realm); - } - else - prompt = g_strdup (_("Please enter proxy password")); - - if (info->username == NULL) - pw_ask_flags |= G_ASK_PASSWORD_NEED_USERNAME; - - res = g_mount_source_ask_password (data->mount_source, - prompt, - info->username, - NULL, - pw_ask_flags, - &aborted, - &new_password, - &new_username, - NULL, - NULL, - &pw_save); - - if (res && !aborted) - { - soup_auth_authenticate (auth, new_username, new_password); - - g_free (info->username); - g_free (info->password); - info->username = new_username; - info->password = new_password; - info->pw_save = pw_save; - } - else - soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); - - g_print ("- soup_authenticate \n"); - g_free (prompt); -} - -static void -keyring_save_authinfo (AuthInfo *info, - SoupURI *uri, - gboolean is_proxy) -{ - const char *type = is_proxy ? "proxy" : "basic"; - - g_vfs_keyring_save_password (info->username, - uri->host, - NULL, - "http", - info->realm, - type, - uri->port, - info->password, - info->pw_save); -} - -/* ************************************************************************* */ - -static SoupURI * -g_mount_spec_to_dav_uri (GMountSpec *spec) -{ - SoupURI *uri; - const char *host; - const char *user; - const char *port; - const char *ssl; - gint port_num; - - host = g_mount_spec_get (spec, "host"); - user = g_mount_spec_get (spec, "user"); - port = g_mount_spec_get (spec, "port"); - ssl = g_mount_spec_get (spec, "ssl"); - - if (host == NULL || *host == 0) - return NULL; - - uri = soup_uri_new (NULL); - - if (ssl != NULL && (strcmp (ssl, "true") == 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); - - if (port && (port_num = atoi (port))) - soup_uri_set_port (uri, port_num); - - soup_uri_set_host (uri, host); - soup_uri_set_path (uri, spec->mount_prefix); - - return uri; -} - -static GMountSpec * -g_mount_spec_from_dav_uri (SoupURI *uri) -{ - GMountSpec *spec; - const char *ssl; - - spec = g_mount_spec_new ("dav"); - - g_mount_spec_set (spec, "host", uri->host); - - if (uri->scheme == SOUP_URI_SCHEME_HTTPS) - ssl = "true"; - else - ssl = "false"; - - g_mount_spec_set (spec, "ssl", ssl); - - if (uri->user) - g_mount_spec_set (spec, "user", uri->user); - - if (! soup_uri_uses_default_port (uri)) - { - char *port = g_strdup_printf ("%u", uri->port); - g_mount_spec_set (spec, "port", port); - g_free (port); - } - - g_mount_spec_set_mount_prefix (spec, uri->path); - - return spec; -} - -/* ************************************************************************* */ -/* Backend Functions */ -static void -do_mount (GVfsBackend *backend, - GVfsJobMount *job, - GMountSpec *mount_spec, - GMountSource *mount_source, - gboolean is_automount) -{ - MountAuthData *data; - SoupSession *session; - SoupMessage *msg_opts; - SoupMessage *msg_stat; - SoupURI *mount_base; - gulong signal_id; - guint status; - gboolean is_success; - gboolean is_webdav; - gboolean res; - char *last_good_path; - char *display_name; - - g_print ("+ mount\n"); - - mount_base = g_mount_spec_to_dav_uri (mount_spec); - - if (mount_base == NULL) - { - g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Invalid mount spec")); - return; - } - - session = G_VFS_BACKEND_HTTP (backend)->session; - G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base; - - data = &(G_VFS_BACKEND_DAV (backend)->auth_info); - data->mount_source = g_object_ref (mount_source); - data->server_auth.username = g_strdup (mount_base->user); - data->server_auth.pw_save = G_PASSWORD_SAVE_NEVER; - data->proxy_auth.pw_save = G_PASSWORD_SAVE_NEVER; - - signal_id = g_signal_connect (session, "authenticate", - G_CALLBACK (soup_authenticate_interactive), - data); - - last_good_path = NULL; - msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base); - msg_stat = stat_location_begin (mount_base, FALSE); - - do { - status = g_vfs_backend_dav_send_message (backend, msg_opts); - - is_success = SOUP_STATUS_IS_SUCCESSFUL (status); - is_webdav = is_success && sm_has_header (msg_opts, "DAV"); - - soup_message_headers_clear (msg_opts->response_headers); - soup_message_body_truncate (msg_opts->response_body); - - if (is_webdav) - { - GFileType file_type; - SoupURI *cur_uri; - - cur_uri = soup_message_get_uri (msg_opts); - soup_message_set_uri (msg_stat, cur_uri); - - g_vfs_backend_dav_send_message (backend, msg_stat); - res = stat_location_finish (msg_stat, &file_type, NULL); - - if (res && file_type == G_FILE_TYPE_DIRECTORY) - { - g_free (last_good_path); - last_good_path = mount_base->path; - } - - mount_base->path = path_get_parent_dir (mount_base->path); - soup_message_set_uri (msg_opts, mount_base); - - soup_message_headers_clear (msg_stat->response_headers); - soup_message_body_truncate (msg_stat->response_body); - } - - } while (is_webdav && mount_base->path != NULL); - - /* we have reached the end of paths we are allowed to - * chdir up to (or couldn't chdir up at all) */ - - /* check if we at all have a good path */ - if (last_good_path == NULL) - { - - /* TODO: set correct error in case of cancellation */ - if (!is_success) - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_FAILED, - _("HTTP Error: %s"), msg_opts->reason_phrase); - else if (!is_webdav) - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Not a WebDAV enabled share")); - else - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Not a WebDAV enabled share")); - - /* TODO: We leak a bunch of stuff here :-( */ - /* TODO: STRING CHANGE: change to: Could not find an enclosing directory */ - return; - } - - /* Success! We are mounted */ - /* Save the auth info in the keyring */ - - keyring_save_authinfo (&(data->server_auth), mount_base, FALSE); - /* TODO: save proxy auth */ - - /* Set the working path in mount path */ - g_free (mount_base->path); - mount_base->path = last_good_path; - - /* dup the mountspec, but only copy known fields */ - mount_spec = g_mount_spec_from_dav_uri (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); - g_vfs_backend_set_display_name (backend, display_name); - g_free (display_name); - - /* cleanup */ - g_mount_spec_unref (mount_spec); - g_object_unref (msg_opts); - g_object_unref (msg_stat); - - /* switch the signal handler */ - g_signal_handler_disconnect (session, signal_id); - g_signal_connect (session, "authenticate", - G_CALLBACK (soup_authenticate_from_data), - data); - - /* also auth the workaround async session we need for SoupInputStream */ - g_signal_connect (G_VFS_BACKEND_HTTP (backend)->session_async, "authenticate", - G_CALLBACK (soup_authenticate_from_data), - data); - - g_vfs_job_succeeded (G_VFS_JOB (job)); - g_print ("- mount\n"); -} - -static PropName ls_propnames[] = { - {"creationdate", NULL}, - {"displayname", NULL}, - {"getcontentlength", NULL}, - {"getcontenttype", NULL}, - {"getetag", NULL}, - {"getlastmodified", NULL}, - {"resourcetype", NULL}, - {NULL, NULL} -}; - -/* *** query_info () *** */ -static void -do_query_info (GVfsBackend *backend, - GVfsJobQueryInfo *job, - const char *filename, - GFileQueryInfoFlags flags, - GFileInfo *info, - GFileAttributeMatcher *matcher) -{ - SoupMessage *msg; - Multistatus ms; - xmlNodeIter iter; - gboolean res; - GError *error; - - error = NULL; - - g_print ("Query info %s\n", filename); - - msg = propfind_request_new (backend, filename, 0, ls_propnames); - - if (msg == NULL) - { - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Could not create request")); - - return; - } - - message_add_redirect_header (msg, flags); - - g_vfs_backend_dav_send_message (backend, msg); - - res = multistatus_parse (msg, &ms, &error); - - if (res == FALSE) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - g_object_unref (msg); - return; - } - - res = FALSE; - multistatus_get_response_iter (&ms, &iter); - - while (xml_node_iter_next (&iter)) - { - MsResponse response; - - if (! multistatus_get_response (&iter, &response)) - continue; - - if (ms_response_is_target (&response)) - { - ms_response_to_file_info (&response, job->file_info); - res = TRUE; - } - } - - multistatus_free (&ms); - g_object_unref (msg); - - if (res) - 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")); - -} - - -/* *** enumerate *** */ -static void -do_enumerate (GVfsBackend *backend, - GVfsJobEnumerate *job, - const char *filename, - GFileAttributeMatcher *matcher, - GFileQueryInfoFlags flags) -{ - SoupMessage *msg; - Multistatus ms; - xmlNodeIter iter; - gboolean res; - GError *error; - - error = NULL; - - g_print ("+ do_enumerate: %s\n", filename); - - msg = propfind_request_new (backend, filename, 1, ls_propnames); - - if (msg == NULL) - { - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Could not create request")); - - return; - } - - message_add_redirect_header (msg, flags); - - g_vfs_backend_dav_send_message (backend, msg); - - res = multistatus_parse (msg, &ms, &error); - - if (res == FALSE) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - g_object_unref (msg); - return; - } - - multistatus_get_response_iter (&ms, &iter); - - while (xml_node_iter_next (&iter)) - { - MsResponse response; - GFileInfo *info; - - if (! multistatus_get_response (&iter, &response)) - continue; - - if (ms_response_is_target (&response)) - continue; - - info = g_file_info_new (); - ms_response_to_file_info (&response, info); - g_vfs_job_enumerate_add_info (job, info); - } - - multistatus_free (&ms); - g_object_unref (msg); - - g_vfs_job_succeeded (G_VFS_JOB (job)); /* should that be called earlier? */ - g_vfs_job_enumerate_done (G_VFS_JOB_ENUMERATE (job)); -} - -/* ************************************************************************* */ -/* */ - -/* *** create () *** */ -static void -try_create_tested_existence (SoupSession *session, SoupMessage *msg, - gpointer user_data) -{ - GVfsJob *job = G_VFS_JOB (user_data); - GVfsBackendHttp *op_backend = job->backend_data; - GOutputStream *stream; - SoupMessage *put_msg; - SoupURI *uri; - - if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) - { - g_vfs_job_failed (job, - G_IO_ERROR, - G_IO_ERROR_EXISTS, - _("Target file already exists")); - return; - } - /* TODO: other errors */ - - uri = soup_message_get_uri (msg); - put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); - - /* - * Doesn't work with apache > 2.2.9 - * soup_message_headers_append (put_msg->request_headers, "If-None-Match", "*"); - */ - stream = soup_output_stream_new (op_backend->session, put_msg, -1); - g_object_unref (put_msg); - - g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), stream); - g_vfs_job_succeeded (job); -} - -static gboolean -try_create (GVfsBackend *backend, - GVfsJobOpenForWrite *job, - const char *filename, - GFileCreateFlags flags) -{ - SoupMessage *msg; - SoupURI *uri; - - /* TODO: if SoupOutputStream supported chunked requests, we could - * use a PUT with "If-None-Match: *" and "Expect: 100-continue" - */ - uri = http_backend_uri_for_filename (backend, filename, FALSE); - msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri); - soup_uri_free (uri); - - g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL); - - http_backend_queue_message (backend, msg, try_create_tested_existence, job); - return TRUE; -} - -/* *** replace () *** */ -static void -open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job, - SoupURI *uri, const char *etag) -{ - SoupMessage *put_msg; - GOutputStream *stream; - - put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); - - if (etag) - soup_message_headers_append (put_msg->request_headers, "If-Match", etag); - - stream = soup_output_stream_new (op_backend->session, put_msg, -1); - g_object_unref (put_msg); - - g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), stream); - g_vfs_job_succeeded (job); -} - -static void -try_replace_checked_etag (SoupSession *session, SoupMessage *msg, - gpointer user_data) -{ - GVfsJob *job = G_VFS_JOB (user_data); - GVfsBackendHttp *op_backend = job->backend_data; - - if (msg->status_code == SOUP_STATUS_PRECONDITION_FAILED) - { - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, - G_IO_ERROR_WRONG_ETAG, - _("The file was externally modified")); - return; - } - /* TODO: other errors */ - - open_for_replace_succeeded (op_backend, job, soup_message_get_uri (msg), - soup_message_headers_get (msg->request_headers, "If-Match")); -} - -static gboolean -try_replace (GVfsBackend *backend, - GVfsJobOpenForWrite *job, - const char *filename, - const char *etag, - gboolean make_backup, - GFileCreateFlags flags) -{ - GVfsBackendHttp *op_backend; - SoupURI *uri; - - /* TODO: if SoupOutputStream supported chunked requests, we could - * use a PUT with "If-Match: ..." and "Expect: 100-continue" - */ - - op_backend = G_VFS_BACKEND_HTTP (backend); - - if (make_backup) - { - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, - G_IO_ERROR_CANT_CREATE_BACKUP, - _("Backup file creation failed")); - return TRUE; - } - - - - uri = http_backend_uri_for_filename (backend, filename, FALSE); - - if (etag) - { - SoupMessage *msg; - - msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri); - soup_uri_free (uri); - soup_message_headers_append (msg->request_headers, "If-Match", etag); - - g_vfs_job_set_backend_data (G_VFS_JOB (job), op_backend, NULL); - soup_session_queue_message (op_backend->session, msg, - try_replace_checked_etag, job); - return TRUE; - } - - open_for_replace_succeeded (op_backend, G_VFS_JOB (job), uri, NULL); - soup_uri_free (uri); - return TRUE; -} - -/* *** write () *** */ -static void -write_ready (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - GOutputStream *stream; - GVfsJob *job; - GError *error; - gssize nwrote; - - stream = G_OUTPUT_STREAM (source_object); - error = NULL; - job = G_VFS_JOB (user_data); - - nwrote = g_output_stream_write_finish (stream, result, &error); - - if (nwrote < 0) - { - g_vfs_job_failed_literal (G_VFS_JOB (job), - error->domain, - error->code, - error->message); - - g_error_free (error); - return; - } - - g_vfs_job_write_set_written_size (G_VFS_JOB_WRITE (job), nwrote); - g_vfs_job_succeeded (job); -} - -static gboolean -try_write (GVfsBackend *backend, - GVfsJobWrite *job, - GVfsBackendHandle handle, - char *buffer, - gsize buffer_size) -{ - GOutputStream *stream; - - stream = G_OUTPUT_STREAM (handle); - - g_output_stream_write_async (stream, - buffer, - buffer_size, - G_PRIORITY_DEFAULT, - G_VFS_JOB (job)->cancellable, - write_ready, - job); - return TRUE; -} - -/* *** close_write () *** */ -static void -close_write_ready (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - GOutputStream *stream; - GVfsJob *job; - GError *error; - gboolean res; - - error = NULL; - job = G_VFS_JOB (user_data); - stream = G_OUTPUT_STREAM (source_object); - res = g_output_stream_close_finish (stream, - result, - &error); - if (res == FALSE) - { - g_vfs_job_failed_literal (G_VFS_JOB (job), - error->domain, - error->code, - error->message); - - g_error_free (error); - } - else - g_vfs_job_succeeded (job); - - g_object_unref (stream); -} - -static gboolean -try_close_write (GVfsBackend *backend, - GVfsJobCloseWrite *job, - GVfsBackendHandle handle) -{ - GOutputStream *stream; - - stream = G_OUTPUT_STREAM (handle); - - g_output_stream_close_async (stream, - G_PRIORITY_DEFAULT, - G_VFS_JOB (job)->cancellable, - close_write_ready, - job); - - return TRUE; -} - -static void -do_make_directory (GVfsBackend *backend, - GVfsJobMakeDirectory *job, - const char *filename) -{ - SoupMessage *msg; - SoupURI *uri; - guint status; - - uri = http_backend_uri_for_filename (backend, filename, TRUE); - msg = soup_message_new_from_uri (SOUP_METHOD_MKCOL, uri); - soup_uri_free (uri); - - status = g_vfs_backend_dav_send_message (backend, msg); - - if (! SOUP_STATUS_IS_SUCCESSFUL (status)) - if (status == SOUP_STATUS_METHOD_NOT_ALLOWED) - g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, - G_IO_ERROR_EXISTS, - _("Target file already exists")); - else - g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, - http_error_code_from_status (status), - msg->reason_phrase); - else - g_vfs_job_succeeded (G_VFS_JOB (job)); - - g_object_unref (msg); -} - -static void -do_delete (GVfsBackend *backend, - GVfsJobDelete *job, - const char *filename) -{ - SoupMessage *msg; - SoupURI *uri; - GFileType file_type; - gboolean res; - guint num_children; - guint status; - GError *error; - - error = NULL; - - uri = http_backend_uri_for_filename (backend, filename, FALSE); - res = stat_location (backend, uri, &file_type, &num_children, &error); - - if (res == FALSE) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - return; - } - - if (file_type == G_FILE_TYPE_DIRECTORY && num_children) - { - g_vfs_job_failed (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_NOT_EMPTY, - _("Directory not empty")); - return; - } - - msg = soup_message_new_from_uri (SOUP_METHOD_DELETE, uri); - - status = g_vfs_backend_dav_send_message (backend, msg); - - if (!SOUP_STATUS_IS_SUCCESSFUL (status)) - g_vfs_job_failed_literal (G_VFS_JOB (job), - G_IO_ERROR, - http_error_code_from_status (status), - msg->reason_phrase); - else - g_vfs_job_succeeded (G_VFS_JOB (job)); - - soup_uri_free (uri); - g_object_unref (msg); -} - -static void -do_set_display_name (GVfsBackend *backend, - GVfsJobSetDisplayName *job, - const char *filename, - const char *display_name) -{ - SoupMessage *msg; - SoupURI *source; - SoupURI *target; - char *target_path; - char *dirname; - guint status; - - source = http_backend_uri_for_filename (backend, filename, FALSE); - msg = soup_message_new_from_uri (SOUP_METHOD_MOVE, source); - - dirname = g_path_get_dirname (filename); - target_path = g_build_filename (dirname, display_name, NULL); - target = http_backend_uri_for_filename (backend, target_path, FALSE); - - message_add_destination_header (msg, target); - message_add_overwrite_header (msg, FALSE); - - status = g_vfs_backend_dav_send_message (backend, msg); - - /* - * The precondition of SOUP_STATUS_PRECONDITION_FAILED (412) in - * this case was triggered by the "Overwrite: F" header which - * means that the target already exists. - * Also if we get a REDIRECTION it means that there was no - * "Location" header, since otherwise that would have triggered - * our redirection handler. This probably means we are dealing - * with an web dav implementation (like mod_dav) that also sends - * redirects for the destionaion (i.e. "Destination: /foo" header) - * which very likely means that the target also exists (and is a - * directory). That or the webdav server is broken. - * We could find out by doing another stat and but I think this is - * such a corner case that we are totally fine with returning - * G_IO_ERROR_EXISTS. - * */ - - if (SOUP_STATUS_IS_SUCCESSFUL (status)) - { - g_print ("new target_path: %s\n", target_path); - g_vfs_job_set_display_name_set_new_path (job, target_path); - g_vfs_job_succeeded (G_VFS_JOB (job)); - } - else if (status == SOUP_STATUS_PRECONDITION_FAILED || - SOUP_STATUS_IS_REDIRECTION (status)) - g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, - G_IO_ERROR_EXISTS, - _("Target file already exists")); - else - g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, - http_error_code_from_status (status), - "%s", msg->reason_phrase); - - g_object_unref (msg); - g_free (dirname); - g_free (target_path); - soup_uri_free (target); - soup_uri_free (source); -} - -static gboolean -try_unmount (GVfsBackend *backend, - GVfsJobUnmount *job) -{ - _exit (0); -} - -/* ************************************************************************* */ -/* */ -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->try_mount = NULL; - backend_class->mount = do_mount; - backend_class->try_query_info = NULL; - backend_class->query_info = do_query_info; - backend_class->enumerate = do_enumerate; - backend_class->try_create = try_create; - backend_class->try_replace = try_replace; - backend_class->try_write = try_write; - backend_class->try_close_write = try_close_write; - backend_class->make_directory = do_make_directory; - backend_class->delete = do_delete; - backend_class->set_display_name = do_set_display_name; - backend_class->try_unmount = try_unmount; -} |