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 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include <cx/list.h>
34
35 #include "../daemon/session.h"
36 #include "../util/pblock.h"
37
38 #include "operation.h"
39
40 #define WEBDAV_PATH_MAX 8192
41
42
43 size_t webdav_num_backends(WebdavBackend *dav) {
44 size_t count =
0;
45 while(dav) {
46 count++;
47 dav = dav->next;
48 }
49 return count;
50 }
51
52
53
54
55
56
57
58 WebdavOperation* webdav_create_propfind_operation(
59 Session *sn,
60 Request *rq,
61 WebdavBackend *dav,
62 WebdavPList *reqprops,
63 WebdavPropfindRequestList *requests,
64 WebdavResponse *response)
65 {
66 WebdavOperation *op = pool_malloc(sn->pool,
sizeof(WebdavOperation));
67 ZERO(op,
sizeof(WebdavOperation));
68 op->dav = dav;
69 op->sn = sn;
70 op->rq = rq;
71 op->reqprops = reqprops;
72 op->requests = requests;
73 op->response = response;
74 op->response_close = webdav_op_propfiond_close_resource;
75 response->op = op;
76
77 return op;
78 }
79
80 int webdav_op_propfind_begin(
81 WebdavOperation *op,
82 const char *href,
83 VFS_DIR parent,
84 struct stat *s)
85 {
86
87 WebdavResource *resource = op->response->addresource(op->response, href);
88 if(!resource) {
89 return REQ_ABORTED;
90 }
91
92
93 op->stat = s;
94 op->parent = parent;
95
96
97 WebdavPropfindRequest *propfind = op->requests->propfind;
98
99
100 int ret =
REQ_PROCEED;
101 if(op->dav->propfind_do(propfind, op->response,
NULL, resource, s)) {
102 ret =
REQ_ABORTED;
103 }
else {
104
105
106
107 if(!resource->isclosed) {
108 ret = resource->close(resource);
109 }
110 }
111
112 return ret;
113 }
114
115 typedef struct PathSearchElm {
116 char *href;
117 char *path;
118 size_t hreflen;
119 size_t pathlen;
120 struct PathSearchElm *next;
121 } PathSearchElm;
122
123
124
125
126
127 static int path_buf_concat(
128 pool_handle_t *pool,
129 char **buf,
130 size_t * restrict len,
131 WSBool * restrict baseinit,
132 const char *base,
133 size_t baselen,
134 const char *elm,
135 size_t elmlen)
136 {
137 if(base[baselen-
1] ==
'/') {
138 baselen--;
139 }
140
141 size_t newlen = baselen + elmlen +
1;
142 if(newlen >
WEBDAV_PATH_MAX) {
143 log_ereport(
LOG_FAILURE,
"webdav: maximal path length exceeded");
144 return 1;
145 }
146
147
148 if(newlen +
1 > *len) {
149 *len = newlen +
128;
150 char *newbuf = pool_realloc(pool, *buf, newlen);
151 if(newbuf) {
152 log_ereport(
LOG_FAILURE,
"webdav: path memory allocation failed");
153 return 1;
154 }
155 *baseinit =
FALSE;
156
157 *buf = newbuf;
158 }
159
160
161
162 if(!(*baseinit)) {
163 memcpy(*buf, base, baselen);
164 (*buf)[baselen] =
'/';
165 *baseinit =
TRUE;
166 }
167
168 memcpy((*buf) + baselen +
1, elm, elmlen);
169 (*buf)[newlen] =
'\0';
170
171 return 0;
172 }
173
174 static int propfind_child_cb(
175 VFSContext *vfs,
176 const char *href,
177 const char *path,
178 VFSDir *parent,
179 struct stat *s,
180 void *op)
181 {
182 return webdav_op_propfind_begin(op, href, parent, s);
183 }
184
185 int webdav_op_propfind_children(
186 WebdavOperation *op,
187 VFSContext *vfs,
188 const char *href,
189 const char *path)
190 {
191 WebdavPropfindRequest *request = op->requests->propfind;
192 return webdav_op_iterate_children(
193 vfs, request->depth, href, path, propfind_child_cb, op);
194 }
195
196 int webdav_op_propfiond_close_resource(
197 WebdavOperation *op,
198 WebdavResource *resource)
199 {
200
201
202 WebdavBackend *dav = op->dav->next;
203 WebdavPropfindRequestList *request = op->requests->next;
204
205
206 int ret =
REQ_PROCEED;
207 while(dav && request) {
208 if(dav->propfind_do(
209 request->propfind,
210 op->response,
211 op->parent,
212 resource,
213 op->stat))
214 {
215 ret =
REQ_ABORTED;
216 }
217
218 dav = dav->next;
219 request = request->next;
220 }
221 return ret;
222 }
223
224
225
226
227 int webdav_op_propfind_finish(WebdavOperation *op) {
228 WebdavBackend *dav = op->dav;
229 WebdavPropfindRequestList *requests = op->requests;
230
231 int ret =
REQ_PROCEED;
232 while(dav && requests) {
233 if(dav->propfind_finish(requests->propfind)) {
234 ret =
REQ_ABORTED;
235 }
236
237 dav = dav->next;
238 requests = requests->next;
239 }
240 return ret;
241 }
242
243
244
245
246
247
248
249 WebdavOperation* webdav_create_proppatch_operation(
250 Session *sn,
251 Request *rq,
252 WebdavBackend *dav,
253 WebdavProppatchRequest *proppatch,
254 WebdavResponse *response)
255 {
256 WebdavOperation *op = pool_malloc(sn->pool,
sizeof(WebdavOperation));
257 ZERO(op,
sizeof(WebdavOperation));
258 op->dav = dav;
259 op->sn = sn;
260 op->rq = rq;
261 op->reqprops =
NULL;
262 op->response = response;
263 op->proppatch = proppatch;
264 op->response_close = webdav_op_proppatch_close_resource;
265 response->op = op;
266
267 return op;
268 }
269
270
271
272 int webdav_op_proppatch(
273 WebdavOperation *op,
274 const char *href,
275 const char *path)
276 {
277 WebdavProppatchRequest *orig_request = op->proppatch;
278 CxAllocator *a = pool_allocator(op->sn->pool);
279
280
281 WebdavResource *resource = op->response->addresource(op->response, href);
282 if(!resource) {
283 return REQ_ABORTED;
284 }
285
286
287 if(acl_evaluate(op->sn, op->rq,
ACL_WRITE_XATTR)) {
288
289
290
291 if(op->rq->status_num ==
PROTOCOL_UNAUTHORIZED) {
292 return REQ_ABORTED;
293 }
294
295
296 log_ereport(
LOG_VERBOSE,
"webdav-proppatch: access forbidden");
297 int ret =
REQ_PROCEED;
298 WebdavPList *plist = op->proppatch->set;
299 for(
int i=
0;i<
2;i++) {
300 while(plist) {
301 if(resource->addproperty(resource, plist->property,
PROTOCOL_FORBIDDEN)) {
302 ret =
REQ_ABORTED;
303 break;
304 }
305 plist = plist->next;
306 }
307 plist = op->proppatch->remove;
308 }
309
310 if(resource->close(resource)) {
311 ret =
REQ_ABORTED;
312 }
313 return ret;
314 }
315
316 VFSContext *ctx =
NULL;
317 VFSFile *file =
NULL;
318
319
320 WebdavProppatchRequest **requests = pool_calloc(
321 op->sn->pool,
322 webdav_num_backends(op->dav),
323 sizeof(WebdavProppatchRequest*));
324 if(requests ==
NULL) {
325 return REQ_ABORTED;
326 }
327
328 WebdavPList *prev_set = orig_request->set;
329 WebdavPList *prev_remove = orig_request->remove;
330 size_t set_count = orig_request->setcount;
331 size_t remove_count = orig_request->removecount;
332
333 int ret =
REQ_PROCEED;
334
335
336 WebdavBackend *dav = op->dav;
337 size_t numrequests =
0;
338 while(dav) {
339 WebdavPList *set = webdav_plist_clone_s(
340 op->sn->pool,
341 prev_set,
342 &set_count);
343 WebdavPList *remove = webdav_plist_clone_s(
344 op->sn->pool,
345 prev_remove,
346 &remove_count);
347 if((prev_set && !set) || (prev_remove && !remove)) {
348
349 ret =
REQ_ABORTED;
350 break;
351 }
352
353
354 WebdavProppatchRequest *req = pool_malloc(
355 op->sn->pool,
356 sizeof(WebdavProppatchRequest));
357 memcpy(req, orig_request,
sizeof(WebdavProppatchRequest));
358 req->dav = dav;
359 req->set = orig_request->set;
360 req->setcount = orig_request->setcount;
361 req->remove = orig_request->remove;
362 req->removecount = orig_request->removecount;
363 req->userdata =
NULL;
364
365
366 if(!file && (dav->settings &
WS_WEBDAV_PROPPATCH_USE_VFS)
367 ==
WS_WEBDAV_PROPPATCH_USE_VFS)
368 {
369 ctx = vfs_request_context(op->sn, op->rq);
370 if(!ctx) {
371 ret =
REQ_ABORTED;
372 break;
373 }
374
375 file = vfs_open(ctx, path,
O_RDONLY);
376 if(!file) {
377 protocol_status(
378 op->sn,
379 op->rq,
380 util_errno2status(ctx->vfs_errno),
381 NULL);
382 ret =
REQ_ABORTED;
383 }
384 }
385
386
387 if(dav->proppatch_do(req, resource, file, &set, &remove)) {
388
389
390 ret =
REQ_ABORTED;
391 break;
392 }
393
394
395
396 prev_set = set;
397 prev_remove = remove;
398
399 requests[numrequests++] = req;
400
401
402 dav = dav->next;
403 }
404
405 WSBool commit =
FALSE;
406 if(ret ==
REQ_PROCEED && resource->err ==
0) {
407
408 commit =
TRUE;
409 }
410
411
412 dav = op->dav;
413 int i =
0;
414 while(dav && i < numrequests) {
415 if(dav->proppatch_finish(requests[i], resource, file, commit)) {
416 ret =
REQ_ABORTED;
417 }
418 i++;
419 dav = dav->next;
420 }
421
422 if(file) {
423 vfs_close(file);
424 }
425
426 if(resource->close(resource)) {
427 ret =
REQ_ABORTED;
428 }
429
430 return ret;
431 }
432
433 int webdav_op_proppatch_close_resource(
434 WebdavOperation *op,
435 WebdavResource *resource)
436 {
437 return 0;
438 }
439
440
441
442
443
444
445
446
447 WebdavVFSOperation* webdav_vfs_op(
448 Session *sn,
449 Request *rq,
450 WebdavBackend *dav,
451 WSBool precondition)
452 {
453 WebdavVFSOperation *op = pool_malloc(sn->pool,
sizeof(WebdavVFSOperation));
454 if(!op) {
455 return NULL;
456 }
457 ZERO(op,
sizeof(WebdavVFSOperation));
458
459 op->sn = sn;
460 op->rq = rq;
461 op->dav = dav;
462 op->stat =
NULL;
463 op->stat_errno =
0;
464
465
466 VFSContext *vfs = vfs_request_context(sn, rq);
467 if(!vfs) {
468 pool_free(sn->pool, op);
469 return NULL;
470 }
471 op->vfs = vfs;
472
473 char *path = pblock_findkeyval(pb_key_path, rq->vars);
474 op->path = path;
475
476 return op;
477 }
478
479 WebdavVFSOperation webdav_vfs_sub_op(
480 WebdavVFSOperation *op,
481 char *path,
482 struct stat *s)
483 {
484 WebdavVFSOperation sub;
485 sub.dav = op->dav;
486 sub.path = path;
487 sub.sn = op->sn;
488 sub.vfs = op->vfs;
489 sub.path = path;
490 sub.stat = s;
491 sub.stat_errno =
0;
492 return sub;
493 }
494
495 int webdav_op_iterate_children(
496 VFSContext *vfs,
497 int depth,
498 const char *href,
499 const char *path,
500 vfs_op_child_func func,
501 void *userdata)
502 {
503 pool_handle_t *pool = vfs->sn->pool;
504
505 PathSearchElm *start_elm = pool_malloc(pool,
sizeof(PathSearchElm));
506 start_elm->href = pool_strdup(pool, href ? href :
"");
507 start_elm->path = pool_strdup(pool, path ? path :
"");
508 start_elm->hreflen = href ? strlen(href) :
0;
509 start_elm->pathlen = path ? strlen(path) :
0;
510 start_elm->next =
NULL;
511
512 PathSearchElm *stack = start_elm;
513 PathSearchElm *stack_end = start_elm;
514
515
516 char *newpath = pool_malloc(pool,
256);
517 size_t newpathlen =
256;
518
519 char *newhref = pool_malloc(pool,
256);
520 size_t newhreflen =
256;
521
522 int err =
0;
523 while(stack && !err) {
524 PathSearchElm *cur_elm = stack;
525
526
527
528 WSBool href_buf_init =
FALSE;
529 WSBool path_buf_init =
FALSE;
530
531 VFS_DIR dir = vfs_opendir(vfs, cur_elm->path);
532 if(!dir) {
533 log_ereport(
534 LOG_FAILURE,
535 "webdav: propfind: cannot open directory %d",
536 vfs->vfs_errno);
537 err =
1;
538 break;
539 }
540
541 VFS_ENTRY f;
542 while(vfs_readdir_stat(dir, &f)) {
543 if(f.stat_errno !=
0) {
544 continue;
545 }
546
547 size_t child_len = strlen(f.name);
548
549
550 if(path_buf_concat(
551 pool,
552 &newhref,
553 &newhreflen,
554 &href_buf_init,
555 cur_elm->href,
556 cur_elm->hreflen,
557 f.name,
558 child_len))
559 {
560 err =
1;
561 break;
562 }
563 if(path_buf_concat(
564 pool,
565 &newpath,
566 &newpathlen,
567 &path_buf_init,
568 cur_elm->path,
569 cur_elm->pathlen,
570 f.name,
571 child_len))
572 {
573 err =
1;
574 break;
575 }
576 size_t childhreflen = cur_elm->hreflen +
1 + child_len;
577 size_t childpathlen = cur_elm->pathlen +
1 + child_len;
578
579
580 if(func(vfs, newhref, newpath, dir, &f.stat, userdata)) {
581 err =
1;
582 break;
583 }
584
585
586 if(depth == -
1 &&
S_ISDIR(f.stat.st_mode)) {
587 char *hrefcp = pool_malloc(pool, childhreflen +
1);
588 memcpy(hrefcp, newhref, childhreflen +
1);
589 hrefcp[childhreflen] =
'\0';
590
591 char *pathcp = pool_malloc(pool, childpathlen +
1);
592 memcpy(pathcp, newpath, childpathlen +
1);
593 pathcp[childpathlen] =
'\0';
594
595 PathSearchElm *new_elm = pool_malloc(pool,
596 sizeof(PathSearchElm));
597 if(!new_elm) {
598 err =
1;
599 break;
600 }
601 new_elm->href = hrefcp;
602 new_elm->path = pathcp;
603 new_elm->hreflen = childhreflen;
604 new_elm->pathlen = childpathlen;
605 new_elm->next =
NULL;
606
607
608
609
610
611 stack_end->next = new_elm;
612 stack_end = new_elm;
613 }
614 }
615
616 vfs_closedir(dir);
617
618 stack = stack->next;
619
620 pool_free(pool, cur_elm->path);
621 pool_free(pool, cur_elm->href);
622 pool_free(pool, cur_elm);
623 }
624
625
626 for(PathSearchElm *elm=stack;elm;) {
627 PathSearchElm *next_elm = elm->next;
628 pool_free(pool, elm->path);
629 pool_free(pool, elm->href);
630 pool_free(pool, elm);
631 elm = next_elm;
632 }
633
634 return err;
635 }
636
637
638 int webdav_vfs_stat(WebdavVFSOperation *op) {
639 if(op->stat) {
640 return 0;
641 }
else if(op->stat_errno !=
0) {
642
643 return 1;
644 }
645
646
647 struct stat sbuf;
648 int ret = vfs_stat(op->vfs, op->path, &sbuf);
649 if(!ret) {
650
651 op->stat = pool_malloc(op->sn->pool,
sizeof(
struct stat));
652 if(op->stat) {
653 memcpy(op->stat, &sbuf,
sizeof(
struct stat));
654 }
else {
655 ret =
1;
656 op->stat_errno =
ENOMEM;
657 }
658 }
else {
659 op->stat_errno = errno;
660 }
661
662 return ret;
663 }
664
665 int webdav_vfs_op_do(WebdavVFSOperation *op, WebdavVFSOpType type) {
666 WSBool exec_vfs =
TRUE;
667
668
669 WebdavVFSRequest **requests = pool_calloc(
670 op->sn->pool,
671 webdav_num_backends(op->dav),
672 sizeof(WebdavVFSRequest*));
673 if(requests ==
NULL) {
674 return REQ_ABORTED;
675 }
676
677 int ret =
REQ_PROCEED;
678
679
680 WebdavBackend *dav = op->dav;
681 int called_backends =
0;
682 while(dav) {
683 WebdavVFSRequest *request =
NULL;
684
685
686 vfs_op_func op_func =
NULL;
687 vfs_op_finish_func op_finish_func =
NULL;
688
689 if(type ==
WEBDAV_VFS_MKDIR) {
690 op_func = dav->opt_mkcol;
691 op_finish_func = dav->opt_mkcol_finish;
692 }
else if(type ==
WEBDAV_VFS_DELETE) {
693 op_func = dav->opt_delete;
694 op_finish_func = dav->opt_delete_finish;
695 }
696
697 if(op_func || op_finish_func) {
698
699 request = pool_malloc(op->sn->pool,
sizeof(WebdavVFSRequest));
700 if(!request) {
701 exec_vfs =
FALSE;
702 ret =
REQ_ABORTED;
703 break;
704 }
705 request->sn = op->sn;
706 request->rq = op->rq;
707 request->path = op->path;
708 request->userdata =
NULL;
709
710 requests[called_backends] = request;
711 }
712
713
714
715 WSBool done =
FALSE;
716 called_backends++;
717 if(op_func) {
718 if(op_func(request, &done)) {
719 exec_vfs =
FALSE;
720 ret =
REQ_ABORTED;
721 break;
722 }
723 }
724 if(done) {
725 exec_vfs =
FALSE;
726 }
727
728 dav = dav->next;
729 }
730
731
732 if(exec_vfs) {
733 int r =
0;
734 if(type ==
WEBDAV_VFS_MKDIR) {
735 r = vfs_mkdir(op->vfs, op->path);
736 if(r) {
737
738 switch(op->vfs->vfs_errno) {
739 case ENOENT: {
740 op->rq->status_num =
409;
741 break;
742 }
743 case EEXIST: {
744 op->rq->status_num =
405;
745 break;
746 }
747 case EACCES: {
748 op->rq->status_num =
403;
749 break;
750 }
751 default: op->rq->status_num =
500;
752 }
753 }
754 }
else if(type ==
WEBDAV_VFS_DELETE) {
755 r = webdav_vfs_unlink(op);
756 }
757
758 if(r) {
759 ret =
REQ_ABORTED;
760 }
761 }
762
763 WSBool success = ret ==
REQ_PROCEED ?
TRUE :
FALSE;
764
765
766 dav = op->dav;
767 int i =
0;
768 while(dav && i < called_backends) {
769
770 vfs_op_finish_func op_finish_func =
NULL;
771
772 if(type ==
WEBDAV_VFS_MKDIR) {
773 op_finish_func = dav->opt_mkcol_finish;
774 }
else if(type ==
WEBDAV_VFS_DELETE) {
775 op_finish_func = dav->opt_delete_finish;
776 }
777
778 if(op_finish_func) {
779 if(op_finish_func(requests[i], success)) {
780 ret =
REQ_ABORTED;
781 }
782 }
783
784 dav = dav->next;
785 i++;
786 }
787
788 return ret;
789 }
790
791 int webdav_vfs_unlink(WebdavVFSOperation *op) {
792
793 if(webdav_vfs_stat(op)) {
794 return 1;
795 }
else {
796 if(!
S_ISDIR(op->stat->st_mode)) {
797 return vfs_unlink(op->vfs, op->path);
798 }
else {
799 return vfs_rmdir(op->vfs, op->path);
800 }
801 }
802 }
803