34 #include "../../util/pblock.h" |
34 #include "../../util/pblock.h" |
35 |
35 |
36 #include "../../daemon/http.h" // etag |
36 #include "../../daemon/http.h" // etag |
37 |
37 |
38 #include <ucx/buffer.h> |
38 #include <ucx/buffer.h> |
|
39 #include <ucx/utils.h> |
39 #include <libxml/tree.h> |
40 #include <libxml/tree.h> |
40 |
41 |
41 |
42 |
42 static WebdavBackend pg_webdav_backend = { |
43 static WebdavBackend pg_webdav_backend = { |
43 pg_dav_propfind_init, |
44 pg_dav_propfind_init, |
275 int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0; |
276 int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0; |
276 return r1+r2 != 0; |
277 return r1+r2 != 0; |
277 } |
278 } |
278 |
279 |
279 |
280 |
280 static int pg_create_propfind_query(WebdavPropfindRequest *rq, WSBool iscollection, UcxBuffer *sql) { |
281 static int propfind_ext_cmp(const void *f1, const void *f2) { |
|
282 const PgPropfindExtCol *e1 = f1; |
|
283 const PgPropfindExtCol *e2 = f2; |
|
284 |
|
285 if(e1->ext->tableindex != e2->ext->tableindex) { |
|
286 return e1->ext->tableindex < e2->ext->tableindex ? -1 : 1; |
|
287 } |
|
288 |
|
289 return 0; |
|
290 } |
|
291 |
|
292 |
|
293 static int pg_create_propfind_query( |
|
294 WebdavPropfindRequest *rq, |
|
295 WSBool iscollection, |
|
296 PgPropfindExtCol *ext, |
|
297 size_t numext, |
|
298 UcxBuffer *sql) |
|
299 { |
281 PgWebdavBackend *pgdav = rq->dav->instance; |
300 PgWebdavBackend *pgdav = rq->dav->instance; |
|
301 PgRepository *repo = pgdav->repository; |
282 int depth = !iscollection ? 0 : rq->depth; |
302 int depth = !iscollection ? 0 : rq->depth; |
283 |
303 |
284 /* |
304 /* |
285 * PROPFIND queries are build from components: |
305 * PROPFIND queries are build from components: |
286 * |
306 * |
312 } |
332 } |
313 |
333 |
314 // cols |
334 // cols |
315 ucx_buffer_puts(sql, sql_propfind_cols); |
335 ucx_buffer_puts(sql, sql_propfind_cols); |
316 |
336 |
|
337 // ext_cols |
|
338 if(ext) { |
|
339 if(rq->allprop) { |
|
340 for(int i=0;i<repo->ntables;i++) { |
|
341 ucx_bprintf(sql, ",x%d.*\n", i); |
|
342 } |
|
343 } else { |
|
344 for(int i=0;i<numext;i++) { |
|
345 PgPropfindExtCol e = ext[i]; |
|
346 ucx_bprintf(sql, ",x%d.%s\n", e.ext->tableindex, e.ext->column); |
|
347 } |
|
348 } |
|
349 } |
|
350 |
317 // from |
351 // from |
318 ucx_buffer_puts(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table); |
352 ucx_buffer_puts(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table); |
319 |
353 |
320 // prop join |
354 // prop join |
321 ucx_buffer_puts(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist); |
355 ucx_buffer_puts(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist); |
|
356 |
|
357 // ext_join |
|
358 if(ext) { |
|
359 if(rq->allprop) { |
|
360 for(int i=0;i<repo->ntables;i++) { |
|
361 ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[i].table, i, i); |
|
362 } |
|
363 } else { |
|
364 int tab = -1; |
|
365 for(int i=0;i<numext;i++) { |
|
366 PgPropfindExtCol e = ext[i]; |
|
367 if(e.ext->tableindex != tab) { |
|
368 tab = e.ext->tableindex; |
|
369 ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[tab].table, tab, tab); |
|
370 } |
|
371 } |
|
372 } |
|
373 |
|
374 } |
322 |
375 |
323 // where |
376 // where |
324 if(depth == 0) { |
377 if(depth == 0) { |
325 ucx_buffer_puts(sql, sql_propfind_where_depth0); |
378 ucx_buffer_puts(sql, sql_propfind_where_depth0); |
326 } else if(depth == 1) { |
379 } else if(depth == 1) { |
375 protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL); |
426 protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL); |
376 } |
427 } |
377 return 1; |
428 return 1; |
378 } |
429 } |
379 |
430 |
|
431 // create a list of requsted extended properties |
|
432 PgPropfindExtCol *ext; |
|
433 size_t numext; |
|
434 if(pgdav->repository->ntables == 0) { |
|
435 // no property extensions configured |
|
436 ext = NULL; |
|
437 numext = 0; |
|
438 } else { |
|
439 numext = pgdav->repository->prop_ext->count; |
|
440 ext = pool_calloc(rq->sn->pool, numext, sizeof(PgPropfindExtCol)); |
|
441 |
|
442 if(rq->allprop) { |
|
443 // the map pgdav->repository->prop_ext contains all property extensions |
|
444 // we can just convert the map to an array |
|
445 UcxMapIterator i = ucx_map_iterator(pgdav->repository->prop_ext); |
|
446 PgPropertyStoreExt *cfg_ext; |
|
447 int j = 0; |
|
448 UCX_MAP_FOREACH(key, cfg_ext, i) { |
|
449 PgPropfindExtCol extcol; |
|
450 extcol.ext = cfg_ext; |
|
451 extcol.field_num = -1; // get the field_num after the PQexec |
|
452 ext[j++] = extcol; |
|
453 } |
|
454 } else { |
|
455 WebdavPListIterator i = webdav_plist_iterator(outplist); |
|
456 WebdavPList *cur; |
|
457 int j = 0; |
|
458 while(webdav_plist_iterator_next(&i, &cur)) { |
|
459 WSNamespace *ns = cur->property->namespace; |
|
460 if(ns) { |
|
461 UcxKey pkey = webdav_property_key((const char*)ns->href, cur->property->name); |
|
462 PgPropertyStoreExt *cfg_ext = ucx_map_get(pgdav->repository->prop_ext, pkey); |
|
463 char *pkey_data = pkey.data; |
|
464 free((void*)pkey.data); |
|
465 if(cfg_ext) { |
|
466 PgPropfindExtCol extcol; |
|
467 extcol.ext = cfg_ext; |
|
468 extcol.field_num = -1; // get the field_num after the PQexec |
|
469 ext[j++] = extcol; |
|
470 |
|
471 webdav_plist_iterator_remove_current(&i); |
|
472 } |
|
473 } |
|
474 } |
|
475 numext = j; |
|
476 } |
|
477 |
|
478 qsort(ext, numext, sizeof(PgPropfindExtCol), propfind_ext_cmp); |
|
479 } |
|
480 |
380 // create sql query |
481 // create sql query |
381 const char *query = NULL; |
482 const char *query = NULL; |
382 UcxBuffer *sql = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); |
483 UcxBuffer *sql = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); |
383 if(pg_create_propfind_query(rq, iscollection, sql)) { |
484 if(pg_create_propfind_query(rq, iscollection, ext, numext, sql)) { |
384 return 1; |
485 return 1; |
385 } |
486 } |
386 query = sql->space; |
487 query = sql->space; |
387 |
488 |
388 // get all resources and properties |
489 // get all resources and properties |
412 pname_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND); |
513 pname_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND); |
413 if(!pname_buf) { |
514 if(!pname_buf) { |
414 ucx_buffer_free(xmlns_buf); |
515 ucx_buffer_free(xmlns_buf); |
415 return 1; |
516 return 1; |
416 } |
517 } |
417 if(pg_create_property_param_arrays(rq->properties, xmlns_buf, pname_buf)) { |
518 if(pg_create_property_param_arrays(*outplist, xmlns_buf, pname_buf)) { |
418 ucx_buffer_free(xmlns_buf); |
519 ucx_buffer_free(xmlns_buf); |
419 ucx_buffer_free(pname_buf); |
520 ucx_buffer_free(pname_buf); |
420 return 1; |
521 return 1; |
421 } |
522 } |
422 xmlns_param = xmlns_buf->space; |
523 xmlns_param = xmlns_buf->space; |
452 pg->resource_id = resource_id; |
553 pg->resource_id = resource_id; |
453 pg->vfsproperties = webdav_vfs_properties(outplist, TRUE, rq->allprop, 0); |
554 pg->vfsproperties = webdav_vfs_properties(outplist, TRUE, rq->allprop, 0); |
454 pg->result = result; |
555 pg->result = result; |
455 pg->nrows = nrows; |
556 pg->nrows = nrows; |
456 |
557 |
|
558 pg->ext = ext; |
|
559 pg->numext = numext; |
|
560 if(ext) { |
|
561 // get field_nums for all property extensions |
|
562 for(int i=0;i<numext;i++) { |
|
563 PgPropfindExtCol *c = &ext[i]; |
|
564 c->field_num = PQfnumber(result, c->ext->column); |
|
565 } |
|
566 } |
|
567 |
457 return 0; |
568 return 0; |
458 } |
569 } |
459 |
570 |
460 int pg_dav_propfind_do( |
571 int pg_dav_propfind_do( |
461 WebdavPropfindRequest *rq, |
572 WebdavPropfindRequest *rq, |
468 pool_handle_t *pool = rq->sn->pool; |
579 pool_handle_t *pool = rq->sn->pool; |
469 PGresult *result = pg->result; |
580 PGresult *result = pg->result; |
470 WebdavVFSProperties vfsprops = pg->vfsproperties; |
581 WebdavVFSProperties vfsprops = pg->vfsproperties; |
471 |
582 |
472 WSBool vfsprops_set = 0; // are live properties added to the response? |
583 WSBool vfsprops_set = 0; // are live properties added to the response? |
|
584 WSBool extprops_set = 0; // are extended properties added to the response? |
473 int64_t current_resource_id = pg->resource_id; |
585 int64_t current_resource_id = pg->resource_id; |
474 for(int r=0;r<pg->nrows;r++) { |
586 for(int r=0;r<pg->nrows;r++) { |
475 // columns: |
587 // columns: |
476 // 0: path |
588 // 0: path |
477 // 1: resource_id |
589 // 1: resource_id |
567 } |
679 } |
568 } |
680 } |
569 } |
681 } |
570 |
682 |
571 vfsprops_set = TRUE; |
683 vfsprops_set = TRUE; |
|
684 } |
|
685 |
|
686 if(!extprops_set) { |
|
687 // extended properties |
|
688 if(pg->ext) { |
|
689 for(int extc=0;extc<pg->numext;extc++) { |
|
690 PgPropfindExtCol ext = pg->ext[extc]; |
|
691 int fieldnum = ext.field_num; |
|
692 |
|
693 if(!PQgetisnull(result, r, fieldnum)) { |
|
694 char *ext_value = PQgetvalue(result, r, fieldnum); |
|
695 int ext_value_len = PQgetlength(result, r, fieldnum); |
|
696 char ext_xmlns_prefix[32]; |
|
697 snprintf(ext_xmlns_prefix, 32, "x%d", ext.ext->tableindex); |
|
698 |
|
699 WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty)); |
|
700 property->lang = NULL; |
|
701 property->name = pool_strdup(pool, ext.ext->name); |
|
702 |
|
703 xmlNs *namespace = pool_malloc(pool, sizeof(xmlNs)); |
|
704 memset(namespace, 0, sizeof(struct _xmlNs)); |
|
705 namespace->href = (xmlChar*)pool_strdup(pool, ext.ext->ns); |
|
706 namespace->prefix = (xmlChar*)pool_strdup(pool, ext_xmlns_prefix); |
|
707 property->namespace = namespace; |
|
708 |
|
709 char *content = pool_malloc(pool, ext_value_len+1); |
|
710 memcpy(content, ext_value, ext_value_len); |
|
711 content[ext_value_len] = '\0'; |
|
712 |
|
713 WebdavNSList *nslist = pool_malloc(pool, sizeof(WebdavNSList)); |
|
714 nslist->namespace = namespace; |
|
715 nslist->prev = NULL; |
|
716 nslist->next = NULL; |
|
717 |
|
718 property->vtype = WS_VALUE_XML_DATA; |
|
719 property->value.data.data = content; |
|
720 property->value.data.length = ext_value_len; |
|
721 property->value.data.namespaces = nslist; |
|
722 |
|
723 resource->addproperty(resource, property, 200); |
|
724 } |
|
725 } |
|
726 } |
|
727 |
|
728 extprops_set = TRUE; |
572 } |
729 } |
573 |
730 |
574 // dead properties |
731 // dead properties |
575 if(!PQgetisnull(result, r, 9)) { |
732 if(!PQgetisnull(result, r, 9)) { |
576 char *prefix = PQgetvalue(result, r, 9); |
733 char *prefix = PQgetvalue(result, r, 9); |
607 if(nsdef_isnull) { |
764 if(nsdef_isnull) { |
608 property->vtype = WS_VALUE_TEXT; |
765 property->vtype = WS_VALUE_TEXT; |
609 property->value.text.str = content; |
766 property->value.text.str = content; |
610 property->value.text.length = pvalue_len; |
767 property->value.text.length = pvalue_len; |
611 } else { |
768 } else { |
612 WebdavNSList *nslist = wsxml_string2nslist(pool, nsdef); |
769 WebdavNSList *nslist = wsxml_string2nslist(pool, nsdef); |
613 property->vtype = WS_VALUE_XML_DATA; |
770 property->vtype = WS_VALUE_XML_DATA; |
614 property->value.data->data = content; |
771 property->value.data.data = content; |
615 property->value.data->length = pvalue_len; |
772 property->value.data.length = pvalue_len; |
616 property->value.data->namespaces = nslist; |
773 property->value.data.namespaces = nslist; |
617 |
774 |
618 } |
775 } |
619 } |
776 } |
620 |
777 |
621 resource->addproperty(resource, property, 200); |
778 resource->addproperty(resource, property, 200); |