SyncEngine: Use QNAM for csync_update

This is for #2507
This commit is contained in:
Markus Goetz 2014-12-02 12:25:51 +01:00
Родитель bd80c034ba
Коммит 906779c4b1
29 изменённых файлов: 806 добавлений и 1176 удалений

Просмотреть файл

@ -62,7 +62,6 @@ set(csync_SRCS
vio/csync_vio_local.c vio/csync_vio_local.c
csync_owncloud.c csync_owncloud.c
csync_owncloud_recursive_propfind.c
csync_owncloud_util.c csync_owncloud_util.c
) )
@ -72,8 +71,6 @@ set(csync_HDRS
${CMAKE_CURRENT_BINARY_DIR}/csync_version.h ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h
csync.h csync.h
vio/csync_vio.h vio/csync_vio.h
vio/csync_vio_file_stat.h
vio/csync_vio_handle.h
vio/csync_vio_method.h vio/csync_vio_method.h
vio/csync_vio_module.h vio/csync_vio_module.h
) )

Просмотреть файл

@ -222,6 +222,12 @@ int csync_update(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "No exclude file loaded or defined!"); 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 */ /* update detection for local replica */
csync_gettime(&start); csync_gettime(&start);
ctx->current = LOCAL_REPLICA; ctx->current = LOCAL_REPLICA;

Просмотреть файл

@ -32,6 +32,8 @@
#ifndef _CSYNC_H #ifndef _CSYNC_H
#define _CSYNC_H #define _CSYNC_H
#include "std/c_private.h"
#include <sys/stat.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
@ -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. * CSync File Traversal structure.
* *
@ -191,6 +281,16 @@ typedef void (*csync_update_callback) (bool local,
const char *dirUrl, const char *dirUrl,
void *userdata); 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. * @brief Allocate a csync context.
* *
@ -229,15 +329,6 @@ int csync_update(CSYNC *ctx);
*/ */
int csync_reconcile(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 * @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); 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 #ifdef __cplusplus
} }
#endif #endif

Просмотреть файл

@ -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); CSYNC_STATUS csync_errno_to_status(int error, CSYNC_STATUS default_status);
char *csync_normalize_etag(const char *);
#endif /* _CSYNC_MISC_H */ #endif /* _CSYNC_MISC_H */

Просмотреть файл

@ -213,8 +213,8 @@ static int configureProxy( csync_owncloud_ctx_t *ctx, ne_session *session )
re = 2; re = 2;
} else { } else {
DEBUG_WEBDAV("%s requested but no proxy host defined.", ctx->dav_session.proxy_type ); 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 /* we used to try ne_system_session_proxy here, but we should rather err out
to behave exactly like the caller. */ to behave exactly like the caller. */
} }
} else { } else {
DEBUG_WEBDAV( "Unsupported Proxy: %s", ctx->dav_session.proxy_type ); 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 * This function sets the flag _connected if the connection is established
* and returns if the flag is set, so calling it frequently is save. * 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 useSSL = 0;
int rc; int rc;
char protocol[6] = {'\0'}; char protocol[6] = {'\0'};
@ -384,6 +384,7 @@ static int dav_connect(csync_owncloud_ctx_t *ctx, const char *base_url) {
char *host = NULL; char *host = NULL;
unsigned int port = 0; unsigned int port = 0;
int proxystate = -1; int proxystate = -1;
csync_owncloud_ctx_t *ctx = csyncCtx->owncloud_context;
if (ctx->_connected) { if (ctx->_connected) {
return 0; return 0;
@ -477,276 +478,6 @@ out:
return rc; 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: "<empty>");
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 : "<nil>");
}
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) char *owncloud_error_string(CSYNC* ctx)
{ {
return ctx->owncloud_context->dav_session.error_string; return ctx->owncloud_context->dav_session.error_string;
@ -757,19 +488,12 @@ int owncloud_commit(CSYNC* ctx) {
return 0; 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 ) { if( ctx->owncloud_context->dav_session.ctx ) {
ne_forget_auth(ctx->owncloud_context->dav_session.ctx); ne_forget_auth(ctx->owncloud_context->dav_session.ctx);
ne_session_destroy(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->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" ); */ /* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */
ctx->owncloud_context->dav_session.ctx = 0; 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; *(ne_session**)data = ctx->owncloud_context->dav_session.ctx;
return 0; 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( c_streq(key, "redirect_callback")) {
if (data) { if (data) {
csync_owncloud_redirect_callback_t* cb_wrapper = 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 = c_malloc( sizeof( struct csync_owncloud_ctx_s ));
ctx->owncloud_context->csync_ctx = ctx; // back reference 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: */ /* vim: set ts=4 sw=4 et cindent: */

Просмотреть файл

@ -22,17 +22,15 @@
#define CSYNC_OWNCLOUD_H #define CSYNC_OWNCLOUD_H
#include "csync.h" #include "csync.h"
#include "vio/csync_vio_file_stat.h"
#include "vio/csync_vio.h" #include "vio/csync_vio.h"
// Public API used by csync // 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); int owncloud_commit(CSYNC* ctx);
void owncloud_destroy(CSYNC* ctx); void owncloud_destroy(CSYNC* ctx);
char *owncloud_error_string(CSYNC* ctx); char *owncloud_error_string(CSYNC* ctx);
void owncloud_init(CSYNC* ctx);
int owncloud_set_property(CSYNC* ctx, const char *key, void *data); 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 */ #endif /* CSYNC_OWNCLOUD_H */

Просмотреть файл

@ -57,7 +57,6 @@
#include "c_private.h" #include "c_private.h"
#include "httpbf.h" #include "httpbf.h"
#include "vio/csync_vio_file_stat.h"
#include "vio/csync_vio.h" #include "vio/csync_vio.h"
#include "csync_log.h" #include "csync_log.h"
@ -87,22 +86,12 @@ struct dav_session_s {
int read_timeout; int read_timeout;
bool no_recursive_propfind;
csync_owncloud_redirect_callback_t redir_callback; csync_owncloud_redirect_callback_t redir_callback;
}; };
struct csync_owncloud_ctx_s { struct csync_owncloud_ctx_s {
CSYNC *csync_ctx; 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 // For the WebDAV connection
struct dav_session_s dav_session; /* The DAV Session, initialised in dav_connect */ struct dav_session_s dav_session; /* The DAV Session, initialised in dav_connect */
int _connected; /* flag to indicate if a connection exists, ie. 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 struct csync_owncloud_ctx_s csync_owncloud_ctx_t;
//typedef csync_owncloud_ctx_t* csync_owncloud_ctx_p; //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_errno_from_http_errcode( int err );
void set_error_message( csync_owncloud_ctx_t *ctx, const char *msg ); 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 ); 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 ); 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); const char* csync_owncloud_get_platform(void);
char *_cleanPath( const char* uri );
#endif // CSYNC_OWNCLOUD_PRIVATE_H #endif // CSYNC_OWNCLOUD_PRIVATE_H

Просмотреть файл

@ -1,285 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* 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: "<empty>");
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 : "<nil>");
}
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);
}
}

Просмотреть файл

@ -24,13 +24,6 @@
#include "csync_misc.h" #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 ) { void set_errno_from_http_errcode( int err ) {
int new_errno = 0; int new_errno = 0;
@ -106,91 +99,6 @@ void set_errno_from_http_errcode( int err ) {
errno = new_errno; 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: "<zero>" );
re = NULL;
} else {
if(path) {
re = ne_path_escape( path );
}
}
SAFE_FREE( path );
return re;
}
#ifndef HAVE_TIMEGM #ifndef HAVE_TIMEGM
#ifdef _WIN32 #ifdef _WIN32
@ -277,186 +185,6 @@ time_t oc_httpdate_parse( const char *date ) {
return result; 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, "<DAV:collection>", 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/ // as per http://sourceforge.net/p/predef/wiki/OperatingSystems/
// extend as required // extend as required
const char* csync_owncloud_get_platform() { const char* csync_owncloud_get_platform() {

Просмотреть файл

@ -41,7 +41,6 @@
#include "std/c_private.h" #include "std/c_private.h"
#include "csync.h" #include "csync.h"
#include "csync_misc.h" #include "csync_misc.h"
#include "vio/csync_vio_file_stat.h"
#ifdef WITH_ICONV #ifdef WITH_ICONV
#include <iconv.h> #include <iconv.h>
@ -89,6 +88,11 @@ struct csync_s {
void *userdata; void *userdata;
csync_update_callback update_callback; csync_update_callback update_callback;
void *update_callback_userdata; 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; } callbacks;
c_strlist_t *excludes; c_strlist_t *excludes;
@ -205,6 +209,10 @@ struct _csync_treewalk_context_s
typedef struct _csync_treewalk_context_s _csync_treewalk_context; 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 );
/** /**
* }@ * }@
*/ */

Просмотреть файл

@ -371,6 +371,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
st->instruction = CSYNC_INSTRUCTION_NEW; st->instruction = CSYNC_INSTRUCTION_NEW;
goto out; 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; st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) { if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) {
csync_rename_record(ctx, tmp->path, path); 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; 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; int asp = 0;
/* permission denied */ /* permission denied */
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR); ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR);

Просмотреть файл

@ -23,7 +23,6 @@
#define _CSYNC_UPDATE_H #define _CSYNC_UPDATE_H
#include "csync.h" #include "csync.h"
#include "vio/csync_vio_file_stat.h"
/** /**
* @file csync_update.h * @file csync_update.h

Просмотреть файл

@ -29,7 +29,6 @@
#include "csync_private.h" #include "csync_private.h"
#include "csync_util.h" #include "csync_util.h"
#include "vio/csync_vio.h" #include "vio/csync_vio.h"
#include "vio/csync_vio_handle_private.h"
#include "vio/csync_vio_local.h" #include "vio/csync_vio_local.h"
#include "csync_statedb.h" #include "csync_statedb.h"
#include "std/c_jhash.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) { if(ctx->remote.read_from_db) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Read from db flag is true, should not!" ); 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; break;
case LOCAL_REPLICA: case LOCAL_REPLICA:
if( ctx->callbacks.update_callback ) { 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 ) { if( ctx->remote.read_from_db ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote ReadFromDb is true, should not!"); 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; break;
case LOCAL_REPLICA: case LOCAL_REPLICA:
rc = csync_vio_local_closedir(dhandle); 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 ) { if( ctx->remote.read_from_db ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote readfromdb is true, should not!"); 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; break;
case LOCAL_REPLICA: case LOCAL_REPLICA:
return csync_vio_local_readdir(dhandle); return csync_vio_local_readdir(dhandle);

Просмотреть файл

@ -25,8 +25,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include "c_private.h" #include "c_private.h"
#include "vio/csync_vio_handle.h" #include "csync.h"
#include "vio/csync_vio_file_stat.h"
#include "csync_private.h" #include "csync_private.h"
typedef struct fhandle_s { typedef struct fhandle_s {

Просмотреть файл

@ -19,7 +19,7 @@
*/ */
#include "c_lib.h" #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 *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)); 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; 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) { void csync_vio_file_stat_destroy(csync_vio_file_stat_t *file_stat) {
/* FIXME: free rest */ /* FIXME: free rest */
if (file_stat == NULL) { if (file_stat == NULL) {

Просмотреть файл

@ -1,121 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#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 */

Просмотреть файл

@ -1,26 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* 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 */

Просмотреть файл

@ -1,26 +0,0 @@
/*
* cynapses libc functions
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* 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 */

Просмотреть файл

@ -36,9 +36,7 @@
#include "csync_log.h" #include "csync_log.h"
#include "csync_vio.h" #include "csync_vio.h"
#include "vio/csync_vio_handle.h"
#include "vio/csync_vio_local.h" #include "vio/csync_vio_local.h"
#include "vio/csync_vio_handle_private.h"
/* /*

Просмотреть файл

@ -19,7 +19,7 @@
*/ */
#include "torture.h" #include "torture.h"
#include "vio/csync_vio_file_stat.h" #include "csync.h"
static void check_csync_vio_file_stat_new(void **state) static void check_csync_vio_file_stat_new(void **state)
{ {

Просмотреть файл

@ -356,7 +356,7 @@ void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list)
void FolderWizardRemotePath::slotRefreshFolders() void FolderWizardRemotePath::slotRefreshFolders()
{ {
LsColJob *job = new LsColJob(AccountManager::instance()->account(), "/", this); LsColJob *job = new LsColJob(AccountManager::instance()->account(), "/", this);
connect(job, SIGNAL(directoryListing(QStringList)), connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
SLOT(slotUpdateDirectories(QStringList))); SLOT(slotUpdateDirectories(QStringList)));
job->start(); job->start();
_ui.folderTreeWidget->clear(); _ui.folderTreeWidget->clear();
@ -366,7 +366,7 @@ void FolderWizardRemotePath::slotItemExpanded(QTreeWidgetItem *item)
{ {
QString dir = item->data(0, Qt::UserRole).toString(); QString dir = item->data(0, Qt::UserRole).toString();
LsColJob *job = new LsColJob(AccountManager::instance()->account(), dir, this); LsColJob *job = new LsColJob(AccountManager::instance()->account(), dir, this);
connect(job, SIGNAL(directoryListing(QStringList)), connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
SLOT(slotUpdateDirectories(QStringList))); SLOT(slotUpdateDirectories(QStringList)));
job->start(); job->start();
} }

Просмотреть файл

@ -51,7 +51,7 @@ SelectiveSyncTreeView::SelectiveSyncTreeView(AccountPtr account, QWidget* parent
void SelectiveSyncTreeView::refreshFolders() void SelectiveSyncTreeView::refreshFolders()
{ {
LsColJob *job = new LsColJob(_account, _folderPath, this); LsColJob *job = new LsColJob(_account, _folderPath, this);
connect(job, SIGNAL(directoryListing(QStringList)), connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
this, SLOT(slotUpdateDirectories(QStringList))); this, SLOT(slotUpdateDirectories(QStringList)));
job->start(); job->start();
clear(); clear();
@ -167,7 +167,7 @@ void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
prefix = _folderPath + QLatin1Char('/'); prefix = _folderPath + QLatin1Char('/');
} }
LsColJob *job = new LsColJob(_account, prefix + dir, this); LsColJob *job = new LsColJob(_account, prefix + dir, this);
connect(job, SIGNAL(directoryListing(QStringList)), connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
SLOT(slotUpdateDirectories(QStringList))); SLOT(slotUpdateDirectories(QStringList)));
job->start(); job->start();
} }

Просмотреть файл

@ -17,6 +17,8 @@
#include <qdebug.h> #include <qdebug.h>
#include <QUrl> #include <QUrl>
#include "account.h"
#include <QFileInfo>
namespace OCC { 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<QString,QString>)),
this, SLOT(directoryListingIteratedSlot(QString,QMap<QString,QString>)));
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<QString,QString> 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<QString,QString> 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<csync_vio_file_stat_t *>)),
this, SLOT(singleDirectoryJobResultSlot(QLinkedList<csync_vio_file_stat_t*>)));
QObject::connect(_singleDirJob, SIGNAL(finishedWithError(int,QString)),
this, SLOT(singleDirectoryJobFinishedWithErrorSlot(int,QString)));
_singleDirJob->start();
}
void DiscoveryMainThread::singleDirectoryJobResultSlot(QLinkedList<csync_vio_file_stat_t *> 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<csync_vio_file_stat_t*>)), this);
_singleDirJob->abort();
}
}
csync_vio_handle_t* DiscoveryJob::remote_vio_opendir_hook (const char *url,
void *userdata)
{
DiscoveryJob *discoveryJob = static_cast<DiscoveryJob*>(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<DiscoveryJob*>(userdata);
if (discoveryJob) {
qDebug() << Q_FUNC_INFO << discoveryJob;
DiscoveryDirectoryResult *directoryResult = static_cast<DiscoveryDirectoryResult*>(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<DiscoveryJob*>(userdata);
if (discoveryJob) {
qDebug() << Q_FUNC_INFO << discoveryJob;
DiscoveryDirectoryResult *directoryResult = static_cast<DiscoveryDirectoryResult*> (dhandle);
delete directoryResult; // just deletes the struct and the iterator, the data itself is owned by the SyncEngine/DiscoveryMainThread
}
}
void DiscoveryJob::start() { void DiscoveryJob::start() {
_selectiveSyncBlackList.sort(); _selectiveSyncBlackList.sort();
_csync_ctx->checkSelectiveSyncBlackListHook = isInSelectiveSyncBlackListCallBack; _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 = update_job_update_callback;
_csync_ctx->callbacks.update_callback_userdata = this; _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_callback(_log_callback);
csync_set_log_level(_log_level); csync_set_log_level(_log_level);

Просмотреть файл

@ -18,18 +18,97 @@
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QStringList> #include <QStringList>
#include <csync.h> #include <csync.h>
#include <QMap>
#include "networkjobs.h"
#include <QMutex>
#include <QWaitCondition>
#include <QLinkedList>
namespace OCC { namespace OCC {
class Account;
/** /**
* The Discovery Phase was once called "update" phase in csync therms. * 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 * 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. * if the files are new, or changed.
*/ */
typedef struct {
QString msg;
int code;
QLinkedList<csync_vio_file_stat_t*>::iterator iterator;
QLinkedList<csync_vio_file_stat_t *> 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<csync_vio_file_stat_t*>);
void finishedWithError(int csyncErrnoCode, QString msg);
private slots:
void directoryListingIteratedSlot(QString,QMap<QString,QString>);
void lsJobFinishedWithoutErrorSlot();
void lsJobFinishedWithErrorSlot(QNetworkReply*);
private:
QLinkedList<csync_vio_file_stat_t*> _results;
QString _subPath;
AccountPtr _account;
bool _ignoredFirst;
QPointer<LsColJob> _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<QString, QLinkedList<csync_vio_file_stat_t*> > _directoryContents;
DiscoveryDirectoryResult *_currentDiscoveryDirectoryResult;
QPointer<DiscoveryJob> _discoveryJob;
QPointer<DiscoverySingleDirectoryJob> _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<csync_vio_file_stat_t*>);
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 { class DiscoveryJob : public QObject {
Q_OBJECT Q_OBJECT
friend class DiscoveryMainThread;
CSYNC *_csync_ctx; CSYNC *_csync_ctx;
csync_log_callback _log_callback; csync_log_callback _log_callback;
int _log_level; int _log_level;
@ -43,9 +122,22 @@ class DiscoveryJob : public QObject {
bool isInSelectiveSyncBlackList(const QString &path) const; bool isInSelectiveSyncBlackList(const QString &path) const;
static int isInSelectiveSyncBlackListCallBack(void *, const char *); static int isInSelectiveSyncBlackListCallBack(void *, const char *);
// Just for progress
static void update_job_update_callback (bool local, static void update_job_update_callback (bool local,
const char *dirname, const char *dirname,
void *userdata); 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: public:
explicit DiscoveryJob(CSYNC *ctx, QObject* parent = 0) explicit DiscoveryJob(CSYNC *ctx, QObject* parent = 0)
: QObject(parent), _csync_ctx(ctx) { : QObject(parent), _csync_ctx(ctx) {
@ -61,6 +153,9 @@ public:
signals: signals:
void finished(int result); void finished(int result);
void folderDiscovered(bool local, QString folderUrl); void folderDiscovered(bool local, QString folderUrl);
// After the discovery job has been woken up again (_vioWaitCondition)
void doOpendirSignal(QString url, DiscoveryDirectoryResult*);
}; };
} }

Просмотреть файл

@ -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" void csync_win32_set_file_hidden( const char *file, bool h );
extern "C" { extern "C" {
#include "vio/csync_vio_handle.h" #include "csync.h"
#include "vio/csync_vio_file_stat.h"
#include "vio/csync_vio_local.h" #include "vio/csync_vio_local.h"
} }

Просмотреть файл

@ -328,11 +328,20 @@ void LsColJob::start()
{ {
QNetworkRequest req; QNetworkRequest req;
req.setRawHeader("Depth", "1"); req.setRawHeader("Depth", "1");
// FIXME The results are delivered without namespace, if this is ever a problem we need to check it..
QByteArray xml("<?xml version=\"1.0\" ?>\n" QByteArray xml("<?xml version=\"1.0\" ?>\n"
"<d:propfind xmlns:d=\"DAV:\">\n" "<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\">\n"
" <d:prop>\n" " <d:prop>\n"
" <d:resourcetype/>\n" " <d:resourcetype/>\n"
" <d:quota-used-bytes/>\n" " <d:quota-used-bytes/>\n"
" <d:getlastmodified/>\n"
" <d:getcontentlength/>\n"
" <d:resourcetype/>\n"
" <d:getetag/>\n"
" <oc:id/>\n"
" <oc:downloadURL/>\n"
" <oc:dDC/>\n"
" <oc:permissions/>\n"
" </d:prop>\n" " </d:prop>\n"
"</d:propfind>\n"); "</d:propfind>\n");
QBuffer *buf = new QBuffer(this); QBuffer *buf = new QBuffer(this);
@ -345,42 +354,128 @@ void LsColJob::start()
AbstractNetworkJob::start(); AbstractNetworkJob::start();
} }
// supposed to read <D:collection> when pointing to <D:resourcetype><D:collection></D:resourcetype>..
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 += "</" + reader.name().toString() + ">";
}
} while (!reader.atEnd());
return result;
}
bool LsColJob::finished() 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 // Parse DAV response
QXmlStreamReader reader(reply()); QByteArray xml = reply()->readAll();
qDebug() << xml;
QXmlStreamReader reader(xml);
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:")); reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
QStringList folders; QStringList folders;
QString currentItem; QString currentHref;
QMap<QString, QString> currentTmpProperties;
QMap<QString, QString> currentHttp200Properties;
bool currentPropsHaveHttp200 = false;
bool insidePropstat = false;
bool insideProp = false;
while (!reader.atEnd()) { while (!reader.atEnd()) {
QXmlStreamReader::TokenType type = reader.readNext(); QXmlStreamReader::TokenType type = reader.readNext();
if (type == QXmlStreamReader::StartElement && QString name = reader.name().toString();
reader.namespaceUri() == QLatin1String("DAV:")) { // Start elements with DAV:
QString name = reader.name().toString(); if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) {
qDebug() << Q_FUNC_INFO << "StartElement" << name;
if (name == QLatin1String("href")) { if (name == QLatin1String("href")) {
currentItem = reader.readElementText(); currentHref = QUrl::fromPercentEncoding(reader.readElementText().toUtf8());
} else if (name == QLatin1String("collection") && } else if (name == QLatin1String("response")) {
!currentItem.isEmpty()) { } else if (name == QLatin1String("propstat")) {
folders.append(QUrl::fromEncoded(currentItem.toLatin1()).path()); qDebug() << "inside propstat!";
} else if (name == QLatin1String("quota-used-bytes") && insidePropstat = true;
!currentItem.isEmpty()) { } else if (name == QLatin1String("status") && insidePropstat) {
bool ok = false; QString httpStatus = reader.readElementText();
auto s = reader.readElementText().toLongLong(&ok); if (httpStatus.startsWith("HTTP/1.1 200")) {
if (ok) { currentPropsHaveHttp200 = true;
_sizes[currentItem] = s; 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<QString,QString>(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; return true;
} }

Просмотреть файл

@ -142,7 +142,10 @@ public:
QHash<QString, qint64> _sizes; QHash<QString, qint64> _sizes;
signals: signals:
void directoryListing(const QStringList &items); void directoryListingSubfolders(const QStringList &items);
void directoryListingIterated(const QString name, QMap<QString,QString> properties);
void finishedWithError(QNetworkReply *reply);
void finishedWithoutError();
private slots: private slots:
virtual bool finished() Q_DECL_OVERRIDE; virtual bool finished() Q_DECL_OVERRIDE;

Просмотреть файл

@ -155,6 +155,9 @@ QString SyncEngine::csyncErrorToString(CSYNC_STATUS err)
case CSYNC_STATUS_SERVICE_UNAVAILABLE: case CSYNC_STATUS_SERVICE_UNAVAILABLE:
errStr = tr("The mounted directory is temporarily not available on the server"); errStr = tr("The mounted directory is temporarily not available on the server");
break; break;
case CSYNC_STATUS_OPENDIR_ERROR:
errStr = tr("An error opening a directory happened");
break;
default: default:
errStr = tr("An internal error number %1 happened.").arg( (int) err ); errStr = tr("An internal error number %1 happened.").arg( (int) err );
} }
@ -596,18 +599,29 @@ void SyncEngine::startSync()
int timeout = OwncloudPropagator::httpTimeout(); int timeout = OwncloudPropagator::httpTimeout();
csync_set_module_property(_csync_ctx, "timeout", &timeout); csync_set_module_property(_csync_ctx, "timeout", &timeout);
_stopWatch.start(); _stopWatch.start();
qDebug() << "#### Discovery start #################################################### >>"; qDebug() << "#### Discovery start #################################################### >>";
DiscoveryJob *job = new DiscoveryJob(_csync_ctx); _discoveryMainThread = new DiscoveryMainThread(account());
job->_selectiveSyncBlackList = _selectiveSyncBlackList; _discoveryMainThread->setParent(this);
job->moveToThread(&_thread); connect(this, SIGNAL(finished()), _discoveryMainThread, SLOT(deleteLater()));
connect(job, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int)));
connect(job, SIGNAL(folderDiscovered(bool,QString)),
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))); 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) 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; ne_session_s *session = 0;
// that call to set property actually is a get which will return the session // that call to set property actually is a get which will return the session
csync_set_module_property(_csync_ctx, "get_dav_session", &session); csync_set_module_property(_csync_ctx, "get_dav_session", &session);
@ -1132,9 +1148,16 @@ AccountPtr SyncEngine::account() const
void SyncEngine::abort() void SyncEngine::abort()
{ {
// Aborts the discovery phase job
if (_discoveryMainThread) {
_discoveryMainThread->abort();
}
// Sets a flag for the update phase
csync_request_abort(_csync_ctx); csync_request_abort(_csync_ctx);
if(_propagator) // For the propagator
if(_propagator) {
_propagator->abort(); _propagator->abort();
}
} }
} // namespace OCC } // namespace OCC

Просмотреть файл

@ -36,6 +36,7 @@
#include "utility.h" #include "utility.h"
#include "syncfilestatus.h" #include "syncfilestatus.h"
#include "accountfwd.h" #include "accountfwd.h"
#include "discoveryphase.h"
class QProcess; class QProcess;
@ -150,6 +151,7 @@ private:
QString _remoteUrl; QString _remoteUrl;
QString _remotePath; QString _remotePath;
SyncJournalDb *_journal; SyncJournalDb *_journal;
QPointer<DiscoveryMainThread> _discoveryMainThread;
QSharedPointer <OwncloudPropagator> _propagator; QSharedPointer <OwncloudPropagator> _propagator;
QString _lastDeleted; // if the last item was a path and it has been deleted QString _lastDeleted; // if the last item was a path and it has been deleted
QSet<QString> _seenFiles; QSet<QString> _seenFiles;