added optional trash for deleted files

Sun, 09 Nov 2014 11:30:24 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 09 Nov 2014 11:30:24 +0100
changeset 65
d4077e8175f3
parent 64
112dbf7ba8b0
child 66
f8c1f685e08e

added optional trash for deleted files

dav/scfg.c file | annotate | diff | comparison | revisions
dav/scfg.h file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/sync.h file | annotate | diff | comparison | revisions
--- 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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 #include <libidav/utils.h>
 #include <ucx/map.h>
 
@@ -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 @@
         "    <name>documents</name>\n\n"
         "    <!-- local path to the directory -->\n"
         "    <path>/home/user/Documents</path>\n\n"
+        "    <!-- optional trash path -->\n"
+        "    <trash>.trash</trash>"
         "    <!-- repository name specified in config.xml -->\n"
         "    <repository>server</repository>\n\n"
         "    <!-- collection to synchronize (optional) -->\n"
--- 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;
--- 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;
-}
 
--- 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

mercurial