add parser for extended postgresql webdav config webdav

Sat, 13 Aug 2022 15:56:51 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 13 Aug 2022 15:56:51 +0200
branch
webdav
changeset 373
f78a585e1a2f
parent 372
1d2538a1ba8f
child 374
77506ec632a4

add parser for extended postgresql webdav config

src/server/daemon/config.c file | annotate | diff | comparison | revisions
src/server/daemon/config.h file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/config.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/config.h file | annotate | diff | comparison | revisions
src/server/webdav/requestparser.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.h file | annotate | diff | comparison | revisions
--- a/src/server/daemon/config.c	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/daemon/config.c	Sat Aug 13 15:56:51 2022 +0200
@@ -58,6 +58,11 @@
 
 pool_handle_t *init_pool;
 
+char* cfg_config_file_path(const char *file) {
+    sstr_t base = ST("config/");
+    sstr_t path = sstrcat(2, base, scstr(file));
+    return path.ptr;
+}
 
 int load_init_conf(char *file) {
     log_ereport(LOG_VERBOSE, "load_init_conf");
--- a/src/server/daemon/config.h	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/daemon/config.h	Sat Aug 13 15:56:51 2022 +0200
@@ -94,6 +94,8 @@
     UcxMap   *map;
 };
 
+char* cfg_config_file_path(const char *file);
+
 int load_init_conf(char *file);
 
 void init_server_config_parser();
--- a/src/server/plugins/postgresql/config.c	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/plugins/postgresql/config.c	Sat Aug 13 15:56:51 2022 +0200
@@ -30,6 +30,17 @@
 
 #include "../../util/util.h"
 
+#include <libxml/tree.h>
+
+#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
+
+static int pg_load_ext_dav_config(
+        ServerConfiguration *cfg,
+        pool_handle_t *pool,
+        PgRepository *repo,
+        const char *file);
+
+
 static const char *sql_get_repository_root = "select resource_id from Resource where parent_id is NULL and nodename = $1 ;";
 
 
@@ -130,6 +141,254 @@
     ZERO(repo, sizeof(PgRepository));
     
     repo->resourcepool = sstrdup_a(&a, cfg_respool);
+    repo->root_resource_id = root_id;
+    
+    // check for extended pg dav config
+    if(cfg_dav.length > 0) {
+        // load extended config from config file
+        char *cfg_file_path = cfg_config_file_path(cfg_dav.ptr);
+        if(pg_load_ext_dav_config(cfg, pool, repo, cfg_file_path)) {
+            // error
+            repo = NULL; // no need to cleanup because everything is from the pool
+        }
+        free(cfg_file_path);
+    }
     
     return repo;
 }
+
+static int pg_ext_get_config(
+        ServerConfiguration *cfg,
+        pool_handle_t *pool,
+        PgRepository *repo,
+        const char *file_path,
+        xmlNode *root);
+static int pg_ext_get_extension(
+        PgExtParser *ext,
+        pool_handle_t *pool,
+        PgRepository *repo,
+        const char *file_path,
+        xmlNode *ext_node);
+
+static const char* pg_util_xml_get_text(const xmlNode *elm) {
+    xmlNode *node = elm->children;
+    while(node) {
+        if(node->type == XML_TEXT_NODE) {
+            return (const char*)node->content;
+        }
+        node = node->next;
+    }
+    return NULL;
+}
+
+// load additional postgresql webdav config from the specified xml file
+static int pg_load_ext_dav_config(
+        ServerConfiguration *cfg,
+        pool_handle_t *pool,
+        PgRepository *repo,
+        const char *file_path)
+{
+    UcxAllocator a = util_pool_allocator(pool);
+    
+    // check if the file exists and can be accessed
+    struct stat s;
+    if(stat(file_path, &s)) {
+        if(errno == ENOENT) {
+            log_ereport(LOG_FAILURE, "pg: config file %s not found", file_path);
+        } else {
+            log_ereport(LOG_FAILURE, "pg: cannot access config file %s", file_path);
+        }
+        
+        return 1;
+    }
+    
+    // prepare PgRepository
+    repo->prop_ext = ucx_map_new_a(&a, 8);
+    if(!repo->prop_ext) {
+        log_ereport(LOG_FAILURE, "pg: cannot load config file: OOM");
+        return 1;
+    }
+    
+    // load xml document
+    xmlDoc *doc = xmlReadFile(file_path, NULL, 0);
+    if(!doc) {
+        log_ereport(LOG_FAILURE, "pg: cannot load config file %s", file_path);
+        return 1;
+    }
+    
+    // the root element must be <repository>
+    int ret = 0;
+    xmlNode *xml_root = xmlDocGetRootElement(doc);
+    if(xstreq(xml_root->name, "repository")) {
+        // parse config
+        ret = pg_ext_get_config(cfg, pool, repo, file_path, xml_root);
+    } else {
+        log_ereport(LOG_MISCONFIG, "pg: config %s: root element <repository> expected", file_path);
+        ret = 1;
+    }
+    xmlFreeDoc(doc);
+    
+    return ret;
+}
+
+
+
+
+static int pg_ext_get_config(
+        ServerConfiguration *cfg,
+        pool_handle_t *pool,
+        PgRepository *repo,
+        const char *file_path,
+        xmlNode *root)
+{
+    xmlNode *node = root->children;
+    int ret = 0;
+    
+    PgExtParser parserData;
+    parserData.table_lookup = ucx_map_new(8);
+    parserData.tables = NULL;
+    
+    while(node && !ret) {
+        // currently, the only possible config element is <extension>
+        if(node->type == XML_ELEMENT_NODE) {
+            if(xstreq(node->name, "extension")) {
+                ret = pg_ext_get_extension(&parserData, pool, repo, file_path, node);
+            }
+        }
+        node = node->next;
+    }
+    
+    // convert parserData
+    if(!ret) {
+        size_t ntables = ucx_list_size(parserData.tables);
+        repo->ntables = ntables;
+        repo->tables = pool_calloc(pool, ntables, sizeof(PgExtTable));
+        if(repo->tables) {
+            int i = 0;
+            UCX_FOREACH(elm, parserData.tables) {
+                PgExtTable *tab = elm->data;
+                repo->tables[i++] = *tab;
+            }
+        } else {
+            ret = 1;
+        }
+        
+    }
+    
+    // cleanup parser
+    ucx_list_free_content(parserData.tables, free);
+    ucx_list_free(parserData.tables);
+    ucx_map_free(parserData.table_lookup);
+    
+    return ret;
+}
+
+static int pg_ext_get_extension(
+        PgExtParser *ext,
+        pool_handle_t *pool,
+        PgRepository *repo,
+        const char *file_path,
+        xmlNode *ext_node)
+{
+    UcxAllocator a = util_pool_allocator(pool);
+    
+    xmlNode *node = ext_node->children;
+    
+    const char *table = NULL;
+    UcxList *properties = NULL;
+    
+    while(node) {
+        if(node->type == XML_ELEMENT_NODE) {
+            if(xstreq(node->name, "table")) {
+                const char *value = pg_util_xml_get_text(node);
+                if(!value) {
+                    log_ereport(LOG_MISCONFIG, "pg: config %s: table: missing value", file_path, table);
+                    return 1;
+                } else if(table) {
+                    log_ereport(LOG_MISCONFIG, "pg: config %s: table %s already set", file_path, table);
+                    return 1;
+                }
+                table = value;
+            } else if(xstreq(node->name, "properties")) {
+                // add all child elements to the properties list
+                xmlNode *ps = node->children;
+                while(ps) {
+                    if(ps->type == XML_ELEMENT_NODE) {
+                        // validate
+                        // required: namespace, value
+                        if(!ps->ns || !ps->ns->href) {
+                            log_ereport(LOG_MISCONFIG, "pg: config %s: property %s: missing namespace", file_path, ps->name);
+                            return 1;
+                        }
+                        const char *value = pg_util_xml_get_text(ps);
+                        if(!value) {
+                            log_ereport(LOG_MISCONFIG, "pg: config %s: no column specified for property %s", file_path, ps->name);
+                            return 1;
+                        }
+                        properties = ucx_list_append_a(&a, properties, ps);
+                    }
+                    ps = ps->next;
+                }
+            }
+        }
+        node = node->next;
+    }
+    
+    // check if anything is missing
+    if(!table) {
+        log_ereport(LOG_MISCONFIG, "pg: config %s: missing table value for extension", file_path);
+        return 1;
+    }
+    if(!properties) {
+        log_ereport(LOG_MISCONFIG, "pg: config %s: no properties configured for extension", file_path);
+        return 1;
+    }  
+    
+    // check if the table was already specified
+    if(ucx_map_cstr_get(ext->table_lookup, table)) {
+        log_ereport(LOG_MISCONFIG, "pg: config %s: extension table %s not unique", file_path, table);
+        return 1;
+    }
+    // mark table as used
+    // tabname will be used later, so ist must be allocated in the pool
+    char *tabname = pool_strdup(pool, table);
+    if(!tabname) return 1;
+    
+    // exttable is only used temporarily
+    PgExtTable *exttable = malloc(sizeof(PgExtTable));
+    if(!exttable) return 1;
+    exttable->table = tabname;
+    exttable->isused = 0; // not relevant in config
+    ext->tables = ucx_list_append(ext->tables, exttable);
+    
+    if(ucx_map_cstr_put(ext->table_lookup, table, table)) {
+        return 1;
+    }
+    
+    int tableindex = (int)ucx_list_size(ext->tables);
+    UCX_FOREACH(elm, properties) {
+        xmlNode *ps = elm->data;
+        const char *value = pg_util_xml_get_text(ps);
+        
+        PgPropertyStoreExt *ext = pool_malloc(pool, sizeof(PgPropertyStoreExt));
+        if(!ext) {
+            return 1;
+        }
+        ext->tableindex = tableindex;
+        ext->ns = pool_strdup(pool, (const char*)ps->ns->href);
+        ext->name = pool_strdup(pool, (const char*)ps->name);
+        ext->column = pool_strdup(pool, (const char*)value);
+        if(!ext->ns || !ext->name || !ext->column) {
+            return 1;
+        }
+        
+        UcxKey key = webdav_property_key(ext->ns, ext->name);
+        int err = ucx_map_put(repo->prop_ext, key, ext);
+        free((void*)key.data);
+        if(err) {
+            return 1;
+        }
+    }
+    
+    return 0;
+}
--- a/src/server/plugins/postgresql/config.h	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/plugins/postgresql/config.h	Sat Aug 13 15:56:51 2022 +0200
@@ -46,11 +46,31 @@
 extern "C" {
 #endif
 
+typedef struct {
+    char   *table;
+    WSBool isused;
+} PgExtTable;
+    
 typedef struct PgRepository {
     int64_t root_resource_id;
     sstr_t resourcepool;
+    PgExtTable *tables;
+    size_t ntables;
+    UcxMap *prop_ext;
 } PgRepository;
 
+typedef struct {
+    char *column;
+    char *ns;
+    char *name;
+    int  tableindex;
+} PgPropertyStoreExt;
+
+typedef struct {
+    UcxMap *table_lookup;
+    UcxList *tables;
+} PgExtParser;
+
 PgRepository* pg_init_repo(ServerConfiguration *cfg, pool_handle_t *pool, WSConfigNode *config);
 
 #ifdef __cplusplus
--- a/src/server/webdav/requestparser.c	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/webdav/requestparser.c	Sat Aug 13 15:56:51 2022 +0200
@@ -35,6 +35,7 @@
 #include <ucx/map.h>
 
 #include "requestparser.h"
+#include "webdav.h"
 
 #define xstreq(a, b) !strcmp((const char*)a, (const char*)b)
 
@@ -59,15 +60,6 @@
     return prop;
 }
 
-static UcxKey propkey(const char *ns, const char *name) {
-    UcxKey key;
-    sstr_t data = ucx_sprintf("%s\n%s", name, ns);
-    key.data = data.ptr;
-    key.len = data.length;
-    key.hash = ucx_hash(data.ptr, data.length);
-    return key;
-}
-
 static int parse_prop(
         Session *sn,
         xmlNode *node,
@@ -88,7 +80,7 @@
         const char* name = (const char*)pnode->name;
         
         // check for prop duplicates
-        UcxKey k = propkey((const char*)ns, (const char*)name);
+        UcxKey k = webdav_property_key((const char*)ns, (const char*)name);
         if(!k.data) {
             *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM;
             return 1;
@@ -119,7 +111,7 @@
             *error = PROPPATCH_PARSER_DUPLICATE;
         }
         
-        free(k.data);
+        free((void*)k.data);
         if(*error) {
             return 1;
         }
--- a/src/server/webdav/webdav.c	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/webdav/webdav.c	Sat Aug 13 15:56:51 2022 +0200
@@ -885,6 +885,21 @@
 }
 
 
+
+/* ------------------------------ Utils ------------------------------ */
+
+UcxKey webdav_property_key(const char *ns, const char *name) {
+    UcxKey key;
+    sstr_t data = ucx_sprintf("%s\n%s", name, ns);
+    key.data = data.ptr;
+    key.len = data.length;
+    key.hash = ucx_hash(data.ptr, data.length);
+    return key;
+}
+
+
+
+
 /* ------------------------------ public API ------------------------------ */
 
 int webdav_getdepth(Request *rq) {
--- a/src/server/webdav/webdav.h	Thu Aug 11 20:51:39 2022 +0200
+++ b/src/server/webdav/webdav.h	Sat Aug 13 15:56:51 2022 +0200
@@ -123,6 +123,8 @@
             VFSFile *file,
             WSBool commit);
 
+UcxKey webdav_property_key(const char *ns, const char *name);
+
 #ifdef	__cplusplus
 }
 #endif

mercurial