src/server/webdav/operation.c

branch
webdav
changeset 246
155bdef7fe7e
parent 245
a193c42fc809
child 247
1df803e06076
equal deleted inserted replaced
245:a193c42fc809 246:155bdef7fe7e
168 (*buf)[newlen] = '\0'; 168 (*buf)[newlen] = '\0';
169 169
170 return 0; 170 return 0;
171 } 171 }
172 172
173 static int propfind_child_cb(
174 VFSContext *vfs,
175 const char *href,
176 const char *path,
177 VFSDir *parent,
178 struct stat *s,
179 void *op)
180 {
181 return webdav_op_propfind_begin(op, href, parent, s);
182 }
183
173 int webdav_op_propfind_children( 184 int webdav_op_propfind_children(
174 WebdavOperation *op, 185 WebdavOperation *op,
175 VFSContext *vfs, 186 VFSContext *vfs,
176 const char *href, 187 const char *href,
177 const char *path) 188 const char *path)
178 { 189 {
179 WebdavPropfindRequest *request = op->requests->data; 190 WebdavPropfindRequest *request = op->requests->data;
180 191 return webdav_op_iterate_children(
181 UcxAllocator *a = session_get_allocator(request->sn); 192 vfs, request->depth, href, path, propfind_child_cb, op);
182 pool_handle_t *pool = request->sn->pool; 193 }
194
195 int webdav_op_propfiond_close_resource(
196 WebdavOperation *op,
197 WebdavResource *resource)
198 {
199 // start with second backend and request, because
200 // the first one was already called by webdav_op_propfind_begin
201 WebdavBackend *dav = op->dav->next;
202 UcxList *request = op->requests->next;
203
204 // call propfind_do of all remaining backends
205 int ret = REQ_PROCEED;
206 while(dav && request) {
207 if(dav->propfind_do(
208 request->data,
209 op->response,
210 op->parent,
211 resource,
212 op->stat))
213 {
214 ret = REQ_ABORTED;
215 }
216
217 dav = dav->next;
218 request = request->next;
219 }
220 return ret;
221 }
222
223 /*
224 * Executes propfind_finish for each Backend
225 */
226 int webdav_op_propfind_finish(WebdavOperation *op) {
227 WebdavBackend *dav = op->dav;
228 UcxList *requests = op->requests;
229
230 int ret = REQ_PROCEED;
231 while(dav && requests) {
232 if(dav->propfind_finish(requests->data)) {
233 ret = REQ_ABORTED;
234 }
235
236 dav = dav->next;
237 requests = requests->next;
238 }
239 return ret;
240 }
241
242 /****************************************************************************
243 *
244 * PROPPATCH OPERATION
245 *
246 ****************************************************************************/
247
248 WebdavOperation* webdav_create_proppatch_operation(
249 Session *sn,
250 Request *rq,
251 WebdavBackend *dav,
252 WebdavProppatchRequest *proppatch,
253 WebdavResponse *response)
254 {
255 WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation));
256 ZERO(op, sizeof(WebdavOperation));
257 op->dav = dav;
258 op->sn = sn;
259 op->rq = rq;
260 op->reqprops = NULL;
261 op->response = response;
262 op->proppatch = proppatch;
263 op->response_close = webdav_op_proppatch_close_resource;
264 response->op = op;
265
266 return op;
267 }
268
269
270
271 int webdav_op_proppatch(
272 WebdavOperation *op,
273 const char *href,
274 const char *path)
275 {
276 WebdavProppatchRequest *orig_request = op->proppatch;
277 UcxAllocator *a = session_get_allocator(op->sn);
278
279 // create WebdavResource object for the requested resource
280 WebdavResource *resource = op->response->addresource(op->response, href);
281 if(!resource) {
282 return REQ_ABORTED;
283 }
284
285 VFSContext *ctx = NULL;
286 VFSFile *file = NULL;
287
288 // requests for each backends
289 WebdavProppatchRequest **requests = pool_calloc(
290 op->sn->pool,
291 webdav_num_backends(op->dav),
292 sizeof(WebdavProppatchRequest*));
293 if(requests == NULL) {
294 return REQ_ABORTED;
295 }
296
297 WebdavPList *prev_set = orig_request->set;
298 WebdavPList *prev_remove = orig_request->remove;
299 size_t set_count = orig_request->setcount;
300 size_t remove_count = orig_request->removecount;
301
302 int ret = REQ_PROCEED;
303
304 // iterate backends and execute proppatch_do
305 WebdavBackend *dav = op->dav;
306 size_t numrequests = 0;
307 while(dav) {
308 WebdavPList *set = webdav_plist_clone_s(
309 op->sn->pool,
310 prev_set,
311 &set_count);
312 WebdavPList *remove = webdav_plist_clone_s(
313 op->sn->pool,
314 prev_remove,
315 &remove_count);
316 if((prev_set && !set) || (prev_remove && !remove)) {
317 // clone failed, OOM
318 ret = REQ_ABORTED;
319 break;
320 }
321
322 // create new WebdavProppatchRequest object for this backend
323 WebdavProppatchRequest *req = pool_malloc(
324 op->sn->pool,
325 sizeof(WebdavProppatchRequest));
326 memcpy(req, orig_request, sizeof(WebdavProppatchRequest));
327 req->set = set;
328 req->setcount = set_count;
329 req->remove = remove;
330 req->removecount = remove_count;
331 req->userdata = NULL;
332
333 // check if we need to open the file because the backend want's it
334 if(!file && (dav->settings & WS_WEBDAV_PROPPATCH_USE_VFS)
335 == WS_WEBDAV_PROPPATCH_USE_VFS)
336 {
337 ctx = vfs_request_context(op->sn, op->rq);
338 if(!ctx) {
339 ret = REQ_ABORTED;
340 break;
341 }
342
343 file = vfs_open(ctx, path, O_RDONLY);
344 if(!file) {
345 protocol_status(
346 op->sn,
347 op->rq,
348 util_errno2status(ctx->vfs_errno),
349 NULL);
350 ret = REQ_ABORTED;
351 }
352 }
353
354 // execute proppatch_do
355 if(dav->proppatch_do(req, resource, file, &set, &remove)) {
356 // return later, because we need do execute proppatch_finish
357 // for all successfully called backends
358 ret = REQ_ABORTED;
359 break;
360 }
361
362 // proppatch_do should remove all handled props from set and remove
363 // in the next iteration, the backend must use these reduced lists
364 prev_set = set;
365 prev_remove = remove;
366
367 requests[numrequests++] = req;
368
369 // continue with next backend
370 dav = dav->next;
371 }
372
373 WSBool commit = FALSE;
374 if(ret == REQ_PROCEED && resource->err == 0) {
375 // no errors, no properties with errors -> save the changes
376 commit = TRUE;
377 }
378
379 // call proppatch_finish for each successfully called proppatch_do
380 dav = op->dav;
381 int i = 0;
382 while(dav && i < numrequests) {
383 if(dav->proppatch_finish(requests[i], resource, file, commit)) {
384 ret = REQ_ABORTED;
385 }
386 i++;
387 dav = dav->next;
388 }
389
390 if(file) {
391 vfs_close(file);
392 }
393
394 if(resource->close(resource)) {
395 ret = REQ_ABORTED;
396 }
397
398 return ret;
399 }
400
401 int webdav_op_proppatch_close_resource(
402 WebdavOperation *op,
403 WebdavResource *resource)
404 {
405 return 0; // NOP
406 }
407
408
409 /****************************************************************************
410 *
411 * VFS OPERATION
412 *
413 ****************************************************************************/
414
415 WebdavVFSOperation* webdav_vfs_op(
416 Session *sn,
417 Request *rq,
418 WebdavBackend *dav,
419 WSBool precondition)
420 {
421 WebdavVFSOperation *op = pool_malloc(sn->pool, sizeof(WebdavVFSOperation));
422 if(!op) {
423 return NULL;
424 }
425 ZERO(op, sizeof(WebdavVFSOperation));
426
427 op->sn = sn;
428 op->rq = rq;
429 op->dav = dav;
430 op->stat = NULL;
431 op->stat_errno = 0;
432
433 // create VFS context
434 VFSContext *vfs = vfs_request_context(sn, rq);
435 if(!vfs) {
436 pool_free(sn->pool, op);
437 return NULL;
438 }
439 op->vfs = vfs;
440
441 char *path = pblock_findkeyval(pb_key_path, rq->vars);
442 op->path = path;
443
444 return op;
445 }
446
447 int webdav_op_iterate_children(
448 VFSContext *vfs,
449 int depth,
450 const char *href,
451 const char *path,
452 vfs_op_child_func func,
453 void *userdata)
454 {
455 UcxAllocator *a = session_get_allocator(vfs->sn);
456 pool_handle_t *pool = vfs->sn->pool;
183 457
184 PathSearchElm *start_elm = pool_malloc(pool, sizeof(PathSearchElm)); 458 PathSearchElm *start_elm = pool_malloc(pool, sizeof(PathSearchElm));
185 start_elm->href = pool_strdup(pool, href); 459 start_elm->href = pool_strdup(pool, href);
186 start_elm->path = pool_strdup(pool, path); 460 start_elm->path = pool_strdup(pool, path);
187 start_elm->hreflen = strlen(href); 461 start_elm->hreflen = strlen(href);
255 break; 529 break;
256 } 530 }
257 size_t childhreflen = cur_elm->hreflen + 1 + child_len; 531 size_t childhreflen = cur_elm->hreflen + 1 + child_len;
258 size_t childpathlen = cur_elm->pathlen + 1 + child_len; 532 size_t childpathlen = cur_elm->pathlen + 1 + child_len;
259 533
260 // propfind for this child 534 // execute callback func for this file
261 if(webdav_op_propfind_begin(op, newhref, dir, &f.stat)) { 535 if(func(vfs, newhref, newpath, dir, &f.stat, userdata)) {
262 err = 1; 536 err = 1;
263 break; 537 break;
264 } 538 }
265 539
266 // depth of -1 means infinity 540 // depth of -1 means infinity
267 if(request->depth == -1 && S_ISDIR(f.stat.st_mode)) { 541 if(depth == -1 && S_ISDIR(f.stat.st_mode)) {
268 char *hrefcp = pool_malloc(pool, childhreflen + 1); 542 char *hrefcp = pool_malloc(pool, childhreflen + 1);
269 memcpy(hrefcp, newhref, childhreflen + 1); 543 memcpy(hrefcp, newhref, childhreflen + 1);
270 hrefcp[childhreflen] = '\0'; 544 hrefcp[childhreflen] = '\0';
271 545
272 char *pathcp = pool_malloc(pool, childpathlen + 1); 546 char *pathcp = pool_malloc(pool, childpathlen + 1);
310 } 584 }
311 585
312 return err; 586 return err;
313 } 587 }
314 588
315 int webdav_op_propfiond_close_resource(
316 WebdavOperation *op,
317 WebdavResource *resource)
318 {
319 // start with second backend and request, because
320 // the first one was already called by webdav_op_propfind_begin
321 WebdavBackend *dav = op->dav->next;
322 UcxList *request = op->requests->next;
323
324 // call propfind_do of all remaining backends
325 int ret = REQ_PROCEED;
326 while(dav && request) {
327 if(dav->propfind_do(
328 request->data,
329 op->response,
330 op->parent,
331 resource,
332 op->stat))
333 {
334 ret = REQ_ABORTED;
335 }
336
337 dav = dav->next;
338 request = request->next;
339 }
340 return ret;
341 }
342
343 /*
344 * Executes propfind_finish for each Backend
345 */
346 int webdav_op_propfind_finish(WebdavOperation *op) {
347 WebdavBackend *dav = op->dav;
348 UcxList *requests = op->requests;
349
350 int ret = REQ_PROCEED;
351 while(dav && requests) {
352 if(dav->propfind_finish(requests->data)) {
353 ret = REQ_ABORTED;
354 }
355
356 dav = dav->next;
357 requests = requests->next;
358 }
359 return ret;
360 }
361
362 /****************************************************************************
363 *
364 * PROPPATCH OPERATION
365 *
366 ****************************************************************************/
367
368 WebdavOperation* webdav_create_proppatch_operation(
369 Session *sn,
370 Request *rq,
371 WebdavBackend *dav,
372 WebdavProppatchRequest *proppatch,
373 WebdavResponse *response)
374 {
375 WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation));
376 ZERO(op, sizeof(WebdavOperation));
377 op->dav = dav;
378 op->sn = sn;
379 op->rq = rq;
380 op->reqprops = NULL;
381 op->response = response;
382 op->proppatch = proppatch;
383 op->response_close = webdav_op_proppatch_close_resource;
384 response->op = op;
385
386 return op;
387 }
388
389
390
391 int webdav_op_proppatch(
392 WebdavOperation *op,
393 const char *href,
394 const char *path)
395 {
396 WebdavProppatchRequest *orig_request = op->proppatch;
397 UcxAllocator *a = session_get_allocator(op->sn);
398
399 // create WebdavResource object for the requested resource
400 WebdavResource *resource = op->response->addresource(op->response, href);
401 if(!resource) {
402 return REQ_ABORTED;
403 }
404
405 VFSContext *ctx = NULL;
406 VFSFile *file = NULL;
407
408 // requests for each backends
409 WebdavProppatchRequest **requests = pool_calloc(
410 op->sn->pool,
411 webdav_num_backends(op->dav),
412 sizeof(WebdavProppatchRequest*));
413 if(requests == NULL) {
414 return REQ_ABORTED;
415 }
416
417 WebdavPList *prev_set = orig_request->set;
418 WebdavPList *prev_remove = orig_request->remove;
419 size_t set_count = orig_request->setcount;
420 size_t remove_count = orig_request->removecount;
421
422 int ret = REQ_PROCEED;
423
424 // iterate backends and execute proppatch_do
425 WebdavBackend *dav = op->dav;
426 size_t numrequests = 0;
427 while(dav) {
428 WebdavPList *set = webdav_plist_clone_s(
429 op->sn->pool,
430 prev_set,
431 &set_count);
432 WebdavPList *remove = webdav_plist_clone_s(
433 op->sn->pool,
434 prev_remove,
435 &remove_count);
436 if((prev_set && !set) || (prev_remove && !remove)) {
437 // clone failed, OOM
438 ret = REQ_ABORTED;
439 break;
440 }
441
442 // create new WebdavProppatchRequest object for this backend
443 WebdavProppatchRequest *req = pool_malloc(
444 op->sn->pool,
445 sizeof(WebdavProppatchRequest));
446 memcpy(req, orig_request, sizeof(WebdavProppatchRequest));
447 req->set = set;
448 req->setcount = set_count;
449 req->remove = remove;
450 req->removecount = remove_count;
451 req->userdata = NULL;
452
453 // check if we need to open the file because the backend want's it
454 if(!file && (dav->settings & WS_WEBDAV_PROPPATCH_USE_VFS)
455 == WS_WEBDAV_PROPPATCH_USE_VFS)
456 {
457 ctx = vfs_request_context(op->sn, op->rq);
458 if(!ctx) {
459 ret = REQ_ABORTED;
460 break;
461 }
462
463 file = vfs_open(ctx, path, O_RDONLY);
464 if(!file) {
465 protocol_status(
466 op->sn,
467 op->rq,
468 util_errno2status(ctx->vfs_errno),
469 NULL);
470 ret = REQ_ABORTED;
471 }
472 }
473
474 // execute proppatch_do
475 if(dav->proppatch_do(req, resource, file, &set, &remove)) {
476 // return later, because we need do execute proppatch_finish
477 // for all successfully called backends
478 ret = REQ_ABORTED;
479 break;
480 }
481
482 // proppatch_do should remove all handled props from set and remove
483 // in the next iteration, the backend must use these reduced lists
484 prev_set = set;
485 prev_remove = remove;
486
487 requests[numrequests++] = req;
488
489 // continue with next backend
490 dav = dav->next;
491 }
492
493 WSBool commit = FALSE;
494 if(ret == REQ_PROCEED && resource->err == 0) {
495 // no errors, no properties with errors -> save the changes
496 commit = TRUE;
497 }
498
499 // call proppatch_finish for each successfully called proppatch_do
500 dav = op->dav;
501 int i = 0;
502 while(dav && i < numrequests) {
503 if(dav->proppatch_finish(requests[i], resource, file, commit)) {
504 ret = REQ_ABORTED;
505 }
506 i++;
507 dav = dav->next;
508 }
509
510 if(file) {
511 vfs_close(file);
512 }
513
514 if(resource->close(resource)) {
515 ret = REQ_ABORTED;
516 }
517
518 return ret;
519 }
520
521 int webdav_op_proppatch_close_resource(
522 WebdavOperation *op,
523 WebdavResource *resource)
524 {
525 return 0; // NOP
526 }
527
528
529 /****************************************************************************
530 *
531 * VFS OPERATION
532 *
533 ****************************************************************************/
534
535 WebdavVFSOperation* webdav_vfs_op(
536 Session *sn,
537 Request *rq,
538 WebdavBackend *dav,
539 WSBool precondition)
540 {
541 WebdavVFSOperation *op = pool_malloc(sn->pool, sizeof(WebdavVFSOperation));
542 if(!op) {
543 return NULL;
544 }
545 ZERO(op, sizeof(WebdavVFSOperation));
546
547 op->sn = sn;
548 op->rq = rq;
549 op->dav = dav;
550 op->stat = NULL;
551 op->stat_errno = 0;
552
553 // create VFS context
554 VFSContext *vfs = vfs_request_context(sn, rq);
555 if(!vfs) {
556 pool_free(sn->pool, op);
557 return NULL;
558 }
559 op->vfs = vfs;
560
561 char *path = pblock_findkeyval(pb_key_path, rq->vars);
562 op->path = path;
563
564 return op;
565 }
566 589
567 int webdav_vfs_stat(WebdavVFSOperation *op) { 590 int webdav_vfs_stat(WebdavVFSOperation *op) {
568 if(op->stat) { 591 if(op->stat) {
569 return 0; 592 return 0;
570 } else if(op->stat_errno != 0) { 593 } else if(op->stat_errno != 0) {

mercurial