adds tag conflict detection for dav-sync push

Sun, 04 Feb 2018 11:14:20 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 04 Feb 2018 11:14:20 +0100
changeset 369
4322b8953bd5
parent 368
11797f33bc24
child 370
ab9c5afdc243

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);
 

mercurial