src/server/webdav/webdav.c

changeset 59
ab25c0a231d0
parent 58
66c22e54aa90
child 64
c7f5b062e622
equal deleted inserted replaced
58:66c22e54aa90 59:ab25c0a231d0
39 39
40 #include "../daemon/vfs.h" 40 #include "../daemon/vfs.h"
41 #include "../daemon/protocol.h" 41 #include "../daemon/protocol.h"
42 42
43 #include "davparser.h" 43 #include "davparser.h"
44 #include "persistence.h"
45
46 static UcxMap *pmgr_map; // char*, PersistenceManager
47
48 int webdav_init(pblock *pb, Session *sn, Request *rq) {
49 pmgr_map = ucx_map_new(8);
50 PersistenceManager *defaultmgr = create_property_backend();
51 ucx_map_cstr_put(pmgr_map, "default", defaultmgr);
52 return REQ_PROCEED;
53 }
54
55 void webdav_add_persistence_manager(char *name, PersistenceManager *mgr) {
56 if(!pmgr_map) {
57 webdav_init(NULL, NULL, NULL);
58 }
59 ucx_map_cstr_put(pmgr_map, name, mgr);
60 }
61
62 int webdav_setcollection(pblock *pb, Session *sn, Request *rq) {
63 //char *name = pblock_findkeyval(pb_key_name, pb);
64 char *db = pblock_findval("db", pb);
65
66 if(!db) {
67 db = "default";
68 }
69
70 // setup DavCollection
71 DavCollection *dav = pool_malloc(sn->pool, sizeof(DavCollection));
72 dav->mgr = ucx_map_cstr_get(pmgr_map, db);
73 rq->davCollection = dav;
74
75 return REQ_NOACTION;
76 }
44 77
45 int webdav_service(pblock *pb, Session *sn, Request *rq) { 78 int webdav_service(pblock *pb, Session *sn, Request *rq) {
46 char *method = pblock_findkeyval(pb_key_method, rq->reqpb); 79 char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
47 if(method == NULL) { 80 if(method == NULL) {
48 return REQ_ABORTED; 81 return REQ_ABORTED;
215 } 248 }
216 249
217 /* 250 /*
218 * get requested properties and initialize some stuff 251 * get requested properties and initialize some stuff
219 */ 252 */
253 DavCollection *collection = rq->davCollection;
254
220 PropfindRequest *davrq = dav_parse_propfind(sn, rq, xml_body, xml_len); 255 PropfindRequest *davrq = dav_parse_propfind(sn, rq, xml_body, xml_len);
221 davrq->sn = sn; 256 davrq->sn = sn;
222 davrq->rq = rq; 257 davrq->rq = rq;
223 davrq->out = sbuf_new(512); 258 davrq->out = sbuf_new(512);
224 davrq->propertyBackend = create_property_backend(); 259 davrq->persistencemgr = collection->mgr;
225
226 /* write xml response header */
227 sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
228 sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n");
229 260
230 /* begin multistatus response */ 261 /* begin multistatus response */
231 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); 262 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
232 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); 263 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars);
264 davrq->uri = uri;
265 davrq->path = ppath;
233 266
234 VFSContext *vfs = vfs_request_context(sn, rq); 267 VFSContext *vfs = vfs_request_context(sn, rq);
235 268
236 struct stat st; 269 struct stat st;
237 if(vfs_stat(vfs, ppath, &st) != 0) { 270 if(vfs_stat(vfs, ppath, &st) != 0) {
238 return REQ_ABORTED; 271 return REQ_ABORTED;
239 } 272 }
273
274 // begin propfind
275 davrq->isdir = S_ISDIR(st.st_mode);
276 davrq->persistencemgr->propfind_begin(davrq->persistencemgr, davrq);
240 277
241 /* 278 /*
242 * if the requested webdav resource(file) is a directory, we create 279 * if the requested webdav resource(file) is a directory, we create
243 * a response for every child 280 * a response for every child
244 */ 281 */
255 // child response 292 // child response
256 dav_resource_response(davrq, newpath, newuri); 293 dav_resource_response(davrq, newpath, newuri);
257 } 294 }
258 } 295 }
259 296
260 /* create the response for the requested resource */ 297 // create the response for the requested resource
261 dav_resource_response(davrq, sstr(ppath), sstr(uri)); 298 dav_resource_response(davrq, sstr(ppath), sstr(uri));
262 299
263 /* end xml */ 300 // end propfind
301 davrq->persistencemgr->propfind_begin(davrq->persistencemgr, davrq);
302
303 // end xml
264 sbuf_puts(davrq->out, "</D:multistatus>\n"); 304 sbuf_puts(davrq->out, "</D:multistatus>\n");
265 305
266 /* send the xml response to the client */ 306 //printf("%s\n", davrq->out->ptr);
307
308 // write xml response header
309 sbuf_t *out = sbuf_new(256);
310 sbuf_puts(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
311 sbuf_puts(out, "<D:multistatus");
312 UcxMapIterator nsiter = ucx_map_iterator(davrq->nsmap->map);
313 XmlNs *ns;
314 UCX_MAP_FOREACH(ns, nsiter) {
315 sbuf_puts(out, " xmlns:");
316 sbuf_puts(out, ns->prefix);
317 sbuf_puts(out, "=\"");
318 sbuf_puts(out, ns->xmlns);
319 sbuf_puts(out, "\"");
320 }
321 sbuf_puts(out, ">\n");
322
323 // send the xml response to the client
267 protocol_status(sn, rq, 207, "Multi Status"); 324 protocol_status(sn, rq, 207, "Multi Status");
268 pblock_removekey(pb_key_content_type, rq->srvhdrs); 325 pblock_removekey(pb_key_content_type, rq->srvhdrs);
269 pblock_nvinsert("content-type", "text/xml", rq->srvhdrs); 326 pblock_nvinsert("content-type", "text/xml", rq->srvhdrs);
270 pblock_nninsert("content-length", davrq->out->length, rq->srvhdrs); 327 pblock_nninsert(
271 328 "content-length",
272 //pblock_nvinsert("connection", "close", rq->srvhdrs); 329 out->length + davrq->out->length,
330 rq->srvhdrs);
331
273 http_start_response(sn, rq); 332 http_start_response(sn, rq);
274 333
275 //printf("%s\n", davrq->out->ptr); 334 // write content
276 ssize_t nr = net_write(sn->csd, davrq->out->ptr, davrq->out->length); 335 size_t nr;
277 //printf("net_write returned: %d\n", r); 336 nr = net_write(sn->csd, out->ptr, out->length);
278 337 nr = net_write(sn->csd, davrq->out->ptr, davrq->out->length);
279 338
339 sbuf_free(out);
280 dav_free_propfind(davrq); 340 dav_free_propfind(davrq);
281 341
282 return REQ_PROCEED; 342 return REQ_PROCEED;
283 } 343 }
284 344
290 if(uri == NULL) { 350 if(uri == NULL) {
291 /* TODO: error */ 351 /* TODO: error */
292 return REQ_ABORTED; 352 return REQ_ABORTED;
293 } 353 }
294 354
295 /* Get request body which contains the webdav XML request */ 355 // Get request body which contains the webdav XML request
296 char *xml_body; 356 char *xml_body;
297 size_t xml_len = 0; 357 size_t xml_len = 0;
298 358
299 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); 359 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers);
300 if(ctlen) { 360 if(ctlen) {
301 xml_len = atoi(ctlen); 361 xml_len = atoi(ctlen);
302 } else { 362 } else {
303 /* invalid request */ 363 // invalid request
304 printf("invalid request\n"); 364 printf("invalid request\n");
305 return REQ_ABORTED; 365 return REQ_ABORTED;
306 } 366 }
307 367
308 xml_body = pool_malloc(sn->pool, xml_len + 1); 368 xml_body = pool_malloc(sn->pool, xml_len + 1);
309 if(xml_body == NULL) { 369 if(xml_body == NULL) {
310 return REQ_ABORTED; 370 return REQ_ABORTED;
311 } 371 }
312 xml_body[xml_len] = 0; 372 xml_body[xml_len] = 0;
313 if(!xml_body) { 373 if(!xml_body) {
314 /* server error */ 374 // server error
315 printf("server error\n"); 375 printf("server error\n");
316 return REQ_ABORTED; 376 return REQ_ABORTED;
317 } 377 }
318 378
319 /* get request body */ 379 // get request body
320 int r = 0; 380 int r = 0;
321 char *xb = xml_body; 381 char *xb = xml_body;
322 size_t xl = xml_len; 382 size_t xl = xml_len;
323 while((r = netbuf_getbytes(sn->inbuf, xb, xl)) != NETBUF_EOF) { 383 while((r = netbuf_getbytes(sn->inbuf, xb, xl)) != NETBUF_EOF) {
324 xb += r; 384 xb += r;
326 } 386 }
327 387
328 /* 388 /*
329 * parse the xml request and create the proppatch object 389 * parse the xml request and create the proppatch object
330 */ 390 */
391 DavCollection *collection = rq->davCollection;
392
331 ProppatchRequest *davrq = dav_parse_proppatch(sn, rq, xml_body, xml_len); 393 ProppatchRequest *davrq = dav_parse_proppatch(sn, rq, xml_body, xml_len);
332 davrq->sn = sn; 394 davrq->sn = sn;
333 davrq->rq = rq; 395 davrq->rq = rq;
334 davrq->out = sbuf_new(512); 396 davrq->out = sbuf_new(512);
335 davrq->backend = create_property_backend(); 397 davrq->backend = collection->mgr;
336 davrq->propstat = propstat_create(sn->pool); 398 davrq->propstat = propstat_create(sn->pool);
337 399
338 /* TODO: create prefixes for every namespace */
339 XmlNs *ns = xmlnsmap_get(davrq->nsmap, "DAV:");
340 ns->prefix = "D";
341 ns->prelen = 1;
342 400
343 /* 401 /*
344 * begin multistatus response 402 * begin multistatus response
345 * 403 *
346 * The webdav backend does the most work. The backend->proppatch function 404 * The webdav backend does the most work. The backend->proppatch function
348 * member of the ProppatchRequest. All we have to do here is to create 406 * member of the ProppatchRequest. All we have to do here is to create
349 * the xml response and send it to the client 407 * the xml response and send it to the client
350 */ 408 */
351 409
352 /* write xml response header */ 410 /* write xml response header */
353 /* TODO: add possible xml namespaces */
354 sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); 411 sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
355 sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n"); 412 //sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n");
413 sbuf_puts(davrq->out, "<D:multistatus");
414 UcxMapIterator nsiter = ucx_map_iterator(davrq->nsmap->map);
415 XmlNs *ns;
416 UCX_MAP_FOREACH(ns, nsiter) {
417 sbuf_puts(davrq->out, " xmlns:");
418 sbuf_puts(davrq->out, ns->prefix);
419 sbuf_puts(davrq->out, "=\"");
420 sbuf_puts(davrq->out, ns->xmlns);
421 sbuf_puts(davrq->out, "\"");
422 }
423 sbuf_puts(davrq->out, ">\n");
356 424
357 sbuf_puts(davrq->out, "<D:response>\n<D:href>"); 425 sbuf_puts(davrq->out, "<D:response>\n<D:href>");
358 sbuf_puts(davrq->out, uri); 426 sbuf_puts(davrq->out, uri);
359 sbuf_puts(davrq->out, "</D:href>\n"); 427 sbuf_puts(davrq->out, "</D:href>\n");
360 428
389 sbuf_puts(davrq->out, "<D:response>\n"); 457 sbuf_puts(davrq->out, "<D:response>\n");
390 sbuf_puts(davrq->out, "<D:href>"); 458 sbuf_puts(davrq->out, "<D:href>");
391 sbuf_append(davrq->out, uri); 459 sbuf_append(davrq->out, uri);
392 sbuf_puts(davrq->out, "</D:href>\n"); 460 sbuf_puts(davrq->out, "</D:href>\n");
393 461
394 davrq->propertyBackend->propfind(davrq->propertyBackend, davrq, path.ptr); 462 if(davrq->persistencemgr->vfs_props) {
463 // get some DAV properties from the file system
464 dav_rq_propfind(davrq->persistencemgr, davrq, path.ptr);
465 }
466 davrq->persistencemgr->propfind(davrq->persistencemgr, davrq, path.ptr);
395 467
396 if(davrq->prop) { 468 if(davrq->prop) {
397 /* 469 /*
398 * there are some properties written, so we close the 470 * there are some properties written, so we close the
399 * prop and propstat tag 471 * prop and propstat tag
405 477
406 if(davrq->notFoundProps != NULL) { 478 if(davrq->notFoundProps != NULL) {
407 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); 479 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n");
408 DAV_FOREACH(elm, davrq->notFoundProps) { 480 DAV_FOREACH(elm, davrq->notFoundProps) {
409 DavProperty *prop = (DavProperty*)elm->data; 481 DavProperty *prop = (DavProperty*)elm->data;
410 sbuf_puts(davrq->out, "<D:"); 482 sbuf_put(davrq->out, '<');
483 sbuf_puts(davrq->out, prop->xmlns->prefix);
484 sbuf_put(davrq->out, ':');
485
411 sbuf_puts(davrq->out, prop->name); 486 sbuf_puts(davrq->out, prop->name);
412 sbuf_puts(davrq->out, " />\n"); 487 sbuf_puts(davrq->out, " />\n");
413 } 488 }
414 sbuf_puts(davrq->out, "</D:prop>\n"); 489 sbuf_puts(davrq->out, "</D:prop>\n");
415 sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n"); 490 sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n");
434 if(!davrq->prop) { 509 if(!davrq->prop) {
435 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); 510 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n");
436 davrq->prop = 1; 511 davrq->prop = 1;
437 } 512 }
438 513
439 sbuf_puts(davrq->out, "<D:"); 514 sbuf_put(davrq->out, '<');
515 sbuf_puts(davrq->out, prop->xmlns->prefix);
516 sbuf_put(davrq->out, ':');
440 sbuf_puts(davrq->out, prop->name); 517 sbuf_puts(davrq->out, prop->name);
441 sbuf_puts(davrq->out, ">"); 518 sbuf_put(davrq->out, '>');
442 519
443 sbuf_append(davrq->out, sstrn(str, len)); 520 sbuf_append(davrq->out, sstrn(str, len));
444 521
445 sbuf_puts(davrq->out, "</D:"); 522 sbuf_puts(davrq->out, "</");
523 sbuf_puts(davrq->out, prop->xmlns->prefix);
524 sbuf_put(davrq->out, ':');
446 sbuf_puts(davrq->out, prop->name); 525 sbuf_puts(davrq->out, prop->name);
447 sbuf_puts(davrq->out, ">\n"); 526 sbuf_puts(davrq->out, ">\n");
448 } 527 }
449 528
450 void dav_propfind_add_prop_error( 529 void dav_propfind_add_prop_error(
458 537
459 538
460 539
461 540
462 /* WebDAV Default Backend */ 541 /* WebDAV Default Backend */
463 static DAVPropertyBackend dav_file_backend = { 542 static PersistenceManager dav_file_backend = {
543 NULL,
544 NULL,
464 dav_rq_propfind, 545 dav_rq_propfind,
465 dav_rq_proppatch 546 dav_rq_proppatch,
547 0
466 }; 548 };
467 549
468 DAVPropertyBackend* create_property_backend() { 550 PersistenceManager* create_property_backend() {
469 return &dav_file_backend; 551 return &dav_file_backend;
470 } 552 }
471 553
472 void dav_rq_propfind(DAVPropertyBackend *b, PropfindRequest *rq ,char *path) { 554 void dav_rq_propfind(PersistenceManager *b, PropfindRequest *rq ,char *path) {
473 struct stat st; 555 struct stat st;
474 if(stat(path, &st) != 0) { 556 if(stat(path, &st) != 0) {
475 perror("dav_be_propfind"); 557 perror("dav_be_propfind");
476 fprintf(stderr, "Cannot get stat of file: %s\n", path); 558 fprintf(stderr, "Cannot get stat of file: %s\n", path);
477 } 559 }
478 560
479 if(rq->allprop) { 561 if(rq->allprop) {
480 DavProperty prop; 562 DavProperty prop;
481 prop.xmlns = "DAV:"; 563 prop.xmlns = xmlnsmap_get(rq->nsmap, "DAV:");
482 564
483 prop.name = "resourcetype"; 565 prop.name = "resourcetype";
484 if(S_ISDIR(st.st_mode)) { 566 if(S_ISDIR(st.st_mode)) {
485 dav_propfind_add_str_prop(rq, &prop, "<D:collection/>", 15); 567 dav_propfind_add_str_prop(rq, &prop, "<D:collection/>", 15);
486 } else { 568 } else {
529 dav_propfind_add_prop_error(rq, prop, 404); 611 dav_propfind_add_prop_error(rq, prop, 404);
530 } 612 }
531 } 613 }
532 } 614 }
533 615
534 void dav_rq_proppatch(DAVPropertyBackend *b, ProppatchRequest *rq) { 616 void dav_rq_proppatch(PersistenceManager *b, ProppatchRequest *rq) {
535 DAV_FOREACH(p, rq->setProps) { 617 DAV_FOREACH(p, rq->setProps) {
536 XmlElement *prop = (XmlElement*)p->data; 618 XmlElement *prop = (XmlElement*)p->data;
537 propstat_add(rq->propstat, 403, prop); 619 propstat_add(rq->propstat, 403, prop);
538 } 620 }
539 621
555 if(map == NULL || uxm == NULL) { 637 if(map == NULL || uxm == NULL) {
556 return NULL; 638 return NULL;
557 } 639 }
558 map->map = uxm; 640 map->map = uxm;
559 map->pool = pool; 641 map->pool = pool;
642 map->num = 0;
643
644 // create DAV: namespace
645 XmlNs *ns = pool_malloc(map->pool, sizeof(XmlNs));
646 ns->xmlns = "DAV:";
647 ns->prefix = "D";
648 ns->nslen = 4;
649 ns->prelen = 1;
650
651 ucx_map_cstr_put(uxm, "DAV:", ns);
652
560 return map; 653 return map;
561 } 654 }
562 655
563 void xmlnsmap_free(XmlNsMap *map) { 656 void xmlnsmap_free(XmlNsMap *map) {
564 ucx_map_free(map->map); 657 ucx_map_free(map->map);
573 xmlns = pool_malloc(map->pool, sizeof(XmlNs)); 666 xmlns = pool_malloc(map->pool, sizeof(XmlNs));
574 if(xmlns == NULL) { 667 if(xmlns == NULL) {
575 return NULL; 668 return NULL;
576 } 669 }
577 670
578 xmlns->xmlns = ns; 671 sstr_t newns = sstrdup(sstr(ns));
579 xmlns->nslen = strlen(ns); 672
580 673 xmlns->xmlns = newns.ptr;
581 xmlns->prefix = NULL; 674 xmlns->nslen = newns.length;
582 xmlns->prelen = 0; 675
676 xmlns->prefix = pool_calloc(map->pool, 1, 8);
677 xmlns->prelen = snprintf(xmlns->prefix, 7, "x%d", map->num);
583 678
584 ucx_map_cstr_put(map->map, ns, xmlns); /* TODO: check return value */ 679 ucx_map_cstr_put(map->map, ns, xmlns); /* TODO: check return value */
680 map->num++;
585 return xmlns; 681 return xmlns;
586 } 682 }
587 683
588 XmlNs* xmlnsmap_get(XmlNsMap *map, char *ns) { 684 XmlNs* xmlnsmap_get(XmlNsMap *map, char *ns) {
589 return ucx_map_cstr_get(map->map, ns); 685 return ucx_map_cstr_get(map->map, ns);
596 if(parent->ctlen == 0) { 692 if(parent->ctlen == 0) {
597 parent->content = ucx_dlist_append(parent->content, child); 693 parent->content = ucx_dlist_append(parent->content, child);
598 } 694 }
599 } 695 }
600 696
601 void xmlelm_write(XmlElement *elm, sbuf_t *out, int wv) { 697 void xmlelm_write(XmlElement *elm, Buffer *out, int wv) {
602 sbuf_append(out, sstrn("<", 1)); 698 sbuf_append(out, sstrn("<", 1));
603 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); 699 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen));
604 sbuf_append(out, sstrn(":", 1)); 700 sbuf_append(out, sstrn(":", 1));
605 sbuf_append(out, elm->name); 701 sbuf_append(out, sstr(elm->name));
606 702
607 if(wv) { 703 if(wv) {
608 if(elm->ctlen == 0) { 704 if(elm->ctlen == 0) {
609 if(elm->content == NULL) { 705 if(elm->content == NULL) {
610 sbuf_append(out, sstrn(" />", 3)); 706 sbuf_append(out, sstrn(" />", 3));
614 xmlelm_write((XmlElement*)pr->data, out, 1); 710 xmlelm_write((XmlElement*)pr->data, out, 1);
615 } 711 }
616 sbuf_append(out, sstrn("</", 2)); 712 sbuf_append(out, sstrn("</", 2));
617 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); 713 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen));
618 sbuf_append(out, sstrn(":", 1)); 714 sbuf_append(out, sstrn(":", 1));
619 sbuf_append(out, elm->name); 715 sbuf_append(out, sstr(elm->name));
620 sbuf_append(out, sstrn(">", 1)); 716 sbuf_append(out, sstrn(">", 1));
621 } 717 }
622 } else { 718 } else {
623 sbuf_append(out, sstrn(" />", 3)); 719 sbuf_append(out, sstrn(" />", 3));
624 sbuf_append(out, sstrn((char*)elm->content, elm->ctlen)); 720 sbuf_append(out, sstrn((char*)elm->content, elm->ctlen));
625 sbuf_append(out, sstrn("</", 2)); 721 sbuf_append(out, sstrn("</", 2));
626 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); 722 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen));
627 sbuf_append(out, sstrn(":", 1)); 723 sbuf_append(out, sstrn(":", 1));
628 sbuf_append(out, elm->name); 724 sbuf_append(out, sstr(elm->name));
629 sbuf_append(out, sstrn(">", 1)); 725 sbuf_append(out, sstrn(">", 1));
630 } 726 }
631 } else { 727 } else {
632 sbuf_append(out, sstrn(" />", 3)); 728 sbuf_append(out, sstrn(" />", 3));
633 } 729 }
657 753
658 ucx_map_put(propstat->map, key, list); 754 ucx_map_put(propstat->map, key, list);
659 } 755 }
660 } 756 }
661 757
662 void propstat_write(Propstat *propstat, sbuf_t *out, int wv) { 758 void propstat_write(Propstat *propstat, Buffer *out, int wv) {
663 if(propstat->okprop) { 759 if(propstat->okprop) {
664 sbuf_puts(out, "<D:propstat>\n<D:prop>\n"); 760 sbuf_puts(out, "<D:propstat>\n<D:prop>\n");
665 761
666 DAV_FOREACH(prop, propstat->okprop) { 762 DAV_FOREACH(prop, propstat->okprop) {
667 xmlelm_write((XmlElement*)prop->data, out, wv); 763 xmlelm_write((XmlElement*)prop->data, out, wv);

mercurial