# HG changeset patch # User Olaf Wintermann # Date 1517669993 -3600 # Node ID 11797f33bc24f4d0ab7e891796802e24e437a903 # Parent 4a6a59f89f9ff2088642a0ffbc66c3ba386bba90 adds more tag config options diff -r 4a6a59f89f9f -r 11797f33bc24 dav/scfg.c --- a/dav/scfg.c Fri Feb 02 18:57:21 2018 +0100 +++ b/dav/scfg.c Sat Feb 03 15:59:53 2018 +0100 @@ -155,7 +155,9 @@ conf.store = TAG_STORE_XATTR; conf.local_format = TAG_FORMAT_TEXT; conf.server_format = TAG_FORMAT_XML; - conf.scan = false; + conf.xattr_name = NULL; + conf.detect_changes = false; + conf.conflict = TAG_NO_CONFLICT; xmlNode *c = node->children; // TODO: error handling @@ -184,16 +186,47 @@ } attr = attr->next; } - } else if(xstreq(c->name, "scan")) { + } else if(xstreq(c->name, "detect-changes")) { + if(!value) { + return NULL; + } + conf.detect_changes = util_getboolean(value); + } else if(xstreq(c->name, "xattr-name")) { + if(!value) { + return NULL; + } + conf.xattr_name = strdup(value); + } else if(xstreq(c->name, "on-conflict")) { if(!value) { return NULL; } - conf.scan = util_getboolean(value); + if(xstreq(value, "no_conflict")) { + conf.conflict = TAG_NO_CONFLICT; + } else if(xstreq(value, "keep_local")) { + conf.conflict = TAG_KEEP_LOCAL; + } else if(xstreq(value, "keep_remote")) { + conf.conflict = TAG_KEEP_REMOTE; + } else if(xstreq(value, "merge")) { + conf.conflict = TAG_MERGE; + } else { + fprintf(stderr, "on-conflict: unknown value: %s\n", value); + return NULL; + } } } c = c->next; } + if(conf.store == TAG_STORE_XATTR && !conf.xattr_name) { + switch(conf.local_format) { + default: + case TAG_FORMAT_TEXT: + case TAG_FORMAT_CSV: + case TAG_FORMAT_XML: conf.xattr_name = strdup(DEFAULT_TAG_XATTR); break; + case TAG_FORMAT_MACOS: conf.xattr_name = strdup(MACOS_TAG_XATTR); break; + } + } + TagConfig *tagconfig = malloc(sizeof(TagConfig)); *tagconfig = conf; return tagconfig; diff -r 4a6a59f89f9f -r 11797f33bc24 dav/scfg.h --- a/dav/scfg.h Fri Feb 02 18:57:21 2018 +0100 +++ b/dav/scfg.h Sat Feb 03 15:59:53 2018 +0100 @@ -42,6 +42,9 @@ #define SYNC_CMD_PUSH 2 #define SYNC_CMD_ARCHIVE 4 +#define DEFAULT_TAG_XATTR "tags" +#define MACOS_TAG_XATTR "com.apple.metadata:_kMDItemUserTags" + typedef struct TagConfig TagConfig; typedef struct SyncDirectory { @@ -78,11 +81,21 @@ }; typedef enum TagStore TagStore; +enum TagConflict { + TAG_NO_CONFLICT = 0, + TAG_KEEP_LOCAL, + TAG_KEEP_REMOTE, + TAG_MERGE +}; +typedef enum TagConflict TagConflict; + struct TagConfig { TagStore store; TagFormat local_format; TagFormat server_format; - bool scan; + char *xattr_name; + TagConflict conflict; + bool detect_changes; }; int load_sync_config(); diff -r 4a6a59f89f9f -r 11797f33bc24 dav/sync.c --- a/dav/sync.c Fri Feb 02 18:57:21 2018 +0100 +++ b/dav/sync.c Sat Feb 03 15:59:53 2018 +0100 @@ -974,7 +974,7 @@ if(dir->tagconfig && local_res->tags_updated && !abort) { // get tags from tagstore (xattr or something else) // and store it in resource property - UcxList *tags = sync_get_file_tags(dir, local_res); + UcxList *tags = sync_get_file_tags(dir, local_res, NULL); DavXmlNode *prop = create_xml_taglist(tags); if(prop) { dav_set_property_ns(res, DAV_NS, "tags", prop); @@ -1248,7 +1248,7 @@ res->etag = strdup(db_res->etag); } - if(dir->tagconfig && dir->tagconfig->scan && !res->tags_updated) { + if(dir->tagconfig && dir->tagconfig->detect_changes && !res->tags_updated) { UcxBuffer *tags = sync_get_file_tag_data(dir, res); if(tags) { if(db_res->tags_hash) { @@ -1330,34 +1330,94 @@ } } - if(!tags) { + DavBool store_empty_tags = FALSE; + if(dir->tagconfig->conflict != TAG_NO_CONFLICT) { + DavBool tags_changed = FALSE; + UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed); + if(tags_changed) { + switch(dir->tagconfig->conflict) { + case TAG_KEEP_LOCAL: { + store_empty_tags = TRUE; + tags = local_tags; + // TODO: free tags + break; + } + 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); + // TODO: free tags and local_tags + + tags = new_tags; + + store_empty_tags = TRUE; + + break; + } + } + } else { + // TODO: free local_tags + } + } + + if(!tags && !store_empty_tags) { return 0; } int ret = 0; if(dir->tagconfig->store == TAG_STORE_XATTR) { UcxBuffer *data = NULL; - switch(dir->tagconfig->local_format) { - default: break; - case TAG_FORMAT_TEXT: { - data = create_text_taglist(tags); - break; - } - case TAG_FORMAT_CSV: { - data = create_csv_taglist(tags); - break; + if(tags) { + switch(dir->tagconfig->local_format) { + default: break; + case TAG_FORMAT_TEXT: { + data = create_text_taglist(tags); + break; + } + case TAG_FORMAT_CSV: { + data = create_csv_taglist(tags); + break; + } + case TAG_FORMAT_MACOS: { + data = create_macos_taglist(tags); + break; + } } - case TAG_FORMAT_MACOS: { - data = create_macos_taglist(tags); - break; + + if(data) { + ret = xattr_set(path, dir->tagconfig->xattr_name, data->space, data->pos); + ucx_buffer_free(data); + } else { + ret = -1; } - } - - if(data) { - ret = xattr_set(path, "tags", data->space, data->pos); - ucx_buffer_free(data); } else { - ret = -1; + // TODO: relete xattr + //ret = xattr_remove(path, dir->tagconfig->xattr_name); } } @@ -1376,7 +1436,7 @@ char *local_path = util_concat_path(dir->path, res->path); char* tag_data = xattr_get( local_path, - dir->tagconfig->local_format == TAG_FORMAT_MACOS ? MACOS_TAG_XATTR : "tags", + dir->tagconfig->xattr_name, &tag_length); free(local_path); @@ -1388,7 +1448,9 @@ return buf; } -UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res) { +UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed) { + if(changed) *changed = FALSE; + UcxList *tags = NULL; if(!dir->tagconfig) { @@ -1400,7 +1462,16 @@ sync_get_file_tag_data(dir, res); if(tag_buf) { - res->tags_hash = dav_create_hash(tag_buf->space, tag_buf->size); + char *newhash = dav_create_hash(tag_buf->space, tag_buf->size); + if(res->tags_hash) { + if(changed && strcmp(res->tags_hash, newhash)) { + *changed = TRUE; + } + free(res->tags_hash); + } else { + if(changed) *changed = TRUE; + } + res->tags_hash = newhash; switch(dir->tagconfig->local_format) { default: break; @@ -1421,6 +1492,8 @@ // we created the buffer therefore we destroy it ucx_buffer_free(tag_buf); } + } else if(res->tags_hash) { + if(changed) *changed = TRUE; } } @@ -1453,7 +1526,7 @@ dav_set_content(res, in, (dav_read_func)fread); if(dir->tagconfig) { - UcxList *tags = sync_get_file_tags(dir, local); + UcxList *tags = sync_get_file_tags(dir, local, NULL); // TODO: check remote tags DavXmlNode *prop = create_xml_taglist(tags); if(prop) { dav_set_property_ns(res, DAV_NS, "tags", prop); diff -r 4a6a59f89f9f -r 11797f33bc24 dav/sync.h --- a/dav/sync.h Fri Feb 02 18:57:21 2018 +0100 +++ b/dav/sync.h Sat Feb 03 15:59:53 2018 +0100 @@ -50,8 +50,6 @@ #endif #define STDIN_BUF_SIZE 2048 - -#define MACOS_TAG_XATTR "com.apple.metadata:_kMDItemUserTags" void print_usage(char *cmd); @@ -86,7 +84,7 @@ int sync_set_status(DavResource *res, char *status); 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); +UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed); int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); int sync_put_resource( SyncDirectory *dir,