src/server/webdav/webdav.c

changeset 385
a1f4cb076d2f
parent 381
7d55d60e1fe2
child 395
224c4e858125
equal deleted inserted replaced
210:21274e5950af 385:a1f4cb076d2f
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2013 Olaf Wintermann. All rights reserved. 4 * Copyright 2019 Olaf Wintermann. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
28 28
29 #include <stdio.h> 29 #include <stdio.h>
30 #include <stdlib.h> 30 #include <stdlib.h>
31 #include <string.h> 31 #include <string.h>
32 32
33 #include <ucx/buffer.h>
34 #include <ucx/list.h>
35
33 #include "webdav.h" 36 #include "webdav.h"
34 37
35 38 #include "search.h"
39 #include "versioning.h"
40 #include "multistatus.h"
41 #include "requestparser.h"
42 #include "operation.h"
43
44 #include "../util/pblock.h"
45 #include "../util/util.h"
46 #include "../daemon/session.h"
47 #include "../daemon/http.h"
48 #include "../daemon/protocol.h"
49 #include "../daemon/vfs.h"
50
51 /*
52 * http method fptr mapping
53 * key: http method name (string)
54 * value: SAF fptr
55 */
56 static UcxMap *method_handler_map;
57
58 /*
59 * webdav backend types
60 * key: backend name (string)
61 * value: WebdavBackend*
62 */
63 static UcxMap *webdav_type_map;
64
65 static WebdavBackend default_backend;
66
67 static WSNamespace dav_namespace;
68
69 static WebdavProperty dav_resourcetype_empty;
70 static WebdavProperty dav_resourcetype_collection;
71 static WSXmlData dav_resourcetype_collection_value;
72
73 #define WEBDAV_RESOURCE_TYPE_COLLECTION "<D:collection/>"
74
75 static void init_default_backend(void) {
76 memset(&default_backend, 0, sizeof(WebdavBackend));
77 default_backend.propfind_init = default_propfind_init;
78 default_backend.propfind_do = default_propfind_do;
79 default_backend.propfind_finish = default_propfind_finish;
80 default_backend.proppatch_do = default_proppatch_do;
81 default_backend.proppatch_finish = default_proppatch_finish;
82 default_backend.opt_mkcol = NULL;
83 default_backend.opt_delete = NULL;
84 default_backend.settings = WS_WEBDAV_PROPFIND_USE_VFS;
85 default_backend.instance = NULL;
86 }
87
88 int webdav_register_backend(const char *name, webdav_init_func webdavInit, webdav_create_func webdavCreate) {
89 WebdavType *webdavType = malloc(sizeof(WebdavType));
90 webdavType->init = webdavInit;
91 webdavType->create = webdavCreate;
92 return ucx_map_cstr_put(webdav_type_map, name, webdavType);
93 }
94
95 WebdavType* webdav_get_type(scstr_t dav_class) {
96 return ucx_map_sstr_get(webdav_type_map, dav_class);
97 }
98
99 void* webdav_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, WebdavType *dav_class, WSConfigNode *config, int *error) {
100 *error = 0;
101 if(dav_class->init) {
102 void *initData = dav_class->init(cfg, pool, config);
103 if(!initData) {
104 *error = 1;
105 }
106 return initData;
107 } else {
108 return NULL;
109 }
110 }
111
112 WebdavBackend* webdav_create(Session *sn, Request *rq, const char *dav_class, pblock *pb, void *initData) {
113 WebdavType *webdavType = ucx_map_cstr_get(webdav_type_map, dav_class);
114 if(!webdavType) {
115 log_ereport(LOG_MISCONFIG, "webdav_create: unkown dav type %s", dav_class);
116 return NULL;
117 }
118
119 return webdavType->create(sn, rq, pb, initData);
120 }
121
122 static WSBool webdav_is_initialized = FALSE;
123
124 int webdav_init(pblock *pb, Session *sn, Request *rq) {
125 if(webdav_is_initialized) {
126 return REQ_NOACTION;
127 }
128 webdav_is_initialized = TRUE;
129
130 webdav_type_map = ucx_map_new(8);
131 if(!webdav_type_map) {
132 return REQ_ABORTED;
133 }
134
135 method_handler_map = ucx_map_new(64);
136 if(!method_handler_map) {
137 return REQ_ABORTED;
138 }
139
140 init_default_backend();
141 ucx_map_cstr_put(webdav_type_map, "default", &default_backend);
142
143 ucx_map_cstr_put(method_handler_map, "OPTIONS", webdav_options);
144 ucx_map_cstr_put(method_handler_map, "PROPFIND", webdav_propfind);
145 ucx_map_cstr_put(method_handler_map, "PROPPATCH", webdav_proppatch);
146 ucx_map_cstr_put(method_handler_map, "MKCOL", webdav_mkcol);
147 ucx_map_cstr_put(method_handler_map, "POST", webdav_post);
148 ucx_map_cstr_put(method_handler_map, "DELETE", webdav_delete);
149 ucx_map_cstr_put(method_handler_map, "PUT", webdav_put);
150 ucx_map_cstr_put(method_handler_map, "COPY", webdav_copy);
151 ucx_map_cstr_put(method_handler_map, "MOVE", webdav_move);
152 ucx_map_cstr_put(method_handler_map, "LOCK", webdav_lock);
153 ucx_map_cstr_put(method_handler_map, "UNLOCK", webdav_unlock);
154 ucx_map_cstr_put(method_handler_map, "REPORT", webdav_report);
155 ucx_map_cstr_put(method_handler_map, "ACL", webdav_acl);
156
157 ucx_map_cstr_put(method_handler_map, "SEARCH", webdav_search);
158
159 ucx_map_cstr_put(method_handler_map, "VERSION-CONTROL", webdav_version_control);
160 ucx_map_cstr_put(method_handler_map, "CHECKOUT", webdav_checkout);
161 ucx_map_cstr_put(method_handler_map, "CHECKIN", webdav_checkin);
162 ucx_map_cstr_put(method_handler_map, "UNCHECKOUT", webdav_uncheckout);
163 ucx_map_cstr_put(method_handler_map, "MKWORKSPACE", webdav_mkworkspace);
164 ucx_map_cstr_put(method_handler_map, "UPDATE", webdav_update);
165 ucx_map_cstr_put(method_handler_map, "LABEL", webdav_label);
166 ucx_map_cstr_put(method_handler_map, "MERGE", webdav_merge);
167
168 dav_namespace.href = (xmlChar*)"DAV:";
169 dav_namespace.prefix = (xmlChar*)"D";
170
171 dav_resourcetype_empty.namespace = &dav_namespace;
172 dav_resourcetype_empty.name = "resourcetype";
173
174 dav_resourcetype_collection_value.data = WEBDAV_RESOURCE_TYPE_COLLECTION;
175 dav_resourcetype_collection_value.length = sizeof(WEBDAV_RESOURCE_TYPE_COLLECTION)-1;
176
177 dav_resourcetype_collection.namespace = &dav_namespace;
178 dav_resourcetype_collection.name = "resourcetype";
179 dav_resourcetype_collection.value.data = dav_resourcetype_collection_value;
180 dav_resourcetype_collection.vtype = WS_VALUE_XML_DATA;
181
182 return REQ_PROCEED;
183 }
184
185
186 int webdav_service(pblock *pb, Session *sn, Request *rq) {
187 if(!method_handler_map) {
188 log_ereport(LOG_FAILURE, "WebDAV module not initialized");
189 protocol_status(sn, rq, 500, NULL);
190 return REQ_ABORTED;
191 }
192 char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
193
194 FuncPtr saf = (FuncPtr)ucx_map_cstr_get(method_handler_map, method);
195 if(!saf) {
196 return REQ_NOACTION;
197 }
198
199 return saf(pb, sn, rq);
200 }
201
202 UcxBuffer* rqbody2buffer(Session *sn, Request *rq) {
203 if(!sn->inbuf) {
204 //request body required, set http response code
205 protocol_status(sn, rq, 400, NULL);
206 return NULL;
207 }
208
209 UcxBuffer *buf = ucx_buffer_new(
210 NULL,
211 sn->inbuf->maxsize,
212 UCX_BUFFER_AUTOEXTEND);
213 if(!buf) {
214 protocol_status(sn, rq, 500, NULL);
215 return NULL;
216 }
217
218 char in[2048];
219 int r;
220 while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) {
221 if(ucx_buffer_write(in, 1, r, buf) != r) {
222 protocol_status(sn, rq, 500, NULL);
223 ucx_buffer_free(buf);
224 return NULL;
225 }
226 }
227
228 return buf;
229 }
230
231 int webdav_options(pblock *pb, Session *sn, Request *rq) {
232 return REQ_ABORTED;
233 }
234
235 int webdav_propfind(pblock *pb, Session *sn, Request *rq) {
236 char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
237 if(expect) {
238 if(!strcasecmp(expect, "100-continue")) {
239 if(http_send_continue(sn)) {
240 return REQ_ABORTED;
241 }
242 }
243 }
244
245 UcxBuffer *reqbody = rqbody2buffer(sn, rq);
246 if(!reqbody) {
247 return REQ_ABORTED;
248 }
249
250 UcxAllocator *a = session_get_allocator(sn);
251
252 int error = 0;
253 WebdavPropfindRequest *propfind = propfind_parse(
254 sn,
255 rq,
256 reqbody->space,
257 reqbody->size,
258 &error);
259 ucx_buffer_free(reqbody);
260 if(!propfind) {
261 switch(error) {
262 // TODO: handle all errors
263 default: return REQ_ABORTED;
264 }
265 }
266
267 WebdavBackend *dav = rq->davCollection ?
268 rq->davCollection : &default_backend;
269
270
271 // requested uri and path
272 char *path = pblock_findkeyval(pb_key_path, rq->vars);
273 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
274
275 // The multistatus response object contains responses for all
276 // requested resources. At the end the Multistatus object will be
277 // serialized to xml
278 Multistatus *ms = multistatus_response(sn, rq);
279 if(!ms) {
280 return REQ_ABORTED;
281 }
282
283
284 int ret = webdav_propfind_do(dav, propfind, (WebdavResponse*)ms, NULL, path, uri);
285
286 // if propfind was successful, send the result to the client
287 if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) {
288 ret = REQ_ABORTED;
289 // TODO: log error
290 } else {
291 // TODO: error response
292 }
293
294 // cleanup
295 if(propfind->doc) {
296 xmlFreeDoc(propfind->doc);
297 }
298
299 return ret;
300 }
301
302 /*
303 * Initializes Backend Chain
304 *
305 * Calls propfind_init of each Backend and generates a list of custom
306 * WebdavPropfindRequest objects for each backend
307 */
308 int webdav_propfind_init(
309 WebdavBackend *dav,
310 WebdavPropfindRequest *propfind,
311 const char *path,
312 const char *uri,
313 UcxList **out_req)
314 {
315 pool_handle_t *pool = propfind->sn->pool;
316 UcxAllocator *a = session_get_allocator(propfind->sn);
317
318 // list of individual WebdavPropfindRequest objects for each Backend
319 UcxList *requestObjects = NULL;
320
321 // new properties after init, start with clone of original plist
322 WebdavPList *newProp = webdav_plist_clone(pool, propfind->properties);
323 size_t newPropCount = propfind->propcount;
324
325 // Call propfind_init for each Backend
326 // propfind_init can return a new property list, which
327 // will be passed to the next backend
328
329 WebdavBackend *davList = dav;
330 while(davList) {
331 // create WebdavPropfindRequest copy
332 WebdavPropfindRequest *pReq = pool_malloc(
333 pool,
334 sizeof(WebdavPropfindRequest));
335 memcpy(pReq, propfind, sizeof(WebdavPropfindRequest));
336 // use new plist after previous init (or orig. plist in the first run)
337 pReq->properties = newProp;
338 pReq->propcount = newPropCount;
339 pReq->dav = davList;
340
341 // add new WebdavPropfindRequest object to list for later use
342 requestObjects = ucx_list_append_a(a, requestObjects, pReq);
343 if(!requestObjects) {
344 return REQ_ABORTED; // OOM
345 }
346
347 // create plist copy as out-plist for init
348 newProp = webdav_plist_clone(pool, newProp);
349
350 // run init: this can generate a new properties list (newProp)
351 // which will be passed to the next backend
352 if(davList->propfind_init(pReq, path, uri, &newProp)) {
353 return REQ_ABORTED;
354 }
355
356 newPropCount = webdav_plist_size(newProp);
357
358 davList = davList->next;
359 }
360
361 *out_req = requestObjects;
362 return REQ_PROCEED;
363 }
364
365 int webdav_propfind_do(
366 WebdavBackend *dav,
367 WebdavPropfindRequest *propfind,
368 WebdavResponse *response,
369 VFSContext *vfs,
370 char *path,
371 char *uri)
372 {
373 Session *sn = propfind->sn;
374 Request *rq = propfind->rq;
375
376 // VFS settings are only taken from the first backend
377 uint32_t settings = dav->settings;
378
379 // list of individual WebdavPropfindRequest objects for each Backend
380 UcxList *requestObjects = NULL;
381
382 // Initialize all Webdav Backends
383 if(webdav_propfind_init(dav, propfind, path, uri, &requestObjects)) {
384 return REQ_ABORTED;
385 }
386
387 WebdavOperation *op = webdav_create_propfind_operation(
388 sn,
389 rq,
390 dav,
391 propfind->properties,
392 requestObjects,
393 response);
394
395 // some Backends can list all children by themselves, but some
396 // require the VFS for this
397 WSBool usevfs = (settings & WS_WEBDAV_PROPFIND_USE_VFS)
398 == WS_WEBDAV_PROPFIND_USE_VFS;
399 struct stat s;
400 struct stat *statptr = NULL;
401
402 if(usevfs && !vfs) {
403 vfs = vfs_request_context(sn, rq);
404 if(!vfs) {
405 return REQ_ABORTED;
406 }
407
408 if(vfs_stat(vfs, path, &s)) {
409 protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
410 return REQ_ABORTED;
411 }
412 statptr = &s;
413 if(!S_ISDIR(s.st_mode)) {
414 // the file is not a directory, therefore we don't need the VFS
415 usevfs = FALSE;
416 }
417 }
418 if(propfind->depth == 0) {
419 usevfs = FALSE;
420 }
421
422 int ret = REQ_PROCEED;
423
424 // create WebdavResource object for requested resource
425 if(!webdav_op_propfind_begin(op, uri, NULL, statptr)) {
426 // propfind for the requested resource was successful
427
428 // usevfsdir is TRUE if
429 // the webdav backend has not disabled vfs usage
430 // the file is a directory
431 // depth is not 0
432 // in this case we need to execute propfind_do for all children
433 if(usevfs) {
434 if(webdav_op_propfind_children(op, vfs, uri, path)) {
435 ret = REQ_ABORTED;
436 }
437 }
438 }
439
440 // finish the propfind request
441 // this function should cleanup all resources, therefore we execute it
442 // even if a previous function failed
443 if(webdav_op_propfind_finish(op)) {
444 // TODO: log error
445 ret = REQ_ABORTED;
446 }
447
448 return ret;
449 }
450
451
452 int webdav_proppatch(pblock *pb, Session *sn, Request *rq) {
453 char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
454 if(expect) {
455 if(!strcasecmp(expect, "100-continue")) {
456 if(http_send_continue(sn)) {
457 return REQ_ABORTED;
458 }
459 }
460 }
461
462 UcxBuffer *reqbody = rqbody2buffer(sn, rq);
463 if(!reqbody) {
464 return REQ_ABORTED;
465 }
466
467 int error = 0;
468 WebdavProppatchRequest *proppatch = proppatch_parse(
469 sn,
470 rq,
471 reqbody->space,
472 reqbody->size,
473 &error);
474 ucx_buffer_free(reqbody);
475 if(!proppatch) {
476 switch(error) {
477 // TODO: handle all errors
478 default: return REQ_ABORTED;
479 }
480 }
481
482 WebdavBackend *dav = rq->davCollection ?
483 rq->davCollection : &default_backend;
484
485 // requested uri and path
486 char *path = pblock_findkeyval(pb_key_path, rq->vars);
487 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
488
489 // The multistatus response object contains responses for all
490 // requested resources. At the end the Multistatus object will be
491 // serialized to xml
492 Multistatus *ms = multistatus_response(sn, rq);
493 if(!ms) {
494 return REQ_ABORTED;
495 }
496 ms->proppatch = TRUE;
497
498 // WebdavResponse is the public interface used by Backends
499 // for adding resources to the response
500 WebdavResponse *response = (WebdavResponse*)ms;
501
502 WebdavOperation *op = webdav_create_proppatch_operation(
503 sn,
504 rq,
505 dav,
506 proppatch,
507 response);
508
509 int ret = REQ_PROCEED;
510
511 // Execute proppatch
512 if(webdav_op_proppatch(op, uri, path)) {
513 ret = REQ_ABORTED;
514 }
515
516 // send response
517 if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) {
518 ret = REQ_ABORTED;
519 // TODO: log error
520 } else {
521 // TODO: error response
522 }
523
524 // cleanup
525 xmlFreeDoc(proppatch->doc);
526
527 return ret;
528 }
529
530 int webdav_mkcol(pblock *pb, Session *sn, Request *rq) {
531 WebdavVFSOperation *op = webdav_vfs_op(sn, rq, rq->davCollection, TRUE);
532 if(!op) {
533 return REQ_ABORTED;
534 }
535
536 int ret = REQ_ABORTED;
537 if(!webdav_vfs_op_do(op, WEBDAV_VFS_MKDIR)) {
538 pblock_nvinsert("content-length", "0", rq->srvhdrs);
539 protocol_status(sn, rq, 201, NULL);
540 protocol_start_response(sn, rq);
541 ret = REQ_PROCEED;
542 } else {
543 int status_code = 500;
544 if(op->vfs->vfs_errno == EEXIST) {
545 // 405 (Method Not Allowed) - MKCOL can only be executed on an unmapped URL.
546 status_code = 405;
547 } else if(op->vfs->vfs_errno == ENOENT) {
548 // 409 (Conflict) - A collection cannot be made at the Request-URI until
549 // one or more intermediate collections have been created. The server
550 // MUST NOT create those intermediate collections automatically.
551 status_code = 409;
552 }
553 protocol_status(sn, rq, status_code, NULL);
554 }
555
556 return ret;
557 }
558
559 int webdav_post(pblock *pb, Session *sn, Request *rq) {
560 return REQ_ABORTED;
561 }
562
563 typedef struct DeleteFile {
564 char *path;
565 struct stat s;
566 } DeleteFile;
567
568 typedef struct DeleteLists {
569 UcxAllocator *a;
570 UcxList *dirs_begin;
571 UcxList *dirs_end;
572 UcxList *files_begin;
573 UcxList *files_end;
574 } DeleteOp;
575
576 static int deletelist_add(
577 VFSContext *vfs,
578 const char *href,
579 const char *path,
580 VFSDir *parent,
581 struct stat *s,
582 void *userdata)
583 {
584 DeleteOp *op = userdata;
585
586 // create object for this file
587 DeleteFile *file = almalloc(op->a, sizeof(DeleteFile));
588 if(!file) {
589 return 1;
590 }
591 file->path = sstrdup_a(op->a, sstr((char*)path)).ptr;
592 if(!file->path) {
593 return 1;
594 }
595 file->s = *s;
596
597 // determine which list to use
598 UcxList **begin;
599 UcxList **end;
600 if(S_ISDIR(s->st_mode)) {
601 begin = &op->dirs_begin;
602 end = &op->dirs_end;
603 } else {
604 begin = &op->files_begin;
605 end = &op->files_end;
606 }
607
608 // add file to list
609 UcxList *elm = ucx_list_append_a(op->a, NULL, file);
610 if(!elm) {
611 alfree(op->a, file->path); // at least do some cleanup, although it
612 alfree(op->a, file); // isn't really necessary
613 return 1;
614 }
615 if(*begin == NULL) {
616 *begin = elm;
617 *end = elm;
618 } else {
619 ucx_list_concat(*end, elm);
620 *end = elm;
621 }
622
623 return 0;
624 }
625
626 static int webdav_delete_collection(WebdavVFSOperation *op)
627 {
628 DeleteOp del;
629 ZERO(&del, sizeof(DeleteOp));
630 del.a = session_get_allocator(op->sn);
631
632 // get a list of all files
633 if(webdav_op_iterate_children(op->vfs, -1, NULL, op->path,
634 deletelist_add, &del))
635 {
636 return 1;
637 }
638
639 // add root to list of dir list
640 DeleteFile root;
641 root.path = op->path;
642 root.s = *op->stat;
643 UcxList root_elm;
644 root_elm.data = &root;
645 root_elm.prev = NULL;
646 root_elm.next = del.dirs_begin;
647
648 if(del.dirs_begin) {
649 del.dirs_begin->prev = &root_elm;
650 del.dirs_begin = &root_elm;
651 } else {
652 del.dirs_begin = &root_elm;
653 del.dirs_end = &root_elm;
654 }
655
656 // delete files first
657 UCX_FOREACH(elm, del.files_begin) {
658 DeleteFile *file = elm->data;
659 WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s);
660 if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) {
661 return 1;
662 }
663 }
664
665 // delete directories, reverse order
666 for(UcxList *elm=del.dirs_end;elm;elm=elm->prev) {
667 DeleteFile *file = elm->data;
668 WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s);
669 if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) {
670 return 1;
671 }
672 }
673
674 return 0;
675 }
676
677 int webdav_delete(pblock *pb, Session *sn, Request *rq) {
678 WebdavVFSOperation *op = webdav_vfs_op(sn, rq, rq->davCollection, TRUE);
679 if(!op) {
680 return REQ_ABORTED;
681 }
682
683 // stat to find out if the resource is a collection
684 struct stat s;
685 if(vfs_stat(op->vfs, op->path, &s)) {
686 sys_set_error_status(op->vfs);
687 return REQ_ABORTED;
688 }
689 op->stat = &s;
690
691 int ret;
692 if(S_ISDIR(s.st_mode)) {
693 ret = webdav_delete_collection(op);
694 } else {
695 ret = webdav_vfs_op_do(op, WEBDAV_VFS_DELETE);
696 }
697
698 // send response
699 if(ret == REQ_PROCEED) {
700 pblock_nvinsert("content-length", "0", rq->srvhdrs);
701 protocol_status(sn, rq, 204, NULL);
702 protocol_start_response(sn, rq);
703 } else {
704 protocol_status(sn, rq, 500, NULL);
705 }
706
707 return ret;
708 }
709
710 int webdav_put(pblock *pb, Session *sn, Request *rq) {
711 char *path = pblock_findkeyval(pb_key_path, rq->vars);
712
713 VFSContext *vfs = vfs_request_context(sn, rq);
714 if(!vfs) {
715 protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
716 return REQ_ABORTED;
717 }
718
719 struct stat s;
720 int create_file = 0;
721 if(vfs_stat(vfs, path, &s)) {
722 if(vfs->vfs_errno == ENOENT) {
723 create_file = O_CREAT;
724 } else {
725 protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
726 return REQ_ABORTED;
727 }
728 } else if(S_ISDIR(s.st_mode)) {
729 // PUT on collections is not allowed
730 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
731 return REQ_ABORTED;
732 }
733
734 SYS_FILE fd = vfs_open(vfs, path, O_WRONLY | O_TRUNC | create_file);
735 if(!fd) {
736 // if it fails, vfs_open sets http status code
737 return REQ_ABORTED;
738 }
739
740 // TODO: check permissions, lock, ...
741
742 // all checks done
743
744 char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
745 if(expect) {
746 if(!strcasecmp(expect, "100-continue")) {
747 if(http_send_continue(sn)) {
748 return REQ_ABORTED;
749 }
750 }
751 }
752
753 char in[4096];
754 int r;
755 while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) {
756 int w = 0;
757 while(w < r) {
758 w += system_fwrite(fd, in, r);
759 }
760 }
761
762 system_fclose(fd);
763
764 int status = create_file ? PROTOCOL_CREATED : PROTOCOL_NO_CONTENT;
765 pblock_nvinsert("content-length", "0", rq->srvhdrs);
766 protocol_status(sn, rq, status, NULL);
767 protocol_start_response(sn, rq);
768
769 return REQ_PROCEED;
770 }
771
772 int webdav_copy(pblock *pb, Session *sn, Request *rq) {
773 char *path = pblock_findkeyval(pb_key_path, rq->vars);
774 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
775
776 char *destination = pblock_findval("destination", rq->headers);
777 if(!destination) {
778 protocol_status(sn, rq, PROTOCOL_BAD_REQUEST, NULL);
779 return REQ_ABORTED;
780 }
781
782 VFSContext *vfs = vfs_request_context(sn, rq);
783 if(!vfs) {
784 protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
785 return REQ_ABORTED;
786 }
787
788 struct stat src_s;
789 if(vfs_stat(vfs, path, &src_s)) {
790 protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
791 return REQ_ABORTED;
792 }
793
794 // TODO: if src is a directory, make sure the uri has a trailing path separator
795
796
797 return REQ_ABORTED;
798 }
799
800 int webdav_move(pblock *pb, Session *sn, Request *rq) {
801 return REQ_ABORTED;
802 }
803
804 int webdav_lock(pblock *pb, Session *sn, Request *rq) {
805 return REQ_ABORTED;
806 }
807
808 int webdav_unlock(pblock *pb, Session *sn, Request *rq) {
809 return REQ_ABORTED;
810 }
811
812 int webdav_report(pblock *pb, Session *sn, Request *rq) {
813 return REQ_ABORTED;
814 }
815
816 int webdav_acl(pblock *pb, Session *sn, Request *rq) {
817 return REQ_ABORTED;
818 }
819
820
821
822 /* ------------------------ default webdav backend ------------------------ */
823
824 int default_propfind_init(
825 WebdavPropfindRequest *rq,
826 const char* path,
827 const char *href,
828 WebdavPList **outplist)
829 {
830 DefaultWebdavData *data = pool_malloc(
831 rq->sn->pool,
832 sizeof(DefaultWebdavData));
833 if(!data) {
834 return 1;
835 }
836 rq->userdata = data;
837
838 data->vfsproperties = webdav_vfs_properties(outplist, TRUE, rq->allprop, 0);
839
840 return 0;
841 }
842
843 int default_propfind_do(
844 WebdavPropfindRequest *request,
845 WebdavResponse *response,
846 VFS_DIR parent,
847 WebdavResource *resource,
848 struct stat *s)
849 {
850 DefaultWebdavData *data = request->userdata;
851
852 // add all requested vfs properties like getcontentlength ...
853 if(webdav_add_vfs_properties(
854 resource,
855 request->sn->pool,
856 data->vfsproperties,
857 s))
858 {
859 return 1;
860 }
861
862 return 0;
863 }
864
865 int default_propfind_finish(WebdavPropfindRequest *rq) {
866 return 0;
867 }
868
869 int default_proppatch_do(
870 WebdavProppatchRequest *request,
871 WebdavResource *response,
872 VFSFile *file,
873 WebdavPList **setInOut,
874 WebdavPList **removeInOut)
875 {
876 return 0;
877 }
878
879 int default_proppatch_finish(
880 WebdavProppatchRequest *request,
881 WebdavResource *response,
882 VFSFile *file,
883 WSBool commit)
884 {
885 return 0;
886 }
887
888
889
890 /* ------------------------------ Utils ------------------------------ */
891
892 UcxKey webdav_property_key(const char *ns, const char *name) {
893 UcxKey key;
894 sstr_t data = ucx_sprintf("%s\n%s", name, ns);
895 key.data = data.ptr;
896 key.len = data.length;
897 key.hash = ucx_hash(data.ptr, data.length);
898 return key;
899 }
900
901
902
903
904 /* ------------------------------ public API ------------------------------ */
905
906 int webdav_getdepth(Request *rq) {
907 char *depth_str = pblock_findkeyval(pb_key_depth, rq->headers);
908 int depth = 0;
909 if(depth_str) {
910 size_t dlen = strlen(depth_str);
911 if(!memcmp(depth_str, "infinity", dlen)) {
912 depth = -1;
913 } else if(dlen == 1 && depth_str[0] == '1') {
914 depth = 1;
915 }
916 }
917 return depth;
918 }
919
920 int webdav_plist_add(
921 pool_handle_t *pool,
922 WebdavPList **begin,
923 WebdavPList **end,
924 WebdavProperty *prop)
925 {
926 WebdavPList *elm = pool_malloc(pool, sizeof(WebdavPList));
927 if(!elm) {
928 return 1;
929 }
930 elm->prev = *end;
931 elm->next = NULL;
932 elm->property = prop;
933
934 if(!*begin) {
935 *begin = elm;
936 *end = elm;
937 return 0;
938 }
939
940 (*end)->next = elm;
941 *end = elm;
942
943 return 0;
944 }
945
946 WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list) {
947 return webdav_plist_clone_s(pool, list, NULL);
948 }
949
950 WebdavPList* webdav_plist_clone_s(
951 pool_handle_t *pool,
952 WebdavPList *list,
953 size_t *newlen)
954 {
955 WebdavPList *new_list = NULL; // start of the new list
956 WebdavPList *new_list_end = NULL; // end of the new list
957
958 size_t len = 0;
959
960 WebdavPList *elm = list;
961 while(elm) {
962 // copy list item
963 WebdavPList *new_elm = pool_malloc(pool, sizeof(WebdavPList));
964 if(!new_elm) {
965 if(newlen) *newlen = 0;
966 return NULL;
967 }
968 new_elm->property = elm->property; // new list contains original ptr
969 new_elm->prev = new_list_end;
970 new_elm->next = NULL;
971
972 if(new_list_end) {
973 new_list_end->next = new_elm;
974 } else {
975 new_list = new_elm;
976 }
977 new_list_end = new_elm;
978
979 len++;
980 elm = elm->next;
981 }
982
983 if(newlen) *newlen = len;
984 return new_list;
985 }
986
987 size_t webdav_plist_size(WebdavPList *list) {
988 size_t count = 0;
989 WebdavPList *elm = list;
990 while(elm) {
991 count++;
992 elm = elm->next;
993 }
994 return count;
995 }
996
997 WebdavPListIterator webdav_plist_iterator(WebdavPList **list) {
998 WebdavPListIterator i;
999 i.list = list;
1000 i.cur = NULL;
1001 i.next = *list;
1002 i.index = 0;
1003 return i;
1004 }
1005
1006 int webdav_plist_iterator_next(WebdavPListIterator *i, WebdavPList **cur) {
1007 if(i->cur) {
1008 i->index++;
1009 }
1010
1011 i->cur = i->next;
1012 i->next = i->cur ? i->cur->next : NULL;
1013 *cur = i->cur;
1014
1015 return i->cur != NULL;
1016 }
1017
1018 void webdav_plist_iterator_remove_current(WebdavPListIterator *i) {
1019 WebdavPList *cur = i->cur;
1020 if(cur->prev) {
1021 cur->prev->next = cur->next;
1022 if(cur->next) {
1023 cur->next->prev = cur->prev;
1024 }
1025 } else {
1026 *i->list = cur->next;
1027 if(cur->next) {
1028 cur->next->prev = NULL;
1029 }
1030 }
1031 }
1032
1033 int webdav_nslist_add(
1034 pool_handle_t *pool,
1035 WebdavNSList **begin,
1036 WebdavNSList **end,
1037 WSNamespace *ns)
1038 {
1039 // same as webdav_plist_add but with different type
1040 WebdavNSList *elm = pool_malloc(pool, sizeof(WebdavNSList));
1041 if(!elm) {
1042 return 1;
1043 }
1044 elm->prev = *end;
1045 elm->next = NULL;
1046 elm->namespace = ns;
1047
1048 if(!*begin) {
1049 *begin = elm;
1050 *end = elm;
1051 return 0;
1052 }
1053
1054 (*end)->next = elm;
1055 *end = elm;
1056
1057 return 0;
1058 }
1059
1060
1061 WSNamespace* webdav_dav_namespace(void) {
1062 return &dav_namespace;
1063 }
1064
1065 WebdavProperty* webdav_resourcetype_collection(void) {
1066 return &dav_resourcetype_collection;
1067 }
1068
1069 WebdavProperty* webdav_resourcetype_empty(void) {
1070 return &dav_resourcetype_empty;
1071 }
1072
1073 WebdavProperty* webdav_dav_property(
1074 pool_handle_t *pool,
1075 const char *name)
1076 {
1077 WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty));
1078 if(!property) {
1079 return NULL;
1080 }
1081 memset(property, 0, sizeof(WebdavProperty));
1082
1083 property->namespace = &dav_namespace;
1084 property->name = name;
1085 return property;
1086 }
1087
1088 int webdav_resource_add_dav_stringproperty(
1089 WebdavResource *res,
1090 pool_handle_t pool,
1091 const char *name,
1092 const char *str,
1093 size_t len)
1094 {
1095 WebdavProperty *property = webdav_dav_property(pool, name);
1096 if(!property) {
1097 return 1;
1098 }
1099
1100 property->name = pool_strdup(pool, name);
1101 if(!property->name) {
1102 return 1;
1103 }
1104
1105 char *value = pool_malloc(pool, len+1);
1106 if(!value) {
1107 return 1;
1108 }
1109 memcpy(value, str, len);
1110 value[len] = '\0';
1111 property->value.text.str = value;
1112 property->value.text.length = len;
1113 property->vtype = WS_VALUE_TEXT;
1114
1115 return res->addproperty(res, property, 200);
1116 }
1117
1118 int webdav_resource_add_stringproperty(
1119 WebdavResource *res,
1120 pool_handle_t pool,
1121 const char *xmlns_prefix,
1122 const char *xmlns_href,
1123 const char *name,
1124 const char *str,
1125 size_t len)
1126 {
1127 WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty));
1128 if(!property) {
1129 return 1;
1130 }
1131 memset(property, 0, sizeof(WebdavProperty));
1132
1133 property->name = pool_strdup(pool, name);
1134 if(!property->name) {
1135 return 1;
1136 }
1137
1138 xmlNs *ns = pool_malloc(pool, sizeof(xmlNs));
1139 if(!ns) {
1140 return 1;
1141 }
1142 memset(ns, 0, sizeof(xmlNs));
1143 ns->prefix = (const xmlChar*)pool_strdup(pool, xmlns_prefix);
1144 ns->href = (const xmlChar*)pool_strdup(pool, xmlns_href);
1145 if(!ns->prefix || !ns->href) {
1146 return 1;
1147 }
1148
1149 char *value = pool_malloc(pool, len+1);
1150 if(!value) {
1151 return 1;
1152 }
1153 memcpy(value, str, len);
1154 value[len] = '\0';
1155 property->value.text.str = value;
1156 property->value.text.length = len;
1157 property->vtype = WS_VALUE_TEXT;
1158
1159 property->value.text.str = value;
1160 property->value.text.length = len;
1161 property->vtype = WS_VALUE_TEXT;
1162
1163 return res->addproperty(res, property, 200);
1164 }
1165
1166 int webdav_property_set_value(
1167 WebdavProperty *p,
1168 pool_handle_t *pool,
1169 char *value)
1170 {
1171 WSXmlNode *node = pool_malloc(pool, sizeof(WSXmlNode));
1172 if(!node) {
1173 return 1;
1174 }
1175 ZERO(node, sizeof(WSXmlNode));
1176
1177 node->content = (xmlChar*)value;
1178 node->type = XML_TEXT_NODE;
1179
1180 p->value.node = node;
1181 p->vtype = WS_VALUE_XML_NODE;
1182 return 0;
1183 }
1184
1185 WebdavVFSProperties webdav_vfs_properties(
1186 WebdavPList **plistInOut,
1187 WSBool removefromlist,
1188 WSBool allprop,
1189 uint32_t flags)
1190 {
1191 WebdavVFSProperties ret;
1192 ZERO(&ret, sizeof(WebdavVFSProperties));
1193
1194 WSBool etag = 1;
1195 WSBool creationdate = 1;
1196
1197 WebdavPListIterator i = webdav_plist_iterator(plistInOut);
1198 WebdavPList *cur;
1199 while(webdav_plist_iterator_next(&i, &cur)) {
1200 WSNamespace *ns = cur->property->namespace;
1201 if(ns && !strcmp((const char*)ns->href, "DAV:")) {
1202 const char *name = cur->property->name;
1203 WSBool remove_prop = removefromlist;
1204 if(!strcmp(name, "getlastmodified")) {
1205 ret.getlastmodified = 1;
1206 } else if(!strcmp(name, "getcontentlength")) {
1207 ret.getcontentlength = 1;
1208 } else if(!strcmp(name, "resourcetype")) {
1209 ret.getresourcetype = 1;
1210 } else if(etag && !strcmp(name, "getetag")) {
1211 ret.getetag = 1;
1212 } else if(creationdate && !strcmp(name, "creationdate")) {
1213 ret.creationdate = 1;
1214 } else {
1215 remove_prop = FALSE;
1216 }
1217
1218 if(remove_prop) {
1219 webdav_plist_iterator_remove_current(&i);
1220 }
1221 }
1222 }
1223
1224 if(allprop) {
1225 ret.creationdate = 1;
1226 ret.getcontentlength = 1;
1227 ret.getetag = 1;
1228 ret.getlastmodified = 1;
1229 ret.getresourcetype = 1;
1230 }
1231
1232 return ret;
1233 }
1234
1235 int webdav_add_vfs_properties(
1236 WebdavResource *res,
1237 pool_handle_t *pool,
1238 WebdavVFSProperties properties,
1239 struct stat *s)
1240 {
1241 if(properties.getresourcetype) {
1242 if(S_ISDIR(s->st_mode)) {
1243 res->addproperty(res, &dav_resourcetype_collection, 200);
1244 } else {
1245 res->addproperty(res, &dav_resourcetype_empty, 200);
1246 }
1247 }
1248 if(properties.getcontentlength) {
1249 char *buf = pool_malloc(pool, 64);
1250 if(!buf) {
1251 return 1;
1252 }
1253 uint64_t contentlength = s->st_size;
1254 snprintf(buf, 64, "%" PRIu64, contentlength);
1255 if(webdav_resource_add_dav_stringproperty(res, pool, "getcontentlength", buf, strlen(buf))) {
1256 return 1;
1257 }
1258 }
1259 if(properties.getlastmodified) {
1260 char *buf = pool_malloc(pool, HTTP_DATE_LEN+1);
1261 if(!buf) {
1262 return 1;
1263 }
1264 buf[HTTP_DATE_LEN] = 0;
1265
1266 struct tm mtms;
1267 struct tm *mtm = system_gmtime(&s->st_mtim.tv_sec, &mtms);
1268
1269 if(mtm) {
1270 strftime(buf, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm);
1271 if(webdav_resource_add_dav_stringproperty(res, pool, "getlastmodified", buf, strlen(buf))) {
1272 return 1;
1273 }
1274 } else {
1275 return 1;
1276 }
1277 }
1278 if(properties.creationdate) {
1279 // TODO
1280 }
1281 if(properties.getetag) {
1282 char *buf = pool_malloc(pool, 96);
1283 if(!buf) {
1284 return 1;
1285 }
1286 snprintf(buf,
1287 96,
1288 "\"%x-%x\"",
1289 (int)s->st_size,
1290 (int)s->st_mtim.tv_sec);
1291 if(webdav_resource_add_dav_stringproperty(res, pool, "getetag", buf, strlen(buf))) {
1292 return 1;
1293 }
1294 }
1295
1296 return 0;
1297 }

mercurial