adds option to restore previous file versions

Thu, 21 Mar 2019 10:51:14 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 21 Mar 2019 10:51:14 +0100
changeset 533
5b9f20aa88c2
parent 532
aeda47714978
child 534
9a4857d6444e

adds option to restore previous file versions

dav/sopt.c file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/sync.h file | annotate | diff | comparison | revisions
--- a/dav/sopt.c	Wed Mar 20 13:20:38 2019 +0100
+++ b/dav/sopt.c	Thu Mar 21 10:51:14 2019 +0100
@@ -91,6 +91,11 @@
                         optchar = 't';
                         break;
                     }
+                    case 'V': {
+                        option = "version";
+                        optchar = 'V';
+                        break;
+                    }
                     case 'R': {
                         ucx_map_cstr_put(a->options, "restore-removed", NOARG);
                         break;
@@ -99,8 +104,8 @@
                         ucx_map_cstr_put(a->options, "restore-modified", NOARG);
                         break;
                     }
-                    case 'V': {
-                        ucx_map_cstr_put(a->options, "versioning", NOARG);
+                    case 'S': {
+                        ucx_map_cstr_put(a->options, "snapshot", NOARG);
                     }
                 }
             }
--- a/dav/sync.c	Wed Mar 20 13:20:38 2019 +0100
+++ b/dav/sync.c	Thu Mar 21 10:51:14 2019 +0100
@@ -72,6 +72,15 @@
     va_end(ap);
 }
 
+static DavPropName defprops[] = {
+    { "DAV:", "getetag" },
+    { DAV_NS, "status" },
+    { DAV_NS, "finfo" },
+    { DAV_NS, "tags" },
+    { DAV_NS, "xattributes" }
+};
+static size_t numdefprops = 5;
+
 /*
  * strcmp version that works with NULL pointers
  */
@@ -194,9 +203,10 @@
     
     fprintf(stderr, "Commands:\n");
     fprintf(stderr, "        pull [-cldr] [-t <tags>] <directory>\n");
-    fprintf(stderr, "        push [-cldrVRM] [-t <tags>] <directory>\n");
-    fprintf(stderr, "        archive [-cldVRM] [-t <tags>] <directory>\n");
-    fprintf(stderr, "        restore [-ldRM] [-s <directory>] [file...]\n");
+    fprintf(stderr, "        push [-cldrSRM] [-t <tags>] <directory>\n");
+    fprintf(stderr, "        archive [-cldSRM] [-t <tags>] <directory>\n");
+    fprintf(stderr,
+        "        restore [-ldRM] [-V <version>] [-s <directory>] [file...]\n");
     fprintf(stderr, "        resolve-conflicts <directory>\n");
     fprintf(stderr, "        delete-conflicts <directory>\n");
     fprintf(stderr, "        trash-info <directory>\n");
@@ -214,7 +224,8 @@
                     "Only sync files which have the specified tags\n");
     fprintf(stderr, "        -r         "
                     "Remove resources not matching the tag filter\n");
-    fprintf(stderr, "        -V         Enable versioning\n");
+    fprintf(stderr, "        -V <vers>  Restore specific version\n");
+    fprintf(stderr, "        -S         Save previous file version\n");
     fprintf(stderr, "        -R         Restore removed files\n");
     fprintf(stderr, "        -M         Restore modified files\n");
     fprintf(stderr, "        -v         Verbose output (all commands)\n\n");
@@ -684,7 +695,7 @@
         }
         
         // download the resource
-        if(sync_get_resource(a, dir, res, db, &sync_success)) {
+        if(sync_get_resource(a, dir, res->path, res, db, &sync_success)) {
             fprintf(stderr, "resource download failed: %s\n", res->path);
             sync_error++;
         }
@@ -908,12 +919,13 @@
 int sync_get_resource(
         CmdArgs *a,
         SyncDirectory *dir,
+        const char *path,
         DavResource *res,
         SyncDatabase *db,
         int *counter)
 { 
-    LocalResource *local = ucx_map_cstr_get(db->resources, res->path);
-    char *local_path = util_concat_path(dir->path, res->path);
+    LocalResource *local = ucx_map_cstr_get(db->resources, path);
+    char *local_path = util_concat_path(dir->path, path);
     
     char *etag = dav_get_string_property(res, "D:getetag");
     SYS_STAT s;
@@ -934,7 +946,7 @@
         free(tmp_path);
         return -1;
     }
-    printf("get: %s\n", res->path);
+    printf("get: %s\n", path);
     if(dav_get_content(res, out, (dav_write_func)fwrite)) {
         ret = -1;
     }
@@ -944,7 +956,7 @@
         (*counter)++;
         
         if(sync_store_metadata(dir, tmp_path, local, res)) {
-            fprintf(stderr, "Cannot store metadata: %s\n", res->path);
+            fprintf(stderr, "Cannot store metadata: %s\n", path);
         }
 
         if(dir->trash && dir->backuppull) {
@@ -970,7 +982,7 @@
         if(!local) {
             // new local resource
             local = calloc(1, sizeof(LocalResource));
-            local->path = strdup(res->path);
+            local->path = strdup(path);
             ucx_map_cstr_put(db->resources, local->path, local);
         }
 
@@ -1177,7 +1189,7 @@
     if(scfg_check_dir(dir)) {
         return -1;
     }
-    if(cmd_getoption(a, "versioning")) {
+    if(cmd_getoption(a, "snapshot")) {
         if(dir->versioning) {
             dir->versioning->always = TRUE;
         } else {
@@ -1429,20 +1441,7 @@
     for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) {
         LocalResource *local_res = elm->data;
         
-        DavResource *res = dav_resource_new(sn, local_res->path);
-        if(dir->tagconfig) {
-            DavPropName properties[] = {
-                {DAV_NS,"tags"},
-            };
-            if(dav_load_prop(res, properties, 1)) {
-                sync_error++;
-                print_resource_error(sn, res->path);
-                ret = -1;
-                error = 1;
-                continue;
-            }
-        }
-        
+        DavResource *res = dav_resource_new(sn, local_res->path);       
         if(local_res->metadata_updated) {
             if(!sync_update_metadata(dir, sn, res, local_res)) {
                 LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path);
@@ -1535,6 +1534,14 @@
         return -1;
     }
     
+    char *version = cmd_getoption(a, "version");
+    if(version) {
+        if(a->argc != 1) {
+            fprintf(stderr, "If the -V option is enabled, only one file can be specified\n");
+            return -1;
+        }
+    }
+    
     SyncDirectory *dir = NULL;
     UcxMap *files = NULL;
     if(syncdir) {
@@ -1684,17 +1691,33 @@
     UCX_FOREACH(elm, resources) {
         LocalResource *resource = elm->data;
         
-        DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:finfo,idav:xattributes");
+        DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:version-collection,idav:finfo,idav:xattributes");
         if(!res) {
             printf("skip: %s\n", resource->path);
             continue;
-            //continue;
         }
         char *status = dav_get_string_property(res, "idav:status");
         if(status && !strcmp(status, "broken")) {
+            fprintf(stderr, "Resource %s broken\n", res->path);
             continue;
         }
         
+        DavResource *vres = NULL;
+        if(version) {
+            if(dir->versioning->type == VERSIONING_SIMPLE) {
+                vres = versioning_simple_find(res, version);
+            } else if(dir->versioning->type == VERSIONING_DELTAV) {
+                vres = versioning_deltav_find(res, version);
+            }
+            if(!vres) {
+                fprintf(stderr, "Cannot find  specified version for resource %s\n", res->path);
+                ret = 1;
+                break;
+            }
+        } else {
+            vres = res;
+        }
+        
         // download the resource
         if(!sync_shutdown) {
             if(resource->isdirectory) {
@@ -1706,7 +1729,7 @@
                 }
                 free(local_path);
             } else {
-                if(sync_get_resource(a, dir, res, db, &sync_success)) {
+                if(sync_get_resource(a, dir, res->path, vres, db, &sync_success)) {
                     fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path);
                     sync_error++;
                 }
@@ -1746,7 +1769,7 @@
                 sync_error, str_error);
     }
     
-    return 0;
+    return ret;
 }
 
 UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) {
@@ -2047,6 +2070,58 @@
     }
 }
 
+DavResource *versioning_simple_find(DavResource *res, const char *version) {
+    char *vcol_href = dav_get_string_property_ns(res, DAV_NS, VERSION_PATH_PROPERTY);
+    if(!vcol_href) {
+        return NULL;
+    }
+    DavResource *vcol = dav_resource_new_href(res->session, vcol_href);
+    if(!vcol) {
+        return NULL;
+    }
+    
+    
+    if(dav_load_prop(vcol, defprops, numdefprops)) {
+        print_resource_error(res->session, vcol->path);
+        dav_resource_free(vcol);
+        return NULL;
+    }
+    
+    DavResource *ret = NULL;
+    DavResource *child = vcol->children;
+    while(child) {
+        DavResource *next = child->next;
+        if(!strcmp(child->name, version)) {
+            ret = child;
+        } else {
+            dav_resource_free(child);
+        }
+        child = next;
+    }
+    dav_resource_free(vcol);
+    
+    return ret;
+}
+
+// TODO: remove code dup (main.c: find_version)
+DavResource* versioning_deltav_find(DavResource *res, const char *version) {
+    DavResource *list = dav_versiontree(res, "D:getetag,idav:status,idav:finfo,idav:xattributes,idav:tags");
+    DavResource *ret = NULL;
+    while(list) {
+        DavResource *next = list->next;
+        if(!ret) {
+            char *vname = dav_get_string_property(list, "D:version-name");
+            if(vname && !strcmp(vname, version)) {
+                ret = list;
+            }
+        }
+        if(list != ret) {
+            dav_resource_free(list);
+        }
+        list = next;
+    }
+    return ret;
+}
 
 int sync_set_status(DavResource *res, char *status) {
     DavResource *resource = dav_resource_new(res->session, res->path);
--- a/dav/sync.h	Wed Mar 20 13:20:38 2019 +0100
+++ b/dav/sync.h	Thu Mar 21 10:51:14 2019 +0100
@@ -103,6 +103,7 @@
 int sync_get_resource(
         CmdArgs *a,
         SyncDirectory *dir,
+        const char *path,
         DavResource *res,
         SyncDatabase *db,
         int *counter);
@@ -111,6 +112,7 @@
 void rename_conflict_file(SyncDirectory *dir, SyncDatabase *db, char *path);
 char* create_tmp_download_path(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);
@@ -131,6 +133,9 @@
 
 int resource_pathlen_cmp(LocalResource *res1, LocalResource *res2, void *n);
 
+DavResource *versioning_simple_find(DavResource *res, const char *version);
+DavResource *versioning_deltav_find(DavResource *res, const char *version);
+
 int sync_set_status(DavResource *res, char *status);
 int sync_remove_status(DavResource *res);
 UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res);

mercurial