src/server/plugins/postgresql/webdav.c

branch
webdav
changeset 306
e03737cea6e2
parent 303
ad9ba51c8634
child 307
8787cb5ebab3
equal deleted inserted replaced
305:4db64fe30588 306:e03737cea6e2
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 #include "webdav.h" 29 #include "webdav.h"
30 #include "vfs.h"
31
32 #include "../../util/util.h"
33
34 #include <libxml/tree.h>
30 35
31 static WebdavBackend pg_webdav_backend = { 36 static WebdavBackend pg_webdav_backend = {
32 pg_dav_propfind_init, 37 pg_dav_propfind_init,
33 pg_dav_propfind_do, 38 pg_dav_propfind_do,
34 pg_dav_propfind_finish, 39 pg_dav_propfind_finish,
41 0, 46 0,
42 NULL, 47 NULL,
43 NULL 48 NULL
44 }; 49 };
45 50
51
52 /*
53 * SQL Queries
54 */
55
56 // propfind with depth = 0
57 // params: $1: resource_id
58 static const char *sql_propfind_allprop_depth0 = "\
59 select\n\
60 NULL as ppath,\n\
61 r.resource_id,\n\
62 r.parent_id,\n\
63 r.nodename,\n\
64 r.iscollection,\n\
65 r.lastmodified,\n\
66 r.creationdate,\n\
67 r.contentlength,\n\
68 p.xmlns,\n\
69 p.pname,\n\
70 p.pvalue\n\
71 from Resource r\n\
72 left join Property p on r.resource_id = p.resource_id\n\
73 where r.resource_id = $1;";
74
75 // propfind with depth = 0 for specific properties
76 // params: $1: resource_id
77 static const char *sql_propfind_depth0 = "\
78 select\n\
79 NULL as ppath,\n\
80 r.resource_id,\n\
81 r.parent_id,\n\
82 r.nodename,\n\
83 r.iscollection,\n\
84 r.lastmodified,\n\
85 r.creationdate,\n\
86 r.contentlength,\n\
87 p.xmlns,\n\
88 p.pname,\n\
89 p.pvalue\n\
90 from Resource r\n\
91 left join Property p on r.resource_id = p.resource_id\n\
92 inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
93 on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
94 where r.resource_id = $1;";
95
96 // propfind with depth = 1
97 // params: $1: resource_id
98 static const char *sql_propfind_allprop_depth1 = "\
99 select\n\
100 NULL ass ppath,\n\
101 r.resource_id,\n\
102 r.parent_id,\n\
103 r.nodename,\n\
104 r.iscollection,\n\
105 r.lastmodified,\n\
106 r.creationdate,\n\
107 r.contentlength,\n\
108 p.xmlns,\n\
109 p.pname,\n\
110 p.pvalue\n\
111 from Resource r\n\
112 left join Property p on r.resource_id = p.resource_id\n\
113 where r.resource_id = $1 or r.parent_id = $1\n\
114 order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
115
116
117 // propfind with depth = 1 for specific properties
118 // params: $1: resource_id
119 static const char *sql_propfind_depth1 = "\
120 select\n\
121 NULL as ppath,\n\
122 r.resource_id,\n\
123 r.parent_id,\n\
124 r.nodename,\n\
125 r.iscollection,\n\
126 r.lastmodified,\n\
127 r.creationdate,\n\
128 r.contentlength,\n\
129 p.xmlns,\n\
130 p.pname,\n\
131 p.pvalue\n\
132 from Resource r\n\
133 left join Property p on r.resource_id = p.resource_id\n\
134 inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
135 on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
136 where r.resource_id = $1 or r.parent_id = $1\n\
137 order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
138
139 // recursive propfind
140 // params: $1: resource_id
141 static const char *sql_propfind_allprop_recursive = "\
142 with recursive resolvepath as (\n\
143 select\n\
144 '' as ppath,\n\
145 *\n\
146 from Resource\n\
147 where resource_id = $1 \n\
148 union\n\
149 select\n\
150 p.ppath || '/' || r.nodename,\n\
151 r.*\n\
152 from Resource r\n\
153 inner join resolvepath p on r.parent_id = p.resource_id\n\
154 )\n\
155 select\n\
156 r.ppath,\n\
157 r.resource_id,\n\
158 r.parent_id,\n\
159 r.nodename,\n\
160 r.iscollection,\n\
161 r.lastmodified,\n\
162 r.creationdate,\n\
163 r.contentlength,\n\
164 p.xmlns,\n\
165 p.pname,\n\
166 p.pvalue\n\
167 from resolvepath r\n\
168 left join Property p on r.resource_id = p.resource_id\n\
169 order by replace(ppath, '/', chr(1)), resource_id;";
170
171 // recursive propfind for specific properties
172 // params: $1: resource_id
173 // $2: array of property xmlns
174 // $3: array of property names
175 static const char *sql_propfind_recursive = "\
176 with recursive resolvepath as (\n\
177 select\n\
178 '' as ppath,\n\
179 *\n\
180 from Resource\n\
181 where resource_id = $1 \n\
182 union\n\
183 select\n\
184 p.ppath || '/' || r.nodename,\n\
185 r.*\n\
186 from Resource r\n\
187 inner join resolvepath p on r.parent_id = p.resource_id\n\
188 )\n\
189 select\n\
190 r.ppath,\n\
191 r.resource_id,\n\
192 r.parent_id,\n\
193 r.nodename,\n\
194 r.iscollection,\n\
195 r.lastmodified,\n\
196 r.creationdate,\n\
197 r.contentlength,\n\
198 p.xmlns,\n\
199 p.pname,\n\
200 p.pvalue\n\
201 from resolvepath r\n\
202 left join Property p on r.resource_id = p.resource_id\n\
203 inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
204 on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
205 order by replace(ppath, '/', chr(1)), resource_id;";
206
46 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) { 207 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) {
47 // resourcepool is required 208 // resourcepool is required
48 char *resource_pool = pblock_findval("resourcepool", pb); 209 char *resource_pool = pblock_findval("resourcepool", pb);
49 if(!resource_pool) { 210 if(!resource_pool) {
50 log_ereport(LOG_MISCONFIG, "pg_webdav_create: missing resourcepool parameter"); 211 log_ereport(LOG_MISCONFIG, "pg_webdav_create: missing resourcepool parameter");
67 return NULL; 228 return NULL;
68 } 229 }
69 *webdav = pg_webdav_backend; 230 *webdav = pg_webdav_backend;
70 231
71 PgWebdavBackend *instance = pool_malloc(sn->pool, sizeof(PgWebdavBackend)); 232 PgWebdavBackend *instance = pool_malloc(sn->pool, sizeof(PgWebdavBackend));
72 if(instance) { 233 if(!instance) {
73 pool_free(sn->pool, webdav); 234 pool_free(sn->pool, webdav);
74 return NULL; 235 return NULL;
75 } 236 }
76 webdav->instance = instance; 237 webdav->instance = instance;
77 238
89 int pg_dav_propfind_init( 250 int pg_dav_propfind_init(
90 WebdavPropfindRequest *rq, 251 WebdavPropfindRequest *rq,
91 const char *path, 252 const char *path,
92 WebdavPList **outplist) 253 WebdavPList **outplist)
93 { 254 {
255 PgWebdavBackend *pgdav = rq->dav->instance;
256
257 // check if the resource exists
258 int64_t parent_id;
259 int64_t resource_id;
260 const char *resourcename;
261 WSBool iscollection;
262 int res_errno = 0;
263 int err = pg_resolve_path(
264 pgdav->connection,
265 path,
266 &parent_id,
267 &resource_id,
268 NULL, // OID
269 &resourcename,
270 &iscollection,
271 NULL, // stat
272 &res_errno);
273
274 if(err) {
275 if(res_errno == ENOENT) {
276 protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL);
277 }
278 return 1;
279 }
280
281 // choose sql query
282 const char *query = NULL;
283 if(!iscollection || rq->depth == 0) {
284 query = rq->allprop ? sql_propfind_allprop_depth0 : sql_propfind_depth0;
285 } else if(rq->depth == 1) {
286 query = rq->allprop ? sql_propfind_allprop_depth1 : sql_propfind_depth1;
287 } else if(rq->depth == -1) {
288 query = rq->allprop ? sql_propfind_allprop_recursive : sql_propfind_recursive;
289 } else {
290 log_ereport(LOG_FAILURE, "%s", "pg_dav_propfind_init: invalid depth");
291 return 1;
292 }
293
294 // get all resources and properties
295 char resource_id_str[32];
296 snprintf(resource_id_str, 32, "%" PRId64, resource_id);
297
298 const char* params[1] = { resource_id_str };
299 PGresult *result = PQexecParams(
300 pgdav->connection,
301 sql_propfind_allprop_recursive,
302 1, // number of parameters
303 NULL,
304 params, // parameter value
305 NULL,
306 NULL,
307 0); // 0: result in text format
308 int nrows = PQntuples(result);
309 if(nrows < 1) {
310 PQclear(result);
311 return 1;
312 }
313
94 PgPropfind *pg = pool_malloc(rq->sn->pool, sizeof(PgPropfind)); 314 PgPropfind *pg = pool_malloc(rq->sn->pool, sizeof(PgPropfind));
95 rq->userdata = pg; 315 rq->userdata = pg;
96 316
97 return 1; 317 pg->path = path;
318 pg->resource_id = resource_id;
319 pg->vfsproperties = webdav_vfs_properties(outplist, TRUE, 0);
320 pg->result = result;
321 pg->nrows = nrows;
322
323 return 0;
98 } 324 }
99 325
100 int pg_dav_propfind_do( 326 int pg_dav_propfind_do(
101 WebdavPropfindRequest *rq, 327 WebdavPropfindRequest *rq,
102 WebdavResponse *response, 328 WebdavResponse *response,
103 VFS_DIR parent, 329 VFS_DIR parent,
104 WebdavResource *resource, 330 WebdavResource *resource,
105 struct stat *s) 331 struct stat *s)
106 { 332 {
107 return 1; 333 PgPropfind *pg = rq->userdata;
334 pool_handle_t *pool = rq->sn->pool;
335 PGresult *result = pg->result;
336 WebdavVFSProperties vfsprops = pg->vfsproperties;
337
338 WSBool vfsprops_set = 0;
339 int64_t current_resource_id = pg->resource_id;
340 for(int r=0;r<pg->nrows;r++) {
341 // columns:
342 // 0: path
343 // 1: resource_id
344 // 2: parent_id
345 // 3: nodename
346 // 4: iscollection
347 // 5: lastmodified
348 // 6: creationdate
349 // 7: contentlength
350 // 8: property xmlns
351 // 9: property name
352 // 10: property value
353
354 char *path = PQgetvalue(result, r, 0);
355 char *res_id = PQgetvalue(result, r, 1);
356 int64_t resource_id;
357 if(!util_strtoint(res_id, &resource_id)) {
358 log_ereport(LOG_FAILURE, "pg_dav_propfind_do: cannot convert resource_id '%s' to int", res_id);
359 return 1;
360 }
361
362 char *nodename = PQgetvalue(result, r, 3);
363 if(resource_id != current_resource_id) {
364 // new resource
365 resource = response->addresource(response, nodename);
366 vfsprops_set = FALSE;
367 current_resource_id = resource_id;
368 }
369
370 // standard webdav properties
371 if(!vfsprops_set) {
372 if(vfsprops.getresourcetype) {
373 char *iscollection = PQgetvalue(result, r, 4);
374 if(iscollection && iscollection[0] == 't') {
375 resource->addproperty(resource, webdav_resourcetype_collection(), 200);
376 } else {
377 resource->addproperty(resource, webdav_resourcetype_empty(), 200);
378 }
379 }
380 if(vfsprops.getlastmodified) {
381 char *lastmodified = PQgetvalue(result, r, 5);
382 }
383 if(vfsprops.creationdate) {
384 char *creationdate = PQgetvalue(result, r, 6);
385 }
386 if(vfsprops.getcontentlength) {
387 char *contentlength = PQgetvalue(result, r, 7);
388 }
389 if(vfsprops.getetag) {
390
391 }
392
393
394 vfsprops_set = TRUE;
395 }
396
397 // dead properties
398 if(!PQgetisnull(result, r, 9)) {
399 char *xmlns = PQgetvalue(result, r, 8);
400 char *pname = PQgetvalue(result, r, 9);
401 char *pvalue = PQgetvalue(result, r, 10);
402
403 WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty));
404 property->lang = NULL;
405 property->name = pool_strdup(pool, pname);
406
407 xmlNs *namespace = pool_malloc(pool, sizeof(xmlNs));
408 memset(namespace, 0, sizeof(struct _xmlNs));
409 namespace->href = pool_strdup(pool, xmlns);
410 namespace->prefix = "zx1";
411 property->namespace = namespace;
412
413 property->vtype = WS_VALUE_TEXT;
414 property->value.text.str = pool_strdup(pool, pvalue);
415 property->value.text.length = strlen(pvalue);
416
417 resource->addproperty(resource, property, 200);
418 }
419
420
421 }
422
423
424
425 return 0;
108 } 426 }
109 427
110 int pg_dav_propfind_finish(WebdavPropfindRequest *rq) { 428 int pg_dav_propfind_finish(WebdavPropfindRequest *rq) {
111 return 1; 429 return 0;
112 } 430 }
113 431
114 int pg_dav_proppatch_do( 432 int pg_dav_proppatch_do(
115 WebdavProppatchRequest *request, 433 WebdavProppatchRequest *request,
116 WebdavResource *response, 434 WebdavResource *response,

mercurial