From 906779c4b143654fa762ead030907bfd34fd0e9f Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Tue, 2 Dec 2014 12:25:51 +0100 Subject: [PATCH] SyncEngine: Use QNAM for csync_update This is for #2507 --- csync/src/CMakeLists.txt | 3 - csync/src/csync.c | 6 + csync/src/csync.h | 112 ++++- csync/src/csync_misc.h | 2 - csync/src/csync_owncloud.c | 291 +------------ csync/src/csync_owncloud.h | 8 +- csync/src/csync_owncloud_private.h | 90 +--- csync/src/csync_owncloud_recursive_propfind.c | 285 ------------- csync/src/csync_owncloud_util.c | 272 ------------ csync/src/csync_private.h | 10 +- csync/src/csync_update.c | 17 +- csync/src/csync_update.h | 1 - csync/src/vio/csync_vio.c | 8 +- csync/src/vio/csync_vio.h | 3 +- csync/src/vio/csync_vio_file_stat.c | 16 +- csync/src/vio/csync_vio_file_stat.h | 121 ------ csync/src/vio/csync_vio_handle.h | 26 -- csync/src/vio/csync_vio_handle_private.h | 26 -- csync/src/vio/csync_vio_local.c | 2 - csync/tests/vio_tests/check_vio_file_stat.c | 2 +- src/gui/folderwizard.cpp | 4 +- src/gui/selectivesyncdialog.cpp | 4 +- src/libsync/discoveryphase.cpp | 386 ++++++++++++++++++ src/libsync/discoveryphase.h | 97 ++++- src/libsync/filesystem.cpp | 3 +- src/libsync/networkjobs.cpp | 141 +++++-- src/libsync/networkjobs.h | 5 +- src/libsync/syncengine.cpp | 39 +- src/libsync/syncengine.h | 2 + 29 files changed, 806 insertions(+), 1176 deletions(-) delete mode 100644 csync/src/csync_owncloud_recursive_propfind.c delete mode 100644 csync/src/vio/csync_vio_file_stat.h delete mode 100644 csync/src/vio/csync_vio_handle.h delete mode 100644 csync/src/vio/csync_vio_handle_private.h diff --git a/csync/src/CMakeLists.txt b/csync/src/CMakeLists.txt index 415fdd565..d5ec9b974 100644 --- a/csync/src/CMakeLists.txt +++ b/csync/src/CMakeLists.txt @@ -62,7 +62,6 @@ set(csync_SRCS vio/csync_vio_local.c csync_owncloud.c - csync_owncloud_recursive_propfind.c csync_owncloud_util.c ) @@ -72,8 +71,6 @@ set(csync_HDRS ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h csync.h vio/csync_vio.h - vio/csync_vio_file_stat.h - vio/csync_vio_handle.h vio/csync_vio_method.h vio/csync_vio_module.h ) diff --git a/csync/src/csync.c b/csync/src/csync.c index 386589ee1..e56d8f617 100644 --- a/csync/src/csync.c +++ b/csync/src/csync.c @@ -222,6 +222,12 @@ int csync_update(CSYNC *ctx) { CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "No exclude file loaded or defined!"); } + /* This is not actually connecting, just setting the info for neon. The legacy propagator can use it.. */ + if (dav_connect( ctx, ctx->remote.uri ) < 0) { + ctx->status_code = CSYNC_STATUS_CONNECT_ERROR; + return -1; + } + /* update detection for local replica */ csync_gettime(&start); ctx->current = LOCAL_REPLICA; diff --git a/csync/src/csync.h b/csync/src/csync.h index 78a092ff7..d3d37d4c8 100644 --- a/csync/src/csync.h +++ b/csync/src/csync.h @@ -32,6 +32,8 @@ #ifndef _CSYNC_H #define _CSYNC_H +#include "std/c_private.h" +#include #include #include #include @@ -136,6 +138,94 @@ enum csync_ftw_type_e { }; +#define FILE_ID_BUF_SIZE 21 + +// currently specified at https://github.com/owncloud/core/issues/8322 are 9 to 10 +#define REMOTE_PERM_BUF_SIZE 15 + +typedef struct csync_vio_file_stat_s csync_vio_file_stat_t; + +enum csync_vio_file_flags_e { + CSYNC_VIO_FILE_FLAGS_NONE = 0, + CSYNC_VIO_FILE_FLAGS_SYMLINK = 1 << 0, + CSYNC_VIO_FILE_FLAGS_HIDDEN = 1 << 1 +}; + +enum csync_vio_file_type_e { + CSYNC_VIO_FILE_TYPE_UNKNOWN, + CSYNC_VIO_FILE_TYPE_REGULAR, + CSYNC_VIO_FILE_TYPE_DIRECTORY, + CSYNC_VIO_FILE_TYPE_FIFO, + CSYNC_VIO_FILE_TYPE_SOCKET, + CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE, + CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE, + CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK +}; + +enum csync_vio_file_stat_fields_e { + CSYNC_VIO_FILE_STAT_FIELDS_NONE = 0, + CSYNC_VIO_FILE_STAT_FIELDS_TYPE = 1 << 0, + CSYNC_VIO_FILE_STAT_FIELDS_MODE = 1 << 1, // local POSIX mode + CSYNC_VIO_FILE_STAT_FIELDS_FLAGS = 1 << 2, + CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3, + CSYNC_VIO_FILE_STAT_FIELDS_INODE = 1 << 4, + CSYNC_VIO_FILE_STAT_FIELDS_LINK_COUNT = 1 << 5, + CSYNC_VIO_FILE_STAT_FIELDS_SIZE = 1 << 6, +// CSYNC_VIO_FILE_STAT_FIELDS_BLOCK_COUNT = 1 << 7, /* will be removed */ +// CSYNC_VIO_FILE_STAT_FIELDS_BLOCK_SIZE = 1 << 8, /* will be removed */ + CSYNC_VIO_FILE_STAT_FIELDS_ATIME = 1 << 9, + CSYNC_VIO_FILE_STAT_FIELDS_MTIME = 1 << 10, + CSYNC_VIO_FILE_STAT_FIELDS_CTIME = 1 << 11, +// CSYNC_VIO_FILE_STAT_FIELDS_SYMLINK_NAME = 1 << 12, +// CSYNC_VIO_FILE_STAT_FIELDS_CHECKSUM = 1 << 13, +// CSYNC_VIO_FILE_STAT_FIELDS_ACL = 1 << 14, +// CSYNC_VIO_FILE_STAT_FIELDS_UID = 1 << 15, +// CSYNC_VIO_FILE_STAT_FIELDS_GID = 1 << 16, + CSYNC_VIO_FILE_STAT_FIELDS_ETAG = 1 << 17, + CSYNC_VIO_FILE_STAT_FIELDS_FILE_ID = 1 << 18, + CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADURL = 1 << 19, + CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADCOOKIES = 1 << 20, + CSYNC_VIO_FILE_STAT_FIELDS_PERM = 1 << 21 // remote oC perm + +}; + + +struct csync_vio_file_stat_s { + char *name; + char *etag; // FIXME: Should this be inlined like file_id and perm? + char file_id[FILE_ID_BUF_SIZE+1]; + char *directDownloadUrl; + char *directDownloadCookies; + char remotePerm[REMOTE_PERM_BUF_SIZE+1]; + + time_t atime; + time_t mtime; + time_t ctime; + + int64_t size; + + mode_t mode; + + dev_t device; + uint64_t inode; + nlink_t nlink; + + int fields; // actually enum csync_vio_file_stat_fields_e fields; + enum csync_vio_file_type_e type; + + enum csync_vio_file_flags_e flags; +}; + +csync_vio_file_stat_t *csync_vio_file_stat_new(void); +csync_vio_file_stat_t *csync_vio_file_stat_copy(csync_vio_file_stat_t *file_stat); + +void csync_vio_file_stat_destroy(csync_vio_file_stat_t *fstat); + +void csync_vio_file_stat_set_file_id( csync_vio_file_stat_t* dst, const char* src ); + +void csync_vio_set_file_id(char* dst, const char *src ); + + /** * CSync File Traversal structure. * @@ -191,6 +281,16 @@ typedef void (*csync_update_callback) (bool local, const char *dirUrl, void *userdata); +typedef void csync_vio_handle_t; +typedef csync_vio_handle_t* (*csync_vio_opendir_hook) (const char *url, + void *userdata); +typedef csync_vio_file_stat_t* (*csync_vio_readdir_hook) (csync_vio_handle_t *dhhandle, + void *userdata); +typedef void (*csync_vio_closedir_hook) (csync_vio_handle_t *dhhandle, + void *userdata); +typedef int (*csync_vio_stat_hook) (csync_vio_handle_t *dhhandle, + void *userdata); + /** * @brief Allocate a csync context. * @@ -229,15 +329,6 @@ int csync_update(CSYNC *ctx); */ int csync_reconcile(CSYNC *ctx); -/** - * @brief Propagation - * - * @param ctx The context to run the propagation on. - * - * @return 0 on success, less than 0 if an error occured. - */ -int csync_propagate(CSYNC *ctx); - /** * @brief Commit the sync results to journal * @@ -482,6 +573,9 @@ int csync_abort_requested(CSYNC *ctx); */ int csync_set_read_from_db(CSYNC* ctx, int enabled); +char *csync_normalize_etag(const char *); +time_t oc_httpdate_parse( const char *date ); + #ifdef __cplusplus } #endif diff --git a/csync/src/csync_misc.h b/csync/src/csync_misc.h index b9fb49395..2769cbc74 100644 --- a/csync/src/csync_misc.h +++ b/csync/src/csync_misc.h @@ -47,6 +47,4 @@ int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags); */ CSYNC_STATUS csync_errno_to_status(int error, CSYNC_STATUS default_status); -char *csync_normalize_etag(const char *); - #endif /* _CSYNC_MISC_H */ diff --git a/csync/src/csync_owncloud.c b/csync/src/csync_owncloud.c index 5cf08c43f..09e2ca646 100644 --- a/csync/src/csync_owncloud.c +++ b/csync/src/csync_owncloud.c @@ -213,8 +213,8 @@ static int configureProxy( csync_owncloud_ctx_t *ctx, ne_session *session ) re = 2; } else { DEBUG_WEBDAV("%s requested but no proxy host defined.", ctx->dav_session.proxy_type ); - /* we used to try ne_system_session_proxy here, but we should rather err out - to behave exactly like the caller. */ + /* we used to try ne_system_session_proxy here, but we should rather err out + to behave exactly like the caller. */ } } else { DEBUG_WEBDAV( "Unsupported Proxy: %s", ctx->dav_session.proxy_type ); @@ -374,7 +374,7 @@ static int post_send_hook(ne_request *req, void *userdata, * This function sets the flag _connected if the connection is established * and returns if the flag is set, so calling it frequently is save. */ -static int dav_connect(csync_owncloud_ctx_t *ctx, const char *base_url) { +int dav_connect(CSYNC *csyncCtx, const char *base_url) { int useSSL = 0; int rc; char protocol[6] = {'\0'}; @@ -384,6 +384,7 @@ static int dav_connect(csync_owncloud_ctx_t *ctx, const char *base_url) { char *host = NULL; unsigned int port = 0; int proxystate = -1; + csync_owncloud_ctx_t *ctx = csyncCtx->owncloud_context; if (ctx->_connected) { return 0; @@ -477,276 +478,6 @@ out: return rc; } -/* - * result parsing list. - * This function is called to parse the result of the propfind request - * to list directories on the WebDAV server. I takes a single resource - * and fills a resource struct and stores it to the result list which - * is stored in the listdir_context. - */ -static void propfind_results_callback(void *userdata, - const ne_uri *uri, - const ne_prop_result_set *set) -{ - struct listdir_context *fetchCtx = userdata; - struct resource *newres = 0; - const ne_status *status = NULL; - char *path = ne_path_unescape( uri->path ); - - (void) status; - - if( ! fetchCtx->target ) { - DEBUG_WEBDAV("error: target must not be zero!" ); - return; - } - - /* 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 ); - fill_webdav_properties_into_resource(newres, set); - - /* prepend the new resource to the result list */ - newres->next = fetchCtx->list; - fetchCtx->list = newres; - fetchCtx->result_count = fetchCtx->result_count + 1; - /* DEBUG_WEBDAV( "results for URI %s: %d %d", newres->name, (int)newres->size, (int)newres->type ); */ -} - - - -/* - * fetches a resource list from the WebDAV server. This is equivalent to list dir. - */ -static struct listdir_context *fetch_resource_list(csync_owncloud_ctx_t *ctx, const char *uri, int depth) -{ - struct listdir_context *fetchCtx; - int ret = 0; - ne_propfind_handler *hdl = NULL; - ne_request *request = NULL; - const char *content_type = NULL; - char *curi = NULL; - const ne_status *req_status = NULL; - - curi = _cleanPath( uri ); - - /* The old legacy one-level PROPFIND cache. Also gets filled - by the recursive cache if 'infinity' did not suceed. */ - if (ctx->propfind_cache) { - if (c_streq(curi, ctx->propfind_cache->target)) { - DEBUG_WEBDAV("fetch_resource_list Using simple PROPFIND cache %s", curi); - ctx->propfind_cache->ref++; - SAFE_FREE(curi); - return ctx->propfind_cache; - } - } - - if( ctx->csync_ctx->callbacks.update_callback ) { - ctx->csync_ctx->callbacks.update_callback(false, curi, - ctx->csync_ctx->callbacks.update_callback_userdata); - } - - fetchCtx = c_malloc( sizeof( struct listdir_context )); - if (!fetchCtx) { - errno = ENOMEM; - SAFE_FREE(curi); - return NULL; - } - fetchCtx->list = NULL; - fetchCtx->target = curi; - fetchCtx->currResource = NULL; - fetchCtx->ref = 1; - - /* do a propfind request and parse the results in the results function, set as callback */ - hdl = ne_propfind_create(ctx->dav_session.ctx, curi, depth); - - if(hdl) { - ret = ne_propfind_named(hdl, ls_props, propfind_results_callback, fetchCtx); - request = ne_propfind_get_request( hdl ); - req_status = ne_get_status( request ); - } - - if( ret == NE_OK ) { - fetchCtx->currResource = fetchCtx->list; - /* 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(ctx, req_status->reason_phrase); - } - DEBUG_WEBDAV("Simple propfind result code %d.", req_status ? req_status->code : -1); - } else { - if( ret == NE_ERROR && req_status->code == 404) { - errno = ENOENT; - } else { - set_errno_from_neon_errcode(ctx, 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: ""); - errno = ERRNO_WRONG_CONTENT; - set_error_message(ctx, "Server error: PROPFIND reply is not XML formatted!"); - ret = NE_CONNECT; - } - } - - if( ret != NE_OK ) { - const char *err = NULL; - set_errno_from_neon_errcode(ctx, ret); - - err = ne_get_error( ctx->dav_session.ctx ); - if(err) { - set_error_message(ctx, err); - } - DEBUG_WEBDAV("WRN: propfind named failed with %d, request error: %s", ret, err ? err : ""); - } - - if( hdl ) - ne_propfind_destroy(hdl); - - if( ret != NE_OK ) { - free_fetchCtx(fetchCtx); - return NULL; - } - - free_fetchCtx(ctx->propfind_cache); - ctx->propfind_cache = fetchCtx; - ctx->propfind_cache->ref++; - return fetchCtx; -} - -static struct listdir_context *fetch_resource_list_attempts(csync_owncloud_ctx_t *ctx, const char *uri, int depth) -{ - int i; - - struct listdir_context *fetchCtx = NULL; - for(i = 0; i < 10; ++i) { - fetchCtx = fetch_resource_list(ctx, uri, depth); - if(fetchCtx) break; - /* only loop in case the content is not XML formatted. Otherwise for every - * non successful stat (for non existing directories) its tried 10 times. */ - if( errno != ERRNO_WRONG_CONTENT ) break; - - DEBUG_WEBDAV("=> Errno after fetch resource list for %s: %d", uri, errno); - DEBUG_WEBDAV(" New attempt %i", i); - } - return fetchCtx; -} - - -/* - * directory functions - */ -csync_vio_handle_t *owncloud_opendir(CSYNC *ctx, const char *uri) { - struct listdir_context *fetchCtx = NULL; - char *curi = NULL; - - DEBUG_WEBDAV("opendir method called on %s", uri ); - - if (dav_connect( ctx->owncloud_context, uri ) < 0) { - DEBUG_WEBDAV("connection failed"); - return NULL; - } - - curi = _cleanPath( uri ); - if (ctx->owncloud_context->is_first_propfind && !ctx->owncloud_context->dav_session.no_recursive_propfind) { - ctx->owncloud_context->is_first_propfind = false; - // Try to fill it - fill_recursive_propfind_cache(ctx->owncloud_context, uri, curi); - } - if (ctx->owncloud_context->propfind_recursive_cache) { - // Try to fetch from recursive cache (if we have one) - fetchCtx = get_listdir_context_from_recursive_cache(ctx->owncloud_context, curi); - } - SAFE_FREE(curi); - ctx->owncloud_context->is_first_propfind = false; - if (fetchCtx) { - return fetchCtx; - } - - /* fetchCtx = fetch_resource_list( uri, NE_DEPTH_ONE ); */ - fetchCtx = fetch_resource_list_attempts( ctx->owncloud_context, uri, NE_DEPTH_ONE); - if( !fetchCtx ) { - /* errno is set properly in fetch_resource_list */ - DEBUG_WEBDAV("Errno set to %d", errno); - return NULL; - } else { - fetchCtx->currResource = fetchCtx->list; - DEBUG_WEBDAV("opendir returning handle %p (count=%d)", (void*) fetchCtx, fetchCtx->result_count ); - return fetchCtx; - } - /* no freeing of curi because its part of the fetchCtx and gets freed later */ -} - -int owncloud_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle) { - struct listdir_context *fetchCtx = dhandle; - free_fetchCtx(fetchCtx); - (void)ctx; - return 0; -} - -csync_vio_file_stat_t *owncloud_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle) { - struct listdir_context *fetchCtx = dhandle; - (void)ctx; - -// DEBUG_WEBDAV("owncloud_readdir" ); -// DEBUG_WEBDAV("owncloud_readdir %s ", fetchCtx->target); -// DEBUG_WEBDAV("owncloud_readdir %d", fetchCtx->result_count ); -// DEBUG_WEBDAV("owncloud_readdir %p %p", fetchCtx->currResource, fetchCtx->list ); - - if( fetchCtx == NULL) { - /* DEBUG_WEBDAV("An empty dir or at end"); */ - return NULL; - } - - while( fetchCtx->currResource ) { - resource* currResource = fetchCtx->currResource; - char *escaped_path = NULL; - - /* set pointer to next element */ - fetchCtx->currResource = fetchCtx->currResource->next; - - /* It seems strange: first uri->path is unescaped to escape it in the next step again. - * The reason is that uri->path is not completely escaped (ie. it seems only to have - * spaces escaped), while the fetchCtx->target is fully escaped. - * See http://bugs.owncloud.org/thebuggenie/owncloud/issues/oc-613 - */ - escaped_path = ne_path_escape( currResource->uri ); - if (ne_path_compare(fetchCtx->target, escaped_path) != 0) { - // Convert the resource for the caller - csync_vio_file_stat_t* lfs = csync_vio_file_stat_new(); - resourceToFileStat(lfs, currResource); - - SAFE_FREE( escaped_path ); - return lfs; - } else { - /* The first item is the root item, memorize its permissions */ - if (!ctx->remote.root_perms) { - if (strlen(currResource->remotePerm) > 0) { - /* Only copy if permissions contain something. Empty string means server didn't return them */ - ctx->remote.root_perms = c_strdup(currResource->remotePerm); - } - } - } - - /* This is the target URI */ - SAFE_FREE( escaped_path ); - } - - return NULL; -} - char *owncloud_error_string(CSYNC* ctx) { return ctx->owncloud_context->dav_session.error_string; @@ -757,19 +488,12 @@ int owncloud_commit(CSYNC* ctx) { return 0; } - clear_propfind_recursive_cache(ctx->owncloud_context); - - free_fetchCtx(ctx->owncloud_context->propfind_cache); - ctx->owncloud_context->propfind_cache = NULL; - if( ctx->owncloud_context->dav_session.ctx ) { ne_forget_auth(ctx->owncloud_context->dav_session.ctx); ne_session_destroy(ctx->owncloud_context->dav_session.ctx ); ctx->owncloud_context->dav_session.ctx = 0; } - ctx->owncloud_context->is_first_propfind = true; - ctx->owncloud_context->dav_session.no_recursive_propfind = true; /* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */ ctx->owncloud_context->dav_session.ctx = 0; @@ -819,10 +543,6 @@ int owncloud_set_property(CSYNC* ctx, const char *key, void *data) { *(ne_session**)data = ctx->owncloud_context->dav_session.ctx; return 0; } - if( c_streq(key, "no_recursive_propfind")) { - ctx->owncloud_context->dav_session.no_recursive_propfind = *(bool*)(data); - return 0; - } if( c_streq(key, "redirect_callback")) { if (data) { csync_owncloud_redirect_callback_t* cb_wrapper = data; @@ -840,10 +560,7 @@ void owncloud_init(CSYNC* ctx) { ctx->owncloud_context = c_malloc( sizeof( struct csync_owncloud_ctx_s )); ctx->owncloud_context->csync_ctx = ctx; // back reference - ctx->owncloud_context->is_first_propfind = true; - /* Disable it, Mirall can enable it for the first sync (= no DB)*/ - ctx->owncloud_context->dav_session.no_recursive_propfind = true; } /* vim: set ts=4 sw=4 et cindent: */ diff --git a/csync/src/csync_owncloud.h b/csync/src/csync_owncloud.h index 8940ca8dd..e198242e7 100644 --- a/csync/src/csync_owncloud.h +++ b/csync/src/csync_owncloud.h @@ -22,17 +22,15 @@ #define CSYNC_OWNCLOUD_H #include "csync.h" -#include "vio/csync_vio_file_stat.h" #include "vio/csync_vio.h" // Public API used by csync -csync_vio_handle_t *owncloud_opendir(CSYNC* ctx, const char *uri); -csync_vio_file_stat_t *owncloud_readdir(CSYNC* ctx, csync_vio_handle_t *dhandle); -int owncloud_closedir(CSYNC* ctx, csync_vio_handle_t *dhandle); int owncloud_commit(CSYNC* ctx); void owncloud_destroy(CSYNC* ctx); char *owncloud_error_string(CSYNC* ctx); -void owncloud_init(CSYNC* ctx); int owncloud_set_property(CSYNC* ctx, const char *key, void *data); +void owncloud_init(CSYNC* ctx); + +int dav_connect(CSYNC* ctx, const char *base_url); #endif /* CSYNC_OWNCLOUD_H */ diff --git a/csync/src/csync_owncloud_private.h b/csync/src/csync_owncloud_private.h index 6da641961..e10402146 100644 --- a/csync/src/csync_owncloud_private.h +++ b/csync/src/csync_owncloud_private.h @@ -57,7 +57,6 @@ #include "c_private.h" #include "httpbf.h" -#include "vio/csync_vio_file_stat.h" #include "vio/csync_vio.h" #include "csync_log.h" @@ -87,22 +86,12 @@ struct dav_session_s { int read_timeout; - bool no_recursive_propfind; - csync_owncloud_redirect_callback_t redir_callback; }; struct csync_owncloud_ctx_s { CSYNC *csync_ctx; - // For the PROPFIND results - bool is_first_propfind; - struct listdir_context *propfind_cache; - c_rbtree_t *propfind_recursive_cache; - int propfind_recursive_cache_depth; - int propfind_recursive_cache_file_count; - int propfind_recursive_cache_folder_count; - // For the WebDAV connection struct dav_session_s dav_session; /* The DAV Session, initialised in dav_connect */ int _connected; /* flag to indicate if a connection exists, ie. @@ -111,74 +100,6 @@ struct csync_owncloud_ctx_s { typedef struct csync_owncloud_ctx_s csync_owncloud_ctx_t; //typedef csync_owncloud_ctx_t* csync_owncloud_ctx_p; -enum resource_type { - resr_normal = 0, - resr_collection, - resr_reference, - resr_error -}; - -/* The list of properties that is fetched in PropFind on a collection */ -static const ne_propname ls_props[] = { - { "DAV:", "getlastmodified" }, - { "DAV:", "getcontentlength" }, - { "DAV:", "resourcetype" }, - { "DAV:", "getetag"}, - { "http://owncloud.org/ns", "id"}, - { "http://owncloud.org/ns", "downloadURL"}, - { "http://owncloud.org/ns", "dDC"}, // directDownloadCookies - { "http://owncloud.org/ns", "permissions"}, - { NULL, NULL } -}; - -/* Struct to store data for each resource found during an opendir operation. - * It represents a single file entry. - */ -typedef struct resource { - char *uri; /* The complete uri */ - char *name; /* The filename only */ - - enum resource_type type; - int64_t size; - time_t modtime; - char* md5; - char file_id[FILE_ID_BUF_SIZE+1]; - // Those two are optional from the server. We can use those URL to download the file directly - // without going through the ownCloud instance. - char *directDownloadUrl; - char *directDownloadCookies; - // See https://github.com/owncloud/core/issues/8322 - char remotePerm[REMOTE_PERM_BUF_SIZE+1]; - - struct resource *next; -} resource; - -/* Struct to hold the context of a WebDAV PropFind operation to fetch - * a directory listing from the server. - */ -struct listdir_context { - struct resource *list; /* The list of result resources */ - struct resource *currResource; /* A pointer to the current resource */ - char *target; /* Request-URI of the PROPFIND */ - unsigned int result_count; /* number of elements stored in list */ - int ref; /* reference count, only destroy when it reaches 0 */ -}; - - -/* Values are propfind_recursive_element: */ -struct propfind_recursive_element { - struct resource *self; - struct resource *children; - struct propfind_recursive_element *parent; -}; -typedef struct propfind_recursive_element propfind_recursive_element_t; - -void clear_propfind_recursive_cache(csync_owncloud_ctx_t *ctx); -struct listdir_context *get_listdir_context_from_recursive_cache(csync_owncloud_ctx_t *ctx, const char *curi); -void fill_recursive_propfind_cache(csync_owncloud_ctx_t *ctx, const char *uri, const char *curi); -struct listdir_context *get_listdir_context_from_cache(csync_owncloud_ctx_t *ctx, const char *curi); -void fetch_resource_list_recursive(csync_owncloud_ctx_t *ctx, const char *uri, const char *curi); - void set_errno_from_http_errcode( int err ); void set_error_message( csync_owncloud_ctx_t *ctx, const char *msg ); void set_errno_from_neon_errcode(csync_owncloud_ctx_t *ctx, int neon_code ); @@ -187,15 +108,8 @@ void set_errno_from_session(csync_owncloud_ctx_t *ctx); time_t oc_httpdate_parse( const char *date ); -char *_cleanPath( const char* uri ); - -void fill_webdav_properties_into_resource(struct resource* newres, const ne_prop_result_set *set); - -void resourceToFileStat( csync_vio_file_stat_t *lfs, struct resource *res ); -void resource_free(struct resource* o); -struct resource* resource_dup(struct resource* o); -void free_fetchCtx( struct listdir_context *ctx ); - const char* csync_owncloud_get_platform(void); +char *_cleanPath( const char* uri ); + #endif // CSYNC_OWNCLOUD_PRIVATE_H diff --git a/csync/src/csync_owncloud_recursive_propfind.c b/csync/src/csync_owncloud_recursive_propfind.c deleted file mode 100644 index b84dbdec7..000000000 --- a/csync/src/csync_owncloud_recursive_propfind.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2011 by Andreas Schneider - * Copyright (c) 2012 by Klaas Freitag - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "csync_owncloud.h" -#include "csync_owncloud_private.h" - -static void _tree_destructor(void *data) { - propfind_recursive_element_t *element = data; - resource_free(element->self); - resource_free(element->children); - SAFE_FREE(element); -} - -void clear_propfind_recursive_cache(csync_owncloud_ctx_t *ctx) -{ - if (ctx->propfind_recursive_cache) { - DEBUG_WEBDAV("clear_propfind_recursive_cache Invalidating.."); - c_rbtree_destroy(ctx->propfind_recursive_cache, _tree_destructor); - ctx->propfind_recursive_cache = NULL; - } -} - -struct listdir_context *get_listdir_context_from_recursive_cache(csync_owncloud_ctx_t *ctx, const char *curi) -{ - propfind_recursive_element_t *element = NULL; - struct listdir_context *fetchCtx = NULL; - struct resource *iterator, *r; - - if (!ctx->propfind_recursive_cache) { - DEBUG_WEBDAV("get_listdir_context_from_recursive_cache No cache"); - return NULL; - } - - element = c_rbtree_node_data(c_rbtree_find(ctx->propfind_recursive_cache, curi)); - if (!element) { - DEBUG_WEBDAV("get_listdir_context_from_recursive_cache No element %s in cache found", curi); - return NULL; - } - if( ctx->csync_ctx->callbacks.update_callback ) { - ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata); - } - - /* Out of the element, create a listdir_context.. if we could be sure that it is immutable, we could ref instead.. need to investigate */ - fetchCtx = c_malloc( sizeof( struct listdir_context )); - ZERO_STRUCTP(fetchCtx); - fetchCtx->list = NULL; - fetchCtx->target = c_strdup(curi); - fetchCtx->currResource = NULL; - fetchCtx->ref = 1; - - iterator = element->children; - 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 propfind_results_recursive_callback(void *userdata, - const ne_uri *uri, - const ne_prop_result_set *set) -{ - struct resource *newres = 0; - - const ne_status *status = NULL; - char *path = ne_path_unescape( uri->path ); - char *parentPath; - propfind_recursive_element_t *element = NULL; - propfind_recursive_element_t *pElement = NULL; - int depth = 0; - csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata; - - - (void) status; - - if (!ctx->propfind_recursive_cache) { - c_rbtree_create(&ctx->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 ); - fill_webdav_properties_into_resource(newres, set); - - if( newres->type == resr_collection) { - ctx->propfind_recursive_cache_folder_count++; - } else { - ctx->propfind_recursive_cache_file_count++; - } - - /* Create new item in rb tree */ - if (newres->type == resr_collection) { - DEBUG_WEBDAV("propfind_results_recursive %s is a folder", newres->uri); - /* Check if in rb tree */ - element = c_rbtree_node_data(c_rbtree_find(ctx->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->self->next = 0; - element->children = NULL; - element->parent = NULL; - c_rbtree_insert(ctx->propfind_recursive_cache, element); - /* DEBUG_WEBDAV("results_recursive Added collection %s", newres->uri); */ - - // We do this here and in get_listdir_context_from_recursive_cache because - // a recursive PROPFIND might take some time but we still want to - // be informed. Later when get_listdir_context_from_recursive_cache is - // called the DB queries might be the problem causing slowness, so do it again there then. - if( ctx->csync_ctx->callbacks.update_callback ) { - ctx->csync_ctx->callbacks.update_callback(false, path, ctx->csync_ctx->callbacks.update_callback_userdata); - } - } - } - - /* Check for parent in tree. If exists: Insert it into the children elements there */ - parentPath = ne_path_parent(uri->path); - if (parentPath) { - propfind_recursive_element_t *parentElement = NULL; - - parentElement = c_rbtree_node_data(c_rbtree_find(ctx->propfind_recursive_cache,parentPath)); - free(parentPath); - - if (parentElement) { - newres->next = parentElement->children; - parentElement->children = newres; - - /* If the current result is a collection we also need to set its parent */ - if (element) - element->parent = parentElement; - - pElement = element; - while (pElement) { - depth++; - pElement = pElement->parent; - } - if (depth > ctx->propfind_recursive_cache_depth) { - DEBUG_WEBDAV("propfind_results_recursive %s new maximum tree depth %d", newres->uri, depth); - ctx->propfind_recursive_cache_depth = depth; - } - - /* DEBUG_WEBDAV("results_recursive Added child %s to collection %s", newres->uri, element->self->uri); */ - return; - } - } - - resource_free(newres); - newres = NULL; -} - -void fetch_resource_list_recursive(csync_owncloud_ctx_t *ctx, 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("fetch_resource_list_recursive Starting recursive propfind %s %s", uri, curi); - if( ctx->csync_ctx->callbacks.update_callback ) { - ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata); - } - - /* do a propfind request and parse the results in the results function, set as callback */ - hdl = ne_propfind_create(ctx->dav_session.ctx, curi, depth); - - if(hdl) { - ret = ne_propfind_named(hdl, ls_props, propfind_results_recursive_callback, ctx); - 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(ctx, req_status->reason_phrase); - } - DEBUG_WEBDAV("Recursive propfind result code %d.", req_status ? req_status->code : 0); - } else { - if( ret == NE_ERROR && req_status->code == 404) { - errno = ENOENT; - } else { - set_errno_from_neon_errcode(ctx, 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: ""); - errno = ERRNO_WRONG_CONTENT; - set_error_message(ctx, "Server error: PROPFIND reply is not XML formatted!"); - ret = NE_CONNECT; - } - } - - if( ret != NE_OK ) { - const char *err = NULL; - - err = ne_get_error( ctx->dav_session.ctx ); - DEBUG_WEBDAV("WRN: propfind named failed with %d, request error: %s", ret, err ? err : ""); - } - - if( hdl ) - ne_propfind_destroy(hdl); - - if( ret != NE_OK ) { - return; - } - - return; -} - -/* Called by owncloud_opendir()->fetch_resource_list() to fill the cache */ -void fill_recursive_propfind_cache(csync_owncloud_ctx_t *ctx, const char *uri, const char *curi) { - fetch_resource_list_recursive(ctx, uri, curi); - - if (ctx->propfind_recursive_cache_depth <= 2) { - DEBUG_WEBDAV("fill_recursive_propfind_cache %s Server maybe did not give us an 'infinity' depth result", curi); - /* transform the cache to the normal cache in propfind_cache */ - ctx->propfind_cache = get_listdir_context_from_recursive_cache(ctx, curi); - /* clear the cache, it is bogus since the server returned only results for Depth 1 */ - clear_propfind_recursive_cache(ctx); - } else { - DEBUG_WEBDAV("fill_recursive_propfind_cache %s We received %d elements deep for 'infinity' depth (%d folders, %d files)", - curi, - ctx->propfind_recursive_cache_depth, - ctx->propfind_recursive_cache_folder_count, - ctx->propfind_recursive_cache_file_count); - - } -} diff --git a/csync/src/csync_owncloud_util.c b/csync/src/csync_owncloud_util.c index 8b017d0a8..3ce39d5bd 100644 --- a/csync/src/csync_owncloud_util.c +++ b/csync/src/csync_owncloud_util.c @@ -24,13 +24,6 @@ #include "csync_misc.h" -void set_error_message( csync_owncloud_ctx_t *ctx, const char *msg ) -{ - SAFE_FREE(ctx->dav_session.error_string); - if( msg ) - ctx->dav_session.error_string = c_strdup(msg); -} - void set_errno_from_http_errcode( int err ) { int new_errno = 0; @@ -106,91 +99,6 @@ void set_errno_from_http_errcode( int err ) { errno = new_errno; } -int http_result_code_from_session(csync_owncloud_ctx_t *ctx) { - const char *p = ne_get_error( ctx->dav_session.ctx ); - char *q; - int err; - - set_error_message(ctx, p); /* remember the error message */ - - err = strtol(p, &q, 10); - if (p == q) { - err = ERRNO_ERROR_STRING; - } - return err; -} - -void set_errno_from_session(csync_owncloud_ctx_t *ctx) { - int err = http_result_code_from_session(ctx); - - if( err == EIO || err == ERRNO_ERROR_STRING) { - errno = err; - } else { - set_errno_from_http_errcode(err); - } -} - -void set_errno_from_neon_errcode(csync_owncloud_ctx_t *ctx, int neon_code ) { - - if( neon_code != NE_OK ) { - DEBUG_WEBDAV("Neon error code was %d", neon_code); - } - - switch(neon_code) { - case NE_OK: /* Success, but still the possiblity of problems */ - case NE_ERROR: /* Generic error; use ne_get_error(session) for message */ - set_errno_from_session(ctx); /* Something wrong with http communication */ - break; - case NE_LOOKUP: /* Server or proxy hostname lookup failed */ - errno = ERRNO_LOOKUP_ERROR; - break; - case NE_AUTH: /* User authentication failed on server */ - errno = ERRNO_USER_UNKNOWN_ON_SERVER; - break; - case NE_PROXYAUTH: /* User authentication failed on proxy */ - errno = ERRNO_PROXY_AUTH; - break; - case NE_CONNECT: /* Could not connect to server */ - errno = ERRNO_CONNECT; - break; - case NE_TIMEOUT: /* Connection timed out */ - errno = ERRNO_TIMEOUT; - break; - case NE_FAILED: /* The precondition failed */ - errno = ERRNO_PRECONDITION; - break; - case NE_RETRY: /* Retry request (ne_end_request ONLY) */ - errno = ERRNO_RETRY; - break; - - case NE_REDIRECT: /* See ne_redirect.h */ - errno = ERRNO_REDIRECT; - break; - default: - errno = ERRNO_GENERAL_ERROR; - } -} - -/* cleanPath to return an escaped path of an uri */ -char *_cleanPath( const char* uri ) { - int rc = 0; - char *path = NULL; - char *re = NULL; - - rc = c_parse_uri( uri, NULL, NULL, NULL, NULL, NULL, &path ); - if( rc < 0 ) { - DEBUG_WEBDAV("Unable to cleanPath %s", uri ? uri: "" ); - re = NULL; - } else { - if(path) { - re = ne_path_escape( path ); - } - } - - SAFE_FREE( path ); - return re; -} - #ifndef HAVE_TIMEGM #ifdef _WIN32 @@ -277,186 +185,6 @@ time_t oc_httpdate_parse( const char *date ) { return result; } - -/* - * helper: convert a resource struct to file_stat struct. - */ -void resourceToFileStat(csync_vio_file_stat_t *lfs, struct resource *res ) -{ - ZERO_STRUCTP(lfs); - - lfs->name = c_strdup( res->name ); - - lfs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; - if( res->type == resr_normal ) { - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - lfs->type = CSYNC_VIO_FILE_TYPE_REGULAR; - } else if( res->type == resr_collection ) { - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - lfs->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; - } else { - DEBUG_WEBDAV("ERROR: Unknown resource type %d", res->type); - } - - lfs->mtime = res->modtime; - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; - lfs->size = res->size; - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; - if( res->md5 ) { - lfs->etag = c_strdup(res->md5); - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ETAG; - } - - csync_vio_file_stat_set_file_id(lfs, res->file_id); - - if (res->directDownloadUrl) { - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADURL; - lfs->directDownloadUrl = c_strdup(res->directDownloadUrl); - } - if (res->directDownloadCookies) { - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADCOOKIES; - lfs->directDownloadCookies = c_strdup(res->directDownloadCookies); - } - if (strlen(res->remotePerm) > 0) { - lfs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_PERM; - strncpy(lfs->remotePerm, res->remotePerm, sizeof(lfs->remotePerm)); - } -} - -void fill_webdav_properties_into_resource(struct resource* newres, const ne_prop_result_set *set) -{ - const char *clength, *modtime, *file_id = NULL; - const char *directDownloadUrl = NULL; - const char *directDownloadCookies = NULL; - const char *resourcetype = NULL; - const char *etag = NULL; - const char *perm = NULL; - - modtime = ne_propset_value( set, &ls_props[0] ); - clength = ne_propset_value( set, &ls_props[1] ); - resourcetype = ne_propset_value( set, &ls_props[2] ); - etag = ne_propset_value( set, &ls_props[3] ); - file_id = ne_propset_value( set, &ls_props[4] ); - directDownloadUrl = ne_propset_value( set, &ls_props[5] ); - directDownloadCookies = ne_propset_value( set, &ls_props[6] ); - - // permission flags: Defined in https://github.com/owncloud/core/issues/8322 - perm = ne_propset_value( set, &ls_props[7] ); - - if( resourcetype && strncmp( resourcetype, "", 16 ) == 0) { - newres->type = resr_collection; - } else { - newres->type = resr_normal; - } - - if (modtime) { - newres->modtime = oc_httpdate_parse(modtime); - } - - /* DEBUG_WEBDAV("Parsing Modtime: %s -> %llu", modtime, (unsigned long long) newres->modtime ); */ - newres->size = 0; - if (clength) { - newres->size = atoll(clength); - /* DEBUG_WEBDAV("Parsed File size for %s from %s: %lld", newres->name, clength, (long long)newres->size ); */ - } - - if( etag ) { - newres->md5 = csync_normalize_etag(etag); - } - - csync_vio_set_file_id(newres->file_id, file_id); - /* - DEBUG_WEBDAV("propfind_results_recursive %s [%s] %s", newres->uri, newres->type == resr_collection ? "collection" : "file", newres->md5); - */ - - if (directDownloadUrl) { - newres->directDownloadUrl = c_strdup(directDownloadUrl); - } - if (directDownloadCookies) { - newres->directDownloadCookies = c_strdup(directDownloadCookies); - } - /* DEBUG_WEBDAV("fill_webdav_properties_into_resource %s >%p< ", newres->name, perm ); */ - if (perm && !perm[0]) { - // special meaning for our code: server returned permissions but are empty - // meaning only reading is allowed for this resource - newres->remotePerm[0] = ' '; - // see _csync_detect_update() - } else if (perm && strlen(perm) < sizeof(newres->remotePerm)) { - strncpy(newres->remotePerm, perm, sizeof(newres->remotePerm)); - } else { - // old server, keep newres->remotePerm empty - } -} - -struct resource* resource_dup(struct resource* o) { - struct resource *r = c_malloc (sizeof( struct resource )); - ZERO_STRUCTP(r); - - r->uri = c_strdup(o->uri); - r->name = c_strdup(o->name); - r->type = o->type; - r->size = o->size; - r->modtime = o->modtime; - if( o->md5 ) { - r->md5 = c_strdup(o->md5); - } - if (o->directDownloadUrl) { - r->directDownloadUrl = c_strdup(o->directDownloadUrl); - } - if (o->directDownloadCookies) { - r->directDownloadCookies = c_strdup(o->directDownloadCookies); - } - if (o->remotePerm) { - strncpy(r->remotePerm, o->remotePerm, sizeof(r->remotePerm)); - } - r->next = o->next; - csync_vio_set_file_id(r->file_id, o->file_id); - - return r; -} -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->directDownloadUrl); - SAFE_FREE(old->directDownloadCookies); - SAFE_FREE(old); - } -} - -void free_fetchCtx( struct listdir_context *ctx ) -{ - struct resource *newres, *res; - if( ! ctx ) return; - newres = ctx->list; - res = newres; - - ctx->ref--; - if (ctx->ref > 0) return; - - SAFE_FREE(ctx->target); - - while( res ) { - SAFE_FREE(res->uri); - SAFE_FREE(res->name); - SAFE_FREE(res->md5); - memset( res->file_id, 0, FILE_ID_BUF_SIZE+1 ); - SAFE_FREE(res->directDownloadUrl); - SAFE_FREE(res->directDownloadCookies); - - newres = res->next; - SAFE_FREE(res); - res = newres; - } - SAFE_FREE(ctx); -} - - // as per http://sourceforge.net/p/predef/wiki/OperatingSystems/ // extend as required const char* csync_owncloud_get_platform() { diff --git a/csync/src/csync_private.h b/csync/src/csync_private.h index 764491647..ef5bf7daf 100644 --- a/csync/src/csync_private.h +++ b/csync/src/csync_private.h @@ -41,7 +41,6 @@ #include "std/c_private.h" #include "csync.h" #include "csync_misc.h" -#include "vio/csync_vio_file_stat.h" #ifdef WITH_ICONV #include @@ -89,6 +88,11 @@ struct csync_s { void *userdata; csync_update_callback update_callback; void *update_callback_userdata; + + csync_vio_opendir_hook remote_opendir_hook; + csync_vio_readdir_hook remote_readdir_hook; + csync_vio_closedir_hook remote_closedir_hook; + void *vio_userdata; } callbacks; c_strlist_t *excludes; @@ -205,6 +209,10 @@ struct _csync_treewalk_context_s typedef struct _csync_treewalk_context_s _csync_treewalk_context; +time_t oc_httpdate_parse( const char *date ); + +void set_errno_from_http_errcode( int err ); + /** * }@ */ diff --git a/csync/src/csync_update.c b/csync/src/csync_update.c index b947f0992..40676da14 100644 --- a/csync/src/csync_update.c +++ b/csync/src/csync_update.c @@ -371,6 +371,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file, st->instruction = CSYNC_INSTRUCTION_NEW; goto out; } + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "remote rename detected based on fileid %s %s", tmp->path, file); st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME; if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) { csync_rename_record(ctx, tmp->path, path); @@ -582,7 +583,21 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn, goto done; } - if ((dh = csync_vio_opendir(ctx, uri)) == NULL) { + const char *uri_for_vio = uri; + if (ctx->current == REMOTE_REPLICA) { + uri_for_vio += strlen(ctx->remote.uri); + if (strlen(uri_for_vio) > 0 && uri_for_vio[0] == '/') { + uri_for_vio++; // cut leading slash + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "URI without fuzz for %s is \"%s\"", uri, uri_for_vio); + } + + if ((dh = csync_vio_opendir(ctx, uri_for_vio)) == NULL) { + if (ctx->abort) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Aborted!"); + ctx->status_code = CSYNC_STATUS_ABORTED; + goto error; + } int asp = 0; /* permission denied */ ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR); diff --git a/csync/src/csync_update.h b/csync/src/csync_update.h index 6f6dee441..4a7495e3a 100644 --- a/csync/src/csync_update.h +++ b/csync/src/csync_update.h @@ -23,7 +23,6 @@ #define _CSYNC_UPDATE_H #include "csync.h" -#include "vio/csync_vio_file_stat.h" /** * @file csync_update.h diff --git a/csync/src/vio/csync_vio.c b/csync/src/vio/csync_vio.c index 4eb17bcb5..9dc81d082 100644 --- a/csync/src/vio/csync_vio.c +++ b/csync/src/vio/csync_vio.c @@ -29,7 +29,6 @@ #include "csync_private.h" #include "csync_util.h" #include "vio/csync_vio.h" -#include "vio/csync_vio_handle_private.h" #include "vio/csync_vio_local.h" #include "csync_statedb.h" #include "std/c_jhash.h" @@ -45,7 +44,7 @@ csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) { if(ctx->remote.read_from_db) { CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Read from db flag is true, should not!" ); } - return owncloud_opendir(ctx, name); + return ctx->callbacks.remote_opendir_hook(name, ctx->callbacks.vio_userdata); break; case LOCAL_REPLICA: if( ctx->callbacks.update_callback ) { @@ -73,7 +72,8 @@ int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle) { if( ctx->remote.read_from_db ) { CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote ReadFromDb is true, should not!"); } - rc = owncloud_closedir(ctx, dhandle); + ctx->callbacks.remote_closedir_hook(dhandle, ctx->callbacks.vio_userdata); + rc = 0; break; case LOCAL_REPLICA: rc = csync_vio_local_closedir(dhandle); @@ -91,7 +91,7 @@ csync_vio_file_stat_t *csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle if( ctx->remote.read_from_db ) { CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote readfromdb is true, should not!"); } - return owncloud_readdir(ctx, dhandle); + return ctx->callbacks.remote_readdir_hook(dhandle, ctx->callbacks.vio_userdata); break; case LOCAL_REPLICA: return csync_vio_local_readdir(dhandle); diff --git a/csync/src/vio/csync_vio.h b/csync/src/vio/csync_vio.h index 499e47b22..715b18542 100644 --- a/csync/src/vio/csync_vio.h +++ b/csync/src/vio/csync_vio.h @@ -25,8 +25,7 @@ #include #include #include "c_private.h" -#include "vio/csync_vio_handle.h" -#include "vio/csync_vio_file_stat.h" +#include "csync.h" #include "csync_private.h" typedef struct fhandle_s { diff --git a/csync/src/vio/csync_vio_file_stat.c b/csync/src/vio/csync_vio_file_stat.c index f109a7877..a7fd2f0b3 100644 --- a/csync/src/vio/csync_vio_file_stat.c +++ b/csync/src/vio/csync_vio_file_stat.c @@ -19,7 +19,7 @@ */ #include "c_lib.h" -#include "vio/csync_vio_file_stat.h" +#include "csync.h" csync_vio_file_stat_t *csync_vio_file_stat_new(void) { csync_vio_file_stat_t *file_stat = (csync_vio_file_stat_t *) c_malloc(sizeof(csync_vio_file_stat_t)); @@ -27,6 +27,20 @@ csync_vio_file_stat_t *csync_vio_file_stat_new(void) { return file_stat; } +csync_vio_file_stat_t* csync_vio_file_stat_copy(csync_vio_file_stat_t *file_stat) { + csync_vio_file_stat_t *file_stat_cpy = csync_vio_file_stat_new(); + memcpy(file_stat_cpy, file_stat, sizeof(csync_vio_file_stat_t)); + file_stat_cpy->etag = c_strdup(file_stat_cpy->etag); + if (file_stat_cpy->directDownloadCookies) { + file_stat_cpy->directDownloadCookies = c_strdup(file_stat_cpy->directDownloadCookies); + } + if (file_stat_cpy->directDownloadUrl) { + file_stat_cpy->directDownloadUrl = c_strdup(file_stat_cpy->directDownloadUrl); + } + file_stat_cpy->name = c_strdup(file_stat_cpy->name); + return file_stat_cpy; +} + void csync_vio_file_stat_destroy(csync_vio_file_stat_t *file_stat) { /* FIXME: free rest */ if (file_stat == NULL) { diff --git a/csync/src/vio/csync_vio_file_stat.h b/csync/src/vio/csync_vio_file_stat.h deleted file mode 100644 index 2c0782d46..000000000 --- a/csync/src/vio/csync_vio_file_stat.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _CSYNC_VIO_FILE_STAT_H -#define _CSYNC_VIO_FILE_STAT_H - -/* - * cannot include csync_private here because - * that would cause circular inclusion - */ -#include "std/c_private.h" -#include "csync.h" -#include -#include -#include -#include - -#define FILE_ID_BUF_SIZE 21 - -// currently specified at https://github.com/owncloud/core/issues/8322 are 9 to 10 -#define REMOTE_PERM_BUF_SIZE 15 - -typedef struct csync_vio_file_stat_s csync_vio_file_stat_t; - -enum csync_vio_file_flags_e { - CSYNC_VIO_FILE_FLAGS_NONE = 0, - CSYNC_VIO_FILE_FLAGS_SYMLINK = 1 << 0, - CSYNC_VIO_FILE_FLAGS_HIDDEN = 1 << 1 -}; - -enum csync_vio_file_type_e { - CSYNC_VIO_FILE_TYPE_UNKNOWN, - CSYNC_VIO_FILE_TYPE_REGULAR, - CSYNC_VIO_FILE_TYPE_DIRECTORY, - CSYNC_VIO_FILE_TYPE_FIFO, - CSYNC_VIO_FILE_TYPE_SOCKET, - CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE, - CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE, - CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK -}; - -enum csync_vio_file_stat_fields_e { - CSYNC_VIO_FILE_STAT_FIELDS_NONE = 0, - CSYNC_VIO_FILE_STAT_FIELDS_TYPE = 1 << 0, - CSYNC_VIO_FILE_STAT_FIELDS_MODE = 1 << 1, // local POSIX mode - CSYNC_VIO_FILE_STAT_FIELDS_FLAGS = 1 << 2, - CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3, - CSYNC_VIO_FILE_STAT_FIELDS_INODE = 1 << 4, - CSYNC_VIO_FILE_STAT_FIELDS_LINK_COUNT = 1 << 5, - CSYNC_VIO_FILE_STAT_FIELDS_SIZE = 1 << 6, -// CSYNC_VIO_FILE_STAT_FIELDS_BLOCK_COUNT = 1 << 7, /* will be removed */ -// CSYNC_VIO_FILE_STAT_FIELDS_BLOCK_SIZE = 1 << 8, /* will be removed */ - CSYNC_VIO_FILE_STAT_FIELDS_ATIME = 1 << 9, - CSYNC_VIO_FILE_STAT_FIELDS_MTIME = 1 << 10, - CSYNC_VIO_FILE_STAT_FIELDS_CTIME = 1 << 11, -// CSYNC_VIO_FILE_STAT_FIELDS_SYMLINK_NAME = 1 << 12, -// CSYNC_VIO_FILE_STAT_FIELDS_CHECKSUM = 1 << 13, -// CSYNC_VIO_FILE_STAT_FIELDS_ACL = 1 << 14, -// CSYNC_VIO_FILE_STAT_FIELDS_UID = 1 << 15, -// CSYNC_VIO_FILE_STAT_FIELDS_GID = 1 << 16, - CSYNC_VIO_FILE_STAT_FIELDS_ETAG = 1 << 17, - CSYNC_VIO_FILE_STAT_FIELDS_FILE_ID = 1 << 18, - CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADURL = 1 << 19, - CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADCOOKIES = 1 << 20, - CSYNC_VIO_FILE_STAT_FIELDS_PERM = 1 << 21 // remote oC perm - -}; - - -struct csync_vio_file_stat_s { - char *name; - char *etag; // FIXME: Should this be inlined like file_id and perm? - char file_id[FILE_ID_BUF_SIZE+1]; - char *directDownloadUrl; - char *directDownloadCookies; - char remotePerm[REMOTE_PERM_BUF_SIZE+1]; - - time_t atime; - time_t mtime; - time_t ctime; - - int64_t size; - - mode_t mode; - - dev_t device; - uint64_t inode; - nlink_t nlink; - - enum csync_vio_file_stat_fields_e fields; - enum csync_vio_file_type_e type; - - enum csync_vio_file_flags_e flags; -}; - -csync_vio_file_stat_t *csync_vio_file_stat_new(void); - -void csync_vio_file_stat_destroy(csync_vio_file_stat_t *fstat); - -void csync_vio_file_stat_set_file_id( csync_vio_file_stat_t* dst, const char* src ); - -void csync_vio_set_file_id(char* dst, const char *src ); - -#endif /* _CSYNC_VIO_METHOD_H */ diff --git a/csync/src/vio/csync_vio_handle.h b/csync/src/vio/csync_vio_handle.h deleted file mode 100644 index 32ae48b07..000000000 --- a/csync/src/vio/csync_vio_handle.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _CSYNC_VIO_HANDLE_H -#define _CSYNC_VIO_HANDLE_H - -typedef void csync_vio_handle_t; - -#endif /* _CSYNC_VIO_HANDLE_H */ diff --git a/csync/src/vio/csync_vio_handle_private.h b/csync/src/vio/csync_vio_handle_private.h deleted file mode 100644 index b42a19f95..000000000 --- a/csync/src/vio/csync_vio_handle_private.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * cynapses libc functions - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _CSYNC_VIO_HANDLE_PRIVATE_H -#define _CSYNC_VIO_HANDLE_PRIVATE_H - -#include "vio/csync_vio_handle.h" - -#endif /* _CSYNC_VIO_HANDLE_PRIVATE_H */ diff --git a/csync/src/vio/csync_vio_local.c b/csync/src/vio/csync_vio_local.c index 19b90e64e..6d24b4e92 100644 --- a/csync/src/vio/csync_vio_local.c +++ b/csync/src/vio/csync_vio_local.c @@ -36,9 +36,7 @@ #include "csync_log.h" #include "csync_vio.h" -#include "vio/csync_vio_handle.h" #include "vio/csync_vio_local.h" -#include "vio/csync_vio_handle_private.h" /* diff --git a/csync/tests/vio_tests/check_vio_file_stat.c b/csync/tests/vio_tests/check_vio_file_stat.c index 5a20cb320..a67255e81 100644 --- a/csync/tests/vio_tests/check_vio_file_stat.c +++ b/csync/tests/vio_tests/check_vio_file_stat.c @@ -19,7 +19,7 @@ */ #include "torture.h" -#include "vio/csync_vio_file_stat.h" +#include "csync.h" static void check_csync_vio_file_stat_new(void **state) { diff --git a/src/gui/folderwizard.cpp b/src/gui/folderwizard.cpp index 30bbcc75a..f98fe25a9 100644 --- a/src/gui/folderwizard.cpp +++ b/src/gui/folderwizard.cpp @@ -356,7 +356,7 @@ void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list) void FolderWizardRemotePath::slotRefreshFolders() { LsColJob *job = new LsColJob(AccountManager::instance()->account(), "/", this); - connect(job, SIGNAL(directoryListing(QStringList)), + connect(job, SIGNAL(directoryListingSubfolders(QStringList)), SLOT(slotUpdateDirectories(QStringList))); job->start(); _ui.folderTreeWidget->clear(); @@ -366,7 +366,7 @@ void FolderWizardRemotePath::slotItemExpanded(QTreeWidgetItem *item) { QString dir = item->data(0, Qt::UserRole).toString(); LsColJob *job = new LsColJob(AccountManager::instance()->account(), dir, this); - connect(job, SIGNAL(directoryListing(QStringList)), + connect(job, SIGNAL(directoryListingSubfolders(QStringList)), SLOT(slotUpdateDirectories(QStringList))); job->start(); } diff --git a/src/gui/selectivesyncdialog.cpp b/src/gui/selectivesyncdialog.cpp index e09e16ee5..7de58295d 100644 --- a/src/gui/selectivesyncdialog.cpp +++ b/src/gui/selectivesyncdialog.cpp @@ -51,7 +51,7 @@ SelectiveSyncTreeView::SelectiveSyncTreeView(AccountPtr account, QWidget* parent void SelectiveSyncTreeView::refreshFolders() { LsColJob *job = new LsColJob(_account, _folderPath, this); - connect(job, SIGNAL(directoryListing(QStringList)), + connect(job, SIGNAL(directoryListingSubfolders(QStringList)), this, SLOT(slotUpdateDirectories(QStringList))); job->start(); clear(); @@ -167,7 +167,7 @@ void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item) prefix = _folderPath + QLatin1Char('/'); } LsColJob *job = new LsColJob(_account, prefix + dir, this); - connect(job, SIGNAL(directoryListing(QStringList)), + connect(job, SIGNAL(directoryListingSubfolders(QStringList)), SLOT(slotUpdateDirectories(QStringList))); job->start(); } diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index e3f6f3067..5e5f3a309 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -17,6 +17,8 @@ #include #include +#include "account.h" +#include namespace OCC { @@ -77,6 +79,386 @@ void DiscoveryJob::update_job_update_callback (bool local, } } + +int get_errno_from_http_errcode( int err ) { + int new_errno = 0; + + switch(err) { + case 200: /* OK */ + case 201: /* Created */ + case 202: /* Accepted */ + case 203: /* Non-Authoritative Information */ + case 204: /* No Content */ + case 205: /* Reset Content */ + case 207: /* Multi-Status */ + case 304: /* Not Modified */ + new_errno = 0; + break; + case 401: /* Unauthorized */ + case 402: /* Payment Required */ + case 407: /* Proxy Authentication Required */ + case 405: + new_errno = EPERM; + break; + case 301: /* Moved Permanently */ + case 303: /* See Other */ + case 404: /* Not Found */ + case 410: /* Gone */ + new_errno = ENOENT; + break; + case 408: /* Request Timeout */ + case 504: /* Gateway Timeout */ + new_errno = EAGAIN; + break; + case 423: /* Locked */ + new_errno = EACCES; + break; + case 400: /* Bad Request */ + case 403: /* Forbidden */ + case 409: /* Conflict */ + case 411: /* Length Required */ + case 412: /* Precondition Failed */ + case 414: /* Request-URI Too Long */ + case 415: /* Unsupported Media Type */ + case 424: /* Failed Dependency */ + case 501: /* Not Implemented */ + new_errno = EINVAL; + break; + case 507: /* Insufficient Storage */ + new_errno = ENOSPC; + break; + case 206: /* Partial Content */ + case 300: /* Multiple Choices */ + case 302: /* Found */ + case 305: /* Use Proxy */ + case 306: /* (Unused) */ + case 307: /* Temporary Redirect */ + case 406: /* Not Acceptable */ + case 416: /* Requested Range Not Satisfiable */ + case 417: /* Expectation Failed */ + case 422: /* Unprocessable Entity */ + case 500: /* Internal Server Error */ + case 502: /* Bad Gateway */ + case 505: /* HTTP Version Not Supported */ + new_errno = EIO; + break; + case 503: /* Service Unavailable */ + new_errno = ERRNO_SERVICE_UNAVAILABLE; + // FIXME Distinguish between service unavailable and storage unavilable + break; + case 413: /* Request Entity too Large */ + new_errno = EFBIG; + break; + default: + new_errno = EIO; + } + return new_errno; +} + + + +DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(AccountPtr account, const QString &path, QObject *parent) + : QObject(parent), _subPath(path), _account(account), _ignoredFirst(false) +{ +} + +void DiscoverySingleDirectoryJob::start() +{ + // Start the actual HTTP job + LsColJob *lsColJob = new LsColJob(_account, _subPath, this); + QObject::connect(lsColJob, SIGNAL(directoryListingIterated(QString,QMap)), + this, SLOT(directoryListingIteratedSlot(QString,QMap))); + QObject::connect(lsColJob, SIGNAL(finishedWithError(QNetworkReply*)), this, SLOT(lsJobFinishedWithErrorSlot(QNetworkReply*))); + QObject::connect(lsColJob, SIGNAL(finishedWithoutError()), this, SLOT(lsJobFinishedWithoutErrorSlot())); + lsColJob->start(); + + _lsColJob = lsColJob; +} + +void DiscoverySingleDirectoryJob::abort() +{ + if (_lsColJob && _lsColJob->reply()) { + _lsColJob->reply()->abort(); + } +} + +static csync_vio_file_stat_t* propertyMapToFileStat(QMap map) +{ + csync_vio_file_stat_t* file_stat = csync_vio_file_stat_new(); + qDebug() << Q_FUNC_INFO; + + for (auto it = map.constBegin(); it != map.constEnd(); ++it) { + qDebug() << it.key() << it.value(); + QString property = it.key(); + QString value = it.value(); + if (property == "resourcetype") { + if (value.contains("collection")) { + file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; + } else { + file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; + } + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + } else if (property == "getlastmodified") { + file_stat->mtime = oc_httpdate_parse(value.toUtf8()); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; + } else if (property == "getcontentlength") { + file_stat->size = value.toLongLong(); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; + } else if (property == "getetag") { + file_stat->etag = csync_normalize_etag(value.toUtf8()); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ETAG; + } else if (property == "id") { + csync_vio_file_stat_set_file_id(file_stat, value.toUtf8()); + } else if (property == "downloadURL") { + file_stat->directDownloadUrl = strdup(value.toUtf8()); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADURL; + } else if (property == "dDC") { + file_stat->directDownloadCookies = strdup(value.toUtf8()); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADCOOKIES; + } else if (property == "permissions") { + if (value.isEmpty()) { + // special meaning for our code: server returned permissions but are empty + // meaning only reading is allowed for this resource + file_stat->remotePerm[0] = ' '; + // see _csync_detect_update() + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_PERM; + } else if (value.length() < sizeof(file_stat->remotePerm)) { + strncpy(file_stat->remotePerm, value.toUtf8(), sizeof(file_stat->remotePerm)); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_PERM; + } else { + // old server, keep file_stat->remotePerm empty + } + } + } + + return file_stat; +} + +void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file,QMap map) +{ + qDebug() << Q_FUNC_INFO << _subPath << file << map.count() << map.keys() << _account->davPath() << _lsColJob->reply()->request().url().path(); + if (!_ignoredFirst) { + // First result is the directory itself. Maybe should have a better check for that? FIXME + _ignoredFirst = true; + qDebug() << "...ignoring"; + if (map.contains("permissions")) { + emit firstDirectoryPermissions(map.value("permissions")); + } + } else { + // Remove /remote.php/webdav/folder/ from /remote.php/webdav/folder/subfile.txt + file.remove(0, _lsColJob->reply()->request().url().path().length()); + // remove trailing slash + while (file.endsWith('/')) { + file.chop(1); + } + // remove leading slash + while (file.startsWith('/')) { + file = file.remove(0, 1); + } + + + csync_vio_file_stat_t *file_stat = propertyMapToFileStat(map); + file_stat->name = strdup(file.toUtf8()); + qDebug() << "!!!!" << file_stat << file_stat->name << file_stat->file_id << map.count(); + _results.append(file_stat); + } + +} + +void DiscoverySingleDirectoryJob::lsJobFinishedWithoutErrorSlot() +{ + emit finishedWithResult(_results); + deleteLater(); +} + +void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r) +{ + QString contentType = r->header(QNetworkRequest::ContentTypeHeader).toString(); + int httpCode = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QString msg = r->errorString(); + int errnoCode = 0; + qDebug() << Q_FUNC_INFO << r->errorString() << httpCode << r->error(); + if (r->error() != QNetworkReply::NoError) { + errnoCode = EIO; + } else if (httpCode != 207) { + errnoCode = get_errno_from_http_errcode(httpCode); + } else if (!contentType.contains("application/xml; charset=utf-8")) { + msg = QLatin1String("Server error: PROPFIND reply is not XML formatted!"); + errnoCode = ERRNO_WRONG_CONTENT; + } + + emit finishedWithError(errnoCode, msg); + deleteLater(); +} + +void DiscoveryMainThread::setupHooks(CSYNC *ctx, DiscoveryJob *discoveryJob, QString pathPrefix) +{ + qDebug() << Q_FUNC_INFO; + _discoveryJob = discoveryJob; + _pathPrefix = pathPrefix; + + connect(discoveryJob, SIGNAL(doOpendirSignal(QString,DiscoveryDirectoryResult*)), + this, SLOT(doOpendirSlot(QString,DiscoveryDirectoryResult*)), + Qt::QueuedConnection); +} + +// Coming from owncloud_opendir -> DiscoveryJob::vio_opendir_hook -> doOpendirSignal +void DiscoveryMainThread::doOpendirSlot(QString subPath, DiscoveryDirectoryResult *r) +{ + QString fullPath = _pathPrefix; + if (!_pathPrefix.endsWith('/')) { + fullPath += '/'; + } + fullPath += subPath; + // remove trailing slash + while (fullPath.endsWith('/')) { + fullPath.chop(1); + } + qDebug() << Q_FUNC_INFO << _pathPrefix << subPath << fullPath; + + + // Result gets written in there + _currentDiscoveryDirectoryResult = r; + + // Schedule the DiscoverySingleDirectoryJob + _singleDirJob = new DiscoverySingleDirectoryJob(_account, fullPath, this); + QObject::connect(_singleDirJob, SIGNAL(finishedWithResult(QLinkedList)), + this, SLOT(singleDirectoryJobResultSlot(QLinkedList))); + QObject::connect(_singleDirJob, SIGNAL(finishedWithError(int,QString)), + this, SLOT(singleDirectoryJobFinishedWithErrorSlot(int,QString))); + _singleDirJob->start(); +} + + +void DiscoveryMainThread::singleDirectoryJobResultSlot(QLinkedList result) +{ + if (!_currentDiscoveryDirectoryResult) { + return; // possibly aborted + } + qDebug() << Q_FUNC_INFO << "Have" << result.count() << "results."; + + _directoryContents.insert("/", result); + + _currentDiscoveryDirectoryResult->list = result; + _currentDiscoveryDirectoryResult->code = 0; + _currentDiscoveryDirectoryResult->iterator = _currentDiscoveryDirectoryResult->list.begin(); + _currentDiscoveryDirectoryResult = 0; // the sync thread owns it now + + _discoveryJob->_vioMutex.lock(); + _discoveryJob->_vioWaitCondition.wakeAll(); + _discoveryJob->_vioMutex.unlock(); +} + +void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg) +{ + if (!_currentDiscoveryDirectoryResult) { + return; // possibly aborted + } + qDebug() << Q_FUNC_INFO; + + _currentDiscoveryDirectoryResult->code = csyncErrnoCode; + _currentDiscoveryDirectoryResult->msg = msg; + _currentDiscoveryDirectoryResult = 0; // the sync thread owns it now + + _discoveryJob->_vioMutex.lock(); + _discoveryJob->_vioWaitCondition.wakeAll(); + _discoveryJob->_vioMutex.unlock(); +} + +void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(QString p) +{ + // Should be thread safe since the sync thread is blocked + if (!_discoveryJob->_csync_ctx->remote.root_perms) { + qDebug() << "Permissions for root dir:" << p; + _discoveryJob->_csync_ctx->remote.root_perms = strdup(p.toUtf8()); + } +} + +// called from SyncEngine +void DiscoveryMainThread::abort() { + if (_currentDiscoveryDirectoryResult) { + if (_discoveryJob->_vioMutex.tryLock()) { + _currentDiscoveryDirectoryResult->code = EIO; // FIXME aborted + _currentDiscoveryDirectoryResult = 0; + _discoveryJob->_vioWaitCondition.wakeAll(); + _discoveryJob->_vioMutex.unlock(); + } + } + if (_singleDirJob) { + _singleDirJob->disconnect(SIGNAL(finishedWithError(int,QString)), this); + _singleDirJob->disconnect(SIGNAL(firstDirectoryPermissions(QString)), this); + _singleDirJob->disconnect(SIGNAL(finishedWithResult(QLinkedList)), this); + _singleDirJob->abort(); + } + +} + +csync_vio_handle_t* DiscoveryJob::remote_vio_opendir_hook (const char *url, + void *userdata) +{ + DiscoveryJob *discoveryJob = static_cast(userdata); + if (discoveryJob) { + qDebug() << Q_FUNC_INFO << discoveryJob << url << "Calling..."; + + DiscoveryDirectoryResult *directoryResult = new DiscoveryDirectoryResult(); + directoryResult->code = EIO; + + discoveryJob->_vioMutex.lock(); + QString qurl = QString::fromUtf8(url); + emit discoveryJob->doOpendirSignal(qurl, directoryResult); + discoveryJob->_vioWaitCondition.wait(&discoveryJob->_vioMutex, ULONG_MAX); // FIXME timeout? + discoveryJob->_vioMutex.unlock(); + + qDebug() << Q_FUNC_INFO << discoveryJob << url << "Returned"; + + // Upon awakening from the _vioWaitCondition, iterator should be a valid iterator. + if (directoryResult->code != 0) { + qDebug() << Q_FUNC_INFO << directoryResult->code << "when opening" << url; + errno = directoryResult->code; + return NULL; + } + + return (csync_vio_handle_t*) directoryResult; + } + return NULL; +} + + +csync_vio_file_stat_t* DiscoveryJob::remote_vio_readdir_hook (csync_vio_handle_t *dhandle, + void *userdata) +{ + DiscoveryJob *discoveryJob = static_cast(userdata); + if (discoveryJob) { + qDebug() << Q_FUNC_INFO << discoveryJob; + + DiscoveryDirectoryResult *directoryResult = static_cast(dhandle); + + if (directoryResult->iterator != directoryResult->list.end()) { + csync_vio_file_stat_t *file_stat = *(directoryResult->iterator); + + qDebug() << Q_FUNC_INFO << file_stat; + qDebug() << Q_FUNC_INFO << file_stat->file_id; + + qDebug() << Q_FUNC_INFO << file_stat->name; + directoryResult->iterator++; + + // Make a copy, csync_update will delete + return csync_vio_file_stat_copy(file_stat); + } + } + return NULL; +} + +void DiscoveryJob::remote_vio_closedir_hook (csync_vio_handle_t *dhandle, void *userdata) +{ + DiscoveryJob *discoveryJob = static_cast(userdata); + if (discoveryJob) { + qDebug() << Q_FUNC_INFO << discoveryJob; + DiscoveryDirectoryResult *directoryResult = static_cast (dhandle); + delete directoryResult; // just deletes the struct and the iterator, the data itself is owned by the SyncEngine/DiscoveryMainThread + } +} + void DiscoveryJob::start() { _selectiveSyncBlackList.sort(); _csync_ctx->checkSelectiveSyncBlackListHook = isInSelectiveSyncBlackListCallBack; @@ -85,6 +467,10 @@ void DiscoveryJob::start() { _csync_ctx->callbacks.update_callback = update_job_update_callback; _csync_ctx->callbacks.update_callback_userdata = this; + _csync_ctx->callbacks.remote_opendir_hook = remote_vio_opendir_hook; + _csync_ctx->callbacks.remote_readdir_hook = remote_vio_readdir_hook; + _csync_ctx->callbacks.remote_closedir_hook = remote_vio_closedir_hook; + _csync_ctx->callbacks.vio_userdata = this; csync_set_log_callback(_log_callback); csync_set_log_level(_log_level); diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 78c13bdd8..226a1065b 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -18,18 +18,97 @@ #include #include #include - +#include +#include "networkjobs.h" +#include +#include +#include namespace OCC { +class Account; + /** * The Discovery Phase was once called "update" phase in csync therms. * Its goal is to look at the files in one of the remote and check comared to the db * if the files are new, or changed. */ +typedef struct { + QString msg; + int code; + QLinkedList::iterator iterator; + QLinkedList list; +} DiscoveryDirectoryResult; + +// Run in the main thread, reporting to the DiscoveryJobMainThread object +class DiscoverySingleDirectoryJob : public QObject { + Q_OBJECT +public: + explicit DiscoverySingleDirectoryJob(AccountPtr account, const QString &path, QObject *parent = 0); + void start(); + void abort(); + // This is not actually a network job, it is just a job +signals: + void firstDirectoryPermissions(QString); + void finishedWithResult(QLinkedList); + void finishedWithError(int csyncErrnoCode, QString msg); +private slots: + void directoryListingIteratedSlot(QString,QMap); + void lsJobFinishedWithoutErrorSlot(); + void lsJobFinishedWithErrorSlot(QNetworkReply*); +private: + QLinkedList _results; + QString _subPath; + AccountPtr _account; + bool _ignoredFirst; + QPointer _lsColJob; +}; + +// Lives in main thread. Deleted by the SyncEngine +class DiscoveryJob; +class DiscoveryMainThread : public QObject { + Q_OBJECT + + // For non-recursive and recursive + // If it is not in this map it needs to be requested + QMap > _directoryContents; + + DiscoveryDirectoryResult *_currentDiscoveryDirectoryResult; + + QPointer _discoveryJob; + QPointer _singleDirJob; + QString _pathPrefix; + AccountPtr _account; + +public: + DiscoveryMainThread(AccountPtr account) : QObject(), _account(account), _currentDiscoveryDirectoryResult(0) { + + } + ~DiscoveryMainThread() { + // FIXME We need to do the deletion of each item inside the map and list _directoryContents + } + void abort(); + + +public slots: + // From DiscoveryJob: + void doOpendirSlot(QString url, DiscoveryDirectoryResult* ); + + // From Job: + void singleDirectoryJobResultSlot(QLinkedList); + void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg); + void singleDirectoryJobFirstDirectoryPermissionsSlot(QString); +public: + void setupHooks(CSYNC *ctx, DiscoveryJob *discoveryJob, QString pathPrefix); +}; + + +// Lives in the other thread +// Deletes itself in start() class DiscoveryJob : public QObject { Q_OBJECT + friend class DiscoveryMainThread; CSYNC *_csync_ctx; csync_log_callback _log_callback; int _log_level; @@ -43,9 +122,22 @@ class DiscoveryJob : public QObject { bool isInSelectiveSyncBlackList(const QString &path) const; static int isInSelectiveSyncBlackListCallBack(void *, const char *); + // Just for progress static void update_job_update_callback (bool local, const char *dirname, void *userdata); + + // For using QNAM to get the directory listings + static csync_vio_handle_t* remote_vio_opendir_hook (const char *url, + void *userdata); + static csync_vio_file_stat_t* remote_vio_readdir_hook (csync_vio_handle_t *dhandle, + void *userdata); + static void remote_vio_closedir_hook (csync_vio_handle_t *dhandle, + void *userdata); + QMutex _vioMutex; + QWaitCondition _vioWaitCondition; + + public: explicit DiscoveryJob(CSYNC *ctx, QObject* parent = 0) : QObject(parent), _csync_ctx(ctx) { @@ -61,6 +153,9 @@ public: signals: void finished(int result); void folderDiscovered(bool local, QString folderUrl); + + // After the discovery job has been woken up again (_vioWaitCondition) + void doOpendirSignal(QString url, DiscoveryDirectoryResult*); }; } diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 39b5510be..ce174d614 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -33,8 +33,7 @@ extern "C" int c_utimes(const char *, const struct timeval *); extern "C" void csync_win32_set_file_hidden( const char *file, bool h ); extern "C" { -#include "vio/csync_vio_handle.h" -#include "vio/csync_vio_file_stat.h" +#include "csync.h" #include "vio/csync_vio_local.h" } diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp index 91578076b..3123e582b 100644 --- a/src/libsync/networkjobs.cpp +++ b/src/libsync/networkjobs.cpp @@ -328,11 +328,20 @@ void LsColJob::start() { QNetworkRequest req; req.setRawHeader("Depth", "1"); + // FIXME The results are delivered without namespace, if this is ever a problem we need to check it.. QByteArray xml("\n" - "\n" + "\n" " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "\n"); QBuffer *buf = new QBuffer(this); @@ -345,42 +354,128 @@ void LsColJob::start() AbstractNetworkJob::start(); } +// supposed to read when pointing to .. +static QString readContentsAsString(QXmlStreamReader &reader) { + QString result; + int level = 0; + do { + QXmlStreamReader::TokenType type = reader.readNext(); + if (type == QXmlStreamReader::StartElement) { + level++; + result += "<" + reader.name().toString() + ">"; + } else if (type == QXmlStreamReader::Characters) { + result += reader.text(); + } else if (type == QXmlStreamReader::EndElement) { + level--; + if (level < 0) { + break; + } + result += ""; + } + + } while (!reader.atEnd()); + return result; +} + bool LsColJob::finished() { - if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) { + QString contentType = reply()->header(QNetworkRequest::ContentTypeHeader).toString(); + int httpCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (httpCode == 207 && contentType.contains("application/xml; charset=utf-8")) { // Parse DAV response - QXmlStreamReader reader(reply()); + QByteArray xml = reply()->readAll(); + qDebug() << xml; + QXmlStreamReader reader(xml); reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:")); QStringList folders; - QString currentItem; + QString currentHref; + QMap currentTmpProperties; + QMap currentHttp200Properties; + bool currentPropsHaveHttp200 = false; + bool insidePropstat = false; + bool insideProp = false; while (!reader.atEnd()) { QXmlStreamReader::TokenType type = reader.readNext(); - if (type == QXmlStreamReader::StartElement && - reader.namespaceUri() == QLatin1String("DAV:")) { - QString name = reader.name().toString(); + QString name = reader.name().toString(); + // Start elements with DAV: + if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) { + qDebug() << Q_FUNC_INFO << "StartElement" << name; if (name == QLatin1String("href")) { - currentItem = reader.readElementText(); - } else if (name == QLatin1String("collection") && - !currentItem.isEmpty()) { - folders.append(QUrl::fromEncoded(currentItem.toLatin1()).path()); - } else if (name == QLatin1String("quota-used-bytes") && - !currentItem.isEmpty()) { - bool ok = false; - auto s = reader.readElementText().toLongLong(&ok); - if (ok) { - _sizes[currentItem] = s; + currentHref = QUrl::fromPercentEncoding(reader.readElementText().toUtf8()); + } else if (name == QLatin1String("response")) { + } else if (name == QLatin1String("propstat")) { + qDebug() << "inside propstat!"; + insidePropstat = true; + } else if (name == QLatin1String("status") && insidePropstat) { + QString httpStatus = reader.readElementText(); + if (httpStatus.startsWith("HTTP/1.1 200")) { + currentPropsHaveHttp200 = true; + qDebug() << Q_FUNC_INFO << "currentPropsHaveHttp200!"; + } else { + currentPropsHaveHttp200 = false; + } + qDebug() << Q_FUNC_INFO << "properties have " << httpStatus; + } else if (name == QLatin1String("prop")) { + qDebug() << "inside prop!"; + insideProp = true; + continue; + } + } + + if (type == QXmlStreamReader::StartElement && insidePropstat && insideProp) { + // All those elements are properties + //QString propertyContent = reader.readElementText(QXmlStreamReader::IncludeChildElements); + QString propertyContent = readContentsAsString(reader); + qDebug() << "PROP" << reader.namespaceUri() << reader.name() << propertyContent; + if (name == QLatin1String("resourcetype") && propertyContent.contains("collection")) { + qDebug() << "IS COLLECTION!"; + folders.append(QUrl::fromEncoded(currentHref.toLatin1()).path()); + } else if (name == QLatin1String("quota-used-bytes")) { + bool ok = false; + auto s = propertyContent.toLongLong(&ok); + if (ok) { + _sizes[currentHref] = s; + } + } + currentTmpProperties.insert(reader.name().toString(), propertyContent); + } + + // End elements with DAV: + if (type == QXmlStreamReader::EndElement) { + qDebug() << Q_FUNC_INFO << "EndElement" << name; + if (reader.namespaceUri() == QLatin1String("DAV:")) { + if (reader.name() == "response") { + qDebug() << Q_FUNC_INFO << "EMITTING" << currentHref; + if (currentHref.endsWith('/')) { + currentHref.chop(1); + } + emit directoryListingIterated(currentHref, currentHttp200Properties); + currentHref.clear(); + currentHttp200Properties.clear(); + } else if (reader.name() == "propstat") { + qDebug() <<"END PROPSTAT" << currentTmpProperties.keys(); + insidePropstat = false; + if (currentPropsHaveHttp200) { + currentHttp200Properties = QMap(currentTmpProperties); + } + currentTmpProperties.clear(); + currentPropsHaveHttp200 = false; + } else if (reader.name() == "prop") { + insideProp = false; } - } - } else if (type == QXmlStreamReader::EndElement && - reader.namespaceUri() == QLatin1String("DAV:") && - reader.name() == QLatin1String("response")) { - currentItem.clear(); } } - emit directoryListing(folders); + emit directoryListingSubfolders(folders); + emit finishedWithoutError(); + } else if (httpCode == 207) { + // wrong content type + emit finishedWithError(reply()); + } else { + // wrong HTTP code + emit finishedWithError(reply()); } return true; } diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h index 9fe50d3cf..496a51bd2 100644 --- a/src/libsync/networkjobs.h +++ b/src/libsync/networkjobs.h @@ -142,7 +142,10 @@ public: QHash _sizes; signals: - void directoryListing(const QStringList &items); + void directoryListingSubfolders(const QStringList &items); + void directoryListingIterated(const QString name, QMap properties); + void finishedWithError(QNetworkReply *reply); + void finishedWithoutError(); private slots: virtual bool finished() Q_DECL_OVERRIDE; diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 2bc72de3e..283e5c737 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -155,6 +155,9 @@ QString SyncEngine::csyncErrorToString(CSYNC_STATUS err) case CSYNC_STATUS_SERVICE_UNAVAILABLE: errStr = tr("The mounted directory is temporarily not available on the server"); break; + case CSYNC_STATUS_OPENDIR_ERROR: + errStr = tr("An error opening a directory happened"); + break; default: errStr = tr("An internal error number %1 happened.").arg( (int) err ); } @@ -596,18 +599,29 @@ void SyncEngine::startSync() int timeout = OwncloudPropagator::httpTimeout(); csync_set_module_property(_csync_ctx, "timeout", &timeout); - _stopWatch.start(); qDebug() << "#### Discovery start #################################################### >>"; - DiscoveryJob *job = new DiscoveryJob(_csync_ctx); - job->_selectiveSyncBlackList = _selectiveSyncBlackList; - job->moveToThread(&_thread); - connect(job, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int))); - connect(job, SIGNAL(folderDiscovered(bool,QString)), + _discoveryMainThread = new DiscoveryMainThread(account()); + _discoveryMainThread->setParent(this); + connect(this, SIGNAL(finished()), _discoveryMainThread, SLOT(deleteLater())); + + + DiscoveryJob *discoveryJob = new DiscoveryJob(_csync_ctx); + discoveryJob->_selectiveSyncBlackList = _selectiveSyncBlackList; + discoveryJob->moveToThread(&_thread); + connect(discoveryJob, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int))); + connect(discoveryJob, SIGNAL(folderDiscovered(bool,QString)), this, SIGNAL(folderDiscovered(bool,QString))); - QMetaObject::invokeMethod(job, "start", Qt::QueuedConnection); + + // This is used for the DiscoveryJob to be able to request the main thread/ + // to read in directory contents. + qDebug() << Q_FUNC_INFO << _remotePath << _remoteUrl; + _discoveryMainThread->setupHooks(_csync_ctx, discoveryJob, _remotePath); + + // Starts the update in a seperate thread + QMetaObject::invokeMethod(discoveryJob, "start", Qt::QueuedConnection); } void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) @@ -691,6 +705,8 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) } } + // FIXME: The propagator could create his session in propagator_legacy.cpp + // There's no reason to keep csync_owncloud.c around ne_session_s *session = 0; // that call to set property actually is a get which will return the session csync_set_module_property(_csync_ctx, "get_dav_session", &session); @@ -1132,9 +1148,16 @@ AccountPtr SyncEngine::account() const void SyncEngine::abort() { + // Aborts the discovery phase job + if (_discoveryMainThread) { + _discoveryMainThread->abort(); + } + // Sets a flag for the update phase csync_request_abort(_csync_ctx); - if(_propagator) + // For the propagator + if(_propagator) { _propagator->abort(); + } } } // namespace OCC diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 07d9400bf..e9db0c4dc 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -36,6 +36,7 @@ #include "utility.h" #include "syncfilestatus.h" #include "accountfwd.h" +#include "discoveryphase.h" class QProcess; @@ -150,6 +151,7 @@ private: QString _remoteUrl; QString _remotePath; SyncJournalDb *_journal; + QPointer _discoveryMainThread; QSharedPointer _propagator; QString _lastDeleted; // if the last item was a path and it has been deleted QSet _seenFiles;