180 // TODO: handle all errors |
181 // TODO: handle all errors |
181 default: return REQ_ABORTED; |
182 default: return REQ_ABORTED; |
182 } |
183 } |
183 } |
184 } |
184 |
185 |
|
186 WebdavBackend *dav = rq->davCollection ? |
|
187 rq->davCollection : &default_backend; |
|
188 |
|
189 |
|
190 // requested uri path |
|
191 char *path = pblock_findkeyval(pb_key_path, rq->vars); |
|
192 |
|
193 // VFS settings are only taken from the first backend |
|
194 uint32_t settings = dav->settings; |
|
195 |
|
196 // list of individual WebdavPropfindRequest objects for each Backend |
|
197 UcxList *requestObjects = NULL; |
|
198 |
|
199 // Initialize all Webdav Backends |
|
200 if(webdav_propfind_init(dav, propfind, path, &requestObjects)) { |
|
201 return REQ_ABORTED; |
|
202 } |
|
203 |
185 // The multistatus response object contains responses for all |
204 // The multistatus response object contains responses for all |
186 // requested resources. At the end the Multistatus object will be |
205 // requested resources. At the end the Multistatus object will be |
187 // serialized to xml |
206 // serialized to xml |
188 Multistatus *ms = multistatus_response(sn, rq); |
207 Multistatus *ms = multistatus_response(sn, rq); |
189 if(!ms) { |
208 if(!ms) { |
191 } |
210 } |
192 // WebdavResponse is the public interface used by Backends |
211 // WebdavResponse is the public interface used by Backends |
193 // for adding resources to the response |
212 // for adding resources to the response |
194 WebdavResponse *response = (WebdavResponse*)ms; |
213 WebdavResponse *response = (WebdavResponse*)ms; |
195 |
214 |
196 WebdavBackend *dav = |
215 WebdavOperation *op = webdav_operation_create( |
197 rq->davCollection ? rq->davCollection : &default_backend; |
216 sn->pool, |
198 |
217 dav, |
199 // requested uri path |
218 requestObjects, |
200 char *path = pblock_findkeyval(pb_key_path, rq->vars); |
219 response); |
201 |
|
202 // VFS settings are only taken from the first backend |
|
203 uint32_t settings = dav->settings; |
|
204 |
|
205 // list of individual WebdavPropfindRequest objects for each Backend |
|
206 UcxList *requestObjects = NULL; |
|
207 |
|
208 // Initialize all Webdav Backends |
|
209 if(webdav_propfind_init(dav, propfind, path, &requestObjects)) { |
|
210 return REQ_ABORTED; |
|
211 } |
|
212 |
220 |
213 // some Backends can list all children by themselves, but some |
221 // some Backends can list all children by themselves, but some |
214 // require the VFS for this |
222 // require the VFS for this |
215 WSBool usevfs = (settings & WS_PROPFIND_NO_VFS) != WS_PROPFIND_NO_VFS; |
223 WSBool usevfs = (settings & WS_PROPFIND_NO_VFS) != WS_PROPFIND_NO_VFS; |
216 struct stat s; |
224 struct stat s; |
232 } |
240 } |
233 if(propfind->depth == 0) { |
241 if(propfind->depth == 0) { |
234 usevfs = FALSE; |
242 usevfs = FALSE; |
235 } |
243 } |
236 |
244 |
237 int ret = REQ_ABORTED; |
245 int ret = REQ_PROCEED; |
238 if(!webdav_propfind_do(dav, requestObjects, response, NULL, path, statptr)) { |
246 |
|
247 // create WebdavResource object for requested resource |
|
248 if(!webdav_op_propfind_begin(op, path, NULL, statptr)) { |
239 // propfind for the requested resource was successful |
249 // propfind for the requested resource was successful |
240 |
250 |
241 // usevfsdir is TRUE if |
251 // usevfsdir is TRUE if |
242 // the webdav backend has not disabled vfs usage |
252 // the webdav backend has not disabled vfs usage |
243 // the file is a directory |
253 // the file is a directory |
244 // depth is not 0 |
254 // depth is not 0 |
245 // in this case we need to execute propfind_do for all children |
255 // in this case we need to execute propfind_do for all children |
246 if(usevfs && !propfind_children(dav, requestObjects, response, vfs, path)) { |
256 if(usevfs) { |
247 ret = REQ_PROCEED; |
257 if(!webdav_op_propfind_children(op, vfs, path)) { |
|
258 ret = REQ_ABORTED; |
|
259 } |
248 } |
260 } |
249 } |
261 } |
250 |
262 |
251 // finish the propfind request |
263 // finish the propfind request |
252 // this function should cleanup all resources, therefore we execute it |
264 // this function should cleanup all resources, therefore we execute it |
253 // even if a previous function failed |
265 // even if a previous function failed |
254 if(dav->propfind_finish(propfind)) { |
266 if(webdav_op_propfind_finish(op)) { |
255 ret = REQ_ABORTED; |
267 ret = REQ_ABORTED; |
256 } |
268 } |
257 |
269 |
258 return ret; |
270 return ret; |
259 } |
271 } |
318 *out_req = requestObjects; |
330 *out_req = requestObjects; |
319 return REQ_PROCEED; |
331 return REQ_PROCEED; |
320 } |
332 } |
321 |
333 |
322 |
334 |
323 /* |
|
324 * Executes propfind_do for each Backend |
|
325 * The list requests must contain all WebdavPropfindRequest objects |
|
326 * of all backends |
|
327 */ |
|
328 int webdav_propfind_do( |
|
329 WebdavBackend *dav, |
|
330 UcxList *requests, |
|
331 WebdavResponse *response, |
|
332 VFS_DIR parent, |
|
333 const char *path, |
|
334 struct stat *s) |
|
335 { |
|
336 while(dav && requests) { |
|
337 if(dav->propfind_do(requests->data, response, parent, path, s)) { |
|
338 return REQ_ABORTED; |
|
339 } |
|
340 |
|
341 dav = dav->next; |
|
342 requests = requests->next; |
|
343 } |
|
344 return REQ_PROCEED; |
|
345 } |
|
346 |
|
347 /* |
|
348 * Executes propfind_finish for each Backend |
|
349 */ |
|
350 int webdav_propfind_finish(WebdavBackend *dav, UcxList *requests) { |
|
351 int ret = REQ_PROCEED; |
|
352 while(dav && requests) { |
|
353 if(dav->propfind_finish(requests->data)) { |
|
354 ret = REQ_ABORTED; |
|
355 } |
|
356 |
|
357 dav = dav->next; |
|
358 requests = requests->next; |
|
359 } |
|
360 return ret; |
|
361 } |
|
362 |
|
363 |
|
364 /* |
|
365 * Uses the VFS to iterate over all children of the requsted resource |
|
366 * and executes propfind for each child |
|
367 */ |
|
368 int propfind_children( |
|
369 WebdavBackend *dav, |
|
370 UcxList *requests, |
|
371 WebdavResponse *response, |
|
372 VFSContext *vfs, |
|
373 char *path) |
|
374 { |
|
375 WebdavPropfindRequest *request = requests->data; |
|
376 |
|
377 UcxAllocator *a = session_get_allocator(request->sn); |
|
378 pool_handle_t *pool = request->sn->pool; |
|
379 UcxList *stack = ucx_list_prepend_a(a, NULL, path); |
|
380 UcxList *stack_end = stack; |
|
381 if(!stack) { |
|
382 return 1; |
|
383 } |
|
384 |
|
385 // reusable buffer for full child path |
|
386 char *newpath = NULL; |
|
387 size_t newpathlen = 0; |
|
388 |
|
389 int err = 0; |
|
390 while(stack && !err) { |
|
391 char *cur_path = stack->data; |
|
392 size_t parent_len = strlen(cur_path); |
|
393 if(parent_len > WEBDAV_PATH_MAX) { |
|
394 log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); |
|
395 err = 1; |
|
396 break; |
|
397 } |
|
398 if(cur_path[parent_len-1] == '/') { |
|
399 parent_len--; |
|
400 } |
|
401 size_t max_child_len = WEBDAV_PATH_MAX - parent_len; |
|
402 |
|
403 // when newpath is initialized with the parent path |
|
404 // set path_buf_init to TRUE |
|
405 WSBool path_buf_init = FALSE; |
|
406 |
|
407 VFS_DIR dir = vfs_opendir(vfs, path); |
|
408 if(!dir) { |
|
409 log_ereport( |
|
410 LOG_FAILURE, |
|
411 "webdav: propfind: cannot open directory %d", |
|
412 vfs->vfs_errno); |
|
413 err = 1; |
|
414 break; |
|
415 } |
|
416 |
|
417 VFS_ENTRY f; |
|
418 while(vfs_readdir_stat(dir, &f)) { |
|
419 if(f.stat_errno != 0) { |
|
420 continue; |
|
421 } |
|
422 |
|
423 size_t child_len = strlen(f.name); |
|
424 if(child_len > max_child_len) { |
|
425 log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); |
|
426 err = 1; |
|
427 break; |
|
428 } |
|
429 size_t childpathlen = parent_len + child_len + 1; // +1 '/' |
|
430 if(childpathlen > newpathlen) { |
|
431 // we're gonna need a bigger boa^H^H^Hbuffer |
|
432 if(newpath) { |
|
433 pool_free(pool, newpath); |
|
434 } |
|
435 newpath = pool_malloc(pool, childpathlen + 1); |
|
436 if(!newpath) { |
|
437 err = 1; |
|
438 break; |
|
439 } |
|
440 newpathlen = childpathlen; |
|
441 path_buf_init = FALSE; |
|
442 } |
|
443 // create full path string for this child |
|
444 if(!path_buf_init) { |
|
445 memcpy(newpath, cur_path, parent_len); |
|
446 newpath[parent_len] = '/'; |
|
447 } |
|
448 memcpy(newpath+parent_len+1, f.name, child_len); |
|
449 newpath[childpathlen] = 0; |
|
450 |
|
451 // propfind for this child |
|
452 if(webdav_propfind_do(dav, requests, response, dir, newpath, &f.stat)) { |
|
453 err = 1; |
|
454 break; |
|
455 } |
|
456 |
|
457 // depth of -1 means infinity |
|
458 if(request->depth == -1 && S_ISDIR(f.stat.st_mode)) { |
|
459 char *pathcp = pool_malloc(pool, childpathlen + 1); |
|
460 memcpy(pathcp, newpath, childpathlen + 1); |
|
461 |
|
462 // add the newpath copy to the stack |
|
463 // stack_end is always not NULL here, because we remove |
|
464 // the first stack element at the end of the loop |
|
465 UcxList *newlistelm = ucx_list_append_a(a, stack_end, pathcp); |
|
466 if(!newlistelm) { |
|
467 err = 1; |
|
468 break; |
|
469 } |
|
470 stack_end = newlistelm; |
|
471 } |
|
472 } |
|
473 |
|
474 vfs_closedir(dir); |
|
475 |
|
476 if(cur_path != path) { |
|
477 pool_free(pool, cur_path); |
|
478 } |
|
479 stack = ucx_list_remove_a(a, stack, stack); |
|
480 } |
|
481 |
|
482 // in case of an error, we have to free all remaining stack elements |
|
483 UCX_FOREACH(elm, stack) { |
|
484 char *data = elm->data; |
|
485 if(data != path) { |
|
486 pool_free(pool, data); |
|
487 } |
|
488 } |
|
489 |
|
490 return err; |
|
491 } |
|
492 |
|
493 int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { |
335 int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { |
494 return REQ_ABORTED; |
336 return REQ_ABORTED; |
495 } |
337 } |
496 |
338 |
497 int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { |
339 int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { |
557 |
399 |
558 int default_propfind_do( |
400 int default_propfind_do( |
559 WebdavPropfindRequest *request, |
401 WebdavPropfindRequest *request, |
560 WebdavResponse *response, |
402 WebdavResponse *response, |
561 VFS_DIR parent, |
403 VFS_DIR parent, |
562 const char *path, |
404 WebdavResource *resource, |
563 struct stat *s) |
405 struct stat *s) |
564 { |
406 { |
565 DefaultWebdavData *data = request->userdata; |
407 DefaultWebdavData *data = request->userdata; |
566 |
408 |
567 // add a resource to the response |
409 // TODO: rework |
568 // usually this will lead to a <response> ... </response> tag in the |
|
569 // multistatus response |
|
570 WebdavResource *resource = response->addresource(response, path); |
|
571 if(!resource) { |
|
572 return 1; |
|
573 } |
|
574 |
410 |
575 // add all requested vfs properties like getcontentlength ... |
411 // add all requested vfs properties like getcontentlength ... |
576 if(webdav_add_vfs_properties( |
412 if(webdav_add_vfs_properties( |
577 resource, |
413 resource, |
578 request->sn->pool, |
414 request->sn->pool, |