[phodav: 7/18] wip: move propfind method
- From: Marc-Andre Lureau <malureau src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [phodav: 7/18] wip: move propfind method
- Date: Thu, 10 Apr 2014 17:51:40 +0000 (UTC)
commit af9989a6486c330efd4362a5934889fbfc81c7b3
Author: Marc-André Lureau <marcandre lureau gmail com>
Date: Thu Apr 10 17:52:12 2014 +0200
wip: move propfind method
Makefile.am | 2 +
libphodav/phodav-lock.c | 59 +++
libphodav/phodav-lock.h | 3 +
libphodav/phodav-method-propfind.c | 746 +++++++++++++++++++++++++++++
libphodav/phodav-method-propfind.h | 31 ++
libphodav/phodav-priv.h | 17 +
libphodav/phodav-server.c | 904 ++----------------------------------
libphodav/phodav-utils.c | 90 +++-
libphodav/phodav-utils.h | 11 +-
9 files changed, 973 insertions(+), 890 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 5b083ac..2b13f5e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,8 @@ libphodav_1_0_la_SOURCES = \
libphodav/phodav-lock.h \
libphodav/phodav-method-get.c \
libphodav/phodav-method-get.h \
+ libphodav/phodav-method-propfind.c \
+ libphodav/phodav-method-propfind.h \
libphodav/phodav-multistatus.c \
libphodav/phodav-multistatus.h \
libphodav/phodav-server.c \
diff --git a/libphodav/phodav-lock.c b/libphodav/phodav-lock.c
index 21f0214..5fea312 100644
--- a/libphodav/phodav-lock.c
+++ b/libphodav/phodav-lock.c
@@ -15,7 +15,9 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+
#include "phodav-lock.h"
+#include "phodav-utils.h"
#include "phodav-path.h"
void
@@ -65,3 +67,60 @@ dav_lock_free (DAVLock *lock)
g_slice_free (DAVLock, lock);
}
+
+static const gchar *
+locktype_to_string (LockType type)
+{
+ if (type == LOCK_WRITE)
+ return "write";
+
+ g_return_val_if_reached (NULL);
+}
+
+static const gchar *
+lockscope_to_string (LockScopeType type)
+{
+ if (type == LOCK_SCOPE_EXCLUSIVE)
+ return "exclusive";
+ else if (type == LOCK_SCOPE_SHARED)
+ return "shared";
+
+ g_return_val_if_reached (NULL);
+}
+
+xmlNodePtr
+dav_lock_get_activelock_node (const DAVLock *lock,
+ xmlNsPtr ns)
+{
+ xmlNodePtr node, active;
+
+ active = xmlNewNode (ns, BAD_CAST "activelock");
+
+ node = xmlNewChild (active, ns, BAD_CAST "locktype", NULL);
+ xmlNewChild (node, ns, BAD_CAST locktype_to_string (lock->type), NULL);
+ node = xmlNewChild (active, ns, BAD_CAST "lockscope", NULL);
+ xmlNewChild (node, ns, BAD_CAST lockscope_to_string (lock->scope), NULL);
+ node = xmlNewChild (active, ns, BAD_CAST "depth", NULL);
+ xmlAddChild (node, xmlNewText (BAD_CAST depth_to_string (lock->depth)));
+
+ if (lock->owner)
+ xmlAddChild (active, xmlCopyNode (lock->owner, 1));
+
+ node = xmlNewChild (active, ns, BAD_CAST "locktoken", NULL);
+ node = xmlNewChild (node, ns, BAD_CAST "href", NULL);
+ xmlAddChild (node, xmlNewText (BAD_CAST lock->token));
+
+ node = xmlNewChild (active, ns, BAD_CAST "lockroot", NULL);
+ node = xmlNewChild (node, ns, BAD_CAST "href", NULL);
+ xmlAddChild (node, xmlNewText (BAD_CAST lock->path->path));
+ if (lock->timeout)
+ {
+ gchar *tmp = g_strdup_printf ("Second-%" G_GINT64_FORMAT, lock->timeout -
+ g_get_monotonic_time () / G_USEC_PER_SEC);
+ node = xmlNewChild (active, ns, BAD_CAST "timeout", NULL);
+ xmlAddChild (node, xmlNewText (BAD_CAST tmp));
+ g_free (tmp);
+ }
+
+ return active;
+}
diff --git a/libphodav/phodav-lock.h b/libphodav/phodav-lock.h
index fb09903..6d9928f 100644
--- a/libphodav/phodav-lock.h
+++ b/libphodav/phodav-lock.h
@@ -31,6 +31,9 @@ void dav_lock_free (DAVLock *lock);
void dav_lock_refresh_timeout (DAVLock *lock, guint timeout);
+xmlNodePtr dav_lock_get_activelock_node (const DAVLock *lock,
+ xmlNsPtr ns);
+
G_END_DECLS
#endif /* __PHODAV_LOCK_H__ */
diff --git a/libphodav/phodav-method-propfind.c b/libphodav/phodav-method-propfind.c
new file mode 100644
index 0000000..c56977e
--- /dev/null
+++ b/libphodav/phodav-method-propfind.c
@@ -0,0 +1,746 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/*
+ * Copyright (C) 2013 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "phodav-utils.h"
+#include "phodav-multistatus.h"
+#include "phodav-lock.h"
+#include "phodav-method-propfind.h"
+
+typedef struct _PropFind
+{
+ PropFindType type;
+ GHashTable *props;
+} PropFind;
+
+static PropFind*
+propfind_new (void)
+{
+ PropFind *pf = g_slice_new0 (PropFind);
+
+ // TODO: a better hash for Node (name, ns)
+ pf->props = g_hash_table_new (g_direct_hash, g_direct_equal);
+ return pf;
+}
+
+static void
+propfind_free (PropFind *pf)
+{
+ if (!pf)
+ return;
+
+ g_hash_table_unref (pf->props);
+ g_slice_free (PropFind, pf);
+}
+
+#define PROP_SET_STATUS(Node, Status) G_STMT_START { \
+ (Node)->_private = GINT_TO_POINTER (Status); \
+ } G_STMT_END
+
+static xmlNodePtr
+prop_resourcetype (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "resourcetype");
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ xmlNewChild (node, ns, BAD_CAST "collection", NULL);
+ else if (!g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
+ {
+ g_warn_if_reached ();
+ status = SOUP_STATUS_NOT_FOUND;
+ }
+
+ end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_supportedlock (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "supportedlock");
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ {
+ xmlNodePtr entry = xmlNewChild (node, NULL, BAD_CAST "lockentry", NULL);
+ xmlNodePtr scope = xmlNewChild (entry, NULL, BAD_CAST "lockscope", NULL);
+ xmlNewChild (scope, NULL, BAD_CAST "exclusive", NULL);
+ xmlNodePtr type = xmlNewChild (entry, NULL, BAD_CAST "locktype", NULL);
+ xmlNewChild (type, NULL, BAD_CAST "write", NULL);
+ }
+
+ {
+ xmlNodePtr entry = xmlNewChild (node, NULL, BAD_CAST "lockentry", NULL);
+ xmlNodePtr scope = xmlNewChild (entry, NULL, BAD_CAST "lockscope", NULL);
+ xmlNewChild (scope, NULL, BAD_CAST "shared", NULL);
+ xmlNodePtr type = xmlNewChild (entry, NULL, BAD_CAST "locktype", NULL);
+ xmlNewChild (type, NULL, BAD_CAST "write", NULL);
+ }
+
+end:
+ PROP_SET_STATUS (node, SOUP_STATUS_OK);
+ return node;
+}
+
+static gboolean
+lockdiscovery_cb (const gchar *key, Path *path, gpointer data)
+{
+ xmlNodePtr node = data;
+ GList *l;
+
+ g_return_val_if_fail (key, FALSE);
+ g_return_val_if_fail (path, FALSE);
+
+ for (l = path->locks; l != NULL; l = l->next)
+ xmlAddChild (node, dav_lock_get_activelock_node (l->data, NULL));
+
+ return TRUE;
+}
+
+static xmlNodePtr
+prop_lockdiscovery (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ PhodavServer *server = handler_get_server (handler);
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "lockdiscovery");
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ server_foreach_parent_path (server, path, lockdiscovery_cb, node);
+
+end:
+ PROP_SET_STATUS (node, SOUP_STATUS_OK);
+ return node;
+}
+
+static void
+node_add_time (xmlNodePtr node, guint64 time, SoupDateFormat format)
+{
+ SoupDate *date;
+ gchar *text;
+
+ g_warn_if_fail (time != 0);
+ date = soup_date_new_from_time_t (time);
+ text = soup_date_to_string (date, format);
+ xmlAddChild (node, xmlNewText (BAD_CAST text));
+ g_free (text);
+ soup_date_free (date);
+}
+
+static xmlNodePtr
+prop_creationdate (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "creationdate");
+ guint64 time;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ time = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_CREATED);
+
+ /* windows seems to want this, even apache returns modified time */
+ if (time == 0)
+ time = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ if (time == 0)
+ status = SOUP_STATUS_NOT_FOUND;
+ else
+ node_add_time (node, time, SOUP_DATE_HTTP);
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_getlastmodified (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getlastmodified");
+ guint64 time;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ time = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ if (time == 0)
+ status = SOUP_STATUS_NOT_FOUND;
+ else
+ node_add_time (node, time, SOUP_DATE_ISO8601);
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_getcontentlength (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getcontentlength");
+ guint64 size;
+ gchar *text;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
+ text = g_strdup_printf ("%" G_GUINT64_FORMAT, size);
+ xmlAddChild (node, xmlNewText (BAD_CAST text));
+ g_free (text);
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_getcontenttype (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getcontenttype");
+ const gchar *type;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+ if (type == NULL)
+ status = SOUP_STATUS_NOT_FOUND;
+ else
+ xmlAddChild (node, xmlNewText (BAD_CAST type));
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_displayname (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "displayname");
+ const gchar *name;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ name = g_file_info_get_display_name (info);
+ if (name == NULL)
+ status = SOUP_STATUS_NOT_FOUND;
+ else
+ xmlAddChild (node, xmlNewText (BAD_CAST name));
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_getetag (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getetag");
+ const gchar *etag;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ etag = g_file_info_get_etag (info);
+ if (etag)
+ {
+ gchar *tmp = g_strdup_printf ("\"%s\"", etag);
+ xmlAddChild (node, xmlNewText (BAD_CAST tmp));
+ g_free (tmp);
+ }
+ else
+ {
+ status = SOUP_STATUS_NOT_FOUND;
+ }
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_executable (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (NULL, BAD_CAST "executable");
+ gboolean exec;
+
+ xmlNewNs (node, BAD_CAST "http://apache.org/dav/props/", NULL);
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ exec = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ exec = FALSE;
+
+ xmlAddChild (node, xmlNewText (exec ? BAD_CAST "T" : BAD_CAST "F"));
+
+end:
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+static xmlNodePtr
+prop_quota_available (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *_info, xmlNsPtr ns)
+{
+ GFileInfo *info = NULL;
+ gint status = SOUP_STATUS_OK;
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "quota-available-bytes");
+ GCancellable *cancellable = handler_get_cancellable(handler);
+ GError *error = NULL;
+ gchar *tmp = NULL;
+ guint64 size;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ info = g_file_query_filesystem_info (handler_get_file (handler), "filesystem::*",
+ cancellable, &error);
+ if (error)
+ {
+ g_warning ("Filesystem info error: %s", error->message);
+ status = SOUP_STATUS_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
+
+ tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, size);
+ xmlAddChild (node, xmlNewText (BAD_CAST tmp));
+
+end:
+ if (info)
+ g_object_unref (info);
+
+ g_free (tmp);
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+
+#if GLIB_CHECK_VERSION (2, 38, 0)
+static xmlNodePtr
+prop_quota_used (PathHandler *handler, PropFind *pf,
+ const gchar *path, GFileInfo *info, xmlNsPtr ns)
+{
+ gint status = SOUP_STATUS_OK;
+ GCancellable *cancellable = handler_get_cancellable(handler);
+ xmlNodePtr node = xmlNewNode (ns, BAD_CAST "quota-used-bytes");
+ guint64 disk_usage = 0;
+ GError *error = NULL;
+ gchar *tmp = NULL;
+
+ if (pf->type == PROPFIND_PROPNAME)
+ goto end;
+
+ if (!g_file_measure_disk_usage (handler_get_file (handler),
+ G_FILE_MEASURE_NONE,
+ cancellable,
+ NULL, NULL,
+ &disk_usage, NULL, NULL,
+ &error))
+ {
+ g_warning ("Filesystem info error: %s", error->message);
+ status = SOUP_STATUS_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, disk_usage);
+ xmlAddChild (node, xmlNewText (BAD_CAST tmp));
+
+end:
+ g_free (tmp);
+ PROP_SET_STATUS (node, status);
+ return node;
+}
+#endif
+
+static gint
+node_compare_int (xmlNodePtr a,
+ xmlNodePtr b)
+{
+ return GPOINTER_TO_INT (a->_private) - GPOINTER_TO_INT (b->_private);
+}
+
+static void
+prop_add (GList **stat, xmlNodePtr node)
+{
+ *stat = g_list_insert_sorted (*stat, node, (GCompareFunc) node_compare_int);
+}
+
+#define PROP(Name, Info) { G_STRINGIFY (Name), G_PASTE (prop_, Name), Info }
+static const struct _PropList
+{
+ const gchar *name;
+ xmlNodePtr (*func) (PathHandler *, PropFind *, const gchar *, GFileInfo *, xmlNsPtr);
+ gboolean need_info;
+ gboolean slow;
+
+} prop_list[] = {
+ PROP (resourcetype, 1),
+ PROP (creationdate, 1),
+ PROP (getlastmodified, 1),
+ PROP (getcontentlength, 1),
+ PROP (getcontenttype, 1),
+ PROP (displayname, 1),
+ PROP (getetag, 1),
+ PROP (executable, 1),
+ PROP (supportedlock, 0),
+ PROP (lockdiscovery, 0),
+ { "quota-available-bytes", prop_quota_available, },
+#if GLIB_CHECK_VERSION (2, 38, 0)
+ { "quota-used-bytes", prop_quota_used, FALSE, TRUE, }
+#endif
+};
+
+static xmlNodePtr
+prop_xattr (gchar *xattr)
+{
+ xmlNodePtr node;
+ gchar *ns = xattr + 7;
+ gchar *name = g_utf8_strchr (ns, -1, '#');
+
+ if (name)
+ {
+ *name = '\0';
+ name = name + 1;
+ }
+ else
+ {
+ name = ns;
+ ns = NULL;
+ }
+
+ node = xmlNewNode (NULL, BAD_CAST name);
+ if (ns)
+ xmlNewNs (node, BAD_CAST ns, NULL);
+
+ PROP_SET_STATUS (node, SOUP_STATUS_OK);
+ return node;
+}
+
+#define FILE_QUERY "standard::*,time::*,access::*,etag::*,xattr::*"
+static GList*
+propfind_populate (PathHandler *handler, const gchar *path,
+ PropFind *pf, GFileInfo *info,
+ xmlNsPtr ns)
+{
+ GHashTableIter iter;
+ xmlNodePtr node;
+ GList *stat = NULL;
+ int i;
+
+ if (pf->type == PROPFIND_ALLPROP || pf->type == PROPFIND_PROPNAME)
+ {
+ for (i = 0; i < G_N_ELEMENTS (prop_list); i++)
+ {
+ if (pf->type != PROPFIND_PROPNAME)
+ {
+ if (prop_list[i].need_info && !info)
+ continue;
+ if (prop_list[i].slow)
+ continue;
+ }
+
+ /* perhaps not include the 404? */
+ prop_add (&stat, prop_list[i].func (handler, pf, path, info, ns));
+ }
+
+ if (info)
+ {
+ gchar **attrs = g_file_info_list_attributes (info, "xattr");
+
+ for (i = 0; attrs[i]; i++)
+ {
+ node = prop_xattr(attrs[i]);
+ prop_add (&stat, node);
+ }
+
+ g_strfreev (attrs);
+ }
+
+ goto end;
+ }
+
+ g_hash_table_iter_init (&iter, pf->props);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &node, NULL))
+ {
+ for (i = 0; i < G_N_ELEMENTS (prop_list); i++)
+ {
+ if (xml_node_has_name (node, prop_list[i].name)) {
+ node = prop_list[i].func (handler, pf, path, info, ns);
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (prop_list))
+ {
+ gchar *xattr = xml_node_get_xattr_name (node, "xattr::");
+ node = xmlCopyNode (node, 2);
+ const gchar *val = NULL;
+
+ if (xattr)
+ {
+ val = g_file_info_get_attribute_string (info, xattr);
+ g_free (xattr);
+ }
+
+ if (val)
+ {
+ xmlAddChild (node, xmlNewText (BAD_CAST val));
+ PROP_SET_STATUS (node, SOUP_STATUS_OK);
+ }
+ else
+ {
+ xml_node_debug (node);
+ PROP_SET_STATUS (node, SOUP_STATUS_NOT_FOUND);
+ }
+ }
+
+ prop_add (&stat, node);
+ }
+
+end:
+ return stat;
+}
+
+static gint
+propfind_query_zero (PathHandler *handler, PropFind *pf,
+ const gchar *path, GHashTable *path_resp,
+ xmlNsPtr ns)
+{
+ GCancellable *cancellable = handler_get_cancellable(handler);
+ GError *err = NULL;
+ GFileInfo *info = NULL;
+ GFile *file;
+ GList *stat = NULL;
+ gint status = SOUP_STATUS_OK;
+
+ file = g_file_get_child (handler_get_file (handler), path + 1);
+ info = g_file_query_info (file, FILE_QUERY,
+ G_FILE_QUERY_INFO_NONE, cancellable, &err);
+ g_object_unref (file);
+ if (err)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ g_warning ("queryinfo: %s", err->message);
+ g_clear_error (&err);
+ return SOUP_STATUS_NOT_FOUND;
+ }
+
+ stat = propfind_populate (handler, path, pf, info, ns);
+ g_hash_table_insert (path_resp, g_strdup (path),
+ response_new (stat, 0));
+ g_clear_object (&info);
+
+ return status;
+}
+
+static gint
+propfind_query_one (PathHandler *handler, PropFind *pf,
+ const gchar *path, GHashTable *path_resp,
+ xmlNsPtr ns)
+{
+ GCancellable *cancellable = handler_get_cancellable(handler);
+ GError *err = NULL;
+ GFile *file;
+ GFileEnumerator *e;
+ gint status;
+
+ status = propfind_query_zero (handler, pf, path, path_resp, ns);
+ if (status != SOUP_STATUS_OK)
+ return status;
+
+ file = g_file_get_child (handler_get_file (handler), path + 1);
+ e = g_file_enumerate_children (file, FILE_QUERY, G_FILE_QUERY_INFO_NONE,
+ cancellable, &err);
+ g_object_unref (file);
+ if (!e)
+ goto end;
+
+ while (1)
+ {
+ GList *stat;
+ GFileInfo *info = g_file_enumerator_next_file (e, cancellable, &err);
+ if (!info)
+ break;
+
+ gchar *escape = g_markup_escape_text (g_file_info_get_name (info), -1);
+ stat = propfind_populate (handler, path, pf, info, ns);
+ g_hash_table_insert (path_resp, g_build_path ("/", path, escape, NULL),
+ response_new (stat, 0));
+ g_free (escape);
+ g_object_unref (info);
+ }
+
+ g_file_enumerator_close (e, cancellable, NULL);
+ g_clear_object (&e);
+
+end:
+ if (err)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+ g_warning ("query: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ return status;
+}
+
+static gboolean
+parse_prop (xmlNodePtr node, GHashTable *props)
+{
+ for (node = node->children; node; node = node->next)
+ {
+ if (!xml_node_is_element (node))
+ continue;
+
+ // only interested in ns&name
+ g_hash_table_add (props, node);
+ }
+
+ return TRUE;
+}
+
+static PropFind*
+parse_propfind (xmlNodePtr xml)
+{
+ PropFind *pf = propfind_new ();
+ xmlNodePtr node;
+
+ for (node = xml->children; node; node = node->next)
+ {
+ if (!xml_node_is_element (node))
+ continue;
+
+ if (xml_node_has_name (node, "allprop"))
+ {
+ pf->type = PROPFIND_ALLPROP;
+ goto end;
+ }
+ else if (xml_node_has_name (node, "propname"))
+ {
+ pf->type = PROPFIND_PROPNAME;
+ goto end;
+ }
+ else if (xml_node_has_name (node, "prop"))
+ {
+ pf->type = PROPFIND_PROP;
+ parse_prop (node, pf->props);
+ goto end;
+ }
+ }
+
+ g_warn_if_reached ();
+ g_clear_pointer (&pf, propfind_free);
+
+end:
+ return pf;
+}
+
+gint
+phodav_method_propfind (PathHandler *handler, SoupMessage *msg,
+ const char *path, GError **err)
+{
+ PropFind *pf = NULL;
+ DepthType depth;
+ GHashTable *mstatus = NULL; // path -> statlist
+ DavDoc doc = {0, };
+ gint status = SOUP_STATUS_NOT_FOUND;
+ xmlNsPtr ns = NULL;
+
+ depth = depth_from_string (soup_message_headers_get_one (msg->request_headers, "Depth"));
+ if (!msg->request_body || !msg->request_body->length)
+ {
+ /* Win kludge: http://code.google.com/p/sabredav/wiki/Windows */
+ pf = propfind_new ();
+ pf->type = PROPFIND_ALLPROP;
+ }
+ else
+ {
+ if (!davdoc_parse (&doc, msg, msg->request_body, "propfind"))
+ {
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto end;
+ }
+
+ pf = parse_propfind (doc.root);
+ if (!pf)
+ goto end;
+ }
+
+ ns = xmlNewNs (NULL, BAD_CAST "DAV:", BAD_CAST "D");
+ mstatus = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) response_free);
+ if (pf->type == PROPFIND_PROP ||
+ pf->type == PROPFIND_ALLPROP ||
+ pf->type == PROPFIND_PROPNAME)
+ {
+ if (depth == DEPTH_ZERO)
+ status = propfind_query_zero (handler, pf, path, mstatus, ns);
+ else if (depth == DEPTH_ONE)
+ status = propfind_query_one (handler, pf, path, mstatus, ns);
+ else
+ {
+ status = SOUP_STATUS_FORBIDDEN;
+ g_warn_if_reached ();
+ }
+ }
+ else
+ g_warn_if_reached ();
+
+ if (status != SOUP_STATUS_OK)
+ goto end;
+
+ status = set_response_multistatus (msg, mstatus);
+
+end:
+ davdoc_free (&doc);
+ propfind_free (pf);
+ if (mstatus)
+ g_hash_table_unref (mstatus);
+ if (ns)
+ xmlFreeNs(ns);
+ return status;
+}
diff --git a/libphodav/phodav-method-propfind.h b/libphodav/phodav-method-propfind.h
new file mode 100644
index 0000000..9d6c10c
--- /dev/null
+++ b/libphodav/phodav-method-propfind.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/*
+ * Copyright (C) 2013 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __PHODAV_METHOD_PROPFIND_H__
+#define __PHODAV_METHOD_PROPFIND_H__
+
+#include <libsoup/soup.h>
+#include "phodav-priv.h"
+
+G_BEGIN_DECLS
+
+gint phodav_method_propfind (PathHandler *handler, SoupMessage *msg,
+ const char *path, GError **err);
+
+G_END_DECLS
+
+#endif /* __PHODAV_METHOD_PROPFIND_H__ */
diff --git a/libphodav/phodav-priv.h b/libphodav/phodav-priv.h
index aa7d598..7ee92cb 100644
--- a/libphodav/phodav-priv.h
+++ b/libphodav/phodav-priv.h
@@ -28,6 +28,7 @@
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
+#include "phodav-server.h"
G_BEGIN_DECLS
@@ -52,6 +53,12 @@ typedef enum _DepthType {
DEPTH_INFINITY
} DepthType;
+typedef enum _PropFindType {
+ PROPFIND_ALLPROP,
+ PROPFIND_PROPNAME,
+ PROPFIND_PROP
+} PropFindType;
+
struct _DAVLock
{
Path *path;
@@ -70,8 +77,18 @@ struct _Path
guint32 refs;
};
+typedef gboolean (* PathCb) (const gchar *key,
+ Path *path,
+ gpointer data);
+
+
GFile * handler_get_file (PathHandler *handler);
GCancellable * handler_get_cancellable (PathHandler *handler);
+PhodavServer * handler_get_server (PathHandler *handler);
+
+gboolean server_foreach_parent_path (PhodavServer *server,
+ const gchar *path,
+ PathCb cb, gpointer data);
G_END_DECLS
diff --git a/libphodav/phodav-server.c b/libphodav/phodav-server.c
index 83c665b..27d0249 100644
--- a/libphodav/phodav-server.c
+++ b/libphodav/phodav-server.c
@@ -29,7 +29,9 @@
#include "phodav-path.h"
#include "phodav-lock.h"
#include "phodav-utils.h"
+
#include "phodav-method-get.h"
+#include "phodav-method-propfind.h"
/**
* SECTION:phodav-server
@@ -118,6 +120,12 @@ struct _PathHandler
GFile *file;
};
+PhodavServer * G_GNUC_PURE
+handler_get_server (PathHandler *handler)
+{
+ return handler->self;
+}
+
GFile * G_GNUC_PURE
handler_get_file (PathHandler *handler)
{
@@ -304,60 +312,13 @@ phodav_server_class_init (PhodavServerClass *klass)
G_PARAM_STATIC_STRINGS));
}
-static void
-node_debug (xmlNodePtr node)
-{
- g_debug ("%s ns:%s", node->name, node->ns ? (gchar *) node->ns->href : "");
-}
-
-static gboolean
-node_has_ns (xmlNodePtr node, const char *ns_href)
-{
- return node->ns && node->ns->href &&
- !g_strcmp0 ((gchar *) node->ns->href, ns_href);
-
-}
-
-static 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 = !g_strcmp0 ((gchar *) node->name, name);
-
- if (ns_href)
- has_ns = node_has_ns (node, ns_href);
-
- return has_name && has_ns;
-}
-
-static gboolean
-node_has_name (xmlNodePtr node, const char *name)
-{
- g_return_val_if_fail (node != NULL, FALSE);
-
- return node_has_name_ns (node, name, "DAV:");
-}
-
-static gboolean
-node_is_element (xmlNodePtr node)
-{
- return node->type == XML_ELEMENT_NODE && node->name != NULL;
-}
-
static LockScopeType
parse_lockscope (xmlNodePtr rt)
{
xmlNodePtr node;
for (node = rt->children; node; node = node->next)
- if (node_is_element (node))
+ if (xml_node_is_element (node))
break;
if (node == NULL)
@@ -371,24 +332,13 @@ parse_lockscope (xmlNodePtr rt)
return LOCK_SCOPE_NONE;
}
-static const gchar *
-lockscope_to_string (LockScopeType type)
-{
- if (type == LOCK_SCOPE_EXCLUSIVE)
- return "exclusive";
- else if (type == LOCK_SCOPE_SHARED)
- return "shared";
-
- g_return_val_if_reached (NULL);
-}
-
static LockType
parse_locktype (xmlNodePtr rt)
{
xmlNodePtr node;
for (node = rt->children; node; node = node->next)
- if (node_is_element (node))
+ if (xml_node_is_element (node))
break;
if (node == NULL)
@@ -400,204 +350,8 @@ parse_locktype (xmlNodePtr rt)
return LOCK_NONE;
}
-static const gchar *
-locktype_to_string (LockType type)
-{
- if (type == LOCK_WRITE)
- return "write";
-
- g_return_val_if_reached (NULL);
-}
-
-typedef enum _PropFindType {
- PROPFIND_ALLPROP,
- PROPFIND_PROPNAME,
- PROPFIND_PROP
-} PropFindType;
-
-typedef struct _PropFind
-{
- PropFindType type;
- GHashTable *props;
-} PropFind;
-
-static PropFind*
-propfind_new (void)
-{
- PropFind *pf = g_slice_new0 (PropFind);
-
- // TODO: a better hash for Node (name, ns)
- pf->props = g_hash_table_new (g_direct_hash, g_direct_equal);
- return pf;
-}
-
-static void
-propfind_free (PropFind *pf)
-{
- if (!pf)
- return;
-
- g_hash_table_unref (pf->props);
- g_slice_free (PropFind, pf);
-}
-
-static gboolean
-parse_prop (xmlNodePtr node, GHashTable *props)
-{
- for (node = node->children; node; node = node->next)
- {
- if (!node_is_element (node))
- continue;
-
- // only interested in ns&name
- g_hash_table_add (props, node);
- }
-
- return TRUE;
-}
-
-static PropFind*
-parse_propfind (xmlNodePtr xml)
-{
- PropFind *pf = propfind_new ();
- xmlNodePtr node;
-
- for (node = xml->children; node; node = node->next)
- {
- if (!node_is_element (node))
- continue;
-
- if (node_has_name (node, "allprop"))
- {
- pf->type = PROPFIND_ALLPROP;
- goto end;
- }
- else if (node_has_name (node, "propname"))
- {
- pf->type = PROPFIND_PROPNAME;
- goto end;
- }
- else if (node_has_name (node, "prop"))
- {
- pf->type = PROPFIND_PROP;
- parse_prop (node, pf->props);
- goto end;
- }
- }
-
- g_warn_if_reached ();
- g_clear_pointer (&pf, propfind_free);
-
-end:
- return pf;
-}
-
-#define PROP_SET_STATUS(Node, Status) G_STMT_START { \
- (Node)->_private = GINT_TO_POINTER (Status); \
-} G_STMT_END
-
-static xmlNodePtr
-prop_resourcetype (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "resourcetype");
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
- xmlNewChild (node, ns, BAD_CAST "collection", NULL);
- else if (!g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
- {
- g_warn_if_reached ();
- status = SOUP_STATUS_NOT_FOUND;
- }
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static gint
-node_compare_int (xmlNodePtr a,
- xmlNodePtr b)
-{
- return GPOINTER_TO_INT (a->_private) - GPOINTER_TO_INT (b->_private);
-}
-
-static void
-node_add_time (xmlNodePtr node, guint64 time, SoupDateFormat format)
-{
- SoupDate *date;
- gchar *text;
-
- g_warn_if_fail (time != 0);
- date = soup_date_new_from_time_t (time);
- text = soup_date_to_string (date, format);
- xmlAddChild (node, xmlNewText (BAD_CAST text));
- g_free (text);
- soup_date_free (date);
-}
-
-static gchar *
-node_get_xattr_name (xmlNodePtr node, const gchar *prefix)
-{
- const gchar *ns = node->ns ? (gchar *) node->ns->href : NULL;
- const gchar *name = (gchar *) node->name;
-
- if (!name)
- return NULL;
-
- if (ns)
- return g_strdup_printf ("%s%s#%s", prefix, ns, name);
- else
- return g_strdup_printf ("%s%s", prefix, name);
-}
-
-static xmlNodePtr
-get_activelock_node (const DAVLock *lock,
- xmlNsPtr ns)
-{
- xmlNodePtr node, active;
-
- active = xmlNewNode (ns, BAD_CAST "activelock");
-
- node = xmlNewChild (active, ns, BAD_CAST "locktype", NULL);
- xmlNewChild (node, ns, BAD_CAST locktype_to_string (lock->type), NULL);
- node = xmlNewChild (active, ns, BAD_CAST "lockscope", NULL);
- xmlNewChild (node, ns, BAD_CAST lockscope_to_string (lock->scope), NULL);
- node = xmlNewChild (active, ns, BAD_CAST "depth", NULL);
- xmlAddChild (node, xmlNewText (BAD_CAST depth_to_string (lock->depth)));
-
- if (lock->owner)
- xmlAddChild (active, xmlCopyNode (lock->owner, 1));
-
- node = xmlNewChild (active, ns, BAD_CAST "locktoken", NULL);
- node = xmlNewChild (node, ns, BAD_CAST "href", NULL);
- xmlAddChild (node, xmlNewText (BAD_CAST lock->token));
-
- node = xmlNewChild (active, ns, BAD_CAST "lockroot", NULL);
- node = xmlNewChild (node, ns, BAD_CAST "href", NULL);
- xmlAddChild (node, xmlNewText (BAD_CAST lock->path->path));
- if (lock->timeout)
- {
- gchar *tmp = g_strdup_printf ("Second-%" G_GINT64_FORMAT, lock->timeout -
- g_get_monotonic_time () / G_USEC_PER_SEC);
- node = xmlNewChild (active, ns, BAD_CAST "timeout", NULL);
- xmlAddChild (node, xmlNewText (BAD_CAST tmp));
- g_free (tmp);
- }
-
- return active;
-}
-
-typedef gboolean (* PathCb) (const gchar *key,
- Path *path,
- gpointer data);
-
-static gboolean
-foreach_parent_path (PhodavServer *self, const gchar *path, PathCb cb, gpointer data)
+gboolean
+server_foreach_parent_path (PhodavServer *self, const gchar *path, PathCb cb, gpointer data)
{
gchar **pathv, *partial = g_strdup ("/");
gboolean ret = FALSE;
@@ -663,8 +417,8 @@ static DAVLock *
path_get_lock (PhodavServer *self, const gchar *path, const gchar *token)
{
PathGetLock p = { .token = token };
- gboolean success = !foreach_parent_path (self, path,
- _path_get_lock, (gpointer) &p);
+ gboolean success = !server_foreach_parent_path (self, path,
+ _path_get_lock, (gpointer) &p);
if (!success)
g_message ("Invalid lock token %s for %s", token, path);
@@ -672,604 +426,6 @@ path_get_lock (PhodavServer *self, const gchar *path, const gchar *token)
return p.lock;
}
-static xmlNodePtr
-prop_supportedlock (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "supportedlock");
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- {
- xmlNodePtr entry = xmlNewChild (node, NULL, BAD_CAST "lockentry", NULL);
- xmlNodePtr scope = xmlNewChild (entry, NULL, BAD_CAST "lockscope", NULL);
- xmlNewChild (scope, NULL, BAD_CAST "exclusive", NULL);
- xmlNodePtr type = xmlNewChild (entry, NULL, BAD_CAST "locktype", NULL);
- xmlNewChild (type, NULL, BAD_CAST "write", NULL);
- }
-
- {
- xmlNodePtr entry = xmlNewChild (node, NULL, BAD_CAST "lockentry", NULL);
- xmlNodePtr scope = xmlNewChild (entry, NULL, BAD_CAST "lockscope", NULL);
- xmlNewChild (scope, NULL, BAD_CAST "shared", NULL);
- xmlNodePtr type = xmlNewChild (entry, NULL, BAD_CAST "locktype", NULL);
- xmlNewChild (type, NULL, BAD_CAST "write", NULL);
- }
-
-end:
- PROP_SET_STATUS (node, SOUP_STATUS_OK);
- return node;
-}
-
-static gboolean
-lockdiscovery_cb (const gchar *key, Path *path, gpointer data)
-{
- xmlNodePtr node = data;
- GList *l;
-
- g_return_val_if_fail (key, FALSE);
- g_return_val_if_fail (path, FALSE);
-
- for (l = path->locks; l != NULL; l = l->next)
- xmlAddChild (node, get_activelock_node (l->data, NULL));
-
- return TRUE;
-}
-
-static xmlNodePtr
-prop_lockdiscovery (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- PhodavServer *self = handler->self;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "lockdiscovery");
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- foreach_parent_path (self, path, lockdiscovery_cb, node);
-
-end:
- PROP_SET_STATUS (node, SOUP_STATUS_OK);
- return node;
-}
-
-static xmlNodePtr
-prop_creationdate (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "creationdate");
- guint64 time;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- time = g_file_info_get_attribute_uint64 (info,
- G_FILE_ATTRIBUTE_TIME_CREATED);
-
- /* windows seems to want this, even apache returns modified time */
- if (time == 0)
- time = g_file_info_get_attribute_uint64 (info,
- G_FILE_ATTRIBUTE_TIME_MODIFIED);
-
- if (time == 0)
- status = SOUP_STATUS_NOT_FOUND;
- else
- node_add_time (node, time, SOUP_DATE_HTTP);
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_getlastmodified (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getlastmodified");
- guint64 time;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- time = g_file_info_get_attribute_uint64 (info,
- G_FILE_ATTRIBUTE_TIME_MODIFIED);
-
- if (time == 0)
- status = SOUP_STATUS_NOT_FOUND;
- else
- node_add_time (node, time, SOUP_DATE_ISO8601);
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_getcontentlength (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getcontentlength");
- guint64 size;
- gchar *text;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
- text = g_strdup_printf ("%" G_GUINT64_FORMAT, size);
- xmlAddChild (node, xmlNewText (BAD_CAST text));
- g_free (text);
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_getcontenttype (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getcontenttype");
- const gchar *type;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
- if (type == NULL)
- status = SOUP_STATUS_NOT_FOUND;
- else
- xmlAddChild (node, xmlNewText (BAD_CAST type));
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_displayname (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "displayname");
- const gchar *name;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- name = g_file_info_get_display_name (info);
- if (name == NULL)
- status = SOUP_STATUS_NOT_FOUND;
- else
- xmlAddChild (node, xmlNewText (BAD_CAST name));
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_getetag (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "getetag");
- const gchar *etag;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- etag = g_file_info_get_etag (info);
- if (etag)
- {
- gchar *tmp = g_strdup_printf ("\"%s\"", etag);
- xmlAddChild (node, xmlNewText (BAD_CAST tmp));
- g_free (tmp);
- }
- else
- {
- status = SOUP_STATUS_NOT_FOUND;
- }
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_executable (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (NULL, BAD_CAST "executable");
- gboolean exec;
-
- xmlNewNs (node, BAD_CAST "http://apache.org/dav/props/", NULL);
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- exec = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
- exec = FALSE;
-
- xmlAddChild (node, xmlNewText (exec ? BAD_CAST "T" : BAD_CAST "F"));
-
-end:
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-static xmlNodePtr
-prop_quota_available (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *_info, xmlNsPtr ns)
-{
- GFileInfo *info = NULL;
- gint status = SOUP_STATUS_OK;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "quota-available-bytes");
- PhodavServer *self = handler->self;
- GError *error = NULL;
- gchar *tmp = NULL;
- guint64 size;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- info = g_file_query_filesystem_info (handler->file, "filesystem::*",
- self->cancellable, &error);
- if (error)
- {
- g_warning ("Filesystem info error: %s", error->message);
- status = SOUP_STATUS_INTERNAL_SERVER_ERROR;
- goto end;
- }
-
- size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
-
- tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, size);
- xmlAddChild (node, xmlNewText (BAD_CAST tmp));
-
-end:
- if (info)
- g_object_unref (info);
-
- g_free (tmp);
- PROP_SET_STATUS (node, status);
- return node;
-}
-
-#if GLIB_CHECK_VERSION (2, 38, 0)
-static xmlNodePtr
-prop_quota_used (PathHandler *handler, PropFind *pf,
- const gchar *path, GFileInfo *info, xmlNsPtr ns)
-{
- gint status = SOUP_STATUS_OK;
- PhodavServer *self = handler->self;
- xmlNodePtr node = xmlNewNode (ns, BAD_CAST "quota-used-bytes");
- guint64 disk_usage = 0;
- GError *error = NULL;
- gchar *tmp = NULL;
-
- if (pf->type == PROPFIND_PROPNAME)
- goto end;
-
- if (!g_file_measure_disk_usage (handler->file,
- G_FILE_MEASURE_NONE,
- self->cancellable,
- NULL, NULL,
- &disk_usage, NULL, NULL,
- &error))
- {
- g_warning ("Filesystem info error: %s", error->message);
- status = SOUP_STATUS_INTERNAL_SERVER_ERROR;
- goto end;
- }
-
- tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, disk_usage);
- xmlAddChild (node, xmlNewText (BAD_CAST tmp));
-
-end:
- g_free (tmp);
- PROP_SET_STATUS (node, status);
- return node;
-}
-#endif
-
-static void
-prop_add (GList **stat, xmlNodePtr node)
-{
- *stat = g_list_insert_sorted (*stat, node, (GCompareFunc) node_compare_int);
-}
-
-#define PROP(Name, Info) { G_STRINGIFY (Name), G_PASTE (prop_, Name), Info }
-static const struct _PropList
-{
- const gchar *name;
- xmlNodePtr (*func) (PathHandler *, PropFind *, const gchar *, GFileInfo *, xmlNsPtr);
- gboolean need_info;
- gboolean slow;
-
-} prop_list[] = {
- PROP (resourcetype, 1),
- PROP (creationdate, 1),
- PROP (getlastmodified, 1),
- PROP (getcontentlength, 1),
- PROP (getcontenttype, 1),
- PROP (displayname, 1),
- PROP (getetag, 1),
- PROP (executable, 1),
- PROP (supportedlock, 0),
- PROP (lockdiscovery, 0),
- { "quota-available-bytes", prop_quota_available, },
-#if GLIB_CHECK_VERSION (2, 38, 0)
- { "quota-used-bytes", prop_quota_used, FALSE, TRUE, }
-#endif
-};
-
-static xmlNodePtr
-prop_xattr (gchar *xattr)
-{
- xmlNodePtr node;
- gchar *ns = xattr + 7;
- gchar *name = g_utf8_strchr (ns, -1, '#');
-
- if (name)
- {
- *name = '\0';
- name = name + 1;
- }
- else
- {
- name = ns;
- ns = NULL;
- }
-
- node = xmlNewNode (NULL, BAD_CAST name);
- if (ns)
- xmlNewNs (node, BAD_CAST ns, NULL);
-
- PROP_SET_STATUS (node, SOUP_STATUS_OK);
- return node;
-}
-
-#define FILE_QUERY "standard::*,time::*,access::*,etag::*,xattr::*"
-static GList*
-propfind_populate (PathHandler *handler, const gchar *path,
- PropFind *pf, GFileInfo *info,
- xmlNsPtr ns)
-{
- GHashTableIter iter;
- xmlNodePtr node;
- GList *stat = NULL;
- int i;
-
- if (pf->type == PROPFIND_ALLPROP || pf->type == PROPFIND_PROPNAME)
- {
- for (i = 0; i < G_N_ELEMENTS (prop_list); i++)
- {
- if (pf->type != PROPFIND_PROPNAME)
- {
- if (prop_list[i].need_info && !info)
- continue;
- if (prop_list[i].slow)
- continue;
- }
-
- /* perhaps not include the 404? */
- prop_add (&stat, prop_list[i].func (handler, pf, path, info, ns));
- }
-
- if (info)
- {
- gchar **attrs = g_file_info_list_attributes (info, "xattr");
-
- for (i = 0; attrs[i]; i++)
- {
- node = prop_xattr(attrs[i]);
- prop_add (&stat, node);
- }
-
- g_strfreev (attrs);
- }
-
- goto end;
- }
-
- g_hash_table_iter_init (&iter, pf->props);
- while (g_hash_table_iter_next (&iter, (gpointer *) &node, NULL))
- {
- for (i = 0; i < G_N_ELEMENTS (prop_list); i++)
- {
- if (node_has_name (node, prop_list[i].name)) {
- node = prop_list[i].func (handler, pf, path, info, ns);
- break;
- }
- }
-
- if (i == G_N_ELEMENTS (prop_list))
- {
- gchar *xattr = node_get_xattr_name (node, "xattr::");
- node = xmlCopyNode (node, 2);
- const gchar *val = NULL;
-
- if (xattr)
- {
- val = g_file_info_get_attribute_string (info, xattr);
- g_free (xattr);
- }
-
- if (val)
- {
- xmlAddChild (node, xmlNewText (BAD_CAST val));
- PROP_SET_STATUS (node, SOUP_STATUS_OK);
- }
- else
- {
- node_debug (node);
- PROP_SET_STATUS (node, SOUP_STATUS_NOT_FOUND);
- }
- }
-
- prop_add (&stat, node);
- }
-
-end:
- return stat;
-}
-
-static gint
-propfind_query_zero (PathHandler *handler, PropFind *pf,
- const gchar *path, GHashTable *path_resp,
- xmlNsPtr ns)
-{
- PhodavServer *self = handler->self;
- GError *err = NULL;
- GFileInfo *info = NULL;
- GFile *file;
- GList *stat = NULL;
- gint status = SOUP_STATUS_OK;
-
- file = g_file_get_child (handler->file, path + 1);
- info = g_file_query_info (file, FILE_QUERY,
- G_FILE_QUERY_INFO_NONE, self->cancellable, &err);
- g_object_unref (file);
- if (err)
- {
- if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- g_warning ("queryinfo: %s", err->message);
- g_clear_error (&err);
- return SOUP_STATUS_NOT_FOUND;
- }
-
- stat = propfind_populate (handler, path, pf, info, ns);
- g_hash_table_insert (path_resp, g_strdup (path),
- response_new (stat, 0));
- g_clear_object (&info);
-
- return status;
-}
-
-static gint
-propfind_query_one (PathHandler *handler, PropFind *pf,
- const gchar *path, GHashTable *path_resp,
- xmlNsPtr ns)
-{
- PhodavServer *self = handler->self;
- GError *err = NULL;
- GFile *file;
- GFileEnumerator *e;
- gint status;
-
- status = propfind_query_zero (handler, pf, path, path_resp, ns);
- if (status != SOUP_STATUS_OK)
- return status;
-
- file = g_file_get_child (handler->file, path + 1);
- e = g_file_enumerate_children (file, FILE_QUERY, G_FILE_QUERY_INFO_NONE,
- self->cancellable, &err);
- g_object_unref (file);
- if (!e)
- goto end;
-
- while (1)
- {
- GList *stat;
- GFileInfo *info = g_file_enumerator_next_file (e, self->cancellable, &err);
- if (!info)
- break;
-
- gchar *escape = g_markup_escape_text (g_file_info_get_name (info), -1);
- stat = propfind_populate (handler, path, pf, info, ns);
- g_hash_table_insert (path_resp, g_build_path ("/", path, escape, NULL),
- response_new (stat, 0));
- g_free (escape);
- g_object_unref (info);
- }
-
- g_file_enumerator_close (e, self->cancellable, NULL);
- g_clear_object (&e);
-
-end:
- if (err)
- {
- if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
- g_warning ("query: %s", err->message);
- g_clear_error (&err);
- }
-
- return status;
-}
-
-static gint
-method_propfind (PathHandler *handler, SoupMessage *msg,
- const char *path, GError **err)
-{
- PropFind *pf = NULL;
- DepthType depth;
- GHashTable *mstatus = NULL; // path -> statlist
- DavDoc doc = {0, };
- gint status = SOUP_STATUS_NOT_FOUND;
- xmlNsPtr ns = NULL;
-
- depth = depth_from_string (soup_message_headers_get_one (msg->request_headers, "Depth"));
- if (!msg->request_body || !msg->request_body->length)
- {
- /* Win kludge: http://code.google.com/p/sabredav/wiki/Windows */
- pf = propfind_new ();
- pf->type = PROPFIND_ALLPROP;
- }
- else
- {
- if (!davdoc_parse (&doc, msg, msg->request_body, "propfind"))
- {
- status = SOUP_STATUS_BAD_REQUEST;
- goto end;
- }
-
- pf = parse_propfind (doc.root);
- if (!pf)
- goto end;
- }
-
- ns = xmlNewNs (NULL, BAD_CAST "DAV:", BAD_CAST "D");
- mstatus = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
- (GDestroyNotify) response_free);
- if (pf->type == PROPFIND_PROP ||
- pf->type == PROPFIND_ALLPROP ||
- pf->type == PROPFIND_PROPNAME)
- {
- if (depth == DEPTH_ZERO)
- status = propfind_query_zero (handler, pf, path, mstatus, ns);
- else if (depth == DEPTH_ONE)
- status = propfind_query_one (handler, pf, path, mstatus, ns);
- else
- {
- status = SOUP_STATUS_FORBIDDEN;
- g_warn_if_reached ();
- }
- }
- else
- g_warn_if_reached ();
-
- if (status != SOUP_STATUS_OK)
- goto end;
-
- status = set_response_multistatus (msg, mstatus);
-
-end:
- davdoc_free (&doc);
- propfind_free (pf);
- if (mstatus)
- g_hash_table_unref (mstatus);
- if (ns)
- xmlFreeNs(ns);
- return status;
-}
static gint
set_attr (PhodavServer *self, GFile *file, xmlNodePtr attrnode,
@@ -1281,7 +437,7 @@ set_attr (PhodavServer *self, GFile *file, xmlNodePtr attrnode,
if (type == G_FILE_ATTRIBUTE_TYPE_INVALID)
{
- attrname = node_get_xattr_name (attrnode, "user.");
+ attrname = xml_node_get_xattr_name (attrnode, "user.");
g_return_val_if_fail (attrname, SOUP_STATUS_BAD_REQUEST);
/* https://bugzilla.gnome.org/show_bug.cgi?id=720506 */
@@ -1295,7 +451,7 @@ set_attr (PhodavServer *self, GFile *file, xmlNodePtr attrnode,
}
else
{
- attrname = node_get_xattr_name (attrnode, "xattr::");
+ attrname = xml_node_get_xattr_name (attrnode, "xattr::");
g_return_val_if_fail (attrname, SOUP_STATUS_BAD_REQUEST);
g_file_set_attribute (file, attrname, type, mem,
@@ -1336,15 +492,15 @@ prop_set (PhodavServer *self, SoupMessage *msg,
for (node = parent->children; node; node = node->next)
{
- if (!node_is_element (node))
+ if (!xml_node_is_element (node))
continue;
- if (node_has_name (node, "prop"))
+ if (xml_node_has_name (node, "prop"))
{
xmlBufferPtr buf = NULL;
attrnode = node->children;
- if (!node_is_element (attrnode))
+ if (!xml_node_is_element (attrnode))
continue;
if (!remove)
@@ -1468,7 +624,7 @@ other_lock_exists (const gchar *key, Path *path, gpointer data)
static gboolean
path_has_other_locks (PhodavServer *self, const gchar *path, GList *locks)
{
- return !foreach_parent_path (self, path, other_lock_exists, locks);
+ return !server_foreach_parent_path (self, path, other_lock_exists, locks);
}
typedef struct _IfState
@@ -1781,12 +937,12 @@ method_proppatch (PathHandler *handler, SoupMessage *msg,
node = doc.root;
for (node = node->children; node; node = node->next)
{
- if (!node_is_element (node))
+ if (!xml_node_is_element (node))
continue;
- if (node_has_name (node, "set"))
+ if (xml_node_has_name (node, "set"))
status = prop_set (self, msg, file, node, &attr, FALSE);
- else if (node_has_name (node, "remove"))
+ else if (xml_node_has_name (node, "remove"))
status = prop_set (self, msg, file, node, &attr, TRUE);
else
g_warn_if_reached ();
@@ -2161,7 +1317,7 @@ try_add_lock (PhodavServer *self, const gchar *path, DAVLock *lock)
{
Path *p;
- if (!foreach_parent_path (self, path, check_lock, lock))
+ if (!server_foreach_parent_path (self, path, check_lock, lock))
return FALSE;
p = get_path (self, path);
@@ -2250,22 +1406,22 @@ method_lock (PathHandler *handler, SoupMessage *msg,
node = doc.root;
for (node = node->children; node; node = node->next)
{
- if (!node_is_element (node))
+ if (!xml_node_is_element (node))
continue;
- if (node_has_name (node, "lockscope"))
+ if (xml_node_has_name (node, "lockscope"))
{
scope = parse_lockscope (node);
if (scope == LOCK_SCOPE_NONE)
break;
}
- else if (node_has_name (node, "locktype"))
+ else if (xml_node_has_name (node, "locktype"))
{
type = parse_locktype (node);
if (type == LOCK_NONE)
break;
}
- else if (node_has_name (node, "owner"))
+ else if (xml_node_has_name (node, "owner"))
{
if (owner == NULL)
owner = node;
@@ -2304,7 +1460,7 @@ body:
xmlSetNs (root, ns);
node = xmlNewChild (root, ns, BAD_CAST "lockdiscovery", NULL);
- xmlAddChild (node, get_activelock_node (lock, ns));
+ xmlAddChild (node, dav_lock_get_activelock_node (lock, ns));
xml_node_to_string (root, &mem, &size);
soup_message_set_response (msg, "application/xml",
@@ -2501,7 +1657,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
msg->method == SOUP_METHOD_HEAD)
status = phodav_method_get (handler, msg, path, &err);
else if (msg->method == SOUP_METHOD_PROPFIND)
- status = method_propfind (handler, msg, path, &err);
+ status = phodav_method_propfind (handler, msg, path, &err);
else if (msg->method == SOUP_METHOD_PROPPATCH)
status = method_proppatch (handler, msg, path, &err);
else if (msg->method == SOUP_METHOD_MKCOL)
diff --git a/libphodav/phodav-utils.c b/libphodav/phodav-utils.c
index 4cd295d..7033440 100644
--- a/libphodav/phodav-utils.c
+++ b/libphodav/phodav-utils.c
@@ -18,20 +18,6 @@
#include "phodav-utils.h"
-void
-xml_node_to_string (xmlNodePtr root, xmlChar **mem, int *size)
-{
- xmlDocPtr doc;
-
- doc = xmlNewDoc (BAD_CAST "1.0");
- xmlDocSetRootElement (doc, root);
- // xmlReconciliateNs
- xmlDocDumpMemoryEnc (doc, mem, size, "utf-8");
- /* FIXME: validate document? */
- /*FIXME, pretty print?*/
- xmlFreeDoc (doc);
-}
-
static xmlDocPtr
parse_xml (const gchar *data,
const goffset len,
@@ -151,3 +137,79 @@ depth_to_string (DepthType depth)
g_return_val_if_reached (NULL);
}
+
+void
+xml_node_to_string (xmlNodePtr root, xmlChar **mem, int *size)
+{
+ xmlDocPtr doc;
+
+ doc = xmlNewDoc (BAD_CAST "1.0");
+ xmlDocSetRootElement (doc, root);
+ // xmlReconciliateNs
+ xmlDocDumpMemoryEnc (doc, mem, size, "utf-8");
+ /* FIXME: validate document? */
+ /*FIXME, pretty print?*/
+ xmlFreeDoc (doc);
+}
+
+void
+xml_node_debug (xmlNodePtr node)
+{
+ g_debug ("%s ns:%s", node->name, node->ns ? (gchar *) node->ns->href : "");
+}
+
+gboolean
+xml_node_has_ns (xmlNodePtr node, const char *ns_href)
+{
+ return node->ns && node->ns->href &&
+ !g_strcmp0 ((gchar *) node->ns->href, ns_href);
+
+}
+
+gboolean
+xml_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 = !g_strcmp0 ((gchar *) node->name, name);
+
+ if (ns_href)
+ has_ns = xml_node_has_ns (node, ns_href);
+
+ return has_name && has_ns;
+}
+
+gboolean
+xml_node_has_name (xmlNodePtr node, const char *name)
+{
+ g_return_val_if_fail (node != NULL, FALSE);
+
+ return xml_node_has_name_ns (node, name, "DAV:");
+}
+
+gboolean
+xml_node_is_element (xmlNodePtr node)
+{
+ return node->type == XML_ELEMENT_NODE && node->name != NULL;
+}
+
+gchar *
+xml_node_get_xattr_name (xmlNodePtr node, const gchar *prefix)
+{
+ const gchar *ns = node->ns ? (gchar *) node->ns->href : NULL;
+ const gchar *name = (gchar *) node->name;
+
+ if (!name)
+ return NULL;
+
+ if (ns)
+ return g_strdup_printf ("%s%s#%s", prefix, ns, name);
+ else
+ return g_strdup_printf ("%s%s", prefix, name);
+}
diff --git a/libphodav/phodav-utils.h b/libphodav/phodav-utils.h
index 18780fc..1367a9e 100644
--- a/libphodav/phodav-utils.h
+++ b/libphodav/phodav-utils.h
@@ -27,8 +27,6 @@ DepthType depth_from_string (const gchar *depth);
const gchar * depth_to_string (DepthType depth);
guint timeout_from_string (const gchar *timeout);
-void xml_node_to_string (xmlNodePtr root, xmlChar **mem, int *size);
-
typedef struct _DavDoc DavDoc;
struct _DavDoc
@@ -45,6 +43,15 @@ gboolean davdoc_parse (DavDoc *dd, SoupMessage *msg,
const gchar *name);
void davdoc_free (DavDoc *dd);
+void xml_node_to_string (xmlNodePtr root, xmlChar **mem, int *size);
+gboolean xml_node_is_element (xmlNodePtr node);
+gboolean xml_node_has_name (xmlNodePtr node, const char *name);
+gboolean xml_node_has_name_ns (xmlNodePtr node, const char *name,
+ const char *ns_href);
+gboolean xml_node_has_ns (xmlNodePtr node, const char *ns_href);
+void xml_node_debug (xmlNodePtr node);
+gchar * xml_node_get_xattr_name (xmlNodePtr node, const gchar *prefix);
+
G_END_DECLS
#endif /* __PHODAV_UTILS_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]