mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
The current way of tracking the need to update the metadata without propagation using a separate flag makes it difficult to track priorities between the local and remote tree. The logic is also difficult to logically cover since the possibilities matrix isn't 100% covered, leaving the flag only used in a few situations (mostly involving folders, but not only). The reason we need to change this is to be able to track the sync state of files for overlay icons. The instruction alone can't be used since CSYNC_INSTRUCTION_SYNC is used for folders even though they won't be propagated. Removing this logic is however not possible without using something else than CSYNC_INSTRUCTION_NONE since too many codepath interpret (rightfully) this as meaning "nothing to do". This patch adds a new CSYNC_INSTRUCTION_UPDATE_METADATA instruction to let the update and reconcile steps tell the SyncEngine to update the metadata of a file without any propagation. Other flags are left to be interpretted by the implementation as implicitly needing metadata update or not, as this was already the case for most file propagation jobs. For example, CSYNC_INSTRUCTION_NEW for directories now also implicitly update the metadata. Since it's not impossible for folders to emit CSYNC_INSTRUCTION_SYNC or CSYNC_INSTRUCTION_CONFLICT, the corresponding code paths in the sync engine have been removed. Since the reconcile step can now know if the local tree needs metadata update while the remote side might want propagation, the localMetadataUpdate logic in SyncEngine::treewalkFile now simply use a CSYNC_INSTRUCTION_UPDATE_METADATA for the local side, which is now implemented as a different database query.
690 lines
16 KiB
C
690 lines
16 KiB
C
/*
|
|
* libcsync -- a library to sync a directory with another
|
|
*
|
|
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
|
* Copyright (c) 2012-2013 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 "config_csync.h"
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <stdbool.h>
|
|
|
|
#ifdef HAVE_ICONV_H
|
|
#include <iconv.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_ICONV_H
|
|
#include <sys/iconv.h>
|
|
#endif
|
|
|
|
#include "c_lib.h"
|
|
#include "csync_private.h"
|
|
#include "csync_exclude.h"
|
|
#include "csync_statedb.h"
|
|
#include "csync_time.h"
|
|
#include "csync_util.h"
|
|
#include "csync_misc.h"
|
|
#include "std/c_private.h"
|
|
|
|
#include "csync_update.h"
|
|
#include "csync_reconcile.h"
|
|
|
|
#include "vio/csync_vio.h"
|
|
|
|
#include "csync_log.h"
|
|
#include "csync_rename.h"
|
|
#include "c_jhash.h"
|
|
|
|
static int _key_cmp(const void *key, const void *data) {
|
|
uint64_t a;
|
|
csync_file_stat_t *b;
|
|
|
|
a = *(uint64_t *) (key);
|
|
b = (csync_file_stat_t *) data;
|
|
|
|
if (a < b->phash) {
|
|
return -1;
|
|
} else if (a > b->phash) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _data_cmp(const void *key, const void *data) {
|
|
csync_file_stat_t *a, *b;
|
|
|
|
a = (csync_file_stat_t *) key;
|
|
b = (csync_file_stat_t *) data;
|
|
|
|
if (a->phash < b->phash) {
|
|
return -1;
|
|
} else if (a->phash > b->phash) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void csync_create(CSYNC **csync, const char *local, const char *remote) {
|
|
CSYNC *ctx;
|
|
size_t len = 0;
|
|
|
|
ctx = c_malloc(sizeof(CSYNC));
|
|
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
/* remove trailing slashes */
|
|
len = strlen(local);
|
|
while(len > 0 && local[len - 1] == '/') --len;
|
|
|
|
ctx->local.uri = c_strndup(local, len);
|
|
|
|
/* remove trailing slashes */
|
|
len = strlen(remote);
|
|
while(len > 0 && remote[len - 1] == '/') --len;
|
|
|
|
ctx->remote.uri = c_strndup(remote, len);
|
|
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
ctx->current_fs = NULL;
|
|
|
|
ctx->abort = false;
|
|
|
|
ctx->ignore_hidden_files = true;
|
|
|
|
*csync = ctx;
|
|
}
|
|
|
|
void csync_init(CSYNC *ctx) {
|
|
assert(ctx);
|
|
/* Do not initialize twice */
|
|
|
|
assert(!(ctx->status & CSYNC_STATUS_INIT));
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
ctx->local.type = LOCAL_REPLICA;
|
|
|
|
ctx->remote.type = REMOTE_REPLICA;
|
|
|
|
c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp);
|
|
c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp);
|
|
|
|
ctx->remote.root_perms = 0;
|
|
|
|
ctx->status = CSYNC_STATUS_INIT;
|
|
|
|
/* initialize random generator */
|
|
srand(time(NULL));
|
|
}
|
|
|
|
int csync_update(CSYNC *ctx) {
|
|
int rc = -1;
|
|
struct timespec start, finish;
|
|
|
|
if (ctx == NULL) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
/* create/load statedb */
|
|
rc = asprintf(&ctx->statedb.file, "%s/.csync_journal.db",
|
|
ctx->local.uri);
|
|
if (rc < 0) {
|
|
ctx->status_code = CSYNC_STATUS_MEMORY_ERROR;
|
|
return rc;
|
|
}
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Journal: %s", ctx->statedb.file);
|
|
|
|
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
|
|
rc = -1;
|
|
return rc;
|
|
}
|
|
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
csync_memstat_check();
|
|
|
|
if (!ctx->excludes) {
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "No exclude file loaded or defined!");
|
|
}
|
|
|
|
/* update detection for local replica */
|
|
csync_gettime(&start);
|
|
ctx->current = LOCAL_REPLICA;
|
|
ctx->replica = ctx->local.type;
|
|
|
|
rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH);
|
|
if (rc < 0) {
|
|
if(ctx->status_code == CSYNC_STATUS_OK) {
|
|
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
csync_gettime(&finish);
|
|
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
|
"Update detection for local replica took %.2f seconds walking %zu files.",
|
|
c_secdiff(finish, start), c_rbtree_size(ctx->local.tree));
|
|
csync_memstat_check();
|
|
|
|
/* update detection for remote replica */
|
|
csync_gettime(&start);
|
|
ctx->current = REMOTE_REPLICA;
|
|
ctx->replica = ctx->remote.type;
|
|
|
|
rc = csync_ftw(ctx, ctx->remote.uri, csync_walker, MAX_DEPTH);
|
|
if (rc < 0) {
|
|
if(ctx->status_code == CSYNC_STATUS_OK) {
|
|
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
csync_gettime(&finish);
|
|
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
|
"Update detection for remote replica took %.2f seconds "
|
|
"walking %zu files.",
|
|
c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree));
|
|
csync_memstat_check();
|
|
|
|
ctx->status |= CSYNC_STATUS_UPDATE;
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
csync_statedb_close(ctx);
|
|
return rc;
|
|
}
|
|
|
|
int csync_reconcile(CSYNC *ctx) {
|
|
int rc = -1;
|
|
struct timespec start, finish;
|
|
|
|
if (ctx == NULL) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
/* Reconciliation for local replica */
|
|
csync_gettime(&start);
|
|
|
|
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
|
|
rc = -1;
|
|
return rc;
|
|
}
|
|
|
|
ctx->current = LOCAL_REPLICA;
|
|
ctx->replica = ctx->local.type;
|
|
|
|
rc = csync_reconcile_updates(ctx);
|
|
|
|
csync_gettime(&finish);
|
|
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
|
"Reconciliation for local replica took %.2f seconds visiting %zu files.",
|
|
c_secdiff(finish, start), c_rbtree_size(ctx->local.tree));
|
|
|
|
if (rc < 0) {
|
|
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
|
|
ctx->status_code = csync_errno_to_status( errno, CSYNC_STATUS_RECONCILE_ERROR );
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
/* Reconciliation for remote replica */
|
|
csync_gettime(&start);
|
|
|
|
ctx->current = REMOTE_REPLICA;
|
|
ctx->replica = ctx->remote.type;
|
|
|
|
rc = csync_reconcile_updates(ctx);
|
|
|
|
csync_gettime(&finish);
|
|
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
|
"Reconciliation for remote replica took %.2f seconds visiting %zu files.",
|
|
c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree));
|
|
|
|
if (rc < 0) {
|
|
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
|
|
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_RECONCILE_ERROR );
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
ctx->status |= CSYNC_STATUS_RECONCILE;
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
csync_statedb_close(ctx);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* local visitor which calls the user visitor with repacked stat info.
|
|
*/
|
|
static int _csync_treewalk_visitor(void *obj, void *data) {
|
|
int rc = 0;
|
|
csync_file_stat_t *cur = NULL;
|
|
CSYNC *ctx = NULL;
|
|
c_rbtree_visit_func *visitor = NULL;
|
|
_csync_treewalk_context *twctx = NULL;
|
|
TREE_WALK_FILE trav;
|
|
c_rbtree_t *other_tree = NULL;
|
|
c_rbnode_t *other_node = NULL;
|
|
|
|
cur = (csync_file_stat_t *) obj;
|
|
ctx = (CSYNC *) data;
|
|
|
|
if (ctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* we need the opposite tree! */
|
|
switch (ctx->current) {
|
|
case LOCAL_REPLICA:
|
|
other_tree = ctx->remote.tree;
|
|
break;
|
|
case REMOTE_REPLICA:
|
|
other_tree = ctx->local.tree;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
other_node = c_rbtree_find(other_tree, &cur->phash);
|
|
|
|
if (!other_node) {
|
|
/* Check the renamed path as well. */
|
|
int len;
|
|
uint64_t h = 0;
|
|
char *renamed_path = csync_rename_adjust_path(ctx, cur->path);
|
|
|
|
if (!c_streq(renamed_path, cur->path)) {
|
|
len = strlen( renamed_path );
|
|
h = c_jhash64((uint8_t *) renamed_path, len, 0);
|
|
other_node = c_rbtree_find(other_tree, &h);
|
|
}
|
|
SAFE_FREE(renamed_path);
|
|
}
|
|
|
|
if (!other_node) {
|
|
/* Check the source path as well. */
|
|
int len;
|
|
uint64_t h = 0;
|
|
char *renamed_path = csync_rename_adjust_path_source(ctx, cur->path);
|
|
|
|
if (!c_streq(renamed_path, cur->path)) {
|
|
len = strlen( renamed_path );
|
|
h = c_jhash64((uint8_t *) renamed_path, len, 0);
|
|
other_node = c_rbtree_find(other_tree, &h);
|
|
}
|
|
SAFE_FREE(renamed_path);
|
|
}
|
|
|
|
if (obj == NULL || data == NULL) {
|
|
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
|
|
return -1;
|
|
}
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
twctx = (_csync_treewalk_context*) ctx->callbacks.userdata;
|
|
if (twctx == NULL) {
|
|
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
if (twctx->instruction_filter > 0 &&
|
|
!(twctx->instruction_filter & cur->instruction) ) {
|
|
return 0;
|
|
}
|
|
|
|
visitor = (c_rbtree_visit_func*)(twctx->user_visitor);
|
|
if (visitor != NULL) {
|
|
trav.path = cur->path;
|
|
trav.size = cur->size;
|
|
trav.modtime = cur->modtime;
|
|
trav.mode = cur->mode;
|
|
trav.type = cur->type;
|
|
trav.instruction = cur->instruction;
|
|
trav.rename_path = cur->destpath;
|
|
trav.etag = cur->etag;
|
|
trav.file_id = cur->file_id;
|
|
trav.remotePerm = cur->remotePerm;
|
|
trav.directDownloadUrl = cur->directDownloadUrl;
|
|
trav.directDownloadCookies = cur->directDownloadCookies;
|
|
trav.inode = cur->inode;
|
|
|
|
trav.error_status = cur->error_status;
|
|
trav.has_ignored_files = cur->has_ignored_files;
|
|
trav.checksum = cur->checksum;
|
|
trav.checksumTypeId = cur->checksumTypeId;
|
|
|
|
if( other_node ) {
|
|
csync_file_stat_t *other_stat = (csync_file_stat_t*)other_node->data;
|
|
trav.other.etag = other_stat->etag;
|
|
trav.other.file_id = other_stat->file_id;
|
|
trav.other.instruction = other_stat->instruction;
|
|
trav.other.modtime = other_stat->modtime;
|
|
trav.other.size = other_stat->size;
|
|
} else {
|
|
trav.other.etag = 0;
|
|
trav.other.file_id = 0;
|
|
trav.other.instruction = CSYNC_INSTRUCTION_NONE;
|
|
trav.other.modtime = 0;
|
|
trav.other.size = 0;
|
|
}
|
|
|
|
rc = (*visitor)(&trav, twctx->userdata);
|
|
cur->instruction = trav.instruction;
|
|
if (trav.etag != cur->etag) { // FIXME It would be nice to have this documented
|
|
SAFE_FREE(cur->etag);
|
|
cur->etag = c_strdup(trav.etag);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* treewalk function, called from its wrappers below.
|
|
*
|
|
* it encapsulates the user visitor function, the filter and the userdata
|
|
* into a treewalk_context structure and calls the rb treewalk function,
|
|
* which calls the local _csync_treewalk_visitor in this module.
|
|
* The user visitor is called from there.
|
|
*/
|
|
static int _csync_walk_tree(CSYNC *ctx, c_rbtree_t *tree, csync_treewalk_visit_func *visitor, int filter)
|
|
{
|
|
_csync_treewalk_context tw_ctx;
|
|
int rc = -1;
|
|
|
|
if (ctx == NULL) {
|
|
errno = EBADF;
|
|
return rc;
|
|
}
|
|
|
|
if (visitor == NULL || tree == NULL) {
|
|
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
|
|
return rc;
|
|
}
|
|
|
|
tw_ctx.userdata = ctx->callbacks.userdata;
|
|
tw_ctx.user_visitor = visitor;
|
|
tw_ctx.instruction_filter = filter;
|
|
|
|
ctx->callbacks.userdata = &tw_ctx;
|
|
|
|
rc = c_rbtree_walk(tree, (void*) ctx, _csync_treewalk_visitor);
|
|
if( rc < 0 ) {
|
|
if( ctx->status_code == CSYNC_STATUS_OK )
|
|
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_TREE_ERROR);
|
|
}
|
|
ctx->callbacks.userdata = tw_ctx.userdata;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* wrapper function for treewalk on the remote tree
|
|
*/
|
|
int csync_walk_remote_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter)
|
|
{
|
|
c_rbtree_t *tree = NULL;
|
|
int rc = -1;
|
|
|
|
if(ctx != NULL) {
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
ctx->current = REMOTE_REPLICA;
|
|
tree = ctx->remote.tree;
|
|
}
|
|
|
|
/* all error handling in the called function */
|
|
rc = _csync_walk_tree(ctx, tree, visitor, filter);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* wrapper function for treewalk on the local tree
|
|
*/
|
|
int csync_walk_local_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter)
|
|
{
|
|
c_rbtree_t *tree = NULL;
|
|
int rc = -1;
|
|
|
|
if (ctx != NULL) {
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
ctx->current = LOCAL_REPLICA;
|
|
tree = ctx->local.tree;
|
|
}
|
|
|
|
/* all error handling in the called function */
|
|
rc = _csync_walk_tree(ctx, tree, visitor, filter);
|
|
return rc;
|
|
}
|
|
|
|
static void _tree_destructor(void *data) {
|
|
csync_file_stat_t *freedata = NULL;
|
|
|
|
freedata = (csync_file_stat_t *) data;
|
|
csync_file_stat_free(freedata);
|
|
}
|
|
|
|
/* reset all the list to empty.
|
|
* used by csync_commit and csync_destroy */
|
|
static void _csync_clean_ctx(CSYNC *ctx)
|
|
{
|
|
/* destroy the rbtrees */
|
|
if (c_rbtree_size(ctx->local.tree) > 0) {
|
|
c_rbtree_destroy(ctx->local.tree, _tree_destructor);
|
|
}
|
|
|
|
if (c_rbtree_size(ctx->remote.tree) > 0) {
|
|
c_rbtree_destroy(ctx->remote.tree, _tree_destructor);
|
|
}
|
|
|
|
csync_rename_destroy(ctx);
|
|
|
|
/* free memory */
|
|
c_rbtree_free(ctx->local.tree);
|
|
c_rbtree_free(ctx->remote.tree);
|
|
|
|
SAFE_FREE(ctx->statedb.file);
|
|
SAFE_FREE(ctx->remote.root_perms);
|
|
}
|
|
|
|
int csync_commit(CSYNC *ctx) {
|
|
int rc = 0;
|
|
|
|
if (ctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
if (ctx->statedb.db != NULL
|
|
&& csync_statedb_close(ctx) < 0) {
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "ERR: closing of statedb failed.");
|
|
rc = -1;
|
|
}
|
|
ctx->statedb.db = NULL;
|
|
|
|
_csync_clean_ctx(ctx);
|
|
|
|
ctx->remote.read_from_db = 0;
|
|
ctx->read_remote_from_db = true;
|
|
ctx->db_is_empty = false;
|
|
|
|
|
|
/* Create new trees */
|
|
c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp);
|
|
c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp);
|
|
|
|
|
|
ctx->status = CSYNC_STATUS_INIT;
|
|
SAFE_FREE(ctx->error_string);
|
|
|
|
rc = 0;
|
|
return rc;
|
|
}
|
|
|
|
int csync_destroy(CSYNC *ctx) {
|
|
int rc = 0;
|
|
|
|
if (ctx == NULL) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
ctx->status_code = CSYNC_STATUS_OK;
|
|
|
|
if (ctx->statedb.db != NULL
|
|
&& csync_statedb_close(ctx) < 0) {
|
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "ERR: closing of statedb failed.");
|
|
rc = -1;
|
|
}
|
|
ctx->statedb.db = NULL;
|
|
|
|
_csync_clean_ctx(ctx);
|
|
|
|
SAFE_FREE(ctx->local.uri);
|
|
SAFE_FREE(ctx->remote.uri);
|
|
SAFE_FREE(ctx->error_string);
|
|
|
|
#ifdef WITH_ICONV
|
|
c_close_iconv();
|
|
#endif
|
|
|
|
SAFE_FREE(ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void *csync_get_userdata(CSYNC *ctx) {
|
|
if (ctx == NULL) {
|
|
return NULL;
|
|
}
|
|
return ctx->callbacks.userdata;
|
|
}
|
|
|
|
int csync_set_userdata(CSYNC *ctx, void *userdata) {
|
|
if (ctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
ctx->callbacks.userdata = userdata;
|
|
|
|
return 0;
|
|
}
|
|
|
|
csync_auth_callback csync_get_auth_callback(CSYNC *ctx) {
|
|
if (ctx == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return ctx->callbacks.auth_function;
|
|
}
|
|
|
|
int csync_set_status(CSYNC *ctx, int status) {
|
|
if (ctx == NULL || status < 0) {
|
|
return -1;
|
|
}
|
|
|
|
ctx->status = status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CSYNC_STATUS csync_get_status(CSYNC *ctx) {
|
|
if (ctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return ctx->status_code;
|
|
}
|
|
|
|
const char *csync_get_status_string(CSYNC *ctx)
|
|
{
|
|
return csync_vio_get_status_string(ctx);
|
|
}
|
|
|
|
#ifdef WITH_ICONV
|
|
int csync_set_iconv_codec(const char *from)
|
|
{
|
|
c_close_iconv();
|
|
|
|
if (from != NULL) {
|
|
c_setup_iconv(from);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void csync_request_abort(CSYNC *ctx)
|
|
{
|
|
if (ctx != NULL) {
|
|
ctx->abort = true;
|
|
}
|
|
}
|
|
|
|
void csync_resume(CSYNC *ctx)
|
|
{
|
|
if (ctx != NULL) {
|
|
ctx->abort = false;
|
|
}
|
|
}
|
|
|
|
int csync_abort_requested(CSYNC *ctx)
|
|
{
|
|
if (ctx != NULL) {
|
|
return ctx->abort;
|
|
} else {
|
|
return (1 == 0);
|
|
}
|
|
}
|
|
|
|
void csync_file_stat_free(csync_file_stat_t *st)
|
|
{
|
|
if (st) {
|
|
SAFE_FREE(st->directDownloadUrl);
|
|
SAFE_FREE(st->directDownloadCookies);
|
|
SAFE_FREE(st->etag);
|
|
SAFE_FREE(st->destpath);
|
|
SAFE_FREE(st->checksum);
|
|
SAFE_FREE(st);
|
|
}
|
|
}
|