1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #include "xattrbackend.h"
31
32 #include "webdav.h"
33
34 #include "../util/util.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
46
47
48 static WebdavBackend webdav_xattr_backend = {
49 webdav_xattr_propfind_init,
50 webdav_xattr_propfind_do,
51 webdav_xattr_propfind_finish,
52 webdav_xattr_proppatch_do,
53 webdav_xattr_proppatch_finish,
54 NULL,
55 NULL,
56 NULL,
57 NULL,
58 0,
59 NULL,
60 NULL
61 };
62
63 int webdav_init_xattr_backend(
void) {
64
65
66 if(webdav_register_backend(
"xattr", webdav_xattr_init, webdav_xattr_create)) {
67 return 1;
68 }
69 return 0;
70 }
71
72 void* webdav_xattr_init(ServerConfiguration *cfg,
pool_handle_t *pool, WSConfigNode *config) {
73 WebdavXAttrRepository *repo = pool_malloc(pool,
sizeof(WebdavXAttrRepository));
74 if(!repo) {
75 return NULL;
76 }
77
78
79 repo->xattr_name =
"webdav_properties";
80
81 return repo;
82 }
83
84 WebdavBackend* webdav_xattr_create(Session *sn, Request *rq, pblock *pb,
void *initData) {
85 WebdavBackend *dav = pool_malloc(sn->pool,
sizeof(WebdavBackend));
86 if(!dav) {
87 return NULL;
88 }
89
90 WebdavXAttrBackend *instance = pool_malloc(sn->pool,
sizeof(WebdavXAttrBackend));
91 if(!instance) {
92 return NULL;
93 }
94 instance->repo = initData;
95
96 *dav = webdav_xattr_backend;
97 dav->instance = instance;
98
99 return dav;
100 }
101
102
103
104
105
106 int webdav_xattr_propfind_init(
107 WebdavPropfindRequest *rq,
108 const char *path,
109 const char *href,
110 WebdavPList **outplist)
111 {
112
113
114 if(rq->rq->vfs) {
115 log_ereport(
LOG_FAILURE,
"webdav-propfind: xattr backend unsupported with non-native VFS");
116 return 1;
117 }
118
119 XAttrPropfind *xprop = pool_malloc(rq->sn->pool,
sizeof(XAttrPropfind));
120 if(!xprop) {
121 return 1;
122 }
123 rq->userdata = xprop;
124
125 xprop->base_href = href;
126 xprop->base_path = path;
127
128 return 0;
129 }
130
131 int webdav_xattr_propfind_do(
132 WebdavPropfindRequest *request,
133 WebdavResponse *response,
134 VFS_DIR parent,
135 WebdavResource *resource,
136 struct stat *s)
137 {
138 Session *sn = request->sn;
139 Request *rq = request->rq;
140 CxAllocator *a = pool_allocator(sn->pool);
141
142 WebdavXAttrBackend *xdav = request->dav->instance;
143 WebdavXAttrRepository *repo = xdav->repo;
144 XAttrPropfind *xprop = request->userdata;
145
146
147 const char *path;
148 char *path_dp =
NULL;
149 if(!parent) {
150
151 path = xprop->base_path;
152 }
else {
153 size_t base_href_len = strlen(xprop->base_href);
154 size_t base_path_len = strlen(xprop->base_path);
155 char *res_path = resource->href + base_href_len;
156 size_t res_path_len = strlen(res_path);
157
158 path_dp = pool_malloc(sn->pool, base_path_len + res_path_len +
2);
159 memcpy(path_dp, xprop->base_path, base_path_len);
160 int s =
0;
161 if(path_dp[base_path_len-
1] !=
'/' && res_path[
0] !=
'/') {
162 path_dp[base_path_len] =
'/';
163 s =
1;
164 }
165 memcpy(path_dp + base_path_len + s, res_path, res_path_len);
166 path_dp[base_path_len + s + res_path_len] =
0;
167
168 path = path_dp;
169 }
170
171 ssize_t xattr_data_len =
0;
172 char *xattr_data = xattr_get_alloc(
173 sn->pool,
174 (libxattr_malloc_func)pool_malloc,
175 (libxattr_free_func)pool_free,
176 path,
177 repo->xattr_name,
178 &xattr_data_len);
179
180 if(!xattr_data) {
181
182 return 0;
183 }
184
185 CxMap *pmap = webdav_xattr_parse_data(a, xattr_data, xattr_data_len, path);
186 pool_free(sn->pool, xattr_data);
187 if(!pmap) {
188 return 1;
189 }
190
191 int err;
192 if(request->allprop || request->propname) {
193 err = webdav_xattr_propfind_allprop(request, resource, a, pmap);
194 }
else {
195 err = webdav_xattr_propfind_get_requested_properties(request, resource, a, pmap);
196 }
197
198 return err;
199 }
200
201 int webdav_xattr_propfind_finish(WebdavPropfindRequest *rq) {
202 return 0;
203 }
204
205
206
207 int webdav_xattr_propfind_get_requested_properties(
208 WebdavPropfindRequest *request,
209 WebdavResource *resource,
210 CxAllocator *a,
211 CxMap *pmap)
212 {
213 WebdavPListIterator i = webdav_plist_iterator(&request->properties);
214 WebdavPList *cur;
215 while(webdav_plist_iterator_next(&i, &cur)) {
216 WebdavProperty *reqprop = cur->property;
217
218 CxHashKey key = webdav_property_key_a(
219 a,
220 (
const char*)reqprop->namespace->href,
221 (
const char*)reqprop->name);
222 if(!key.data) {
223 return 1;
224 }
225
226 WebdavProperty *prop = cxMapGet(pmap, key);
227 if(prop) {
228 if(resource->addproperty(resource, prop,
200)) {
229 return 1;
230 }
231 }
232 }
233
234 return 0;
235 }
236
237 int webdav_xattr_propfind_allprop(
238 WebdavPropfindRequest *request,
239 WebdavResource *resource,
240 CxAllocator *a,
241 CxMap *pmap)
242 {
243 CxIterator i = cxMapIteratorValues(pmap);
244 cx_foreach(WebdavProperty*, prop, i) {
245 if(request->propname) {
246 prop->vtype =
WS_VALUE_NO_TYPE;
247 }
248 if(resource->addproperty(resource, prop,
200)) {
249 return 1;
250 }
251 }
252 return 0;
253 }
254
255
256
257 int webdav_xattr_proppatch_do(
258 WebdavProppatchRequest *request,
259 WebdavResource *response,
260 VFSFile *file,
261 WebdavPList **setInOut,
262 WebdavPList **removeInOut)
263 {
264 Session *sn = request->sn;
265 Request *rq = request->rq;
266 CxAllocator *a = pool_allocator(sn->pool);
267
268 WebdavXAttrBackend *xdav = request->dav->instance;
269 WebdavXAttrRepository *repo = xdav->repo;
270
271 char *path = pblock_findkeyval(pb_key_path, rq->vars);
272
273
274
275
276
277
278
279
280 XAttrProppatch *xprop = pool_malloc(sn->pool,
sizeof(XAttrProppatch));
281 if(!xprop) {
282 return 1;
283 }
284 request->userdata = xprop;
285
286
287 ssize_t xattr_data_len =
0;
288 char *xattr_data = xattr_get_alloc(
289 sn->pool,
290 (libxattr_malloc_func)pool_malloc,
291 (libxattr_free_func)pool_free,
292 path,
293 repo->xattr_name,
294 &xattr_data_len);
295
296 CxMap *pmap;
297 if(xattr_data) {
298 pmap = webdav_xattr_parse_data(a, xattr_data, xattr_data_len, path);
299 pool_free(sn->pool, xattr_data);
300 }
else {
301
302 pmap = cxHashMapCreate(a,
CX_STORE_POINTERS, request->setcount +
8);
303 }
304 if(!pmap) {
305 return 1;
306 }
307 xprop->properties = pmap;
308
309
310 WebdavPListIterator i = webdav_plist_iterator(removeInOut);
311 WebdavPList *cur;
312 while(webdav_plist_iterator_next(&i, &cur)) {
313 WebdavProperty *prop = cur->property;
314 if(!prop->namespace || !prop->namespace->prefix) {
315
316 log_ereport(
LOG_WARN,
"webdav_xattr_proppatch_do: property %s has missing namespace infos", prop->name);
317 continue;
318 }
319
320 CxHashKey key = webdav_property_key_a(
321 a,
322 (
const char*)prop->namespace->href,
323 (
const char*)prop->name);
324 if(!key.data) {
325 cxMapDestroy(pmap);
326 return 1;
327 }
328 void *rmprop = cxMapRemoveAndGet(pmap, key);
329 cxFree(a, (
void*)key.data);
330
331
332
333 if(rmprop) {
334 webdav_plist_iterator_remove_current(&i);
335 }
336 }
337
338 i = webdav_plist_iterator(setInOut);
339 while(webdav_plist_iterator_next(&i, &cur)) {
340 WebdavProperty *prop = cur->property;
341 if(!prop->namespace || !prop->namespace->prefix) {
342
343 log_ereport(
LOG_WARN,
"webdav_xattr_proppatch_do: property %s has missing namespace infos", prop->name);
344 continue;
345 }
346
347 if(webdav_xattr_put_prop(pmap, prop)) {
348 cxMapDestroy(pmap);
349 return 1;
350 }
351
352 webdav_plist_iterator_remove_current(&i);
353 }
354
355 return 0;
356 }
357
358 int webdav_xattr_proppatch_finish(
359 WebdavProppatchRequest *request,
360 WebdavResource *response,
361 VFSFile *file,
362 WSBool commit)
363 {
364 Session *sn = request->sn;
365 Request *rq = request->rq;
366 CxAllocator *a = pool_allocator(sn->pool);
367
368 WebdavXAttrBackend *xdav = request->dav->instance;
369 WebdavXAttrRepository *repo = xdav->repo;
370
371 XAttrProppatch *xprop = request->userdata;
372
373 char *path = pblock_findkeyval(pb_key_path, rq->vars);
374
375 int ret =
0;
376 if(commit) {
377 cxmutstr new_data = webdav_xattr_serialze_map(a, xprop->properties);
378 if(new_data.ptr) {
379 if(xattr_set(path, repo->xattr_name, new_data.ptr, new_data.length)) {
380 ret =
1;
381 }
382 }
else {
383 ret =
1;
384 }
385 }
386
387 return 0;
388 }
389
390
391
392
393 static int get_next_line(cxstring data,
size_t *pos, cxstring *line) {
394 size_t p = *pos;
395 cxstring str = cx_strsubs(data, p);
396 size_t i;
397 int skip =
0;
398 for(i=
0;i<str.length;i++) {
399 if(str.ptr[i] ==
'\n') {
400 skip =
1;
401 break;
402 }
403 }
404 p += i;
405 *pos = p + skip;
406 *line = cx_strsubsl(str,
0, i);
407 return i >
0 ?
TRUE :
FALSE;
408 }
409
410 static int webdav_xattr_parse_elm(cxstring line, cxstring *name, cxstring *prefix, cxstring *xmlns, cxstring *lang) {
411 cxstring s_xmlns =
CX_STR(
"xmlns:");
412
413
414 if(!cx_strprefix(line, s_xmlns)) {
415 return 1;
416 }
417 line.ptr +=
6;
418 line.length -=
6;
419
420
421
422
423 size_t i;
424 size_t token_end =
0;
425 for(i=
0;i<line.length;i++) {
426 if(line.ptr[i] ==
'=') {
427 token_end = i;
428 break;
429 }
430 }
431 if(token_end ==
0) {
432 return 1;
433 }
434 *prefix = cx_strn(line.ptr, token_end);
435
436
437 if(token_end +
4 > line.length) {
438 return 1;
439 }
440 if(line.ptr[token_end +
1] !=
'\"') {
441 return 1;
442 }
443 line.ptr += token_end +
2;
444 line.length -= token_end +
2;
445
446
447 int escape =
0;
448 token_end =
0;
449 for(i=
0;i<line.length;i++) {
450 if(line.ptr[i] ==
'\\') {
451 escape =
1;
452 continue;
453 }
else if(!escape && line.ptr[i] ==
'\"') {
454 token_end = i;
455 break;
456 }
457 escape =
0;
458 }
459 if(token_end ==
0) {
460 return 1;
461 }
462 *xmlns = cx_strn(line.ptr, token_end);
463
464
465 if(token_end +
2 > line.length) {
466 return 1;
467 }
468 line.ptr += token_end +
2;
469 line.length -= token_end +
2;
470 *name = cx_strtrim(line);
471
472 if(name->length ==
0) {
473 return 1;
474 }
475 if(prefix->length ==
0) {
476 return 1;
477 }
478 if(xmlns->length ==
0) {
479 return 1;
480 }
481
482 return 0;
483 }
484
485 static int webdav_xattr_parse_ns(cxstring line, cxstring *prefix, cxstring *href) {
486 size_t i =
0;
487 for(i=
1;i<line.length;i++) {
488 if(line.ptr[i] ==
':') {
489 break;
490 }
491 }
492 if(i ==
0 || i+
1 >= line.length) {
493
494 return 1;
495 }
496
497 cxstring pre;
498 pre.ptr = line.ptr;
499 pre.length = i;
500 *prefix = pre;
501
502 *href = cx_strsubs(line, i+
1);
503
504 return 0;
505 }
506
507 int webdav_xattr_put_prop(CxMap *pmap, WebdavProperty *prop) {
508 CxHashKey key = webdav_property_key_a(
509 pmap->allocator,
510 (
const char*)prop->namespace->href,
511 (
const char*)prop->name);
512 if(!key.data) {
513 return 1;
514 }
515 int ret = cxMapPut(pmap, key, prop);
516 cxFree(pmap->allocator, (
void*)key.data);
517 return ret;
518 }
519
520 CxMap* webdav_xattr_parse_data(CxAllocator *a,
const char *data,
size_t len,
const char *path) {
521 CxMap *pmap = cxHashMapCreate(a,
CX_STORE_POINTERS,
32);
522 if(!pmap) {
523 return NULL;
524 }
525 cxstring dat = cx_strn(data, len);
526
527 cxstring s_elm =
CX_STR(
"prop ");
528 cxstring s_ns =
CX_STR(
"ns ");
529 cxstring s_data =
CX_STR(
"data ");
530
531 WebdavProperty *prop =
NULL;
532 WebdavNSList *ns_begin =
NULL;
533 WebdavNSList *ns_end =
NULL;
534
535 int error =
0;
536
537 size_t elmno =
0;
538 cxstring line;
539 size_t pos =
0;
540 while(get_next_line(dat, &pos, &line)) {
541 if(cx_strprefix(line, s_elm)) {
542 if(prop) {
543 if(webdav_xattr_put_prop(pmap, prop)) {
544 error =
1;
545 break;
546 }
547 }
548
549 line = cx_strsubs(line,
5);
550
551
552 cxstring name;
553 cxstring prefix;
554 cxstring xmlns;
555 cxstring lang;
556 if(webdav_xattr_parse_elm(line, &name, &prefix, &xmlns, &lang)) {
557 log_ereport(
558 LOG_FAILURE,
559 "webdav xattr backend: file %s: invalid xattr format[%d]: cannot parse elm line",
560 path,
561 elmno);
562 error =
1;
563 break;
564 }
565
566
567 prop = cxMalloc(a,
sizeof(WebdavProperty));
568 if(!prop) {
569 error =
1;
570 break;
571 }
572 ZERO(prop,
sizeof(WebdavProperty));
573 ns_begin =
NULL;
574 ns_end =
NULL;
575
576 WSNamespace *ns = cxMalloc(a,
sizeof(WSNamespace));
577 if(!ns) {
578 error =
1;
579 break;
580 }
581 ZERO(ns,
sizeof(WSNamespace));
582
583 char *name_str = cx_strdup_a(a, name).ptr;
584 char *prefix_str = cx_strdup_a(a, prefix).ptr;
585 char *xmlns_str = cx_strdup_a(a, xmlns).ptr;
586 if(!(name_str && prefix_str && xmlns_str)) {
587 error =
1;
588 break;
589 }
590
591 ns->prefix = (
const xmlChar*)prefix_str;
592 ns->href = (
const xmlChar*)xmlns_str;
593
594 prop->name = name_str;
595 prop->namespace = ns;
596
597 elmno++;
598 }
else if(prop) {
599 if(cx_strprefix(line, s_ns)) {
600 line = cx_strsubs(line,
3);
601
602 cxstring prefix;
603 cxstring href;
604 if(webdav_xattr_parse_ns(line, &prefix, &href)) {
605 log_ereport(
606 LOG_FAILURE,
607 "webdav xattr backend: file %s: invalid xattr format[%d]: cannot parse ns",
608 path,
609 elmno);
610 error =
1;
611 break;
612 }
613
614 WSNamespace *ns_decl = cxMalloc(a,
sizeof(WSNamespace));
615 if(!ns_decl) {
616 error =
1;
617 break;
618 }
619 ZERO(ns_decl,
sizeof(WSNamespace));
620
621 ns_decl->prefix = (
const xmlChar*)cx_strdup_a(a, prefix).ptr;
622 ns_decl->href = (
const xmlChar*)cx_strdup_a(a, href).ptr;
623 if(!(ns_decl->prefix && ns_decl->href)) {
624 error =
1;
625 break;
626 }
627
628 if(webdav_nslist_add(a->data, &ns_begin, &ns_end, ns_decl)) {
629 error =
1;
630 break;
631 }
632 }
else if(cx_strprefix(line, s_data)) {
633 line = cx_strsubs(line,
5);
634
635
636
637 int64_t data_len =
0;
638 if(!util_strtoint(line.ptr, &data_len)) {
639 log_ereport(
640 LOG_FAILURE,
641 "webdav xattr backend: file %s: invalid xattr format[%d]: number expected after data",
642 path,
643 elmno);
644 error =
1;
645 break;
646 }
647
648
649 if(data_len >
0) {
650 if(data_len > dat.length - pos) {
651 log_ereport(
652 LOG_FAILURE,
653 "webdav xattr backend: file %s: invalid data length %" PRId64
" in prop %d",
654 path,
655 data_len,
656 elmno);
657 error =
1;
658 break;
659 }
660
661 cxstring propdata;
662 propdata.ptr = dat.ptr + pos;
663 propdata.length = data_len;
664 pos += data_len;
665
666 cxmutstr propdata_cp = cx_strdup_a(a, propdata);
667 if(!propdata_cp.ptr) {
668 error =
1;
669 break;
670 }
671 prop->vtype =
WS_VALUE_XML_DATA;
672 prop->value.data.namespaces = ns_begin;
673 prop->value.data.data = propdata_cp.ptr;
674 prop->value.data.length = propdata_cp.length;
675
676 if(pos < dat.length && dat.ptr[pos] ==
'\n') {
677 pos++;
678 }
679 }
680 }
else {
681 log_ereport(
682 LOG_FAILURE,
683 "webdav xattr backend: file %s: invalid xattr format[%d]: unknown element",
684 path,
685 elmno);
686 error =
1;
687 break;
688 }
689 }
690 }
691
692
693 if(prop) {
694 if(webdav_xattr_put_prop(pmap, prop)) {
695 error =
1;
696 }
697 }
698
699 if(error) {
700
701 cxMapDestroy(pmap);
702 pmap =
NULL;
703 }
704
705 return pmap;
706 }
707
708 cxmutstr webdav_xattr_serialze_map(CxAllocator *a, CxMap *pmap) {
709 pool_handle_t *pool = a->data;
710
711 CxBuffer buf;
712 if(cxBufferInit(&buf,
NULL,
8192, a,
CX_BUFFER_AUTO_EXTEND)) {
713 return (cxmutstr){
NULL,
0};
714 }
715
716 CxIterator i = cxMapIteratorValues(pmap);
717 cx_foreach(WebdavProperty*, prop, i) {
718 WSXmlData *property_value =
NULL;
719 if(prop->vtype ==
WS_VALUE_XML_NODE) {
720 property_value = wsxml_node2data(pool, prop->value.node);
721 }
else if(prop->vtype ==
WS_VALUE_XML_DATA) {
722 property_value = &prop->value.data;
723 }
724
725 int ret = cx_bprintf(&buf,
"prop xmlns:%s=\"%s\" %s\n", prop->namespace->prefix, prop->namespace->href, prop->name);
726 if(ret <=
0) {
727 pool_free(pool, buf.space);
728 return (cxmutstr){
NULL,
0};
729 }
730 if(property_value) {
731 WebdavNSList *ns = property_value->namespaces;
732 while(ns) {
733 ret = cx_bprintf(&buf,
"ns %s:%s\n", ns->namespace->prefix, ns->namespace->href);
734 if(ret <=
0) {
735 pool_free(pool, buf.space);
736 return (cxmutstr){
NULL,
0};
737 }
738 ns = ns->next;
739 }
740
741 ret = cx_bprintf(&buf,
"data %zu\n", property_value->length);
742 if(ret <=
0) {
743 pool_free(pool, buf.space);
744 return (cxmutstr){
NULL,
0};
745 }
746
747 cxBufferWrite(property_value->data,
1, property_value->length, &buf);
748 if(cxBufferPut(&buf,
'\n') <
0) {
749 pool_free(pool, buf.space);
750 return (cxmutstr){
NULL,
0};
751 }
752 }
753 }
754
755 return (cxmutstr){buf.space, buf.size};
756 }
757
758