dav/sync.c

changeset 491
fdc2fb090cc7
parent 490
d94c4fd35c21
child 492
7bde663719dc
--- a/dav/sync.c	Sat Oct 27 15:05:13 2018 +0200
+++ b/dav/sync.c	Sun Nov 04 16:35:44 2018 +0100
@@ -1981,6 +1981,135 @@
     return ret;
 }
 
+int gen_random_name(char *buf, size_t len) {
+    char name_prefix[8];
+    memset(name_prefix, 0, 8);
+    dav_rand_bytes(name_prefix, 8);   
+    char *pre = util_hexstr(name_prefix, 8);
+    int64_t ts = (int64_t)time(NULL);
+    int w = snprintf(buf, len, "%s-%"PRId64"\0", pre, ts);
+    free(pre);
+    return w >= len;
+}
+
+#define VBEGIN_ERROR_MKCOL     1
+#define VBEGIN_ERROR_MOVE      2
+#define VBEGIN_ERROR_PROPPATCH 3
+#define VBEGIN_ERROR_CHECKOUT  4
+int versioning_begin(SyncDirectory *dir, DavResource *res, int *exists) {
+    int ret = 0;
+    
+    if(dir->versioning->type == VERSIONING_SIMPLE) {
+        DavResource *history_collection = dav_resource_new(
+                    res->session,
+                    dir->versioning->collection);
+        
+        // get the path to the version history collection for this resource
+        // if propfind fails we just assume that it doesn't exist
+        // better error handling is done later (sync_put_resource)
+        // if there is no history collection for this resource, we create a one
+        
+        DavPropName prop;
+        prop.ns = DAV_NS;
+        prop.name = VERSION_PATH_PROPERTY;
+        *exists = dav_load_prop(res, &prop, 1);
+        
+        char *history_href = NULL;
+        char *vcol_path = dav_get_string_property_ns(res, DAV_NS, VERSION_PATH_PROPERTY);
+        if(!vcol_path) {
+            DavResource *history_res = NULL;
+            
+            // create a new collection for version history
+            // the name is a combination of a random prefix and a timestamp
+            while(!history_res) {
+                char history_res_name[128];
+                gen_random_name(history_res_name, 128);
+
+                history_res = dav_resource_new_child(
+                        res->session,
+                        history_collection,
+                        history_res_name);
+                if(dav_exists(history_res)) {
+                    dav_resource_free(history_res);
+                    history_res = NULL;
+                }
+            }
+            
+            history_res->iscollection = TRUE;
+            if(dav_create(history_res)) {
+                dav_resource_free(history_res);
+                dav_resource_free(history_collection);
+                return VBEGIN_ERROR_MKCOL;
+            }
+            
+            history_href = strdup(history_res->href);
+            
+            dav_resource_free(history_res);
+        } else {
+            history_href = vcol_path;
+        }
+        
+        // find a free url and move 'res' to this location
+        DavResource *version_res = NULL;
+        while(!version_res) {
+            char version_name[128];
+            gen_random_name(version_name, 128);
+
+            char *href = util_concat_path(history_href, version_name);
+            version_res = dav_resource_new_href(res->session, href);
+            free(href);
+
+            char *dest = util_get_url(res->session, version_res->href);
+            int err = dav_moveto(res, dest, FALSE);
+            free(dest);
+
+            if(err) {
+                dav_resource_free(version_res);
+                version_res = NULL;
+                if(res->session->error != DAV_PRECONDITION_FAILED) {
+                    ret = VBEGIN_ERROR_MOVE;
+                    break;
+                }
+            }
+        }
+
+        if(!ret) {
+            dav_set_string_property_ns(version_res, DAV_NS, "origin", res->href);
+            if(dav_store(version_res)) {
+                ret = VBEGIN_ERROR_PROPPATCH;
+            }
+            dav_resource_free(version_res);
+            
+            // we can just set the property here and don't need dav_store
+            // because sync_put_resource will call dav_store(res) later
+            dav_set_string_property_ns(
+                    res,
+                    DAV_NS,
+                    VERSION_PATH_PROPERTY,
+                    history_href);
+        }
+        
+        if(vcol_path != history_href) {
+            free(history_href);
+        }
+        
+        dav_resource_free(history_collection);
+    } else {
+        // Delta V is so much easier :) 
+        if(dav_checkout(res)) {
+            ret = VBEGIN_ERROR_CHECKOUT;
+        }
+    }
+    
+    return ret;
+}
+
+int versioning_end(SyncDirectory *dir, DavResource *res) {
+    if(dir->versioning->type == VERSIONING_DELTAV) {
+        return dav_checkin(res);
+    }
+}
+
 int sync_put_resource(
         SyncDirectory *dir,
         DavResource *res,
@@ -1991,7 +2120,7 @@
     
     SYS_STAT s;
     if(sys_stat(local_path, &s)) {
-        fprintf(stderr, "cannot stat file: %s\n", local_path);
+        fprintf(stderr, "Cannot stat file: %s\n", local_path);
         perror("");
         free(local_path);
         return -1;
@@ -2014,14 +2143,25 @@
             dav_set_property_ns(res, DAV_NS, "tags", prop);
         }
     }
-     
+    
+    int exists = dav_exists(res);
+    if(dir->versioning && dir->versioning->always) {
+        int err = versioning_begin(dir, res, &exists);
+        if(err) {
+            fprintf(stderr, "Cannot store version for resource: %s\n", res->href);
+            free(local_path);
+            return -1;
+        }
+    } else {
+        exists = dav_exists(res);
+    }
+    
     int ret = -1;
-    int created = 0;
     for(int i=0;i<=dir->max_retry;i++) {
-        if(!created && dav_create(res)) {
+        if(!exists && dav_create(res)) {
             continue;
         }
-        created = 1;
+        exists = 1;
         if(dav_store(res)) {
             continue;
         }
@@ -2029,7 +2169,12 @@
         break;
     }
     
-
+    if(dir->versioning && dir->versioning->always) {
+        if(versioning_end(dir, res)) {
+            fprintf(stderr, "Cannot checkin resource\n");
+            ret = 1;
+        }
+    }
 
     if(ret == 0) {
         (*counter)++;

mercurial