diff -r d94c4fd35c21 -r fdc2fb090cc7 dav/sync.c --- 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)++;