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