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"); |
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 } |