src/server/plugins/postgresql/webdav.c

branch
webdav
changeset 318
60870dbac94f
parent 317
09676b559091
child 324
44cf877b3d9f
equal deleted inserted replaced
317:09676b559091 318:60870dbac94f
28 28
29 #include "webdav.h" 29 #include "webdav.h"
30 #include "vfs.h" 30 #include "vfs.h"
31 31
32 #include "../../util/util.h" 32 #include "../../util/util.h"
33 #include "../../util/pblock.h"
33 34
34 #include <ucx/buffer.h> 35 #include <ucx/buffer.h>
35 #include <libxml/tree.h> 36 #include <libxml/tree.h>
37
36 38
37 static WebdavBackend pg_webdav_backend = { 39 static WebdavBackend pg_webdav_backend = {
38 pg_dav_propfind_init, 40 pg_dav_propfind_init,
39 pg_dav_propfind_do, 41 pg_dav_propfind_do,
40 pg_dav_propfind_finish, 42 pg_dav_propfind_finish,
235 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ 237 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\
236 on p.xmlns = n.xmlns and p.pname = n.pname\n\ 238 on p.xmlns = n.xmlns and p.pname = n.pname\n\
237 ) p on r.resource_id = p.resource_id\n\ 239 ) p on r.resource_id = p.resource_id\n\
238 order by replace(ppath, '/', chr(1)), resource_id;"; 240 order by replace(ppath, '/', chr(1)), resource_id;";
239 241
242 // proppatch: set property
243 // params: $1: resource_id
244 // $2: xmlns prefix
245 // $3: xmlns href
246 // $4: property name
247 // $5: lang attribute value
248 // $6: namespace list string
249 // $7: property value
250 static const char *sql_proppatch_set = "\
251 insert into Property(resource_id, prefix, xmlns, pname, lang, nsdeflist, pvalue)\n\
252 values($1, $2, $3, $4, $5, $6, $7)\n\
253 on conflict (resource_id, xmlns, pname) do\n\
254 update set prefix=$2, lang=$5, nsdeflist=$6, pvalue=$7;";
255
256 // proppatch: remove property
257 // params: $1: resource_id
258 // $2: xmlns href
259 // $3: property name
260 static const char *sql_proppatch_remove = "\
261 delete from Property where resource_id = $1 and xmlns = $2 and pname = $3";
262
263
240 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) { 264 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) {
241 // resourcepool is required 265 // resourcepool is required
242 char *resource_pool = pblock_findval("resourcepool", pb); 266 char *resource_pool = pblock_findval("resourcepool", pb);
243 if(!resource_pool) { 267 if(!resource_pool) {
244 log_ereport(LOG_MISCONFIG, "pg_webdav_create: missing resourcepool parameter"); 268 log_ereport(LOG_MISCONFIG, "pg_webdav_create: missing resourcepool parameter");
559 namespace->href = (xmlChar*)pool_strdup(pool, xmlns); 583 namespace->href = (xmlChar*)pool_strdup(pool, xmlns);
560 namespace->prefix = (xmlChar*)pool_strdup(pool, prefix); 584 namespace->prefix = (xmlChar*)pool_strdup(pool, prefix);
561 property->namespace = namespace; 585 property->namespace = namespace;
562 586
563 if(!lang_isnull) { 587 if(!lang_isnull) {
564 property->lang = (xmlChar*)pool_strdup(pool, lang); 588 property->lang = pool_strdup(pool, lang);
565 } 589 }
566 590
567 if(!pvalue_isnull) { 591 if(!pvalue_isnull) {
568 char *content = pool_malloc(pool, pvalue_len+1); 592 char *content = pool_malloc(pool, pvalue_len+1);
569 memcpy(content, pvalue, pvalue_len); 593 memcpy(content, pvalue, pvalue_len);
593 PQclear(result); 617 PQclear(result);
594 618
595 return 0; 619 return 0;
596 } 620 }
597 621
622 enum PgDavProp {
623 PG_DAV_PROPPATCH_NOT_ALLOWED = 0,
624 PG_DAV_CREATIONDATE,
625 PG_DAV_DISPLAYNAME,
626 PG_DAV_DEADPROP
627 };
628 /*
629 * checks if the property can be manipulated
630 */
631 static enum PgDavProp proppatch_check_dav_prop(const char *name) {
632 if(!strcmp(name, "getlastmodified")) {
633 return PG_DAV_PROPPATCH_NOT_ALLOWED;
634 } else if(!strcmp(name, "getcontentlength")) {
635 return PG_DAV_PROPPATCH_NOT_ALLOWED;
636 } else if(!strcmp(name, "resourcetype")) {
637 return PG_DAV_PROPPATCH_NOT_ALLOWED;
638 } else if(!strcmp(name, "getetag")) {
639 return PG_DAV_PROPPATCH_NOT_ALLOWED;
640 } else if(!strcmp(name, "creationdate")) {
641 return PG_DAV_CREATIONDATE;
642 } else if(!strcmp(name, "displayname")) {
643 return PG_DAV_DISPLAYNAME;
644 }
645 return PG_DAV_DEADPROP;
646 }
647
648 typedef struct {
649 WebdavProperty *creationdate;
650 WebdavProperty *displayname;
651 int error;
652 } PgProppatchOpResult;
653
654 typedef int(*pg_proppatch_func)(PgWebdavBackend*, WebdavProppatchRequest*, WebdavResource*, WebdavProperty*, void*);
655
656 /*
657 * This function iterates the property list 'plist',
658 * analyses if any DAV: property is in the list
659 * and calls opfunc for the each property
660 *
661 * If the property list contains the properties creationdate or displayname,
662 * the pointers to these properties will be stored in the result structure
663 */
664 static PgProppatchOpResult pg_proppatch_op(
665 PgWebdavBackend *pgdav,
666 WebdavProppatchRequest *request,
667 WebdavResource *response,
668 WebdavPList **plist,
669 enum PgDavProp forbidden_extra,
670 pg_proppatch_func opfunc,
671 void *op_userdata)
672 {
673 PgProppatchOpResult result;
674 result.creationdate = NULL;
675 result.displayname = NULL;
676 result.error = 0;
677
678 WebdavPListIterator i = webdav_plist_iterator(plist);
679 WebdavPList *cur;
680 while(webdav_plist_iterator_next(&i, &cur)) {
681 WebdavProperty *property = cur->property;
682 WSNamespace *ns = property->namespace;
683 if(!ns) {
684 continue; // maybe we should abort
685 }
686
687 // check if the property is a DAV: property that requires special
688 // handling
689 // get* properties can't be manipulated
690 // some properties can't be removed
691 if(!strcmp((const char*)ns->href, "DAV:")) {
692 const char *name = property->name;
693 enum PgDavProp davprop = proppatch_check_dav_prop(name);
694 if(davprop != PG_DAV_DEADPROP) {
695 if(davprop == PG_DAV_PROPPATCH_NOT_ALLOWED || davprop == forbidden_extra) {
696 response->addproperty(response, property, 409);
697 } else if(davprop == PG_DAV_CREATIONDATE) {
698 result.creationdate = property;
699 } else if(davprop == PG_DAV_DISPLAYNAME) {
700 result.displayname = property;
701 }
702 webdav_plist_iterator_remove_current(&i);
703 continue;
704 }
705 }
706
707 // call op func (set, remove specific code)
708 if(opfunc(pgdav, request, response, property, op_userdata)) {
709 result.error = 1;
710 break;
711 }
712
713 webdav_plist_iterator_remove_current(&i);
714 }
715
716 return result;
717 }
718
719 static int pg_dav_set_property(
720 PgWebdavBackend *pgdav,
721 WebdavProppatchRequest *request,
722 WebdavResource *response,
723 WebdavProperty *property,
724 void *userdata)
725 {
726 pool_handle_t *pool = request->sn->pool;
727 WSNamespace *ns = property->namespace;
728 char *resource_id_str = userdata;
729 int ret = 0;
730
731 // convert the property value to WSXmlData
732 // property->vtype == WS_VALUE_XML_NODE should always be true
733 WSXmlData *property_value = property->vtype == WS_VALUE_XML_NODE ? wsxml_node2data(pool, property->value.node) : NULL;
734 char *value_str = property_value ? property_value->data : NULL;
735
736 // TODO: convert WebdavNSList to a string
737
738 // exec sql
739 const char* params[7] = { resource_id_str, (const char*)ns->prefix, (const char*)ns->href, property->name, NULL, NULL, value_str};
740 PGresult *result = PQexecParams(
741 pgdav->connection,
742 sql_proppatch_set,
743 7, // number of parameters
744 NULL,
745 params, // parameter value
746 NULL,
747 NULL,
748 0); // 0: result in text format
749
750 if(PQresultStatus(result) != PGRES_COMMAND_OK) {
751 response->addproperty(response, property, 500);
752 //printf(PQerrorMessage(pgdav->connection));
753 //fflush(stdout);
754 ret = 1;
755 } else {
756 response->addproperty(response, property, 200);
757 }
758 PQclear(result);
759 if(value_str) pool_free(pool, value_str);
760
761 return ret;
762 }
763
764
765 static int pg_dav_remove_property(
766 PgWebdavBackend *pgdav,
767 WebdavProppatchRequest *request,
768 WebdavResource *response,
769 WebdavProperty *property,
770 void *userdata)
771 {
772 pool_handle_t *pool = request->sn->pool;
773 WSNamespace *ns = property->namespace;
774 char *resource_id_str = userdata;
775 int ret = 0;
776
777 // exec sql
778 const char* params[3] = { resource_id_str, (const char*)ns->href, property->name };
779 PGresult *result = PQexecParams(
780 pgdav->connection,
781 sql_proppatch_remove,
782 3, // number of parameters
783 NULL,
784 params, // parameter value
785 NULL,
786 NULL,
787 0); // 0: result in text format
788
789 if(PQresultStatus(result) != PGRES_COMMAND_OK) {
790 response->addproperty(response, property, 500);
791 //printf(PQerrorMessage(pgdav->connection));
792 //fflush(stdout);
793 ret = 1;
794 }
795 PQclear(result);
796
797 return ret;
798 }
799
598 int pg_dav_proppatch_do( 800 int pg_dav_proppatch_do(
599 WebdavProppatchRequest *request, 801 WebdavProppatchRequest *request,
600 WebdavResource *response, 802 WebdavResource *response,
601 VFSFile *file, 803 VFSFile *file,
602 WebdavPList **out_set, 804 WebdavPList **out_set,
603 WebdavPList **out_remove) 805 WebdavPList **out_remove)
604 { 806 {
605 return 1; 807 PgWebdavBackend *pgdav = request->dav->instance;
808 pool_handle_t *pool = request->sn->pool;
809 char *path = pblock_findkeyval(pb_key_path, request->rq->vars);
810
811 // check if the resource exists, we also need the resource_id
812 int64_t parent_id;
813 int64_t resource_id;
814 const char *resourcename;
815 WSBool iscollection;
816 int res_errno = 0;
817 int err = pg_resolve_path(
818 pgdav->connection,
819 path,
820 &parent_id,
821 &resource_id,
822 NULL, // OID
823 &resourcename,
824 &iscollection,
825 NULL, // stat
826 &res_errno);
827
828 if(err) {
829 return 1;
830 }
831
832 // because proppatch must be atomic and we have multiple sql
833 // queries and other backends that do stuff that could fail
834 // we need the possibility to reverse all changes
835 // we use a transaction savepoint for this
836 PGresult *result = PQexec(pgdav->connection, "savepoint proppatch;");
837 ExecStatusType execStatus = PQresultStatus(result);
838 PQclear(result);
839 if(execStatus != PGRES_COMMAND_OK) {
840 return 1;
841 }
842
843 char resource_id_str[32];
844 snprintf(resource_id_str, 32, "%" PRId64, resource_id);
845
846 int ret = 0;
847 PgProppatchOpResult set_res = pg_proppatch_op(
848 pgdav,
849 request,
850 response,
851 out_set,
852 PG_DAV_PROPPATCH_NOT_ALLOWED,
853 pg_dav_set_property,
854 resource_id_str);
855 if(set_res.error) {
856 return 1;
857 }
858 PgProppatchOpResult rm_res = pg_proppatch_op(
859 pgdav,
860 request,
861 response,
862 out_remove,
863 PG_DAV_CREATIONDATE, // creationdate can't be removed
864 pg_dav_remove_property,
865 resource_id_str);
866 if(rm_res.error) {
867 return 1;
868 }
869
870 return ret;
606 } 871 }
607 872
608 int pg_dav_proppatch_finish( 873 int pg_dav_proppatch_finish(
609 WebdavProppatchRequest *request, 874 WebdavProppatchRequest *request,
610 WebdavResource *response, 875 WebdavResource *response,
611 VFSFile *file, 876 VFSFile *file,
612 WSBool commit) 877 WSBool commit)
613 { 878 {
614 return 1; 879 PgWebdavBackend *pgdav = request->dav->instance;
615 } 880 int ret = 0;
881 if(!commit) {
882 PGresult *result = PQexec(pgdav->connection, "rollback to savepoint proppatch;");
883 if(PQresultStatus(result) != PGRES_COMMAND_OK) {
884 log_ereport(LOG_FAILURE, "pg_dav_proppatch_finish: rollback failed: %s", PQerrorMessage(pgdav->connection));
885 ret = 1;
886 }
887 PQclear(result);
888 }
889 return ret;
890 }

mercurial