[phodav: 9/18] wip: move proppatch and if
- From: Marc-Andre Lureau <malureau src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [phodav: 9/18] wip: move proppatch and if
- Date: Thu, 10 Apr 2014 17:51:50 +0000 (UTC)
commit f674673c611898f127483672bcbcb3ce9ab482cc
Author: Marc-André Lureau <marcandre lureau gmail com>
Date: Thu Apr 10 18:37:37 2014 +0200
wip: move proppatch and if
Makefile.am | 2 +
libphodav/phodav-if.c | 301 ++++++++++++++++++++
libphodav/phodav-lock.c | 44 +++
libphodav/phodav-lock.h | 10 +
libphodav/phodav-method-proppatch.c | 184 ++++++++++++
libphodav/phodav-priv.h | 13 +
libphodav/phodav-server.c | 529 +----------------------------------
libphodav/phodav-utils.c | 11 +
libphodav/phodav-utils.h | 2 +
9 files changed, 579 insertions(+), 517 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 0df61d7..ae64b53 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,8 +33,10 @@ libphodav_1_0_la_SOURCES = \
libphodav/phodav-path.h \
libphodav/phodav-lock.c \
libphodav/phodav-lock.h \
+ libphodav/phodav-if.c \
libphodav/phodav-method-get.c \
libphodav/phodav-method-propfind.c \
+ libphodav/phodav-method-proppatch.c \
libphodav/phodav-multistatus.c \
libphodav/phodav-multistatus.h \
libphodav/phodav-server.c \
diff --git a/libphodav/phodav-if.c b/libphodav/phodav-if.c
new file mode 100644
index 0000000..0d0496b
--- /dev/null
+++ b/libphodav/phodav-if.c
@@ -0,0 +1,301 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/*
+ * Copyright (C) 2014 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-priv.h"
+#include "phodav-lock.h"
+
+typedef struct _IfState
+{
+ gchar *cur;
+ gchar *path;
+ GList *locks;
+ gboolean error;
+} IfState;
+
+static gboolean
+eat_whitespaces (IfState *state)
+{
+ while (*state->cur && strchr (" \f\n\r\t\v", *state->cur))
+ state->cur++;
+
+ return !*state->cur;
+}
+
+static gboolean
+next_token (IfState *state, const gchar *token)
+{
+ eat_whitespaces (state);
+
+ return g_str_has_prefix (state->cur, token);
+}
+
+static gboolean
+accept_token (IfState *state, const gchar *token)
+{
+ gboolean success = next_token (state, token);
+
+ if (success)
+ state->cur += strlen (token);
+
+ return success;
+}
+
+static const gchar*
+accept_ref (IfState *state)
+{
+ gchar *url, *end;
+
+ if (!accept_token (state, "<"))
+ return FALSE;
+
+ url = state->cur;
+ end = strchr (state->cur, '>');
+ if (end)
+ {
+ *end = '\0';
+ state->cur = end + 1;
+ return url;
+ }
+
+ return NULL;
+}
+
+static gchar*
+accept_etag (IfState *state)
+{
+ GString *str = NULL;
+ gboolean success = FALSE;
+
+ str = g_string_sized_new (strlen (state->cur));
+
+ if (!accept_token (state, "["))
+ goto end;
+
+ if (!accept_token (state, "\""))
+ goto end;
+
+ while (*state->cur)
+ {
+ if (*state->cur == '"')
+ break;
+ else if (*state->cur == '\\')
+ state->cur++;
+
+ g_string_append_c (str, *state->cur);
+ state->cur++;
+ }
+
+ if (!accept_token (state, "\""))
+ goto end;
+
+ if (!accept_token (state, "]"))
+ goto end;
+
+ success = TRUE;
+
+end:
+ return g_string_free (str, !success);
+}
+
+static gboolean
+check_token (PathHandler *handler, const gchar *path, const gchar *token)
+{
+ PhodavServer *server = handler_get_server (handler);
+
+ g_debug ("check %s for %s", token, path);
+
+ if (!g_strcmp0 (token, "DAV:no-lock"))
+ return FALSE;
+
+ return !!server_path_get_lock (server, path, token);
+}
+
+static gboolean
+check_etag (PathHandler *handler, const gchar *path, const gchar *etag)
+{
+ GCancellable *cancellable = handler_get_cancellable (handler);
+ GFile *file = NULL;
+ GFileInfo *info = NULL;
+ GError *error = NULL;
+ const gchar *fetag;
+ gboolean success = FALSE;
+
+ g_debug ("check etag %s for %s", etag, path);
+
+ file = g_file_get_child (handler_get_file (handler), path + 1);
+ info = g_file_query_info (file, "etag::*",
+ G_FILE_QUERY_INFO_NONE, cancellable, &error);
+ if (!info)
+ goto end;
+
+ fetag = g_file_info_get_etag (info);
+ g_warn_if_fail (fetag != NULL);
+
+ success = !g_strcmp0 (etag, fetag);
+
+end:
+ if (error)
+ {
+ g_warning ("check_etag error: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_clear_object (&info);
+ g_clear_object (&file);
+
+ return success;
+}
+
+static gboolean
+eval_if_condition (PathHandler *handler, IfState *state)
+{
+ gboolean success = FALSE;
+
+ if (next_token (state, "<"))
+ {
+ const gchar *token = accept_ref (state);
+ LockSubmitted *l = lock_submitted_new (state->path, token);
+
+ state->locks = g_list_append (state->locks, l);
+
+ success = check_token (handler, state->path, token);
+ }
+ else if (next_token (state, "["))
+ {
+ gchar *etag = accept_etag (state);
+
+ success = check_etag (handler, state->path, etag);
+ g_free (etag);
+ }
+ else
+ g_warn_if_reached ();
+
+ return success;
+}
+
+static gboolean
+eval_if_not_condition (PathHandler *handler, IfState *state)
+{
+ gboolean not = FALSE;
+ gboolean res;
+
+ if (accept_token (state, "Not"))
+ not = TRUE;
+
+ res = eval_if_condition (handler, state);
+
+ return not ? !res : res;
+}
+
+static gboolean
+eval_if_list (PathHandler *handler, IfState *state)
+{
+ gboolean success;
+
+ g_return_val_if_fail (accept_token (state, "("), FALSE);
+
+ success = eval_if_not_condition (handler, state);
+
+ while (!accept_token (state, ")"))
+ success &= eval_if_not_condition (handler, state);
+
+ return success;
+}
+
+static gboolean
+eval_if_lists (PathHandler *handler, IfState *state)
+{
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (next_token (state, "("), FALSE);
+
+ while (next_token (state, "("))
+ success |= eval_if_list (handler, state);
+
+ return success;
+}
+
+static gboolean
+eval_if_tag (PathHandler *handler, IfState *state)
+{
+ SoupURI *uri;
+ const gchar *path;
+ const gchar *ref = accept_ref (state);
+
+ g_return_val_if_fail (ref != NULL, FALSE);
+
+ uri = soup_uri_new (ref);
+ path = soup_uri_get_path (uri);
+ g_free (state->path);
+ state->path = g_strdup (path);
+ soup_uri_free (uri);
+
+ return eval_if_lists (handler, state);
+}
+
+
+static gboolean
+eval_if (PathHandler *handler, IfState *state)
+{
+ gboolean success = FALSE;
+
+ if (next_token (state, "<")) {
+ while (!eat_whitespaces (state))
+ success |= eval_if_tag (handler, state);
+ } else {
+ while (!eat_whitespaces (state))
+ success |= eval_if_lists (handler, state);
+ }
+
+ return success;
+}
+
+gint
+phodav_check_if (PathHandler *handler, SoupMessage *msg, const gchar *path, GList **locks)
+{
+ PhodavServer *server = handler_get_server (handler);
+ gboolean success = TRUE;
+ gint status;
+ gchar *str = g_strdup (soup_message_headers_get_one (msg->request_headers, "If"));
+ IfState state = { .cur = str, .path = g_strdup (path) };
+ gboolean copy = msg->method == SOUP_METHOD_COPY;
+
+ if (!str)
+ goto end;
+
+ if (eval_if (handler, &state))
+ {
+ *locks = state.locks;
+ }
+ else
+ {
+ g_list_free_full (state.locks, (GDestroyNotify) lock_submitted_free);
+ success = FALSE;
+ }
+
+end:
+ status = success ? SOUP_STATUS_OK
+ : str ? SOUP_STATUS_PRECONDITION_FAILED : SOUP_STATUS_LOCKED;
+
+ if (success && !copy && server_path_has_other_locks (server, path, *locks))
+ status = SOUP_STATUS_LOCKED;
+
+ g_free (str);
+ g_free (state.path);
+ return status;
+}
diff --git a/libphodav/phodav-lock.c b/libphodav/phodav-lock.c
index 5fea312..e6d8222 100644
--- a/libphodav/phodav-lock.c
+++ b/libphodav/phodav-lock.c
@@ -124,3 +124,47 @@ dav_lock_get_activelock_node (const DAVLock *lock,
return active;
}
+
+LockSubmitted *
+lock_submitted_new (const gchar *path, const gchar *token)
+{
+ LockSubmitted *l;
+
+ g_return_val_if_fail (path, NULL);
+ g_return_val_if_fail (token, NULL);
+
+ l = g_slice_new (LockSubmitted);
+
+ l->path = g_strdup (path);
+ l->token = g_strdup (token);
+
+ remove_trailing (l->path, '/');
+
+ return l;
+}
+
+void
+lock_submitted_free (LockSubmitted *l)
+{
+ g_free (l->path);
+ g_free (l->token);
+ g_slice_free (LockSubmitted, l);
+}
+
+gboolean
+locks_submitted_has (GList *locks, DAVLock *lock)
+{
+ GList *l;
+
+ for (l = locks; l != NULL; l = l->next)
+ {
+ LockSubmitted *sub = l->data;
+ if (!g_strcmp0 (sub->path, lock->path->path) &&
+ !g_strcmp0 (sub->token, lock->token))
+ return TRUE;
+ }
+
+ g_message ("missing lock: %s %s", lock->path->path, lock->token);
+
+ return FALSE;
+}
diff --git a/libphodav/phodav-lock.h b/libphodav/phodav-lock.h
index 6d9928f..d197003 100644
--- a/libphodav/phodav-lock.h
+++ b/libphodav/phodav-lock.h
@@ -22,6 +22,12 @@
G_BEGIN_DECLS
+typedef struct _LockSubmitted
+{
+ gchar *path;
+ gchar *token;
+} LockSubmitted;
+
DAVLock * dav_lock_new (Path *path, const gchar *token,
LockScopeType scope, LockType type,
DepthType depth, const xmlNodePtr owner,
@@ -34,6 +40,10 @@ void dav_lock_refresh_timeout (DAVLock *lock, guint timeout);
xmlNodePtr dav_lock_get_activelock_node (const DAVLock *lock,
xmlNsPtr ns);
+LockSubmitted * lock_submitted_new (const gchar *path, const gchar *token);
+void lock_submitted_free (LockSubmitted *l);
+gboolean locks_submitted_has (GList *locks, DAVLock *lock);
+
G_END_DECLS
#endif /* __PHODAV_LOCK_H__ */
diff --git a/libphodav/phodav-method-proppatch.c b/libphodav/phodav-method-proppatch.c
new file mode 100644
index 0000000..5c1f2dd
--- /dev/null
+++ b/libphodav/phodav-method-proppatch.c
@@ -0,0 +1,184 @@
+/* -*- 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-priv.h"
+#include "phodav-multistatus.h"
+#include "phodav-utils.h"
+
+#include <sys/types.h>
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+
+
+static xmlBufferPtr
+node_children_to_string (xmlNodePtr node)
+{
+ xmlBufferPtr buf = xmlBufferCreate ();
+
+ for (node = node->children; node; node = node->next)
+ xmlNodeDump (buf, node->doc, node, 0, 0);
+
+ return buf;
+}
+
+static gint
+set_attr (GFile *file, xmlNodePtr attrnode,
+ GFileAttributeType type, gchar *mem, GCancellable *cancellable)
+{
+ gchar *attrname;
+ gint status = SOUP_STATUS_OK;
+ GError *error = NULL;
+
+ if (type == G_FILE_ATTRIBUTE_TYPE_INVALID)
+ {
+ 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 */
+ gchar *path = g_file_get_path (file);
+#ifdef HAVE_ATTR_XATTR_H
+ removexattr (path, attrname);
+#else
+ g_debug ("fixme");
+#endif
+ g_free (path);
+ }
+ else
+ {
+ 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,
+ G_FILE_QUERY_INFO_NONE, cancellable, &error);
+ }
+
+ g_free (attrname);
+
+ if (error)
+ {
+ g_warning ("failed to set property: %s", error->message);
+ g_clear_error (&error);
+ status = SOUP_STATUS_NOT_FOUND;
+ }
+
+ return status;
+}
+
+static gint
+prop_set (SoupMessage *msg,
+ GFile *file, xmlNodePtr parent, xmlNodePtr *attr,
+ gboolean remove, GCancellable *cancellable)
+{
+ xmlNodePtr node, attrnode;
+ gint type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+ gint status;
+
+ for (node = parent->children; node; node = node->next)
+ {
+ if (!xml_node_is_element (node))
+ continue;
+
+ if (xml_node_has_name (node, "prop"))
+ {
+ xmlBufferPtr buf = NULL;
+
+ attrnode = node->children;
+ if (!xml_node_is_element (attrnode))
+ continue;
+
+ if (!remove)
+ {
+ *attr = xmlCopyNode (attrnode, 2);
+
+ buf = node_children_to_string (attrnode);
+ type = G_FILE_ATTRIBUTE_TYPE_STRING;
+ }
+
+ status = set_attr (file, attrnode, type, (gchar *) xmlBufferContent (buf), cancellable);
+
+ if (buf)
+ xmlBufferFree (buf);
+
+ return status;
+ }
+ }
+
+ g_return_val_if_reached (SOUP_STATUS_BAD_REQUEST);
+}
+
+gint
+phodav_method_proppatch (PathHandler *handler, SoupMessage *msg,
+ const char *path, GError **err)
+{
+ GCancellable *cancellable = handler_get_cancellable (handler);
+ GFile *file = NULL;
+ GHashTable *mstatus = NULL; // path -> statlist
+ DavDoc doc = {0, };
+ xmlNodePtr node = NULL, attr = NULL;
+ GList *props = NULL, *submitted = NULL;
+ gint status;
+
+ if (!davdoc_parse (&doc, msg, msg->request_body, "propertyupdate"))
+ {
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto end;
+ }
+
+ status = phodav_check_if (handler, msg, path, &submitted);
+ if (status != SOUP_STATUS_OK)
+ goto end;
+
+ file = g_file_get_child (handler_get_file (handler), path + 1);
+ mstatus = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) response_free);
+
+ node = doc.root;
+ for (node = node->children; node; node = node->next)
+ {
+ if (!xml_node_is_element (node))
+ continue;
+
+ if (xml_node_has_name (node, "set"))
+ status = prop_set (msg, file, node, &attr, FALSE, cancellable);
+ else if (xml_node_has_name (node, "remove"))
+ status = prop_set (msg, file, node, &attr, TRUE, cancellable);
+ else
+ g_warn_if_reached ();
+
+ if (attr)
+ {
+ attr->_private = GINT_TO_POINTER (status);
+ props = g_list_append (props, attr);
+ }
+ }
+
+ g_hash_table_insert (mstatus, g_strdup (path),
+ response_new (props, 0));
+
+ if (g_hash_table_size (mstatus) > 0)
+ status = set_response_multistatus (msg, mstatus);
+
+end:
+ davdoc_free (&doc);
+ if (mstatus)
+ g_hash_table_unref (mstatus);
+ g_clear_object (&file);
+
+ return status;
+}
diff --git a/libphodav/phodav-priv.h b/libphodav/phodav-priv.h
index ab6d903..8a5ea0c 100644
--- a/libphodav/phodav-priv.h
+++ b/libphodav/phodav-priv.h
@@ -89,11 +89,24 @@ PhodavServer * handler_get_server (PathHandler *handl
gboolean server_foreach_parent_path (PhodavServer *server,
const gchar *path,
PathCb cb, gpointer data);
+DAVLock * server_path_get_lock (PhodavServer *server,
+ const gchar *path,
+ const gchar *token);
+gboolean server_path_has_other_locks (PhodavServer *self,
+ const gchar *path,
+ GList *locks);
+
+gint phodav_check_if (PathHandler *handler, SoupMessage *msg,
+ const gchar *path, GList **locks);
gint phodav_method_get (PathHandler *handler, SoupMessage *msg,
const char *path, GError **err);
gint phodav_method_propfind (PathHandler *handler, SoupMessage *msg,
const char *path, GError **err);
+gint phodav_method_proppatch (PathHandler *handler, SoupMessage *msg,
+ const char *path, GError **err);
+
+
G_END_DECLS
#endif /* __PHODAV_PRIV_H__ */
diff --git a/libphodav/phodav-server.c b/libphodav/phodav-server.c
index 3242b2b..69b29e5 100644
--- a/libphodav/phodav-server.c
+++ b/libphodav/phodav-server.c
@@ -18,11 +18,6 @@
#include "config.h"
-#include <sys/types.h>
-#ifdef HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#endif
-
#include "guuid.h"
#include "phodav-server.h"
#include "phodav-multistatus.h"
@@ -82,17 +77,6 @@ static void request_started (SoupServer *server,
SoupClientContext *client,
gpointer user_data);
-static void
-remove_trailing (gchar *str, gchar c)
-{
- gsize len = strlen (str);
-
- while (len > 0 && str[len - 1] == c)
- len--;
-
- str[len] = '\0';
-}
-
static Path *
get_path (PhodavServer *self, const gchar *_path)
{
@@ -410,8 +394,8 @@ _path_get_lock (const gchar *key, Path *path, gpointer data)
return TRUE;
}
-static DAVLock *
-path_get_lock (PhodavServer *self, const gchar *path, const gchar *token)
+DAVLock *
+server_path_get_lock (PhodavServer *self, const gchar *path, const gchar *token)
{
PathGetLock p = { .token = token };
gboolean success = !server_foreach_parent_path (self, path,
@@ -423,103 +407,6 @@ path_get_lock (PhodavServer *self, const gchar *path, const gchar *token)
return p.lock;
}
-
-static gint
-set_attr (PhodavServer *self, GFile *file, xmlNodePtr attrnode,
- GFileAttributeType type, gchar *mem)
-{
- gchar *attrname;
- gint status = SOUP_STATUS_OK;
- GError *error = NULL;
-
- if (type == G_FILE_ATTRIBUTE_TYPE_INVALID)
- {
- 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 */
- gchar *path = g_file_get_path (file);
-#ifdef HAVE_ATTR_XATTR_H
- removexattr (path, attrname);
-#else
- g_debug ("fixme");
-#endif
- g_free (path);
- }
- else
- {
- 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,
- G_FILE_QUERY_INFO_NONE, self->cancellable, &error);
- }
-
- g_free (attrname);
-
- if (error)
- {
- g_warning ("failed to set property: %s", error->message);
- g_clear_error (&error);
- status = SOUP_STATUS_NOT_FOUND;
- }
-
- return status;
-}
-
-static xmlBufferPtr
-node_children_to_string (xmlNodePtr node)
-{
- xmlBufferPtr buf = xmlBufferCreate ();
-
- for (node = node->children; node; node = node->next)
- xmlNodeDump (buf, node->doc, node, 0, 0);
-
- return buf;
-}
-
-static gint
-prop_set (PhodavServer *self, SoupMessage *msg,
- GFile *file, xmlNodePtr parent, xmlNodePtr *attr,
- gboolean remove)
-{
- xmlNodePtr node, attrnode;
- gint type = G_FILE_ATTRIBUTE_TYPE_INVALID;
- gint status;
-
- for (node = parent->children; node; node = node->next)
- {
- if (!xml_node_is_element (node))
- continue;
-
- if (xml_node_has_name (node, "prop"))
- {
- xmlBufferPtr buf = NULL;
-
- attrnode = node->children;
- if (!xml_node_is_element (attrnode))
- continue;
-
- if (!remove)
- {
- *attr = xmlCopyNode (attrnode, 2);
-
- buf = node_children_to_string (attrnode);
- type = G_FILE_ATTRIBUTE_TYPE_STRING;
- }
-
- status = set_attr (self, file, attrnode, type, (gchar *) xmlBufferContent (buf));
-
- if (buf)
- xmlBufferFree (buf);
-
- return status;
- }
- }
-
- g_return_val_if_reached (SOUP_STATUS_BAD_REQUEST);
-}
-
static gint
put_start (SoupMessage *msg, GFile *file,
GFileOutputStream **output, GCancellable *cancellable,
@@ -552,56 +439,6 @@ end:
return status;
}
-typedef struct _LockSubmitted
-{
- gchar *path;
- gchar *token;
-} LockSubmitted;
-
-static LockSubmitted *
-lock_submitted_new (const gchar *path, const gchar *token)
-{
- LockSubmitted *l;
-
- g_return_val_if_fail (path, NULL);
- g_return_val_if_fail (token, NULL);
-
- l = g_slice_new (LockSubmitted);
-
- l->path = g_strdup (path);
- l->token = g_strdup (token);
-
- remove_trailing (l->path, '/');
-
- return l;
-}
-
-static void
-lock_submitted_free (LockSubmitted *l)
-{
- g_free (l->path);
- g_free (l->token);
- g_slice_free (LockSubmitted, l);
-}
-
-static gboolean
-locks_submitted_has (GList *locks, DAVLock *lock)
-{
- GList *l;
-
- for (l = locks; l != NULL; l = l->next)
- {
- LockSubmitted *sub = l->data;
- if (!g_strcmp0 (sub->path, lock->path->path) &&
- !g_strcmp0 (sub->token, lock->token))
- return TRUE;
- }
-
- g_message ("missing lock: %s %s", lock->path->path, lock->token);
-
- return FALSE;
-}
-
static gboolean
other_lock_exists (const gchar *key, Path *path, gpointer data)
{
@@ -618,354 +455,12 @@ other_lock_exists (const gchar *key, Path *path, gpointer data)
return TRUE;
}
-static gboolean
-path_has_other_locks (PhodavServer *self, const gchar *path, GList *locks)
+gboolean
+server_path_has_other_locks (PhodavServer *self, const gchar *path, GList *locks)
{
return !server_foreach_parent_path (self, path, other_lock_exists, locks);
}
-typedef struct _IfState
-{
- gchar *cur;
- gchar *path;
- GList *locks;
- gboolean error;
-} IfState;
-
-static gboolean
-eat_whitespaces (IfState *state)
-{
- while (*state->cur && strchr (" \f\n\r\t\v", *state->cur))
- state->cur++;
-
- return !*state->cur;
-}
-
-static gboolean
-next_token (IfState *state, const gchar *token)
-{
- eat_whitespaces (state);
-
- return g_str_has_prefix (state->cur, token);
-}
-
-static gboolean
-accept_token (IfState *state, const gchar *token)
-{
- gboolean success = next_token (state, token);
-
- if (success)
- state->cur += strlen (token);
-
- return success;
-}
-
-static const gchar*
-accept_ref (IfState *state)
-{
- gchar *url, *end;
-
- if (!accept_token (state, "<"))
- return FALSE;
-
- url = state->cur;
- end = strchr (state->cur, '>');
- if (end)
- {
- *end = '\0';
- state->cur = end + 1;
- return url;
- }
-
- return NULL;
-}
-
-static gchar*
-accept_etag (IfState *state)
-{
- GString *str = NULL;
- gboolean success = FALSE;
-
- str = g_string_sized_new (strlen (state->cur));
-
- if (!accept_token (state, "["))
- goto end;
-
- if (!accept_token (state, "\""))
- goto end;
-
- while (*state->cur)
- {
- if (*state->cur == '"')
- break;
- else if (*state->cur == '\\')
- state->cur++;
-
- g_string_append_c (str, *state->cur);
- state->cur++;
- }
-
- if (!accept_token (state, "\""))
- goto end;
-
- if (!accept_token (state, "]"))
- goto end;
-
- success = TRUE;
-
-end:
- return g_string_free (str, !success);
-}
-
-static gboolean
-check_token (PathHandler *handler, const gchar *path, const gchar *token)
-{
- PhodavServer *self = handler->self;
-
- g_debug ("check %s for %s", token, path);
-
- if (!g_strcmp0 (token, "DAV:no-lock"))
- return FALSE;
-
- return !!path_get_lock (self, path, token);
-}
-
-static gboolean
-check_etag (PathHandler *handler, const gchar *path, const gchar *etag)
-{
- PhodavServer *self = handler->self;
- GFile *file = NULL;
- GFileInfo *info = NULL;
- GError *error = NULL;
- const gchar *fetag;
- gboolean success = FALSE;
-
- g_debug ("check etag %s for %s", etag, path);
-
- file = g_file_get_child (handler->file, path + 1);
- info = g_file_query_info (file, "etag::*",
- G_FILE_QUERY_INFO_NONE, self->cancellable, &error);
- if (!info)
- goto end;
-
- fetag = g_file_info_get_etag (info);
- g_warn_if_fail (fetag != NULL);
-
- success = !g_strcmp0 (etag, fetag);
-
-end:
- if (error)
- {
- g_warning ("check_etag error: %s", error->message);
- g_clear_error (&error);
- }
-
- g_clear_object (&info);
- g_clear_object (&file);
-
- return success;
-}
-
-static gboolean
-eval_if_condition (PathHandler *handler, IfState *state)
-{
- gboolean success = FALSE;
-
- if (next_token (state, "<"))
- {
- const gchar *token = accept_ref (state);
- LockSubmitted *l = lock_submitted_new (state->path, token);
-
- state->locks = g_list_append (state->locks, l);
-
- success = check_token (handler, state->path, token);
- }
- else if (next_token (state, "["))
- {
- gchar *etag = accept_etag (state);
-
- success = check_etag (handler, state->path, etag);
- g_free (etag);
- }
- else
- g_warn_if_reached ();
-
- return success;
-}
-
-static gboolean
-eval_if_not_condition (PathHandler *handler, IfState *state)
-{
- gboolean not = FALSE;
- gboolean res;
-
- if (accept_token (state, "Not"))
- not = TRUE;
-
- res = eval_if_condition (handler, state);
-
- return not ? !res : res;
-}
-
-static gboolean
-eval_if_list (PathHandler *handler, IfState *state)
-{
- gboolean success;
-
- g_return_val_if_fail (accept_token (state, "("), FALSE);
-
- success = eval_if_not_condition (handler, state);
-
- while (!accept_token (state, ")"))
- success &= eval_if_not_condition (handler, state);
-
- return success;
-}
-
-static gboolean
-eval_if_lists (PathHandler *handler, IfState *state)
-{
- gboolean success = FALSE;
-
- g_return_val_if_fail (next_token (state, "("), FALSE);
-
- while (next_token (state, "("))
- success |= eval_if_list (handler, state);
-
- return success;
-}
-
-static gboolean
-eval_if_tag (PathHandler *handler, IfState *state)
-{
- SoupURI *uri;
- const gchar *path;
- const gchar *ref = accept_ref (state);
-
- g_return_val_if_fail (ref != NULL, FALSE);
-
- uri = soup_uri_new (ref);
- path = soup_uri_get_path (uri);
- g_free (state->path);
- state->path = g_strdup (path);
- soup_uri_free (uri);
-
- return eval_if_lists (handler, state);
-}
-
-
-static gboolean
-eval_if (PathHandler *handler, IfState *state)
-{
- gboolean success = FALSE;
-
- if (next_token (state, "<")) {
- while (!eat_whitespaces (state))
- success |= eval_if_tag (handler, state);
- } else {
- while (!eat_whitespaces (state))
- success |= eval_if_lists (handler, state);
- }
-
- return success;
-}
-
-static gint
-check_if (PathHandler *handler, SoupMessage *msg, const gchar *path, GList **locks)
-{
- PhodavServer *self = handler->self;
- gboolean success = TRUE;
- gint status;
- gchar *str = g_strdup (soup_message_headers_get_one (msg->request_headers, "If"));
- IfState state = { .cur = str, .path = g_strdup (path) };
- gboolean copy = msg->method == SOUP_METHOD_COPY;
-
- if (!str)
- goto end;
-
- if (eval_if (handler, &state))
- {
- *locks = state.locks;
- }
- else
- {
- g_list_free_full (state.locks, (GDestroyNotify) lock_submitted_free);
- success = FALSE;
- }
-
-end:
- status = success ? SOUP_STATUS_OK
- : str ? SOUP_STATUS_PRECONDITION_FAILED : SOUP_STATUS_LOCKED;
-
- if (success && !copy && path_has_other_locks (self, path, *locks))
- status = SOUP_STATUS_LOCKED;
-
- g_free (str);
- g_free (state.path);
- return status;
-}
-
-static gint
-method_proppatch (PathHandler *handler, SoupMessage *msg,
- const char *path, GError **err)
-{
- PhodavServer *self = handler->self;
- GFile *file = NULL;
- GHashTable *mstatus = NULL; // path -> statlist
- DavDoc doc = {0, };
- xmlNodePtr node = NULL, attr = NULL;
- GList *props = NULL, *submitted = NULL;
- gint status;
-
- if (!davdoc_parse (&doc, msg, msg->request_body, "propertyupdate"))
- {
- status = SOUP_STATUS_BAD_REQUEST;
- goto end;
- }
-
- status = check_if (handler, msg, path, &submitted);
- if (status != SOUP_STATUS_OK)
- goto end;
-
- file = g_file_get_child (handler->file, path + 1);
- mstatus = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
- (GDestroyNotify) response_free);
-
- node = doc.root;
- for (node = node->children; node; node = node->next)
- {
- if (!xml_node_is_element (node))
- continue;
-
- if (xml_node_has_name (node, "set"))
- status = prop_set (self, msg, file, node, &attr, FALSE);
- else if (xml_node_has_name (node, "remove"))
- status = prop_set (self, msg, file, node, &attr, TRUE);
- else
- g_warn_if_reached ();
-
- if (attr)
- {
- attr->_private = GINT_TO_POINTER (status);
- props = g_list_append (props, attr);
- }
- }
-
- g_hash_table_insert (mstatus, g_strdup (path),
- response_new (props, 0));
-
- if (g_hash_table_size (mstatus) > 0)
- status = set_response_multistatus (msg, mstatus);
-
-end:
- davdoc_free (&doc);
- if (mstatus)
- g_hash_table_unref (mstatus);
- g_clear_object (&file);
-
- return status;
-}
-
static gint
do_mkcol_file (SoupMessage *msg, GFile *file,
GCancellable *cancellable, GError **err)
@@ -1007,7 +502,7 @@ method_mkcol (PathHandler *handler, SoupMessage *msg,
goto end;
}
- status = check_if (handler, msg, path, &submitted);
+ status = phodav_check_if (handler, msg, path, &submitted);
if (status != SOUP_STATUS_OK)
goto end;
@@ -1092,7 +587,7 @@ method_delete (PathHandler *handler, SoupMessage *msg,
/* depth = depth_from_string(soup_message_headers_get_one (msg->request_headers, "Depth")); */
/* must be == infinity with collection */
- status = check_if (handler, msg, path, &submitted);
+ status = phodav_check_if (handler, msg, path, &submitted);
if (status != SOUP_STATUS_OK)
goto end;
@@ -1262,11 +757,11 @@ method_movecopy (PathHandler *handler, SoupMessage *msg,
if (!dest || !*dest)
goto end;
- status = check_if (handler, msg, path, &submitted);
+ status = phodav_check_if (handler, msg, path, &submitted);
if (status != SOUP_STATUS_OK)
goto end;
- if (path_has_other_locks (self, dest, submitted))
+ if (server_path_has_other_locks (self, dest, submitted))
{
status = SOUP_STATUS_LOCKED;
goto end;
@@ -1387,7 +882,7 @@ method_lock (PathHandler *handler, SoupMessage *msg,
token = g_strndup (hif + 2, len - 4);
g_debug ("refresh token %s", token);
- lock = path_get_lock (self, path, token);
+ lock = server_path_get_lock (self, path, token);
if (!lock)
goto end;
@@ -1499,7 +994,7 @@ method_unlock (PathHandler *handler, SoupMessage *msg,
g_return_val_if_fail (token != NULL, SOUP_STATUS_BAD_REQUEST);
- lock = path_get_lock (self, path, token);
+ lock = server_path_get_lock (self, path, token);
if (!lock)
return SOUP_STATUS_CONFLICT;
@@ -1558,7 +1053,7 @@ method_put (PathHandler *handler, const gchar *path, SoupMessage *msg, GError **
GFileOutputStream *output = NULL;
gint status;
- status = check_if (handler, msg, path, &submitted);
+ status = phodav_check_if (handler, msg, path, &submitted);
if (status != SOUP_STATUS_OK)
goto end;
@@ -1656,7 +1151,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
else if (msg->method == SOUP_METHOD_PROPFIND)
status = phodav_method_propfind (handler, msg, path, &err);
else if (msg->method == SOUP_METHOD_PROPPATCH)
- status = method_proppatch (handler, msg, path, &err);
+ status = phodav_method_proppatch (handler, msg, path, &err);
else if (msg->method == SOUP_METHOD_MKCOL)
status = method_mkcol (handler, msg, path, &err);
else if (msg->method == SOUP_METHOD_DELETE)
diff --git a/libphodav/phodav-utils.c b/libphodav/phodav-utils.c
index 5d23925..9e28805 100644
--- a/libphodav/phodav-utils.c
+++ b/libphodav/phodav-utils.c
@@ -18,6 +18,17 @@
#include "phodav-utils.h"
+void
+remove_trailing (gchar *str, gchar c)
+{
+ gsize len = strlen (str);
+
+ while (len > 0 && str[len - 1] == c)
+ len--;
+
+ str[len] = '\0';
+}
+
static xmlDocPtr
parse_xml (const gchar *data,
const goffset len,
diff --git a/libphodav/phodav-utils.h b/libphodav/phodav-utils.h
index 1367a9e..c021741 100644
--- a/libphodav/phodav-utils.h
+++ b/libphodav/phodav-utils.h
@@ -23,6 +23,8 @@
G_BEGIN_DECLS
+void remove_trailing (gchar *str, gchar c);
+
DepthType depth_from_string (const gchar *depth);
const gchar * depth_to_string (DepthType depth);
guint timeout_from_string (const gchar *timeout);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]