src/server/plugins/postgresql/config.c

branch
webdav
changeset 373
f78a585e1a2f
parent 372
1d2538a1ba8f
child 374
77506ec632a4
--- 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;
+}

mercurial