src/server/webdav/xattrbackend.c

changeset 481
31affbf33911
parent 480
9f69e4b8b695
child 482
b02da778362e
equal deleted inserted replaced
480:9f69e4b8b695 481:31affbf33911
27 */ 27 */
28 28
29 29
30 #include "xattrbackend.h" 30 #include "xattrbackend.h"
31 31
32 #include "webdav.h"
33
32 #include "../util/util.h" 34 #include "../util/util.h"
33 #include "../util/libxattr.h" 35 #include "../util/libxattr.h"
36 #include "../util/pblock.h"
37
38 #include <inttypes.h>
39
40 #include <cx/hash_map.h>
41 #include <cx/buffer.h>
42 #include <cx/utils.h>
43 #include <cx/printf.h>
44 #include <libxml/tree.h>
45
34 46
35 47
36 static WebdavBackend webdav_xattr_backend = { 48 static WebdavBackend webdav_xattr_backend = {
37 webdav_xattr_propfind_init, 49 webdav_xattr_propfind_init,
38 webdav_xattr_propfind_do, 50 webdav_xattr_propfind_do,
41 webdav_xattr_proppatch_finish, 53 webdav_xattr_proppatch_finish,
42 NULL, // opt_mkcol 54 NULL, // opt_mkcol
43 NULL, // opt_mkcol_finish 55 NULL, // opt_mkcol_finish
44 NULL, // opt_delete 56 NULL, // opt_delete
45 NULL, // opt_delete_finish 57 NULL, // opt_delete_finish
46 WS_WEBDAV_PROPFIND_USE_VFS | WS_WEBDAV_PROPPATCH_USE_VFS, // settings 58 0, // settings
47 NULL, // instance 59 NULL, // instance
48 NULL // next 60 NULL // next
49 }; 61 };
50 62
51 int webdav_init_xattr_backend(void) { 63 int webdav_init_xattr_backend(void) {
161 (libxattr_free_func)pool_free, 173 (libxattr_free_func)pool_free,
162 path, 174 path,
163 repo->xattr_name, 175 repo->xattr_name,
164 &xattr_data_len); 176 &xattr_data_len);
165 177
178 if(xattr_data) {
179
180 }
181
166 182
167 return 0; 183 return 0;
168 } 184 }
169 185
170 int webdav_xattr_propfind_finish(WebdavPropfindRequest *rq) { 186 int webdav_xattr_propfind_finish(WebdavPropfindRequest *rq) {
176 WebdavResource *response, 192 WebdavResource *response,
177 VFSFile *file, 193 VFSFile *file,
178 WebdavPList **setInOut, 194 WebdavPList **setInOut,
179 WebdavPList **removeInOut) 195 WebdavPList **removeInOut)
180 { 196 {
181 return 0; 197 Session *sn = request->sn;
198 Request *rq = request->rq;
199 CxAllocator *a = pool_allocator(sn->pool);
200
201 WebdavXAttrBackend *xdav = request->dav->instance;
202 WebdavXAttrRepository *repo = xdav->repo;
203
204 XAttrProppatch *xprop = pool_malloc(sn->pool, sizeof(XAttrProppatch));
205 if(!xprop) {
206 return 1;
207 }
208 request->userdata = xprop;
209
210 char *path = pblock_findkeyval(pb_key_path, rq->vars);
211
212 // get existing property data
213 ssize_t xattr_data_len = 0;
214 char *xattr_data = xattr_get_alloc(
215 sn->pool,
216 (libxattr_malloc_func)pool_malloc,
217 (libxattr_free_func)pool_free,
218 path,
219 repo->xattr_name,
220 &xattr_data_len);
221
222 CxMap *pmap;
223 if(xattr_data) {
224 pmap = webdav_xattr_parse_data(a, xattr_data, xattr_data_len, path);
225 } else {
226 // empty map
227 pmap = cxHashMapCreate(a, request->setcount + 8);
228 }
229 if(!pmap) {
230 return 1;
231 }
232
233 // remove properties
234 WebdavPListIterator i = webdav_plist_iterator(removeInOut);
235 WebdavPList *cur;
236 while(webdav_plist_iterator_next(&i, &cur)) {
237 WebdavProperty *prop = cur->property;
238 if(!prop->namespace || !prop->namespace->prefix) {
239 // not sure if this check is required
240 log_ereport(LOG_WARN, "webdav_xattr_proppatch_do: property %s has missing namespace infos", prop->name);
241 continue;
242 }
243
244 CxHashKey key = webdav_property_key_a(
245 a,
246 (const char*)prop->namespace->href,
247 (const char*)prop->name);
248 if(!key.data.str) {
249 cxMapDestroy(pmap);
250 return 1;
251 }
252 void *rmprop = cxMapRemove(pmap, key);
253 cxFree(a, key.data.str);
254
255 // TODO: free rmprop
256
257 if(rmprop) {
258 webdav_plist_iterator_remove_current(&i);
259 }
260 }
261
262 i = webdav_plist_iterator(setInOut);
263 while(webdav_plist_iterator_next(&i, &cur)) {
264 WebdavProperty *prop = cur->property;
265 if(!prop->namespace || !prop->namespace->prefix) {
266 // not sure if this check is required
267 log_ereport(LOG_WARN, "webdav_xattr_proppatch_do: property %s has missing namespace infos", prop->name);
268 continue;
269 }
270
271 if(webdav_xattr_put_prop(pmap, prop)) {
272 cxMapDestroy(pmap);
273 return 1;
274 }
275
276 webdav_plist_iterator_remove_current(&i);
277 }
278
279 //printf("\n\n%.*s\n\n", (int)buf.size, buf.space);
280 //fflush(stdout);
281
282 int ret = 0;
283 cxmutstr new_data = webdav_xattr_serialze_map(a, pmap);
284 if(new_data.ptr) {
285 xattr_set(path, repo->xattr_name, new_data.ptr, new_data.length);
286 } else {
287 cxMapDestroy(pmap);
288 ret = 1;
289 }
290
291 return ret;
182 } 292 }
183 293
184 int webdav_xattr_proppatch_finish( 294 int webdav_xattr_proppatch_finish(
185 WebdavProppatchRequest *request, 295 WebdavProppatchRequest *request,
186 WebdavResource *response, 296 WebdavResource *response,
187 VFSFile *file, 297 VFSFile *file,
188 WSBool commit) 298 WSBool commit)
189 { 299 {
190 return 0; 300 return 0;
191 } 301 }
302
303
304 /* ----------------------- properties xattr data ----------------------- */
305
306 static int get_next_line(cxstring data, size_t *pos, cxstring *line) {
307 size_t p = *pos;
308 cxstring str = cx_strsubs(data, p);
309 size_t i;
310 int skip = 0;
311 for(i=0;i<str.length;i++) {
312 if(str.ptr[i] == '\n') {
313 skip = 1;
314 break;
315 }
316 }
317 p += i;
318 *pos = p + skip;
319 *line = cx_strsubsl(str, 0, i);
320 return i > 0 ? TRUE : FALSE;
321 }
322
323 static int webdav_xattr_parse_elm(cxstring line, cxstring *name, cxstring *prefix, cxstring *xmlns, cxstring *lang) {
324 cxstring s_xmlns = CX_STR("xmlns:");
325
326 // check if line starts with 'xmlns:'
327 if(!cx_strprefix(line, s_xmlns)) {
328 return 1;
329 }
330 line.ptr += 6;
331 line.length -= 6;
332
333
334 // format: <prefix>="<href>"
335 // find '='
336 size_t i;
337 size_t token_end = 0;
338 for(i=0;i<line.length;i++) {
339 if(line.ptr[i] == '=') {
340 token_end = i;
341 break;
342 }
343 }
344 if(token_end == 0) {
345 return 1;
346 }
347 *prefix = cx_strn(line.ptr, token_end);
348
349 // make sure the line length is big enough
350 if(token_end + 4 > line.length) {
351 return 1;
352 }
353 if(line.ptr[token_end + 1] != '\"') {
354 return 1;
355 }
356 line.ptr += token_end + 2;
357 line.length -= token_end + 2;
358
359 // get <href>
360 int escape = 0;
361 token_end = 0;
362 for(i=0;i<line.length;i++) {
363 if(line.ptr[i] == '\\') {
364 escape = 1;
365 continue;
366 } else if(!escape && line.ptr[i] == '\"') {
367 token_end = i;
368 break;
369 }
370 escape = 0;
371 }
372 if(token_end == 0) {
373 return 1;
374 }
375 *xmlns = cx_strn(line.ptr, token_end);
376
377 // check length
378 if(token_end + 2 > line.length) {
379 return 1;
380 }
381 line.ptr += token_end + 2;
382 line.length -= token_end + 2;
383 *name = cx_strtrim(line);
384
385 if(name->length == 0) {
386 return 1;
387 }
388 if(prefix->length == 0) {
389 return 1;
390 }
391 if(xmlns->length == 0) {
392 return 1;
393 }
394
395 return 0;
396 }
397
398 int webdav_xattr_put_prop(CxMap *pmap, WebdavProperty *prop) {
399 CxHashKey key = webdav_property_key_a(
400 pmap->allocator,
401 (const char*)prop->namespace->href,
402 (const char*)prop->name);
403 if(!key.data.str) {
404 return 1;
405 }
406 int ret = cxMapPut(pmap, key, prop);
407 cxFree(pmap->allocator, key.data.str);
408 return ret;
409 }
410
411 CxMap* webdav_xattr_parse_data(CxAllocator *a, const char *data, size_t len, const char *path) {
412 CxMap *pmap = cxHashMapCreate(a, 32);
413 if(!pmap) {
414 return NULL;
415 }
416 cxstring dat = cx_strn(data, len);
417
418 printf("\n\n");
419
420 cxstring s_elm = CX_STR("prop ");
421 cxstring s_ns = CX_STR("ns ");
422 cxstring s_data = CX_STR("data ");
423
424 WebdavProperty *prop = NULL;
425
426 int error = 0;
427
428 size_t elmno = 0;
429 cxstring line;
430 size_t pos = 0;
431 while(get_next_line(dat, &pos, &line)) {
432 if(cx_strprefix(line, s_elm)) {
433 if(prop) {
434 if(webdav_xattr_put_prop(pmap, prop)) {
435 error = 1;
436 break;
437 }
438 }
439
440 line = cx_strsubs(line, 5);
441
442 // get prop name, namespace and lang
443 cxstring name;
444 cxstring prefix;
445 cxstring xmlns;
446 cxstring lang;
447 if(webdav_xattr_parse_elm(line, &name, &prefix, &xmlns, &lang)) {
448 log_ereport(
449 LOG_FAILURE,
450 "webdav xattr backend: %file %s: invalid xattr format[%d]: cannot parse elm line",
451 path,
452 elmno);
453 error = 1;
454 break;
455 }
456
457 // create property
458 prop = cxMalloc(a, sizeof(WebdavProperty));
459 if(!prop) {
460 error = 1;
461 break;
462 }
463 ZERO(prop, sizeof(WebdavProperty));
464
465 WSNamespace *ns = cxMalloc(a, sizeof(WSNamespace));
466 if(!ns) {
467 error = 1;
468 break;
469 }
470
471 char *name_str = cx_strdup_a(a, name).ptr;
472 char *prefix_str = cx_strdup_a(a, prefix).ptr;
473 char *xmlns_str = cx_strdup_a(a, xmlns).ptr;
474 if(!(name_str && prefix_str && xmlns_str)) {
475 error = 1;
476 break;
477 }
478
479 ns->prefix = (const xmlChar*)prefix_str;
480 ns->href = (const xmlChar*)xmlns_str;
481
482 prop->name = name_str;
483 prop->namespace = ns;
484
485 elmno++;
486 } else if(prop) {
487 if(cx_strprefix(line, s_ns)) {
488 // TODO
489 } else if(cx_strprefix(line, s_data)) {
490 line = cx_strsubs(line, 5);
491
492 // util_strtoint just works here, because the line ends with \n
493 // the xattr content data is also garanteed to be 0-terminated
494 int64_t data_len = 0;
495 if(!util_strtoint(line.ptr, &data_len)) {
496 log_ereport(
497 LOG_FAILURE,
498 "webdav xattr backend: %file %s: invalid xattr format[%d]: number expected after data",
499 path,
500 elmno);
501 error = 1;
502 break;
503 }
504
505 // get data
506 if(data_len > 0) {
507 if(data_len > dat.length - pos) {
508 log_ereport(
509 LOG_FAILURE,
510 "webdav xattr backend: %file %s: invalid data length %" PRId64 " in prop %d",
511 path,
512 data_len,
513 elmno);
514 error = 1;
515 break;
516 }
517
518 cxstring propdata;
519 propdata.ptr = dat.ptr + pos;
520 propdata.length = data_len;
521 pos += data_len;
522
523 cxmutstr propdata_cp = cx_strdup_a(a, propdata);
524 if(!propdata_cp.ptr) {
525 error = 1;
526 break;
527 }
528 prop->vtype = WS_VALUE_XML_DATA;
529 prop->value.data.namespaces = NULL;
530 prop->value.data.data = propdata_cp.ptr;
531 prop->value.data.length = propdata_cp.length;
532
533 if(pos < dat.length && dat.ptr[pos] == '\n') {
534 pos++;
535 }
536 }
537 } else {
538 log_ereport(
539 LOG_FAILURE,
540 "webdav xattr backend: %file %s: invalid xattr format[%d]: unknown element",
541 path,
542 elmno);
543 error = 1;
544 break;
545 }
546 }
547
548 //printf("line: {%.*s}\n", (int)line.length, line.ptr);
549 //fflush(stdout);
550 }
551
552 // add last property
553 if(prop) {
554 if(webdav_xattr_put_prop(pmap, prop)) {
555 error = 1;
556 }
557 }
558
559 if(error) {
560 // TODO: free pmap content
561 cxMapDestroy(pmap);
562 pmap = NULL;
563 }
564
565 //printf("\n\n");
566 //fflush(stdout);
567
568
569 return pmap;
570 }
571
572 cxmutstr webdav_xattr_serialze_map(CxAllocator *a, CxMap *pmap) {
573 pool_handle_t *pool = a->data;
574
575 CxBuffer buf;
576 if(cxBufferInit(&buf, NULL, 8192, a, CX_BUFFER_AUTO_EXTEND)) {
577 return (cxmutstr){NULL,0};
578 }
579
580 CxIterator i = cxMapIteratorValues(pmap);
581 cx_foreach(WebdavProperty*, prop, i) {
582 WSXmlData *property_value = NULL;
583 if(prop->vtype == WS_VALUE_XML_NODE) {
584 property_value = wsxml_node2data(pool, prop->value.node);
585 } else if(prop->vtype == WS_VALUE_XML_DATA) {
586 property_value = &prop->value.data;
587 }
588
589 cx_bprintf(&buf, "prop xmlns:%s=\"%s\" %s\n", prop->namespace->prefix, prop->namespace->href, prop->name);
590 if(property_value) {
591 WebdavNSList *ns = property_value->namespaces;
592 while(ns) {
593 cx_bprintf(&buf, "ns %s:%s\n", prop->namespace->prefix, prop->namespace->href);
594 ns = ns->next;
595 }
596
597 cx_bprintf(&buf, "data %zu\n", property_value->length);
598 cxBufferWrite(property_value->data, 1, property_value->length, &buf);
599 cxBufferPut(&buf, '\n');
600 }
601 }
602
603 return (cxmutstr){buf.space, buf.size};
604 }
605

mercurial