зеркало из https://github.com/nextcloud/desktop.git
Родитель
bd80c034ba
Коммит
906779c4b1
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#ifndef _CSYNC_H
|
||||
#define _CSYNC_H
|
||||
|
||||
#include "std/c_private.h"
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.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.
|
||||
*
|
||||
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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: "<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)
|
||||
{
|
||||
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: */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
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: "<zero>" );
|
||||
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, "<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/
|
||||
// extend as required
|
||||
const char* csync_owncloud_get_platform() {
|
||||
|
|
|
@ -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 <iconv.h>
|
||||
|
@ -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 );
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#define _CSYNC_UPDATE_H
|
||||
|
||||
#include "csync.h"
|
||||
#include "vio/csync_vio_file_stat.h"
|
||||
|
||||
/**
|
||||
* @file csync_update.h
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_vio.h"
|
||||
|
||||
#include "vio/csync_vio_handle.h"
|
||||
#include "vio/csync_vio_local.h"
|
||||
#include "vio/csync_vio_handle_private.h"
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <qdebug.h>
|
||||
|
||||
#include <QUrl>
|
||||
#include "account.h"
|
||||
#include <QFileInfo>
|
||||
|
||||
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() {
|
||||
_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);
|
||||
|
|
|
@ -18,18 +18,97 @@
|
|||
#include <QElapsedTimer>
|
||||
#include <QStringList>
|
||||
#include <csync.h>
|
||||
|
||||
#include <QMap>
|
||||
#include "networkjobs.h"
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QLinkedList>
|
||||
|
||||
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<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 {
|
||||
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*);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
|
@ -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("<?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:resourcetype/>\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:propfind>\n");
|
||||
QBuffer *buf = new QBuffer(this);
|
||||
|
@ -345,42 +354,128 @@ void LsColJob::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()
|
||||
{
|
||||
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<QString, QString> currentTmpProperties;
|
||||
QMap<QString, QString> 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<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;
|
||||
}
|
||||
|
|
|
@ -142,7 +142,10 @@ public:
|
|||
QHash<QString, qint64> _sizes;
|
||||
|
||||
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:
|
||||
virtual bool finished() Q_DECL_OVERRIDE;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> _discoveryMainThread;
|
||||
QSharedPointer <OwncloudPropagator> _propagator;
|
||||
QString _lastDeleted; // if the last item was a path and it has been deleted
|
||||
QSet<QString> _seenFiles;
|
||||
|
|
Загрузка…
Ссылка в новой задаче