Sun, 04 Feb 2018 11:14:20 +0100
adds tag conflict detection for dav-sync push
dav/libxattr.c | file | annotate | diff | comparison | revisions | |
dav/sync.c | file | annotate | diff | comparison | revisions | |
dav/sync.h | file | annotate | diff | comparison | revisions |
--- a/dav/libxattr.c Sat Feb 03 15:59:53 2018 +0100 +++ b/dav/libxattr.c Sun Feb 04 11:14:20 2018 +0100 @@ -339,7 +339,7 @@ } int xattr_set(const char *path, const char *name, const void *value, size_t len) { - int attrfile = open_attrfile(path, name, O_CREAT|O_WRONLY|O_XATTR); + int attrfile = open_attrfile(path, name, O_CREAT|O_WRONLY|O_XATTR|O_TRUNC); if(attrfile == -1) { return -1; }
--- a/dav/sync.c Sat Feb 03 15:59:53 2018 +0100 +++ b/dav/sync.c Sun Feb 04 11:14:20 2018 +0100 @@ -548,6 +548,7 @@ } if(!strcmp(e.ptr, local->etag)) { // resource is already up-to-date on the client + sync_store_tags(dir, local_path, local, res); free(local_path); return 0; } @@ -1317,6 +1318,38 @@ return ret; } +UcxList* sync_merge_tags(UcxList *tags1, UcxList *tags2) { + // this map is used to check the existence of tags + UcxMap *tag_map = ucx_map_new(32); + // merged taglist + UcxList *new_tags = NULL; + + // add all local tags + UCX_FOREACH(elm, tags1) { + DavTag *t = elm->data; + ucx_map_cstr_put(tag_map, t->name, t); + DavTag *newt = calloc(1, sizeof(DavTag)); + newt->color = t->color ? strdup(t->color) : NULL; + newt->name = strdup(t->name); + new_tags = ucx_list_append(new_tags, newt); + } + // check if a remote tag is already in the map + // and if not add it to the new taglist + UCX_FOREACH(elm, tags2) { + DavTag *t = elm->data; + if(!ucx_map_cstr_get(tag_map, t->name)) { + DavTag *newt = calloc(1, sizeof(DavTag)); + newt->color = t->color ? strdup(t->color) : NULL; + newt->name = strdup(t->name); + new_tags = ucx_list_append(new_tags, newt); + } + } + + ucx_map_free(tag_map); + + return new_tags; +} + int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res) { if(!dir->tagconfig) { return 0; @@ -1327,6 +1360,7 @@ DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_NS, "tags"); if(tagsprop) { tags = parse_dav_xml_taglist(tagsprop); + } } @@ -1344,39 +1378,12 @@ } case TAG_KEEP_REMOTE: break; case TAG_MERGE: { - // this map is used to check the existence of tags - UcxMap *tag_map = ucx_map_new(32); - // merged taglist - UcxList *new_tags = NULL; - - // add all local tags - UCX_FOREACH(elm, local_tags) { - DavTag *t = elm->data; - ucx_map_cstr_put(tag_map, t->name, t); - DavTag *newt = calloc(1, sizeof(DavTag)); - newt->color = t->color ? strdup(t->color) : NULL; - newt->name = strdup(t->name); - new_tags = ucx_list_append(new_tags, newt); - } - // check if a remote tag is already in the map - // and if not add it to the new taglist - UCX_FOREACH(elm, tags) { - DavTag *t = elm->data; - if(!ucx_map_cstr_get(tag_map, t->name)) { - DavTag *newt = calloc(1, sizeof(DavTag)); - newt->color = t->color ? strdup(t->color) : NULL; - newt->name = strdup(t->name); - new_tags = ucx_list_append(new_tags, newt); - } - } - - ucx_map_free(tag_map); + UcxList *new_tags = sync_merge_tags(local_tags, tags); // TODO: free tags and local_tags - tags = new_tags; - - store_empty_tags = TRUE; - + store_empty_tags = TRUE; + // make sure the merged tags will be pushed the next time + local->tags_updated = TRUE; break; } } @@ -1410,7 +1417,11 @@ } if(data) { - ret = xattr_set(path, dir->tagconfig->xattr_name, data->space, data->pos); + char *data_hash = dav_create_hash(data->space, data->size); + if(!local->tags_hash || strcmp(data_hash, local->tags_hash)) { + printf("update: %s\n", local->path); + ret = xattr_set(path, dir->tagconfig->xattr_name, data->space, data->pos); + } ucx_buffer_free(data); } else { ret = -1; @@ -1430,6 +1441,9 @@ if(!dir->tagconfig) { return NULL; } + if(res->cached_tags) { + return res->cached_tags; + } UcxBuffer *buf = NULL; if(dir->tagconfig->store == TAG_STORE_XATTR) { ssize_t tag_length = 0; @@ -1445,6 +1459,7 @@ buf->size = (size_t)tag_length; } } + res->cached_tags = buf; return buf; } @@ -1488,10 +1503,7 @@ break; } } - if(!res->cached_tags) { - // we created the buffer therefore we destroy it - ucx_buffer_free(tag_buf); - } + res->cached_tags = tag_buf; } else if(res->tags_hash) { if(changed) *changed = TRUE; } @@ -1553,7 +1565,7 @@ (*counter)++; // check contentlength and get new etag - DavResource *up_res = dav_get(res->session, res->path, "D:getetag,idav:status"); + DavResource *up_res = dav_get(res->session, res->path, "D:getetag,idav:status,idav:tags"); if(up_res) { // the new content length must be equal or greater than the file size @@ -1658,6 +1670,80 @@ return ret; } +int sync_update_tags(SyncDirectory *dir, DavSession *sn, DavResource *res, LocalResource *local) { + if(!dir->tagconfig) { + return 0; + } + + // get local tags + DavBool changed = FALSE; + UcxList *tags = sync_get_file_tags(dir, local, &changed); + if(!tags || !changed) { + return 0; + } + + // local tags changed + DavBool update_tags = TRUE; + if(dir->tagconfig->conflict != TAG_NO_CONFLICT) { + // get tags from the server + DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_NS, "tags"); + UcxList *remote_tags = NULL; + if(tagsprop) { + remote_tags = parse_dav_xml_taglist(tagsprop); + } + + if(remote_tags) { + // compare the local tags with server tags + DavBool tags_equal = TRUE; + UcxList *local_t = tags; + UcxList *remote_t = remote_tags; + while(local_t) { + if(!remote_t) { + tags_equal = FALSE; + break; + } + + DavTag *lt = local_t->data; + DavTag *rt = remote_t->data; + if(strcmp(lt->name, rt->name)) { + tags_equal = FALSE; + break; + } + + local_t = local_t->next; + remote_t = remote_t->next; + } + + if(!tags_equal) { + switch(dir->tagconfig->conflict) { + case TAG_KEEP_LOCAL: break; + case TAG_KEEP_REMOTE: + case TAG_MERGE: { + printf("skip update: %s\n", local->path); + update_tags = FALSE; + break; + } + } + } + + // TODO: free remote_tags + } + } + + if(update_tags) { + DavXmlNode *prop = create_xml_taglist(tags); + if(prop) { + dav_set_property_ns(res, DAV_NS, "tags", prop); + if(dav_store(res)) { + print_resource_error(sn, local->path); + } + } + } + + // TODO: free stuff + return 0; +} + void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db) { char **dc = calloc(sizeof(void*), db->conflict->count);
--- a/dav/sync.h Sat Feb 03 15:59:53 2018 +0100 +++ b/dav/sync.h Sun Feb 04 11:14:20 2018 +0100 @@ -85,6 +85,7 @@ int sync_remove_status(DavResource *res); UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res); UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed); +UcxList* sync_merge_tags(UcxList *tags1, UcxList *tags2); int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); int sync_put_resource( SyncDirectory *dir, @@ -93,6 +94,7 @@ int *counter); int sync_mkdir(SyncDirectory *dir, DavResource *res, LocalResource *local); int sync_delete_remote_resource(DavSession *sn, LocalResource *res, int *counter); +int sync_update_tags(SyncDirectory *dir, DavSession *sn, DavResource *res, LocalResource *local); void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db);