mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-28 11:48:56 +03:00
Recursive PROPFIND cache for ownCloud module
This commit is contained in:
parent
c8fdf1d4fb
commit
980c176073
2 changed files with 299 additions and 0 deletions
|
@ -43,6 +43,8 @@
|
||||||
#include <neon/ne_compress.h>
|
#include <neon/ne_compress.h>
|
||||||
#include <neon/ne_redirect.h>
|
#include <neon/ne_redirect.h>
|
||||||
|
|
||||||
|
#include "c_rbtree.h"
|
||||||
|
|
||||||
#include "c_lib.h"
|
#include "c_lib.h"
|
||||||
#include "csync.h"
|
#include "csync.h"
|
||||||
#include "csync_misc.h"
|
#include "csync_misc.h"
|
||||||
|
@ -191,11 +193,18 @@ long long chunked_total_size = 0;
|
||||||
long long chunked_done = 0;
|
long long chunked_done = 0;
|
||||||
|
|
||||||
struct listdir_context *propfind_cache = 0;
|
struct listdir_context *propfind_cache = 0;
|
||||||
|
|
||||||
|
bool is_first_propfind = true;
|
||||||
|
static void clear_propfind_recursive_cache();
|
||||||
|
|
||||||
csync_vio_file_stat_t _stat_cache;
|
csync_vio_file_stat_t _stat_cache;
|
||||||
/* id cache, cache the ETag: header of a GET request */
|
/* id cache, cache the ETag: header of a GET request */
|
||||||
struct { char *uri; char *id; } _id_cache = { NULL, NULL };
|
struct { char *uri; char *id; } _id_cache = { NULL, NULL };
|
||||||
|
|
||||||
static void clean_caches() {
|
static void clean_caches() {
|
||||||
|
clear_propfind_recursive_cache();
|
||||||
|
is_first_propfind = true;
|
||||||
|
|
||||||
free_fetchCtx(propfind_cache);
|
free_fetchCtx(propfind_cache);
|
||||||
propfind_cache = NULL;
|
propfind_cache = NULL;
|
||||||
|
|
||||||
|
@ -377,6 +386,10 @@ static char *_cleanPath( const char* uri ) {
|
||||||
SAFE_FREE( path );
|
SAFE_FREE( path );
|
||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
/* ***************************************************************************** */
|
||||||
|
#include "csync_owncloud_recursive_propfind.c"
|
||||||
|
/* ***************************************************************************** */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* helper method to build up a user text for SSL problems, called from the
|
* helper method to build up a user text for SSL problems, called from the
|
||||||
|
@ -992,6 +1005,20 @@ static struct listdir_context *fetch_resource_list(const char *uri, int depth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (propfind_recursive_cache) {
|
||||||
|
fetchCtx = get_listdir_context_from_cache(curi);
|
||||||
|
if (fetchCtx) {
|
||||||
|
return fetchCtx;
|
||||||
|
} else {
|
||||||
|
/* Not found in the recursive cache, fetch some */
|
||||||
|
return fetch_resource_list_recursive(uri, curi);
|
||||||
|
}
|
||||||
|
} else if (!is_first_propfind) {
|
||||||
|
/* 2nd propfind */
|
||||||
|
return fetch_resource_list_recursive(uri, curi);
|
||||||
|
}
|
||||||
|
is_first_propfind = false;
|
||||||
|
|
||||||
fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
||||||
if (!fetchCtx) {
|
if (!fetchCtx) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
|
|
272
modules/csync_owncloud_recursive_propfind.c
Normal file
272
modules/csync_owncloud_recursive_propfind.c
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
static time_t oc_httpdate_parse( const char *date );
|
||||||
|
|
||||||
|
// Our cache, key is a char*
|
||||||
|
c_rbtree_t *propfind_recursive_cache = NULL;
|
||||||
|
// Values are propfind_recursive_element:
|
||||||
|
struct propfind_recursive_element {
|
||||||
|
struct resource *self;
|
||||||
|
struct resource *children;
|
||||||
|
};
|
||||||
|
typedef struct propfind_recursive_element propfind_recursive_element_t;
|
||||||
|
|
||||||
|
static struct resource* resource_dup(struct resource* o) {
|
||||||
|
struct resource *r = c_malloc (sizeof( struct resource ));
|
||||||
|
r->uri = c_strdup(o->uri);
|
||||||
|
r->name = c_strdup(o->name);
|
||||||
|
r->type = o->type;
|
||||||
|
r->size = o->size;
|
||||||
|
r->modtime = o->modtime;
|
||||||
|
r->md5 = c_strdup(o->md5);
|
||||||
|
r->next = o->next;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
static void resource_free(struct resource* o) {
|
||||||
|
struct resource* old = NULL;
|
||||||
|
while (o)
|
||||||
|
{
|
||||||
|
old = o;
|
||||||
|
o = o->next;
|
||||||
|
SAFE_FREE(old->uri);
|
||||||
|
SAFE_FREE(old->name);
|
||||||
|
SAFE_FREE(old->md5);
|
||||||
|
SAFE_FREE(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tree_destructor(void *data) {
|
||||||
|
propfind_recursive_element_t *element = data;
|
||||||
|
resource_free(element->self);
|
||||||
|
resource_free(element->children);
|
||||||
|
SAFE_FREE(element);
|
||||||
|
}
|
||||||
|
static void clear_propfind_recursive_cache()
|
||||||
|
{
|
||||||
|
c_rbtree_destroy(propfind_recursive_cache, _tree_destructor);
|
||||||
|
propfind_recursive_cache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct listdir_context *get_listdir_context_from_cache(const char *curi)
|
||||||
|
{
|
||||||
|
if (!propfind_recursive_cache) {
|
||||||
|
DEBUG_WEBDAV("get_listdir_context_from_cache No cache");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
propfind_recursive_element_t *element = c_rbtree_node_data(c_rbtree_find(propfind_recursive_cache, curi));
|
||||||
|
if (!element) {
|
||||||
|
DEBUG_WEBDAV("get_listdir_context_from_cache No element %s in cache found", curi);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!element->children) {
|
||||||
|
DEBUG_WEBDAV("get_listdir_context_from_cache Element %s in cache found, but no children, assuming that recursive propfind didn't work", curi);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out of the element, create a listdir_context.. if we could be sure that it is immutable, we could ref instead.. need to investigate
|
||||||
|
struct listdir_context *fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
||||||
|
fetchCtx->list = NULL;
|
||||||
|
fetchCtx->target = c_strdup(curi);
|
||||||
|
fetchCtx->currResource = NULL;
|
||||||
|
fetchCtx->ref = 1;
|
||||||
|
|
||||||
|
struct resource *iterator = element->children;
|
||||||
|
struct resource *r = NULL;
|
||||||
|
while (iterator) {
|
||||||
|
r = resource_dup(iterator);
|
||||||
|
r->next = fetchCtx->list;
|
||||||
|
fetchCtx->list = r;
|
||||||
|
iterator = iterator->next;
|
||||||
|
fetchCtx->result_count++;
|
||||||
|
/* DEBUG_WEBDAV("get_listdir_context_from_cache Returning cache for %s element %s", fetchCtx->target, fetchCtx->list->uri); */
|
||||||
|
}
|
||||||
|
r = resource_dup(element->self);
|
||||||
|
r->next = fetchCtx->list;
|
||||||
|
fetchCtx->result_count++;
|
||||||
|
fetchCtx->list = r;
|
||||||
|
fetchCtx->currResource = fetchCtx->list;
|
||||||
|
DEBUG_WEBDAV("get_listdir_context_from_cache Returning cache for %s (%d elements)", fetchCtx->target, fetchCtx->result_count);
|
||||||
|
return fetchCtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _key_cmp(const void *key, const void *b) {
|
||||||
|
const char *elementAUri = (char*)key;
|
||||||
|
const propfind_recursive_element_t *elementB = b;
|
||||||
|
return ne_path_compare(elementAUri, elementB->self->uri);
|
||||||
|
}
|
||||||
|
static int _data_cmp(const void *a, const void *b) {
|
||||||
|
const propfind_recursive_element_t *elementA = a;
|
||||||
|
const propfind_recursive_element_t *elementB = b;
|
||||||
|
return ne_path_compare(elementA->self->uri, elementB->self->uri);
|
||||||
|
}
|
||||||
|
static void results_recursive(void *userdata,
|
||||||
|
const ne_uri *uri,
|
||||||
|
const ne_prop_result_set *set)
|
||||||
|
{
|
||||||
|
struct resource *newres = 0;
|
||||||
|
const char *clength, *modtime = NULL;
|
||||||
|
const char *resourcetype = NULL;
|
||||||
|
const char *md5sum = NULL;
|
||||||
|
const ne_status *status = NULL;
|
||||||
|
char *path = ne_path_unescape( uri->path );
|
||||||
|
|
||||||
|
(void) status;
|
||||||
|
|
||||||
|
if (!propfind_recursive_cache) {
|
||||||
|
c_rbtree_create(&propfind_recursive_cache, _key_cmp, _data_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the resource structure with the data about the file */
|
||||||
|
newres = c_malloc(sizeof(struct resource));
|
||||||
|
newres->uri = path; /* no need to strdup because ne_path_unescape already allocates */
|
||||||
|
newres->name = c_basename( path );
|
||||||
|
|
||||||
|
modtime = ne_propset_value( set, &ls_props[0] );
|
||||||
|
clength = ne_propset_value( set, &ls_props[1] );
|
||||||
|
resourcetype = ne_propset_value( set, &ls_props[2] );
|
||||||
|
md5sum = ne_propset_value( set, &ls_props[3] );
|
||||||
|
|
||||||
|
newres->type = resr_normal;
|
||||||
|
if( resourcetype && strncmp( resourcetype, "<DAV:collection>", 16 ) == 0) {
|
||||||
|
newres->type = resr_collection;
|
||||||
|
} else {
|
||||||
|
DEBUG_WEBDAV("results_recursive %s [%d]", newres->uri, newres->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modtime) {
|
||||||
|
newres->modtime = oc_httpdate_parse(modtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEBUG_WEBDAV("Parsing Modtime: %s -> %llu", modtime, (unsigned long long) newres->modtime ); */
|
||||||
|
|
||||||
|
if (clength) {
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
newres->size = DAV_STRTOL(clength, &p, 10);
|
||||||
|
if (*p) {
|
||||||
|
newres->size = 0;
|
||||||
|
}
|
||||||
|
/* DEBUG_WEBDAV("Parsed File size for %s from %s: %lld", newres->name, clength, (long long)newres->size ); */
|
||||||
|
}
|
||||||
|
|
||||||
|
if( md5sum ) {
|
||||||
|
int len = strlen(md5sum)-2;
|
||||||
|
if( len > 0 ) {
|
||||||
|
/* Skip the " around the string coming back from the ne_propset_value call */
|
||||||
|
newres->md5 = c_malloc(len+1);
|
||||||
|
strncpy( newres->md5, md5sum+1, len );
|
||||||
|
newres->md5[len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_WEBDAV("results_recursive %s [%s] >%s<", newres->uri, newres->type == resr_collection ? "collection" : "file", resourcetype);
|
||||||
|
|
||||||
|
/* Create new item in rb tree */
|
||||||
|
if (newres->type == resr_collection) {
|
||||||
|
DEBUG_WEBDAV("results_recursiveIt is a collection %s", newres->uri);
|
||||||
|
// Check if in rb tree
|
||||||
|
propfind_recursive_element_t *element = c_rbtree_node_data(c_rbtree_find(propfind_recursive_cache,uri->path));
|
||||||
|
// If not, create a new item and insert it
|
||||||
|
if (!element) {
|
||||||
|
element = c_malloc(sizeof(propfind_recursive_element_t));
|
||||||
|
element->self = resource_dup(newres);
|
||||||
|
element->children = NULL;
|
||||||
|
c_rbtree_insert(propfind_recursive_cache, element);
|
||||||
|
/* DEBUG_WEBDAV("results_recursive Added collection %s", newres->uri); */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for parent in tree. If exists: Insert it into the children elements there */
|
||||||
|
char *parentPath = ne_path_parent(uri->path);
|
||||||
|
if (parentPath) {
|
||||||
|
free(parentPath);
|
||||||
|
propfind_recursive_element_t *element = c_rbtree_node_data(c_rbtree_find(propfind_recursive_cache,parentPath));
|
||||||
|
if (element) {
|
||||||
|
newres->next = element->children;
|
||||||
|
element->children = newres;
|
||||||
|
/* DEBUG_WEBDAV("results_recursive Added child %s to collection %s", newres->uri, element->self->uri); */
|
||||||
|
} else {
|
||||||
|
/* DEBUG_WEBDAV("results_recursive No parent %s found for child %s", parentPath, newres->uri); */
|
||||||
|
resource_free(newres);
|
||||||
|
newres = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fetches a resource list from the WebDAV server. This is equivalent to list dir.
|
||||||
|
*/
|
||||||
|
static struct listdir_context *fetch_resource_list_recursive(const char *uri, const char *curi)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ne_propfind_handler *hdl = NULL;
|
||||||
|
ne_request *request = NULL;
|
||||||
|
const char *content_type = NULL;
|
||||||
|
const ne_status *req_status = NULL;
|
||||||
|
int depth = NE_DEPTH_INFINITE;
|
||||||
|
|
||||||
|
DEBUG_WEBDAV("Starting recursive propfind %s %s", uri, curi);
|
||||||
|
|
||||||
|
/* do a propfind request and parse the results in the results function, set as callback */
|
||||||
|
hdl = ne_propfind_create(dav_session.ctx, curi, depth);
|
||||||
|
|
||||||
|
if(hdl) {
|
||||||
|
ret = ne_propfind_named(hdl, ls_props, results_recursive, NULL);
|
||||||
|
request = ne_propfind_get_request( hdl );
|
||||||
|
req_status = ne_get_status( request );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ret == NE_OK ) {
|
||||||
|
/* Check the request status. */
|
||||||
|
if( req_status && req_status->klass != 2 ) {
|
||||||
|
set_errno_from_http_errcode(req_status->code);
|
||||||
|
DEBUG_WEBDAV("ERROR: Request failed: status %d (%s)", req_status->code,
|
||||||
|
req_status->reason_phrase);
|
||||||
|
ret = NE_CONNECT;
|
||||||
|
set_error_message(req_status->reason_phrase);
|
||||||
|
if (_progresscb) {
|
||||||
|
_progresscb(uri, CSYNC_NOTIFY_ERROR, req_status->code, (long long)(req_status->reason_phrase) ,dav_session.userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_WEBDAV("Recursive propfind result code %d.", req_status->code);
|
||||||
|
} else {
|
||||||
|
if( ret == NE_ERROR && req_status->code == 404) {
|
||||||
|
errno = ENOENT;
|
||||||
|
} else {
|
||||||
|
set_errno_from_neon_errcode(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ret == NE_OK ) {
|
||||||
|
/* Check the content type. If the server has a problem, ie. database is gone or such,
|
||||||
|
* the content type is not xml but a html error message. Stop on processing if it's
|
||||||
|
* not XML.
|
||||||
|
* FIXME: Generate user error message from the reply content.
|
||||||
|
*/
|
||||||
|
content_type = ne_get_response_header( request, "Content-Type" );
|
||||||
|
if( !(content_type && c_streq(content_type, "application/xml; charset=utf-8") ) ) {
|
||||||
|
DEBUG_WEBDAV("ERROR: Content type of propfind request not XML: %s.",
|
||||||
|
content_type ? content_type: "<empty>");
|
||||||
|
errno = ERRNO_WRONG_CONTENT;
|
||||||
|
set_error_message("Server error: PROPFIND reply is not XML formatted!");
|
||||||
|
ret = NE_CONNECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ret != NE_OK ) {
|
||||||
|
const char *err = NULL;
|
||||||
|
|
||||||
|
err = ne_get_error( dav_session.ctx );
|
||||||
|
DEBUG_WEBDAV("WRN: propfind named failed with %d, request error: %s", ret, err ? err : "<nil>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( hdl )
|
||||||
|
ne_propfind_destroy(hdl);
|
||||||
|
|
||||||
|
if( ret != NE_OK ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_listdir_context_from_cache(curi);
|
||||||
|
}
|
Loading…
Reference in a new issue