UNIXworkcode

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