Mon, 01 Jan 2018 19:53:36 +0100
adds xattr lib and tag support
dav/Makefile | file | annotate | diff | comparison | revisions | |
dav/libxattr.c | file | annotate | diff | comparison | revisions | |
dav/libxattr.h | file | annotate | diff | comparison | revisions | |
dav/main.c | file | annotate | diff | comparison | revisions | |
dav/scfg.c | file | annotate | diff | comparison | revisions | |
dav/scfg.h | file | annotate | diff | comparison | revisions | |
dav/sync.c | file | annotate | diff | comparison | revisions | |
dav/sync.h | file | annotate | diff | comparison | revisions | |
dav/tags.c | file | annotate | diff | comparison | revisions | |
dav/tags.h | file | annotate | diff | comparison | revisions | |
libidav/davqlexec.c | file | annotate | diff | comparison | revisions | |
libidav/methods.c | file | annotate | diff | comparison | revisions | |
libidav/resource.c | file | annotate | diff | comparison | revisions | |
libidav/webdav.h | file | annotate | diff | comparison | revisions | |
libidav/xml.c | file | annotate | diff | comparison | revisions | |
libidav/xml.h | file | annotate | diff | comparison | revisions |
--- a/dav/Makefile Mon Dec 18 16:24:32 2017 +0100 +++ b/dav/Makefile Mon Jan 01 19:53:36 2018 +0100 @@ -44,6 +44,8 @@ SYNC_SRC += db.c SYNC_SRC += error.c SYNC_SRC += assistant.c +SYNC_SRC += libxattr.c +SYNC_SRC += tags.c DAV_OBJ = $(DAV_SRC:%.c=../build/tool/%$(OBJ_EXT))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/libxattr.c Mon Jan 01 19:53:36 2018 +0100 @@ -0,0 +1,444 @@ +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> + +#include "libxattr.h" + +#include <errno.h> +#include <sys/types.h> + +#include <string.h> + +#define LIST_BUF_LEN 1024 +#define LIST_ARRAY_LEN 8 +#define ATTR_BUF_LEN 1024 + +#define ARRAY_ADD(array, pos, len, obj) if(pos >= len) { \ + len *= 2; \ + array = realloc(array, len * sizeof(char*)); \ + } \ + array[pos] = obj; \ + pos++; + + +#ifdef __linux__ +#define XATTR_SUPPORTED +#include <sys/xattr.h> + +static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { + size_t arraylen = LIST_ARRAY_LEN; + size_t arraypos = 0; + char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); + + char *begin = buf; + char *name = NULL; + for(int i=0;i<length;i++) { + if(!name && buf[i] == '.') { + int nslen = (buf+i-begin); + //printf("%.*s\n", nslen, begin); + name = buf + i + 1; + } + if(buf[i] == '\0') { + char *attrname = strdup(name); + ARRAY_ADD(array, arraypos, arraylen, attrname); + begin = buf + i + 1; + name = 0; + } + } + + if(arraypos == 0) { + free(array); + array = NULL; + } + + *nelm = arraypos; + return array; +} + +char ** xattr_list(const char *path, ssize_t *nelm) { + char *list = malloc(LIST_BUF_LEN); + ssize_t len = listxattr(path, list, LIST_BUF_LEN); + if(len == -1) { + switch(errno) { + case ERANGE: { + // buffer too, get size of attribute list + ssize_t newlen = listxattr(path, NULL, 0); + if(newlen > 0) { + // second try + list = realloc(list, newlen); + len = listxattr(path, list, newlen); + if(len != -1) { + // this time it worked + break; + } + } + } + default: { + free(list); + *nelm = -1; + return NULL; + } + } + } + + char **ret = parse_xattrlist(list, len, nelm); + free(list); + return ret; +} + +static char* name2nsname(const char *name) { + // add the 'user' namespace to the name + size_t namelen = strlen(name); + char *attrname = malloc(8 + namelen); + memcpy(attrname, "user.", 5); + memcpy(attrname+5, name, namelen + 1); + return attrname; +} + +char * xattr_get(const char *path, const char *attr, ssize_t *len) { + char *attrname = name2nsname(attr); + + char *buf = malloc(ATTR_BUF_LEN); + ssize_t vlen = getxattr(path, attrname, buf, ATTR_BUF_LEN); + if(vlen < 0) { + switch(errno) { + case ERANGE: { + ssize_t attrlen = getxattr(path, attrname, NULL, 0); + if(attrlen > 0) { + free(buf); + buf = malloc(attrlen); + vlen = getxattr(path, attrname, buf, attrlen); + if(vlen > 0) { + break; + } + } + } + default: { + *len = -1; + free(buf); + free(attrname); + return NULL; + } + } + } + + free(attrname); + *len = vlen; + return buf; +} + +int xattr_set(const char *path, const char *name, const void *value, size_t len) { + char *attrname = name2nsname(name); + int ret = setxattr(path, attrname, value, len, 0); + free(attrname); + return ret; +} + +#endif /* Linux */ + +#ifdef __APPLE__ +#define XATTR_SUPPORTED +#include <sys/xattr.h> + +static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { + size_t arraylen = LIST_ARRAY_LEN; + size_t arraypos = 0; + char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); + + char *name = buf; + for(int i=0;i<length;i++) { + if(buf[i] == '\0') { + char *attrname = strdup(name); + ARRAY_ADD(array, arraypos, arraylen, attrname); + name = buf + i + 1; + } + } + + if(arraypos == 0) { + free(array); + array = NULL; + } + + *nelm = arraypos; + return array; +} + +char ** xattr_list(const char *path, ssize_t *nelm) { + char *list = malloc(LIST_BUF_LEN); + ssize_t len = listxattr(path, list, LIST_BUF_LEN, 0); + if(len == -1) { + switch(errno) { + case ERANGE: { + // buffer too, get size of attribute list + ssize_t newlen = listxattr(path, NULL, 0, 0); + if(newlen > 0) { + // second try + list = realloc(list, newlen); + len = listxattr(path, list, newlen, 0); + if(len != -1) { + // this time it worked + break; + } + } + } + default: { + free(list); + *nelm = -1; + return NULL; + } + } + } + + char **ret = parse_xattrlist(list, len, nelm); + free(list); + return ret; +} + +char * xattr_get(const char *path, const char *attr, ssize_t *len) { + // get attribute length + ssize_t attrlen = getxattr(path, attr, NULL, 0, 0, 0); + if(attrlen < 0) { + *len = -1; + return NULL; + } + + char *buf = malloc(attrlen); + ssize_t vlen = getxattr(path, attr, buf, attrlen, 0, 0); + if(vlen < 0) { + *len = -1; + free(buf); + return NULL; + } + + *len = vlen; + return buf; +} + +int xattr_set(const char *path, const char *name, const void *value, size_t len) { + int ret = setxattr(path, name, value, len, 0, 0); + return ret; +} + +#endif /* Apple */ + +#ifdef __sun +#define XATTR_SUPPORTED +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> + +static int open_attrfile(const char *path, const char *attr, int oflag) { + int file = open(path, O_RDONLY); + if(file == -1) { + return -1; + } + + int attrfile = openat(file, attr, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + close(file); + return attrfile; +} + +char ** xattr_list(const char *path, ssize_t *nelm) { + *nelm = -1; + + int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); + if(attrdir == -1) { + return NULL; + } + + DIR *dir = fdopendir(attrdir); + if(!dir) { + close(attrdir); + return NULL; + } + + size_t arraylen = LIST_ARRAY_LEN; + size_t arraypos = 0; + char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); + + struct dirent *ent; + while((ent = readdir(dir)) != NULL) { + if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "SUNWattr_ro") || !strcmp(ent->d_name, "SUNWattr_rw")) { + continue; + } + char *name = strdup(ent->d_name); + ARRAY_ADD(array, arraypos, arraylen, name); + } + closedir(dir); + + *nelm = arraypos; + return array; +} + +char * xattr_get(const char *path, const char *attr, ssize_t *len) { + *len = -1; + + int attrfile = open_attrfile(path, attr, O_RDONLY|O_XATTR); + if(attrfile == -1) { + return NULL; + } + + struct stat s; + if(fstat(attrfile, &s)) { + close(attrfile); + return NULL; + } + + size_t bufsize = (size_t)s.st_size; + char *buf = malloc(bufsize); + + char *b = buf; + size_t cur = 0; + while(cur < bufsize) { + ssize_t r = read(attrfile, buf + cur, bufsize - cur); + if(r <= 0) { + break; + } + cur += r; + } + + close(attrfile); + if(cur != bufsize) { + free(buf); + return NULL; + } + + *len = (ssize_t)bufsize; + return buf; +} + +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); + if(attrfile == -1) { + return -1; + } + + const char *p = value; + size_t remaining = len; + while(remaining > 0) { + ssize_t w = write(attrfile, p, remaining); + if(w <= 0) { + break; + } + p += w; + remaining -= w; + } + + close(attrfile); + + return remaining > 0 ? -1 : 0; +} + +#endif /* Sun */ + + +#ifdef __FreeBSD__ +#define XATTR_SUPPORTED + +#include <sys/types.h> +#include <sys/extattr.h> + +static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { + size_t arraylen = LIST_ARRAY_LEN; + size_t arraypos = 0; + char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); + + char *name = buf; + for(int i=0;i<length;i++) { + char namelen = buf[i]; + char *name = buf + i + 1; + char *attrname = malloc(namelen + 1); + memcpy(attrname, name, namelen); + attrname[namelen] = 0; + ARRAY_ADD(array, arraypos, arraylen, attrname); + i += namelen; + } + + if(arraypos == 0) { + free(array); + array = NULL; + } + + *nelm = arraypos; + return array; +} + +char ** xattr_list(const char *path, ssize_t *nelm) { + *nelm = -1; + ssize_t lslen = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); + if(lslen <= 0) { + if(lslen == 0) { + *nelm = 0; + } + return NULL; + } + + char *list = malloc(lslen); + ssize_t len = extattr_list_file(path, EXTATTR_NAMESPACE_USER, list, lslen); + if(len == -1) { + free(list); + return NULL; + } + + char **ret = parse_xattrlist(list, len, nelm); + free(list); + return ret; +} + +char * xattr_get(const char *path, const char *attr, ssize_t *len) { + // get attribute length + ssize_t attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, NULL, 0); + if(attrlen < 0) { + *len = -1; + return NULL; + } + + char *buf = malloc(attrlen); + ssize_t vlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, buf, attrlen); + if(vlen < 0) { + *len = -1; + free(buf); + return NULL; + } + + *len = vlen; + return buf; +} + +int xattr_set(const char *path, const char *name, const void *value, size_t len) { + int ret = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, value, len); + return ret; +} + +#endif /* FreeBSD */ + + +#ifndef XATTR_SUPPORTED + +char ** xattr_list(const char *path, ssize_t *nelm) { + *nelm = -1; + return NULL; +} + +char * xattr_get(const char *path, const char *attr, ssize_t *len) { + *len = -1; + return NULL; +} + +int xattr_set(const char *path, const char *name, const void *value, size_t len) { + return -1; +} + +#endif /* unsupported platform */ + +void xattr_free_list(char **attrnames, ssize_t nelm) { + if(attrnames) { + for(int i=0;i<nelm;i++) { + free(attrnames[i]); + } + free(attrnames); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/libxattr.h Mon Jan 01 19:53:36 2018 +0100 @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBXATTR_H +#define LIBXATTR_H + +#include <sys/types.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +char ** xattr_list(const char *path, ssize_t *nelm); + +char * xattr_get(const char *path, const char *attr, ssize_t *len); + +int xattr_set(const char *path, const char *name, const void *value, size_t len); + +void xattr_free_list(char **attrnames, ssize_t nelm); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBXATTR_H */ +
--- a/dav/main.c Mon Dec 18 16:24:32 2017 +0100 +++ b/dav/main.c Mon Jan 01 19:53:36 2018 +0100 @@ -41,6 +41,7 @@ #include <libidav/utils.h> #include <libidav/crypto.h> #include <libidav/session.h> +#include <libidav/xml.h> #include "config.h" #include "error.h" #include "assistant.h" @@ -61,6 +62,7 @@ //define DO_THE_TEST #include <libidav/davqlparser.h> #include <libidav/davqlexec.h> +#include "tags.h" void test() { } @@ -1488,9 +1490,9 @@ char *value = a->argc > 2 ? a->argv[2] : stdin2str(); if(namespace) { - dav_set_property_ns(res, namespace, property, value); + dav_set_string_property_ns(res, namespace, property, value); } else { - dav_set_property(res, property, value); + dav_set_string_property(res, property, value); } int ret = 0; @@ -1789,44 +1791,6 @@ return space; } -static void printnode(FILE *out, UcxMap *nsmap, DavXmlNode *node) { - while(node) { - if(node->type == DAV_XML_ELEMENT) { - char *prefix = ucx_map_cstr_get(nsmap, node->namespace); - char *freethis = NULL; - char *tagend = node->children ? ">" : " />"; - if(!prefix) { - sstr_t newpre = ucx_sprintf("x%d", (int)nsmap->count); - ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr); - freethis = newpre.ptr; - prefix = newpre.ptr; - fprintf( - out, - "<%s:%s xmlns:%s=\"%s\"%s", - prefix, - node->name, - prefix, - node->namespace, - tagend); - } else { - fprintf(out, "<%s:%s%s", prefix, node->name, tagend); - } - - if(node->children) { - printnode(out, nsmap, node->children); - fprintf(out, "</%s:%s>", prefix, node->name); - } - if(freethis) { - free(freethis); - } - } else if(node->type == DAV_XML_TEXT) { - fwrite(node->content, 1, node->contentlength, out); - } - - node = node->next; - } -} - void printxmldoc(FILE *out, char *root, char *rootns, DavXmlNode *content) { UcxMap *nsmap = ucx_map_new(16); @@ -1834,9 +1798,14 @@ fprintf(out, "%s", "<?xml version=\"1.0\"?>\n"); fprintf(out, "<x0:%s xmlns:x0=\"%s\">", root, rootns); - printnode(out, nsmap, content); + dav_print_node(out, (write_func)fwrite, nsmap, content); fprintf(out, "</x0:%s>\n", root); + + // cleanup namespace map + ucx_map_cstr_remove(nsmap, rootns); + ucx_map_free_content(nsmap, free); + ucx_map_free(nsmap); }
--- a/dav/scfg.c Mon Dec 18 16:24:32 2017 +0100 +++ b/dav/scfg.c Mon Jan 01 19:53:36 2018 +0100 @@ -125,6 +125,72 @@ return 0; } + +static char* get_attr_content(xmlNode *node) { + // TODO: remove code duplication (util_xml_get_text) and config.h + while(node) { + if(node->type == XML_TEXT_NODE) { + return (char*)node->content; + } + node = node->next; + } + return NULL; +} + +static TagFormat str2tagformat(const char *str) { + if(!strcmp(str, "text")) { + return TAG_FORMAT_TEXT; + } else if(!strcmp(str, "csv")) { + return TAG_FORMAT_CSV; + } else if(!strcmp(str, "xml")) { + return TAG_FORMAT_XML; + } + return TAG_FORMAT_UNKNOWN; +} + +static TagConfig* parse_tagconfig(xmlNode *node) { + TagConfig conf; + conf.store = TAG_STORE_XATTR; + conf.local_format = TAG_FORMAT_TEXT; + conf.server_format = TAG_FORMAT_XML; + xmlNode *c = node->children; + + // TODO: error handling + while(c) { + if(node->type == XML_ELEMENT_NODE) { + char *value = util_xml_get_text(c); + if(xstreq(c->name, "local-store")) { + if(!value) { + return NULL; + } else if(xstreq(value, "xattr")) { + conf.store = TAG_STORE_XATTR; + } else { + return NULL; + } + + xmlAttr *attr = c->properties; + while(attr) { + if(attr->type == XML_ATTRIBUTE_NODE) { + const char *value = get_attr_content(attr->children); + if(!value) { + break; + } + if(xstreq(attr->name, "format")) { + conf.local_format = str2tagformat(value); + } + } + attr = attr->next; + } + } + } + c = c->next; + } + + TagConfig *tagconfig = malloc(sizeof(TagConfig)); + *tagconfig = conf; + return tagconfig; +} + static int scfg_load_directory(xmlNode *node) { char *name = NULL; char *path = NULL; @@ -132,6 +198,7 @@ char *collection = NULL; char *repository = NULL; char *database = NULL; + TagConfig *tagconfig = NULL; UcxList *include = NULL; UcxList *exclude = NULL; int max_retry = 0; @@ -172,6 +239,8 @@ } } else if(xstreq(node->name, "database")) { database = value; + } else if(xstreq(node->name, "tags")) { + tagconfig = parse_tagconfig(node); } else if(xstreq(node->name, "max-retry")) { int64_t i; if(util_strtoint(value, &i) && i >= 0) { @@ -247,6 +316,7 @@ dir->collection = collection ? strdup(collection) : NULL; dir->repository = strdup(repository); dir->database = strdup(database); + dir->tagconfig = tagconfig; dir->max_retry = max_retry; dir->allow_cmd = allow_cmd; dir->backuppull = backuppull;
--- a/dav/scfg.h Mon Dec 18 16:24:32 2017 +0100 +++ b/dav/scfg.h Mon Jan 01 19:53:36 2018 +0100 @@ -42,6 +42,8 @@ #define SYNC_CMD_PUSH 2 #define SYNC_CMD_ARCHIVE 4 +typedef struct TagConfig TagConfig; + typedef struct SyncDirectory { char *name; char *path; @@ -49,6 +51,7 @@ char *collection; char *repository; char *database; + TagConfig *tagconfig; UcxList *include; UcxList *exclude; int max_retry; @@ -59,6 +62,27 @@ bool lockpush; } SyncDirectory; + +enum TagFormat { + TAG_FORMAT_TEXT = 0, + TAG_FORMAT_CSV, + TAG_FORMAT_XML, + TAG_FORMAT_UNKNOWN +}; +typedef enum TagFormat TagFormat; + +enum TagStore { + TAG_STORE_XATTR = 0, + TAG_STORE_UNKNOWN +}; +typedef enum TagStore TagStore; + +struct TagConfig { + TagStore store; + TagFormat local_format; + TagFormat server_format; +}; + int load_sync_config(); UcxMapIterator scfg_directory_iterator();
--- a/dav/sync.c Mon Dec 18 16:24:32 2017 +0100 +++ b/dav/sync.c Mon Jan 01 19:53:36 2018 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Olaf Wintermann. All rights reserved. + * Copyright 2018 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -49,7 +49,8 @@ #include "db.h" #include "error.h" #include "assistant.h" - +#include "libxattr.h" +#include "tags.h" #include "sync.h" #include "libidav/session.h" @@ -344,7 +345,7 @@ } int ret = 0; - DavResource *ls = dav_query(sn, "select D:getetag,idav:status from / with depth = infinity"); + DavResource *ls = dav_query(sn, "select D:getetag,idav:status,idav:tags from / with depth = infinity"); if(!ls) { print_resource_error(sn, "/"); if(locked) { @@ -584,6 +585,8 @@ local->last_modified = 0; ucx_map_cstr_put(db->resources, local->path, local); } + + sync_store_tags(dir, local_path, local, res); } } else { if(!tmp_path) { @@ -604,6 +607,8 @@ } fclose(out); + sync_store_tags(dir, tmp_path, local, res); + if(ret == 0) { (*counter)++; @@ -1234,7 +1239,7 @@ int sync_set_status(DavResource *res, char *status) { DavResource *resource = dav_resource_new(res->session, res->path); - dav_set_property(resource, "idav:status", status); + dav_set_string_property(resource, "idav:status", status); int ret = dav_store(resource); dav_resource_free(resource); return ret; @@ -1248,6 +1253,81 @@ return ret; } +int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res) { + if(!dir->tagconfig) { + return 0; + } + + UcxList *tags = NULL; + if(dir->tagconfig) { + DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_NS, "tags"); + if(tagsprop) { + tags = parse_dav_xml_taglist(tagsprop); + } + } + + if(!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(data) { + ret = xattr_set(path, "tags", data->space, data->pos); + ucx_buffer_free(data); + } else { + ret = -1; + } + } + + // TODO: free stuff + + return ret; +} + +UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res) { + UcxList *tags = NULL; + + if(!dir->tagconfig) { + return NULL; + } + if(dir->tagconfig->store == TAG_STORE_XATTR) { + ssize_t tag_length = 0; + char *local_path = util_concat_path(dir->path, res->path); + char* tag_data = xattr_get(local_path, "tags", &tag_length); + free(local_path); + + if(tag_length > 0) { + switch(dir->tagconfig->local_format) { + default: break; + case TAG_FORMAT_TEXT: { + tags = parse_text_taglist(tag_data, tag_length); + break; + } + case TAG_FORMAT_CSV: { + tags = parse_csv_taglist(tag_data, tag_length); + break; + } + } + } + } + + return tags; +} + int sync_put_resource( SyncDirectory *dir, DavResource *res, @@ -1273,6 +1353,14 @@ dav_set_content(res, in, (dav_read_func)fread); + if(dir->tagconfig) { + UcxList *tags = sync_get_file_tags(dir, local); + DavXmlNode *prop = create_xml_taglist(tags); + if(prop) { + dav_set_property_ns(res, DAV_NS, "tags", prop); + } + } + int ret = -1; int created = 0; for(int i=0;i<=dir->max_retry;i++) {
--- a/dav/sync.h Mon Dec 18 16:24:32 2017 +0100 +++ b/dav/sync.h Mon Jan 01 19:53:36 2018 +0100 @@ -83,6 +83,8 @@ int sync_set_status(DavResource *res, char *status); int sync_remove_status(DavResource *res); +UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res); +int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); int sync_put_resource( SyncDirectory *dir, DavResource *res,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/tags.c Mon Jan 01 19:53:36 2018 +0100 @@ -0,0 +1,177 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <ucx/string.h> +#include <ucx/utils.h> + +#include "libxattr.h" + +#include "tags.h" + +UcxList* parse_text_taglist(const char *buf, size_t length) { + UcxList *tags = NULL; + + int line_start = 0; + for(int i=0;i<length;i++) { + if(buf[i] == '\n' || i == length-1) { + sstr_t line = sstrtrim(sstrn((char*)buf + line_start, i - line_start)); + if(line.length > 0) { + DavTag *tag = calloc(1, sizeof(DavTag)); + tag->name = sstrdup(line).ptr; + tag->color = NULL; + tags = ucx_list_append(tags, tag); + } + line_start = i+1; + } + } + + return tags; +} + +UcxBuffer* create_text_taglist(UcxList *tags) { + if(!tags) { + return NULL; + } + + UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + UCX_FOREACH(elm, tags) { + DavTag *tag = elm->data; + ucx_bprintf(buf, "%s\n", tag->name); + } + return buf; +} + + +UcxList* parse_csv_taglist(const char *buf, size_t length) { + UcxList *taglist = NULL; + + sstr_t str = sstrn((char*)buf, length); + ssize_t count = 0; + sstr_t *tags = sstrsplit(str, S(","), &count); + for(int i=0;i<count;i++) { + DavTag *tag = malloc(sizeof(DavTag)); + tag->name = sstrdup(sstrtrim(tags[i])).ptr; + tag->color = NULL; + taglist = ucx_list_append(taglist, tag); + free(tags[i].ptr); + } + if(tags) { + free(tags); + } + return taglist; +} + +UcxBuffer* create_csv_taglist(UcxList *tags) { + UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + int insertsep = 0; + UCX_FOREACH(elm, tags) { + DavTag *tag = elm->data; + if(insertsep) { + ucx_buffer_putc(buf, ','); + } + ucx_buffer_puts(buf, tag->name); + insertsep = 1; + } + return buf; +} + + +static DavTag* parse_xml_dav_tag(DavXmlNode *node) { + char *name = NULL; + + DavXmlNode *c = node->children; + while(c) { + if(c->type == DAV_XML_ELEMENT) { + char *value = dav_xml_getstring(c->children); + if(value) { + if(!strcmp(c->namespace, DAV_NS)) { + if(!strcmp(c->name, "name")) { + char *value = dav_xml_getstring(c->children); + if(value) { + name = value; + } + } + // TODO: color, ... + } + } + } + c = c->next; + } + + DavTag *tag = NULL; + if(name) { + tag = malloc(sizeof(DavTag)); + tag->name = strdup(name); + tag->color = NULL; + } + return tag; +} + +UcxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) { + UcxList *tags = NULL; + + DavXmlNode *node = taglistnode; + while(node) { + if(node->type == DAV_XML_ELEMENT) { + if(!strcmp(node->namespace, DAV_NS) && !strcmp(node->name, "tag")) { + DavTag *tag = parse_xml_dav_tag(node); + if(tag) { + tags = ucx_list_append(tags, tag); + } + } + } + node = node->next; + } + + return tags; +} + +DavXmlNode* create_xml_taglist(UcxList *tags) { + DavXmlNode *tag1 = NULL; + DavXmlNode *lasttag = NULL; + UCX_FOREACH(elm, tags) { + DavTag *tag = elm->data; + + DavXmlNode *tagelm = dav_xml_createnode(DAV_NS, "tag"); + DavXmlNode *tagname = dav_xml_createnode_with_text(DAV_NS, "name", tag->name); + // TODO: color + tagelm->children = tagname; + + if(lasttag) { + lasttag->next = tagelm; + tagelm->prev = lasttag; + } else { + tag1 = tagelm; + } + lasttag = tagelm; + } + return tag1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/tags.h Mon Jan 01 19:53:36 2018 +0100 @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TAGS_H +#define TAGS_H + +#include <ucx/string.h> +#include <ucx/buffer.h> +#include <ucx/list.h> + +#include <libidav/webdav.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DavTag { + char *name; + char *color; +} DavTag; + +UcxList* parse_text_taglist(const char *buf, size_t length); +UcxBuffer* create_text_taglist(UcxList *tags); + +UcxList* parse_csv_taglist(const char *buf, size_t length); +UcxBuffer* create_csv_taglist(UcxList *tags); + +UcxList* parse_dav_xml_taglist(DavXmlNode *taglistnode); +DavXmlNode* create_xml_taglist(UcxList *tags); + + +#ifdef __cplusplus +} +#endif + +#endif /* TAGS_H */ +
--- a/libidav/davqlexec.c Mon Dec 18 16:24:32 2017 +0100 +++ b/libidav/davqlexec.c Mon Jan 01 19:53:36 2018 +0100 @@ -279,7 +279,7 @@ field_result.length)); } } else if(field_result.type == 2) { - // TODO: + node = dav_copy_node(field_result.data.node); } else { // unknown type // TODO: error @@ -1072,10 +1072,17 @@ } case DAVQL_CMD_PROP_IDENTIFIER: { //printf("property %s:%s\n", cmd.data.property.ns, cmd.data.property.name); - char *value = dav_get_string_property_ns(res, cmd.data.property.ns, cmd.data.property.name); - obj.type = 1; - obj.length = value ? strlen(value) : 0; - obj.data.string = value; + //char *value = dav_get_string_property_ns(res, cmd.data.property.ns, cmd.data.property.name); + DavXmlNode *value = dav_get_property_ns(res, cmd.data.property.ns, cmd.data.property.name); + if(dav_xml_isstring(value)) { + obj.type = 1; + obj.length = (uint32_t)value->contentlength; + obj.data.string = value->content; + } else { + obj.type = 2; + obj.length = 0; + obj.data.node = value; + } DAVQL_PUSH(obj); break; }
--- a/libidav/methods.c Mon Dec 18 16:24:32 2017 +0100 +++ b/libidav/methods.c Mon Jan 01 19:53:36 2018 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Olaf Wintermann. All rights reserved. + * Copyright 2018 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -817,7 +817,7 @@ if(content->type == DAV_XML_TEXT) { ucx_buffer_write(content->content, 1, content->contentlength, buf); } else { - // TODO: implement + dav_print_node(buf, (write_func)ucx_buffer_write, namespaces, content); } // end tag
--- a/libidav/resource.c Mon Dec 18 16:24:32 2017 +0100 +++ b/libidav/resource.c Mon Jan 01 19:53:36 2018 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Olaf Wintermann. All rights reserved. + * Copyright 2018 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -475,14 +475,14 @@ return property; } -void dav_set_property(DavResource *res, char *name, char *value) { +void dav_set_string_property(DavResource *res, char *name, char *value) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); - dav_set_property_ns(res, pns, pname, value); + dav_set_string_property_ns(res, pns, pname, value); } -void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { +void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value) { UcxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; @@ -502,6 +502,33 @@ data->set = ucx_list_append_a(a, data->set, property); } +void dav_set_property(DavResource *res, char *name, DavXmlNode *value) { + char *pns; + char *pname; + dav_get_property_namespace_str(res->session->context, name, &pns, &pname); + dav_set_property_ns(res, pns, pname, value); +} + +void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { + UcxAllocator *a = res->session->mp->allocator; + DavResourceData *data = res->data; + + DavProperty *property = dav_session_malloc( + res->session, + sizeof(DavProperty)); + property->name = dav_session_strdup(res->session, name); + property->value = value; // TODO: copy node? + + DavNamespace *namespace = dav_session_malloc( + res->session, + sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = dav_session_strdup(res->session, ns); + property->ns = namespace; + + data->set = ucx_list_append_a(a, data->set, property); +} + void dav_remove_property(DavResource *res, char *name) { char *pns; char *pname;
--- a/libidav/webdav.h Mon Dec 18 16:24:32 2017 +0100 +++ b/libidav/webdav.h Mon Jan 01 19:53:36 2018 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Olaf Wintermann. All rights reserved. + * Copyright 2018 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -288,8 +288,10 @@ DavXmlNode* dav_get_property_ns(DavResource *res, char *ns, char *name); char* dav_get_string_property(DavResource *res, char *name); char* dav_get_string_property_ns(DavResource *res, char *ns, char *name); -void dav_set_property(DavResource *res, char *name, char *value); -void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value); +void dav_set_string_property(DavResource *res, char *name, char *value); +void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value); +void dav_set_property(DavResource *res, char *name, DavXmlNode *value); +void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value); void dav_remove_property(DavResource *res, char *name); void dav_remove_property_ns(DavResource *res, char *ns, char *name); @@ -307,10 +309,18 @@ // private int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf); +/* ------------------------ xml functions ------------------------ */ char* dav_xml_getstring(DavXmlNode *node); DavBool dav_xml_isstring(DavXmlNode *node); DavXmlNode* dav_text_node(DavSession *sn, char *text); +DavXmlNode* dav_copy_node(DavXmlNode *node); + +DavXmlNode* dav_xml_createnode(const char *ns, const char *name); +DavXmlNode* dav_xml_createnode_with_text(const char *ns, const char *name, const char *text); +DavXmlNode* dav_xml_createtextnode(const char *text); +void dav_xml_add_child(DavXmlNode *node, DavXmlNode *child); + #ifdef __cplusplus } #endif
--- a/libidav/xml.c Mon Dec 18 16:24:32 2017 +0100 +++ b/libidav/xml.c Mon Jan 01 19:53:36 2018 +0100 @@ -30,6 +30,8 @@ #include <stdlib.h> #include <string.h> +#include <ucx/utils.h> + #include "xml.h" static DavXmlNodeType convert_type(xmlElementType type) { @@ -135,6 +137,40 @@ } } +void dav_print_node(void *stream, write_func writef, UcxMap *nsmap, DavXmlNode *node) { + while(node) { + if(node->type == DAV_XML_ELEMENT) { + char *prefix = ucx_map_cstr_get(nsmap, node->namespace); + char *tagend = node->children ? ">" : " />"; + if(!prefix) { + sstr_t newpre = ucx_sprintf("x%d", (int)nsmap->count); + ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr); + prefix = newpre.ptr; + ucx_fprintf( + stream, + writef, + "<%s:%s xmlns:%s=\"%s\"%s", + prefix, + node->name, + prefix, + node->namespace, + tagend); + } else { + ucx_fprintf(stream, writef, "<%s:%s%s", prefix, node->name, tagend); + } + + if(node->children) { + dav_print_node(stream, writef, nsmap, node->children); + ucx_fprintf(stream, writef, "</%s:%s>", prefix, node->name); + } + } else if(node->type == DAV_XML_TEXT) { + writef(node->content, 1, node->contentlength, stream); + } + + node = node->next; + } +} + /* ------------------------- public API ------------------------- */ char* dav_xml_getstring(DavXmlNode *node) { @@ -163,3 +199,79 @@ return newxn; } + + + +DavXmlNode* dav_copy_node(DavXmlNode *node) { + DavXmlNode *ret = NULL; + DavXmlNode *prev = NULL; + while(node) { + DavXmlNode *copy = calloc(1, sizeof(DavXmlNode)); + copy->type = node->type; + if(node->type == DAV_XML_ELEMENT) { + copy->namespace = strdup(node->namespace); + copy->name = strdup(node->name); + copy->children = dav_copy_node(node->children); + } else { + copy->contentlength = node->contentlength; + copy->content = malloc(node->contentlength+1); + memcpy(copy->content, node->content, node->contentlength); + copy->content[copy->contentlength] = 0; + } + if(!ret) { + ret = copy; + } + if(prev) { + prev->next = copy; + copy->prev = prev; + } + prev = copy; + node = node->next; + } + return ret; +} + + +DavXmlNode* dav_xml_createnode(const char *ns, const char *name) { + DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); + node->type = DAV_XML_ELEMENT; + node->namespace = strdup(ns); + node->name = strdup(name); + return node; +} + +DavXmlNode* dav_xml_createnode_with_text(const char *ns, const char *name, const char *text) { + DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); + node->type = DAV_XML_ELEMENT; + node->namespace = strdup(ns); + node->name = strdup(name); + + DavXmlNode *textnode = dav_xml_createtextnode(text); + node->children = textnode; + + return node; +} + +DavXmlNode* dav_xml_createtextnode(const char *text) { + DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); + node->type = DAV_XML_TEXT; + sstr_t content = sstrdup(sstr((char*)text)); + node->content = content.ptr; + node->contentlength = content.length; + return node; +} + +void dav_xml_add_child(DavXmlNode *node, DavXmlNode *child) { + DavXmlNode *last_child = NULL; + DavXmlNode *c = node->children; + while(c) { + last_child = c; + c = c->next; + } + if(last_child) { + last_child->next = child; + child->prev = last_child; + } else { + node->children = child; + } +}