# HG changeset patch # User Olaf Wintermann # Date 1415529024 -3600 # Node ID d4077e8175f3c2e07d3db1a836a82ff6188b04f8 # Parent 112dbf7ba8b0e709a0e12ce2ba618e8328b3118c added optional trash for deleted files diff -r 112dbf7ba8b0 -r d4077e8175f3 dav/scfg.c --- a/dav/scfg.c Sat Nov 08 20:27:10 2014 +0100 +++ b/dav/scfg.c Sun Nov 09 11:30:24 2014 +0100 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -88,6 +89,7 @@ int scfg_load_directory(xmlNode *node) { char *name = NULL; char *path = NULL; + char *trash = NULL; char *collection = NULL; char *repository = NULL; char *database = NULL; @@ -104,6 +106,8 @@ name = value; } else if(xstreq(node->name, "path")) { path = value; + } else if(xstreq(node->name, "trash")) { + trash = value; } else if(xstreq(node->name, "collection")) { collection = value; } else if(xstreq(node->name, "repository")) { @@ -156,6 +160,23 @@ regcomp(matchnothing, "///", REG_NOSUB); dir->exclude = ucx_list_append(NULL, matchnothing); } + if (trash) { + if (trash[0] == '/') { + dir->trash = strdup(trash); + } else { + char *t = util_concat_path(path, trash); + dir->trash = util_concat_path(t, "/"); + free(t); + } + + // create trash directory + mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + if (util_mkdir(dir->trash, mode)) { + if (errno != EEXIST) { + fprintf(stderr, "Cannot create trash directory.\n"); + } + } + } ucx_map_cstr_put(directories, name, dir); @@ -172,6 +193,8 @@ " documents\n\n" " \n" " /home/user/Documents\n\n" + " \n" + " .trash" " \n" " server\n\n" " \n" diff -r 112dbf7ba8b0 -r d4077e8175f3 dav/scfg.h --- a/dav/scfg.h Sat Nov 08 20:27:10 2014 +0100 +++ b/dav/scfg.h Sun Nov 09 11:30:24 2014 +0100 @@ -41,6 +41,7 @@ typedef struct SyncDirectory { char *name; char *path; + char *trash; char *collection; char *repository; char *database; diff -r 112dbf7ba8b0 -r d4077e8175f3 dav/sync.c --- a/dav/sync.c Sat Nov 08 20:27:10 2014 +0100 +++ b/dav/sync.c Sun Nov 09 11:30:24 2014 +0100 @@ -87,8 +87,6 @@ ret = cmd_pull(args); } else if(!strcmp(cmd, "push")) { ret = cmd_push(args); - } else if(!strcmp(cmd, "sync")) { - ret = cmd_sync(args); } // TODO: cleanup sync config (don't forget to call regfree for regex) @@ -109,6 +107,17 @@ } static int res_matches_filter(SyncDirectory *dir, char *res_path) { + // trash filter + if (dir->trash) { + sstr_t rpath = sstr(util_concat_path(dir->path, res_path)); + if (sstrprefix(rpath, sstr(dir->trash))) { + free(rpath.ptr); + return 1; + } + free(rpath.ptr); + } + + // include/exclude filter UCX_FOREACH(inc, dir->include) { regex_t* pattern = (regex_t*) inc->data; if (regexec(pattern, res_path, 0, NULL, 0) == 0) { @@ -215,7 +224,7 @@ continue; } // sync_remove_resource does all necessary tests - sync_remove_resource(dir, local); + sync_remove_local_resource(dir, local); } ucx_map_free(db->resources); db->resources = svrres; @@ -232,10 +241,6 @@ } int sync_get_resource(CmdArgs *a, SyncDirectory *dir, DavResource *res, SyncDatabase *db) { - LocalResource *removed = ucx_map_cstr_get(db->remove, res->path); - if(removed) { - return 0; - } int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection LocalResource *local = ucx_map_cstr_get(db->resources, res->path); @@ -243,7 +248,8 @@ char *etag = dav_get_property(res, "D:getetag"); struct stat s; - if(local) { + if(local) { + int exists = 1; if(stat(local_path, &s)) { // Ignore the fact, that the file is locally removed. If the // server has an updated version, we readd the file or the @@ -252,6 +258,8 @@ fprintf(stderr, "Cannot stat file: %s\n", local_path); free(local_path); return -1; + } else { + exists = 0; } } @@ -266,7 +274,7 @@ } } - if(cdt && s.st_mtime != local->last_modified) { + if(cdt && exists && s.st_mtime != local->last_modified) { // file modified on the server and on the client rename_local_file(dir, db, local->path); } @@ -328,7 +336,7 @@ return ret; } -void sync_remove_resource(SyncDirectory *dir, LocalResource *res) { +void sync_remove_local_resource(SyncDirectory *dir, LocalResource *res) { char *local_path = util_concat_path(dir->path, res->path); struct stat s; if(stat(local_path, &s)) { @@ -342,7 +350,10 @@ } printf("delete: %s\n", res->path); - if(unlink(local_path)) { + + if(dir->trash) { + move_to_trash(dir, local_path); + } else if(unlink(local_path)) { fprintf(stderr, "Cannot remove file %s\n", local_path); } free(local_path); @@ -369,7 +380,7 @@ loop = 0; printf("conflict: %s\n", local_path); if(rename(local_path, new_path.ptr)) { - printf("errno: %d\n", errno); + //printf("errno: %d\n", errno); fprintf( stderr, "Cannot rename file %s to %s\n", @@ -384,6 +395,43 @@ free(parent); } +void move_to_trash(SyncDirectory *dir, char *path) { + char *new_path = NULL; + for (int i=0;;i++) { + sstr_t np = ucx_asprintf( + ucx_default_allocator(), + "%s%d-%s", + dir->trash, + i, + util_resource_name(path)); + + struct stat s; + if(stat(np.ptr, &s)) { + if(errno == ENOENT) { + new_path = np.ptr; + } + break; + } + free(np.ptr); + }; + + if(!new_path) { + fprintf(stderr, "Cannot move file %s to trash.\n", path); + return; + } + + if(rename(path, new_path)) { + //printf("errno: %d\n", errno); + fprintf( + stderr, + "Cannot rename file %s to %s\n", + path, + new_path); + } + + free(new_path); +} + int cmd_push(CmdArgs *a) { if(a->argc != 1) { fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few" : "many"); @@ -453,18 +501,9 @@ UcxMapIterator i = ucx_map_iterator(db->resources); LocalResource *local; UCX_MAP_FOREACH(key, local, i) { - // TODO: save deletion: check for remote changes - - DavResource *res = dav_resource_new(sn, local->path); - printf("delete: %s\n", res->path); - if(dav_delete(res)) { - if(sn->error != DAV_NOT_FOUND) { - fprintf(stderr, "Cannot delete resource %s\n", res->path); - } + if(sync_delete_remote_resource(sn, local)) { + ucx_map_cstr_put(lclres, local->path, local); } - dav_resource_free(res); - // TODO: free local resource - ucx_map_remove(db->remove, key); } ucx_map_free(db->resources); db->resources = lclres; @@ -667,9 +706,38 @@ return ret; } +int sync_delete_remote_resource(DavSession *sn, LocalResource *local_res) { + DavResource *res = dav_get(sn, local_res->path, "D:getetag"); + if(!res) { + return sn->error == DAV_NOT_FOUND ? 0 : 1; + } + + char *etag = dav_get_property(res, "D:getetag"); + if(etag) { + if(strlen(etag) > 2 && etag[0] == 'W' && etag[1] == '/') { + etag = etag + 2; + } + } + + int ret = 0; + if(!strcmp(etag, local_res->etag)) { + // local resource metadata == remote resource metadata + // resource can be deleted + printf("delete: %s\n", res->path); + if(dav_delete(res)) { + if(sn->error != DAV_NOT_FOUND) { + fprintf(stderr, "Cannot delete resource %s\n", res->path); + } + } + } else { + ret = 1; + } + + // cleanup + dav_resource_free(res); + + return ret; +} -int cmd_sync(CmdArgs *a) { - return 0; -} diff -r 112dbf7ba8b0 -r d4077e8175f3 dav/sync.h --- a/dav/sync.h Sat Nov 08 20:27:10 2014 +0100 +++ b/dav/sync.h Sun Nov 09 11:30:24 2014 +0100 @@ -48,15 +48,16 @@ int cmd_sync(CmdArgs *args); int sync_get_resource(CmdArgs *a, SyncDirectory *dir, DavResource *res, SyncDatabase *db); -void sync_remove_resource(SyncDirectory *dir, LocalResource *res); +void sync_remove_local_resource(SyncDirectory *dir, LocalResource *res); void rename_local_file(SyncDirectory *dir, SyncDatabase *db, char *path); +void move_to_trash(SyncDirectory *dir, char *path); UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db); UcxList* read_changes(SyncDirectory *dir, SyncDatabase *db); LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path, int *isdir); int local_resource_is_changed(SyncDirectory *dir, SyncDatabase *db, LocalResource *res); -void local_resource_update_etag(LocalResource *local_res, DavResource *dav_res); int sync_put_resource(SyncDirectory *dir, DavResource *res, LocalResource *local); +int sync_delete_remote_resource(DavSession *sn, LocalResource *res); #ifdef __cplusplus