diff -r 32b8017f5308 -r 61d481d3c2e4 src/server/plugins/postgresql/webdav.c --- a/src/server/plugins/postgresql/webdav.c Sun Aug 14 12:43:14 2022 +0200 +++ b/src/server/plugins/postgresql/webdav.c Sun Aug 14 16:46:52 2022 +0200 @@ -36,6 +36,7 @@ #include "../../daemon/http.h" // etag #include +#include #include @@ -277,8 +278,27 @@ } -static int pg_create_propfind_query(WebdavPropfindRequest *rq, WSBool iscollection, UcxBuffer *sql) { +static int propfind_ext_cmp(const void *f1, const void *f2) { + const PgPropfindExtCol *e1 = f1; + const PgPropfindExtCol *e2 = f2; + + if(e1->ext->tableindex != e2->ext->tableindex) { + return e1->ext->tableindex < e2->ext->tableindex ? -1 : 1; + } + + return 0; +} + + +static int pg_create_propfind_query( + WebdavPropfindRequest *rq, + WSBool iscollection, + PgPropfindExtCol *ext, + size_t numext, + UcxBuffer *sql) +{ PgWebdavBackend *pgdav = rq->dav->instance; + PgRepository *repo = pgdav->repository; int depth = !iscollection ? 0 : rq->depth; /* @@ -314,12 +334,45 @@ // cols ucx_buffer_puts(sql, sql_propfind_cols); + // ext_cols + if(ext) { + if(rq->allprop) { + for(int i=0;intables;i++) { + ucx_bprintf(sql, ",x%d.*\n", i); + } + } else { + for(int i=0;itableindex, e.ext->column); + } + } + } + // from ucx_buffer_puts(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table); // prop join ucx_buffer_puts(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist); + // ext_join + if(ext) { + if(rq->allprop) { + for(int i=0;intables;i++) { + ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[i].table, i, i); + } + } else { + int tab = -1; + for(int i=0;itableindex != tab) { + tab = e.ext->tableindex; + ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[tab].table, tab, tab); + } + } + } + + } + // where if(depth == 0) { ucx_buffer_puts(sql, sql_propfind_where_depth0); @@ -337,9 +390,6 @@ // end ucx_buffer_puts(sql, ";\0"); - //printf("\n\n%s\n\n", sql->space); - //fflush(stdout); - return 0; } @@ -351,7 +401,8 @@ { PgWebdavBackend *pgdav = rq->dav->instance; - // check if the resource exists + // first, check if the resource exists + // if it doesn't exist, we can return immediately int64_t parent_id; int64_t resource_id; const char *resourcename; @@ -377,10 +428,60 @@ return 1; } + // create a list of requsted extended properties + PgPropfindExtCol *ext; + size_t numext; + if(pgdav->repository->ntables == 0) { + // no property extensions configured + ext = NULL; + numext = 0; + } else { + numext = pgdav->repository->prop_ext->count; + ext = pool_calloc(rq->sn->pool, numext, sizeof(PgPropfindExtCol)); + + if(rq->allprop) { + // the map pgdav->repository->prop_ext contains all property extensions + // we can just convert the map to an array + UcxMapIterator i = ucx_map_iterator(pgdav->repository->prop_ext); + PgPropertyStoreExt *cfg_ext; + int j = 0; + UCX_MAP_FOREACH(key, cfg_ext, i) { + PgPropfindExtCol extcol; + extcol.ext = cfg_ext; + extcol.field_num = -1; // get the field_num after the PQexec + ext[j++] = extcol; + } + } else { + WebdavPListIterator i = webdav_plist_iterator(outplist); + WebdavPList *cur; + int j = 0; + while(webdav_plist_iterator_next(&i, &cur)) { + WSNamespace *ns = cur->property->namespace; + if(ns) { + UcxKey pkey = webdav_property_key((const char*)ns->href, cur->property->name); + PgPropertyStoreExt *cfg_ext = ucx_map_get(pgdav->repository->prop_ext, pkey); + char *pkey_data = pkey.data; + free((void*)pkey.data); + if(cfg_ext) { + PgPropfindExtCol extcol; + extcol.ext = cfg_ext; + extcol.field_num = -1; // get the field_num after the PQexec + ext[j++] = extcol; + + webdav_plist_iterator_remove_current(&i); + } + } + } + numext = j; + } + + qsort(ext, numext, sizeof(PgPropfindExtCol), propfind_ext_cmp); + } + // create sql query const char *query = NULL; UcxBuffer *sql = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); - if(pg_create_propfind_query(rq, iscollection, sql)) { + if(pg_create_propfind_query(rq, iscollection, ext, numext, sql)) { return 1; } query = sql->space; @@ -414,7 +515,7 @@ ucx_buffer_free(xmlns_buf); return 1; } - if(pg_create_property_param_arrays(rq->properties, xmlns_buf, pname_buf)) { + if(pg_create_property_param_arrays(*outplist, xmlns_buf, pname_buf)) { ucx_buffer_free(xmlns_buf); ucx_buffer_free(pname_buf); return 1; @@ -454,6 +555,16 @@ pg->result = result; pg->nrows = nrows; + pg->ext = ext; + pg->numext = numext; + if(ext) { + // get field_nums for all property extensions + for(int i=0;ifield_num = PQfnumber(result, c->ext->column); + } + } + return 0; } @@ -470,6 +581,7 @@ WebdavVFSProperties vfsprops = pg->vfsproperties; WSBool vfsprops_set = 0; // are live properties added to the response? + WSBool extprops_set = 0; // are extended properties added to the response? int64_t current_resource_id = pg->resource_id; for(int r=0;rnrows;r++) { // columns: @@ -571,6 +683,51 @@ vfsprops_set = TRUE; } + if(!extprops_set) { + // extended properties + if(pg->ext) { + for(int extc=0;extcnumext;extc++) { + PgPropfindExtCol ext = pg->ext[extc]; + int fieldnum = ext.field_num; + + if(!PQgetisnull(result, r, fieldnum)) { + char *ext_value = PQgetvalue(result, r, fieldnum); + int ext_value_len = PQgetlength(result, r, fieldnum); + char ext_xmlns_prefix[32]; + snprintf(ext_xmlns_prefix, 32, "x%d", ext.ext->tableindex); + + WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty)); + property->lang = NULL; + property->name = pool_strdup(pool, ext.ext->name); + + xmlNs *namespace = pool_malloc(pool, sizeof(xmlNs)); + memset(namespace, 0, sizeof(struct _xmlNs)); + namespace->href = (xmlChar*)pool_strdup(pool, ext.ext->ns); + namespace->prefix = (xmlChar*)pool_strdup(pool, ext_xmlns_prefix); + property->namespace = namespace; + + char *content = pool_malloc(pool, ext_value_len+1); + memcpy(content, ext_value, ext_value_len); + content[ext_value_len] = '\0'; + + WebdavNSList *nslist = pool_malloc(pool, sizeof(WebdavNSList)); + nslist->namespace = namespace; + nslist->prev = NULL; + nslist->next = NULL; + + property->vtype = WS_VALUE_XML_DATA; + property->value.data.data = content; + property->value.data.length = ext_value_len; + property->value.data.namespaces = nslist; + + resource->addproperty(resource, property, 200); + } + } + } + + extprops_set = TRUE; + } + // dead properties if(!PQgetisnull(result, r, 9)) { char *prefix = PQgetvalue(result, r, 9); @@ -609,11 +766,11 @@ property->value.text.str = content; property->value.text.length = pvalue_len; } else { - WebdavNSList *nslist = wsxml_string2nslist(pool, nsdef); + WebdavNSList *nslist = wsxml_string2nslist(pool, nsdef); property->vtype = WS_VALUE_XML_DATA; - property->value.data->data = content; - property->value.data->length = pvalue_len; - property->value.data->namespaces = nslist; + property->value.data.data = content; + property->value.data.length = pvalue_len; + property->value.data.namespaces = nslist; } }