86 r.contentlength,\n\ |
87 r.contentlength,\n\ |
87 p.xmlns,\n\ |
88 p.xmlns,\n\ |
88 p.pname,\n\ |
89 p.pname,\n\ |
89 p.pvalue\n\ |
90 p.pvalue\n\ |
90 from Resource r\n\ |
91 from Resource r\n\ |
91 left join Property p on r.resource_id = p.resource_id\n\ |
92 left join (\n\ |
92 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
93 select p.* from Property p\ |
93 on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\ |
94 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
|
95 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
|
96 ) p on r.resource_id = p.resource_id\n\ |
94 where r.resource_id = $1;"; |
97 where r.resource_id = $1;"; |
95 |
98 |
96 // propfind with depth = 1 |
99 // propfind with depth = 1 |
97 // params: $1: resource_id |
100 // params: $1: resource_id |
98 static const char *sql_propfind_allprop_depth1 = "\ |
101 static const char *sql_propfind_allprop_depth1 = "\ |
111 p.pname,\n\ |
114 p.pname,\n\ |
112 p.pvalue\n\ |
115 p.pvalue\n\ |
113 from Resource r\n\ |
116 from Resource r\n\ |
114 left join Property p on r.resource_id = p.resource_id\n\ |
117 left join Property p on r.resource_id = p.resource_id\n\ |
115 where r.resource_id = $1 or r.parent_id = $1\n\ |
118 where r.resource_id = $1 or r.parent_id = $1\n\ |
116 order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;"; |
119 order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id;"; |
117 |
120 |
118 |
121 |
119 // propfind with depth = 1 for specific properties |
122 // propfind with depth = 1 for specific properties |
120 // params: $1: resource_id |
123 // params: $1: resource_id |
121 static const char *sql_propfind_depth1 = "\ |
124 static const char *sql_propfind_depth1 = "\ |
132 r.contentlength,\n\ |
135 r.contentlength,\n\ |
133 p.xmlns,\n\ |
136 p.xmlns,\n\ |
134 p.pname,\n\ |
137 p.pname,\n\ |
135 p.pvalue\n\ |
138 p.pvalue\n\ |
136 from Resource r\n\ |
139 from Resource r\n\ |
137 left join Property p on r.resource_id = p.resource_id\n\ |
140 left join (\n\ |
138 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
141 select p.* from Property p\ |
139 on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\ |
142 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
|
143 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
|
144 ) p on r.resource_id = p.resource_id\n\ |
140 where r.resource_id = $1 or r.parent_id = $1\n\ |
145 where r.resource_id = $1 or r.parent_id = $1\n\ |
141 order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;"; |
146 order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id;"; |
142 |
147 |
143 // recursive propfind |
148 // recursive propfind |
144 // params: $1: resource_id |
149 // params: $1: resource_id |
145 static const char *sql_propfind_allprop_recursive = "\ |
150 static const char *sql_propfind_allprop_recursive = "\ |
146 with recursive resolvepath as (\n\ |
151 with recursive resolvepath as (\n\ |
205 r.contentlength,\n\ |
210 r.contentlength,\n\ |
206 p.xmlns,\n\ |
211 p.xmlns,\n\ |
207 p.pname,\n\ |
212 p.pname,\n\ |
208 p.pvalue\n\ |
213 p.pvalue\n\ |
209 from resolvepath r\n\ |
214 from resolvepath r\n\ |
210 left join Property p on r.resource_id = p.resource_id\n\ |
215 left join (\n\ |
211 inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\ |
216 select p.* from Property p\ |
212 on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\ |
217 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
|
218 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
|
219 ) p on r.resource_id = p.resource_id\n\ |
213 order by replace(ppath, '/', chr(1)), resource_id;"; |
220 order by replace(ppath, '/', chr(1)), resource_id;"; |
214 |
221 |
215 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) { |
222 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) { |
216 // resourcepool is required |
223 // resourcepool is required |
217 char *resource_pool = pblock_findval("resourcepool", pb); |
224 char *resource_pool = pblock_findval("resourcepool", pb); |
250 return webdav; |
257 return webdav; |
251 } |
258 } |
252 |
259 |
253 WebdavBackend* pg_webdav_prop_create(Session *sn, Request *rq, pblock *pb) { |
260 WebdavBackend* pg_webdav_prop_create(Session *sn, Request *rq, pblock *pb) { |
254 return NULL; |
261 return NULL; |
|
262 } |
|
263 |
|
264 /* |
|
265 * adds str to the buffer |
|
266 * some characters will be escaped: \,{} |
|
267 */ |
|
268 static void buf_addstr_escaped(UcxBuffer *buf, const char *str) { |
|
269 size_t len = strlen(str); |
|
270 for(size_t i=0;i<len;i++) { |
|
271 char c = str[i]; |
|
272 if(c == '{' || c == '}' || c == ',' || c == '\\') { |
|
273 ucx_buffer_putc(buf, '\\'); |
|
274 } |
|
275 ucx_buffer_putc(buf, c); |
|
276 } |
|
277 } |
|
278 |
|
279 /* |
|
280 * convert a property list to two pg array parameter strings |
|
281 * array format: {elm1,elm2,elm3} |
|
282 * xmlns: buffer for the xmlns array |
|
283 * pname: buffer for the property name array |
|
284 * |
|
285 * returns 0 on success, 1 otherwise |
|
286 */ |
|
287 int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname) { |
|
288 ucx_buffer_putc(xmlns, '{'); |
|
289 ucx_buffer_putc(pname, '{'); |
|
290 while(plist) { |
|
291 WebdavProperty *property = plist->property; |
|
292 if(property && property->namespace && property->namespace->href && property->name) { |
|
293 buf_addstr_escaped(xmlns, (const char*)property->namespace->href); |
|
294 buf_addstr_escaped(pname, (const char*)property->name); |
|
295 if(plist->next) { |
|
296 ucx_buffer_putc(xmlns, ','); |
|
297 ucx_buffer_putc(pname, ','); |
|
298 } |
|
299 } |
|
300 plist = plist->next; |
|
301 } |
|
302 int r1 = ucx_buffer_write("}\0", 2, 1, xmlns) == 0; |
|
303 int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0; |
|
304 return r1+r2 != 0; |
255 } |
305 } |
256 |
306 |
257 |
307 |
258 int pg_dav_propfind_init( |
308 int pg_dav_propfind_init( |
259 WebdavPropfindRequest *rq, |
309 WebdavPropfindRequest *rq, |
310 if(href_param[href_len-1] == '/') { |
360 if(href_param[href_len-1] == '/') { |
311 href_len--; |
361 href_len--; |
312 } |
362 } |
313 href_param[href_len] = '\0'; |
363 href_param[href_len] = '\0'; |
314 |
364 |
315 |
365 // if allprop is false, create array pair for xmlns/property names |
316 const char* params[2] = { resource_id_str, href_param }; |
366 UcxBuffer *xmlns_buf = NULL; |
|
367 UcxBuffer *pname_buf = NULL; |
|
368 char *xmlns_param = NULL; |
|
369 char *pname_param = NULL; |
|
370 int nparam = 2; |
|
371 if(!rq->allprop) { |
|
372 size_t bufsize = rq->propcount < 200 ? 8 + rq->propcount * 32 : 4096; |
|
373 xmlns_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND); |
|
374 if(!xmlns_buf) { |
|
375 return 1; |
|
376 } |
|
377 pname_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND); |
|
378 if(!pname_buf) { |
|
379 ucx_buffer_free(xmlns_buf); |
|
380 return 1; |
|
381 } |
|
382 if(pg_create_property_param_arrays(rq->properties, xmlns_buf, pname_buf)) { |
|
383 ucx_buffer_free(xmlns_buf); |
|
384 ucx_buffer_free(pname_buf); |
|
385 return 1; |
|
386 } |
|
387 xmlns_param = xmlns_buf->space; |
|
388 pname_param = pname_buf->space; |
|
389 nparam = 4; |
|
390 } |
|
391 |
|
392 const char* params[4] = { resource_id_str, href_param, xmlns_param, pname_param }; |
317 PGresult *result = PQexecParams( |
393 PGresult *result = PQexecParams( |
318 pgdav->connection, |
394 pgdav->connection, |
319 sql_propfind_allprop_recursive, |
395 query, |
320 2, // number of parameters |
396 nparam, // number of parameters |
321 NULL, |
397 NULL, |
322 params, // parameter value |
398 params, // parameter value |
323 NULL, |
399 NULL, |
324 NULL, |
400 NULL, |
325 0); // 0: result in text format |
401 0); // 0: result in text format |
326 int nrows = PQntuples(result); |
402 int nrows = PQntuples(result); |
327 pool_free(rq->sn->pool, href_param); |
403 pool_free(rq->sn->pool, href_param); |
|
404 if(xmlns_buf) { |
|
405 ucx_buffer_free(xmlns_buf); |
|
406 ucx_buffer_free(pname_buf); |
|
407 } |
328 if(nrows < 1) { |
408 if(nrows < 1) { |
329 PQclear(result); |
409 PQclear(result); |
330 return 1; |
410 return 1; |
331 } |
411 } |
332 |
412 |