#include "config.h"
#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 ;";
int pg_lookup_root(ResourceData *res,
const char *rootnode,
int64_t *rootid) {
PGconn *connection = res->data;
PGresult *result = PQexecParams(
connection,
sql_get_repository_root,
1,
NULL,
&rootnode,
NULL,
NULL,
0);
if(!result) {
log_ereport(
LOG_FAILURE,
"pg: root lookup failed: %s", PQerrorMessage(connection));
return 1;
}
int ret =
0;
int nrows = PQntuples(result);
if(nrows ==
1) {
char *resource_id_str = PQgetvalue(result,
0,
0);
if(resource_id_str) {
if(!util_strtoint(resource_id_str, rootid)) {
log_ereport(
LOG_FAILURE,
"pg: unexpected result for column resource_id", rootnode);
ret =
1;
}
}
}
else {
log_ereport(
LOG_FAILURE,
"pg: cannot find root resource ''%s''", rootnode);
ret =
1;
}
PQclear(result);
return ret;
}
PgRepository* pg_init_repo(ServerConfiguration *cfg,
pool_handle_t *pool, WSConfigNode *config) {
CxAllocator *a = pool_allocator(pool);
ConfigNode *pg = serverconfig_get_node(config,
CONFIG_NODE_OBJECT, cx_str(
"Postgresql"));
if(!pg) {
log_ereport(
LOG_MISCONFIG,
"pg_init_repo: missing postgresql config object");
return NULL;
}
cxstring cfg_respool = serverconfig_object_directive_value(pg, cx_str(
"ResourcePool"));
cxstring cfg_rootid = serverconfig_object_directive_value(pg, cx_str(
"RootId"));
cxstring cfg_rootnode = serverconfig_object_directive_value(pg, cx_str(
"RootNode"));
cxstring cfg_dav = serverconfig_object_directive_value(pg, cx_str(
"PGDavConfig"));
if(cfg_respool.length ==
0) {
return NULL;
}
int64_t root_id =
1;
if(cfg_rootid.length >
0) {
if(!util_strtoint(cfg_rootid.ptr, &root_id)) {
log_ereport(
LOG_MISCONFIG,
"pg_init_repo: RootId parameter is not an integer: %s", cfg_rootid.ptr);
return NULL;
}
}
if(cfg_rootid.length >
0 && cfg_rootnode.length >
0) {
log_ereport(
LOG_WARN,
"log_init_repo: RootId and RootNode specified, RootNode ignored");
}
else if(cfg_rootnode.length >
0) {
ResourceData *res = resourcepool_cfg_lookup(cfg, cfg_respool.ptr,
0);
if(!res) {
log_ereport(
LOG_MISCONFIG,
"pg_init_repo: resource lookup failed");
return NULL;
}
int lookup_err = pg_lookup_root(res, cfg_rootnode.ptr, &root_id);
resourcepool_free(
NULL,
NULL, res);
if(lookup_err) {
return NULL;
}
}
PgRepository *repo = pool_malloc(pool,
sizeof(PgRepository));
ZERO(repo,
sizeof(PgRepository));
repo->resourcepool = cx_strdup_a(a, cfg_respool);
repo->root_resource_id = root_id;
if(cfg_dav.length >
0) {
char *cfg_file_path = cfg_config_file_path(cfg_dav.ptr);
if(pg_load_ext_dav_config(cfg, pool, repo, cfg_file_path)) {
repo =
NULL;
}
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;
}
static int pg_load_ext_dav_config(
ServerConfiguration *cfg,
pool_handle_t *pool,
PgRepository *repo,
const char *file_path)
{
CxAllocator *a = pool_allocator(pool);
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;
}
repo->prop_ext = cxHashMapCreate(a,
CX_STORE_POINTERS,
8);
if(!repo->prop_ext) {
log_ereport(
LOG_FAILURE,
"pg: cannot load config file: OOM");
return 1;
}
xmlDoc *doc = xmlReadFile(file_path,
NULL,
0);
if(!doc) {
log_ereport(
LOG_FAILURE,
"pg: cannot load config file %s", file_path);
return 1;
}
int ret =
0;
xmlNode *xml_root = xmlDocGetRootElement(doc);
if(xstreq(xml_root->name,
"repository")) {
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 = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
parserData.tables = cxLinkedListCreate(cxDefaultAllocator,
NULL,
sizeof(PgExtTable));
while(node && !ret) {
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;
}
if(!ret) {
size_t ntables = parserData.tables->size;
repo->ntables = ntables;
repo->tables = pool_calloc(pool, ntables,
sizeof(PgExtTable));
if(repo->tables) {
int i =
0;
CxIterator iter = cxListIterator(parserData.tables);
cx_foreach(PgExtTable *, tab, iter) {
repo->tables[i++] = *tab;
}
}
else {
ret =
1;
}
}
cxListDestroy(parserData.tables);
cxMapDestroy(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)
{
CxAllocator *a = pool_allocator(pool);
xmlNode *node = ext_node->children;
const char *table =
NULL;
CxList *properties = cxLinkedListCreate(a,
NULL,
CX_STORE_POINTERS);
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")) {
xmlNode *ps = node->children;
while(ps) {
if(ps->type ==
XML_ELEMENT_NODE) {
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;
}
cxListAdd(properties, ps);
}
ps = ps->next;
}
}
}
node = node->next;
}
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;
}
if(cxMapGet(ext->table_lookup, cx_hash_key_str(table))) {
log_ereport(
LOG_MISCONFIG,
"pg: config %s: extension table %s not unique", file_path, table);
return 1;
}
char *tabname = pool_strdup(pool, table);
if(!tabname)
return 1;
PgExtTable exttable;
exttable.table = tabname;
exttable.isused =
0;
int tableindex = (
int)ext->tables->size;
cxListAdd(ext->tables, &exttable);
if(cxMapPut(ext->table_lookup, cx_hash_key_str(table), (
void*)table)) {
return 1;
}
CxIterator iter = cxListIterator(properties);
cx_foreach(xmlNode *, ps, iter) {
const char *value = pg_util_xml_get_text(ps);
PgPropertyStoreExt *ext_col = pool_malloc(pool,
sizeof(PgPropertyStoreExt));
if(!ext_col) {
return 1;
}
ext_col->tableindex = tableindex;
ext_col->ns = pool_strdup(pool, (
const char*)ps->ns->href);
ext_col->name = pool_strdup(pool, (
const char*)ps->name);
ext_col->column = pool_strdup(pool, (
const char*)value);
if(!ext_col->ns || !ext_col->name || !ext_col->column) {
return 1;
}
CxHashKey key = webdav_property_key(ext_col->ns, ext_col->name);
int err = cxMapPut(repo->prop_ext, key, ext_col);
free((
void*)key.data);
if(err) {
return 1;
}
}
return 0;
}