1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include "../daemon/session.h"
33 #include "../daemon/protocol.h"
34 #include "../util/platform.h"
35
36 #include <cx/string.h>
37 #include <cx/hash_map.h>
38
39 #include "multistatus.h"
40
41 #include "operation.h"
42 #include "xml.h"
43
44 #define MULTISTATUS_BUFFER_LENGTH 2048
45
46 Multistatus* multistatus_response(Session *sn, Request *rq) {
47 Multistatus *ms = pool_malloc(sn->pool,
sizeof(Multistatus));
48 if(!ms) {
49 return NULL;
50 }
51 ZERO(ms,
sizeof(Multistatus));
52 ms->response.addresource = multistatus_addresource;
53 ms->sn = sn;
54 ms->rq = rq;
55 ms->namespaces = cxHashMapCreate(pool_allocator(sn->pool),
CX_STORE_POINTERS,
8);
56 ms->proppatch =
FALSE;
57 if(!ms->namespaces) {
58 return NULL;
59 }
60 if(cxMapPut(ms->namespaces, cx_hash_key_str(
"D"), webdav_dav_namespace())) {
61 return NULL;
62 }
63 return ms;
64 }
65
66 static int send_xml_root(Multistatus *ms, Writer *out) {
67 writer_put_lit(out,
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
68 "<D:multistatus");
69
70
71
72
73 CxIterator i = cxMapIterator(ms->namespaces);
74 cx_foreach(CxMapEntry*, entry, i) {
75 WSNamespace *ns = entry->value;
76 writer_put_lit(out,
" xmlns:");
77 writer_put (out, entry->key->data, entry->key->len);
78 writer_put_lit(out,
"=\"");
79 writer_put_str(out, (
char*)ns->href);
80 writer_put_lit(out,
"\"");
81 }
82
83 writer_put_lit(out,
">\n");
84
85 return out->error;
86 }
87
88 static void send_nsdef(WSNamespace *ns, Writer *out) {
89 writer_put_lit(out,
" xmlns:");
90 writer_put_str(out, (
char*)ns->prefix);
91 writer_put_lit(out,
"=\"");
92 writer_put_str(out, (
char*)ns->href);
93 writer_putc (out,
'\"');
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 static const char hex_ch[] =
"0123456789ABCDEF";
151
152 static void send_string_escaped(Writer *out, cxmutstr str) {
153 char *begin = str.ptr;
154 char *end = begin;
155
156 char escape[
4];
157 escape[
0] =
'%';
158
159 for(
size_t i=
0;i<str.length;i++) {
160 unsigned char c = (
unsigned char)str.ptr[i];
161 end = str.ptr + i;
162
163
164 if( (c >=
'A' && c <=
'Z') ||
165 (c >=
'a' && c <=
'z') ||
166 (c >=
'/' && c <=
'9') ||
167 (strchr(
"_.\\-~", c) !=
NULL))
168 {
169 continue;
170 }
171
172
173 escape[
1] = hex_ch[(c >>
4) & 0x0F];
174 escape[
2] = hex_ch[c & 0x0F];
175
176
177 ptrdiff_t len = end - begin;
178 if(len >
0) {
179 writer_put(out, begin, len);
180 }
181
182
183 writer_put(out, escape,
3);
184
185
186 begin = end +
1;
187 }
188 ptrdiff_t len = str.ptr + str.length - begin;
189 if(len >
0) {
190 writer_put(out, begin, len);
191 begin = end +
1;
192 }
193 }
194
195 static int send_property(
196 Multistatus *ms,
197 WebdavProperty *property,
198 WebdavNSList *nsdef,
199 WSBool writeContent,
200 Writer *out)
201 {
202
203 writer_putc (out,
'<');
204 writer_put_str(out, (
char*)property->namespace->prefix);
205 writer_putc (out,
':');
206 writer_put_str(out, (
char*)property->name);
207
208
209 WebdavNSList *def = nsdef;
210 while(def) {
211 send_nsdef(def->namespace, out);
212 def = def->next;
213 }
214
215
216 if(property->lang) {
217 writer_put_lit(out,
" xml:lang=\"");
218 writer_put_str(out, (
char*)property->lang);
219 writer_putc (out,
'\"');
220 }
221
222
223 if(writeContent) {
224 writer_putc(out,
'>');
225
226
227 switch(property->vtype) {
228 case WS_VALUE_NO_TYPE:
break;
229 case WS_VALUE_XML_NODE: {
230 wsxml_write_nodes_without_nsdef(
231 ms->sn->pool,
232 out,
233 property->value.node);
234 break;
235 }
236 case WS_VALUE_XML_DATA: {
237
238 writer_put(
239 out,
240 property->value.data.data,
241 property->value.data.length);
242 break;
243 }
244 case WS_VALUE_TEXT: {
245
246 writer_put(
247 out,
248 property->value.text.str,
249 property->value.text.length);
250 break;
251 }
252 }
253
254
255 writer_put_lit(out,
"</");
256 writer_put_str(out, (
char*)property->namespace->prefix);
257 writer_putc (out,
':');
258 writer_put_str(out, (
char*)property->name);
259 writer_putc (out,
'>');
260 }
else {
261 writer_put_lit(out,
"/>");
262 }
263
264 return out->error;
265 }
266
267 static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) {
268 writer_put_lit(out,
" <D:response>\n"
269 " <D:href>");
270 send_string_escaped(out, cx_mutstr(rp->resource.href));
271 writer_put_lit(out,
"</D:href>\n");
272
273 WSBool writeContent = ms->proppatch ?
FALSE :
TRUE;
274
275 if(rp->plist_begin) {
276 writer_put_lit(out,
" <D:propstat>\n"
277 " <D:prop>\n");
278
279 PropertyOkList *p = rp->plist_begin;
280 while(p) {
281 writer_put_lit(out,
" ");
282 if(send_property(ms, p->property, p->nsdef, writeContent, out)) {
283 return out->error;
284 }
285 writer_put_lit(out,
"\n");
286 p = p->next;
287 }
288
289 writer_put_lit(out,
" </D:prop>\n"
290 " <D:status>HTTP/1.1 200 OK</D:status>\n"
291 " </D:propstat>\n");
292 }
293
294
295 PropertyErrorList *error = rp->errors;
296 while(error) {
297 writer_put_lit(out,
" <D:propstat>\n"
298 " <D:prop>\n");
299
300 WebdavPList *errprop = error->begin;
301 while(errprop) {
302 writer_put_lit(out,
" ");
303 if(send_property(ms, errprop->property,
NULL,
FALSE, out)) {
304 return out->error;
305 }
306 writer_putc(out,
'\n');
307 errprop = errprop->next;
308 }
309
310 char statuscode[
8];
311 int sclen = snprintf(statuscode,
8,
"%d ", error->status);
312 if(sclen >
4) {
313 statuscode[
0] =
'5';
314 statuscode[
1] =
'0';
315 statuscode[
2] =
'0';
316 statuscode[
3] =
' ';
317 sclen =
4;
318 }
319 writer_put_lit(out,
" </D:prop>\n"
320 " <D:status>HTTP/1.1 ");
321 writer_put(out, statuscode, sclen);
322 const char *status_msg = protocol_status_message(error->status);
323 if(status_msg) {
324 writer_put(out, status_msg, strlen(status_msg));
325 }
else {
326 writer_put_lit(out,
"Server Error");
327 }
328 writer_put_lit(out,
"</D:status>\n"
329 " </D:propstat>\n");
330
331
332 error = error->next;
333 }
334
335
336 writer_put_lit(out,
" </D:response>\n");
337
338 return out->error;
339 }
340
341 int multistatus_send(Multistatus *ms,
SYS_NETFD net) {
342
343 if(ms->current && !ms->current->resource.isclosed) {
344 if(msresponse_close((WebdavResource*)ms->current)) {
345 return 1;
346 }
347 }
348
349
350 protocol_status(ms->sn, ms->rq,
207,
NULL);
351 if(protocol_start_response(ms->sn, ms->rq)) {
352 return 1;
353 }
354
355 char buffer[
MULTISTATUS_BUFFER_LENGTH];
356
357 Writer writer;
358 Writer *out = &writer;
359 writer_init(out, net, buffer,
MULTISTATUS_BUFFER_LENGTH);
360
361
362 if(send_xml_root(ms, out)) {
363 return 1;
364 }
365
366
367 MSResponse *response = ms->first;
368 while(response) {
369 if(send_response_tag(ms, response, out)) {
370 return 1;
371 }
372 response = response->next;
373 }
374
375
376 writer_put_lit(out,
"</D:multistatus>\n");
377
378
379
380
381 writer_flush(out);
382
383 return 0;
384 }
385
386 WebdavResource * multistatus_addresource(
387 WebdavResponse *response,
388 const char *path)
389 {
390 Multistatus *ms = (Multistatus*)response;
391 MSResponse *res = pool_malloc(ms->sn->pool,
sizeof(MSResponse));
392 if(!res) {
393 return NULL;
394 }
395 ZERO(res,
sizeof(MSResponse));
396
397
398 res->resource.href = pool_strdup(ms->sn->pool, path);
399 if(!res->resource.href) {
400 return NULL;
401 }
402
403 res->resource.err =
0;
404
405
406 res->resource.addproperty = msresponse_addproperty;
407 res->resource.close = msresponse_close;
408
409 res->properties = cxHashMapCreate(pool_allocator(ms->sn->pool),
CX_STORE_POINTERS,
32);
410 if(!res->properties) {
411 return NULL;
412 }
413
414 res->multistatus = ms;
415 res->errors =
NULL;
416 res->resource.isclosed =
0;
417 res->closing =
0;
418
419
420 if(ms->current) {
421
422 if(!ms->current->resource.isclosed) {
423 msresponse_close((WebdavResource*)ms->current);
424 }
425 ms->current->next = res;
426 }
else {
427 ms->first = res;
428 }
429 ms->current = res;
430
431 return (WebdavResource*)res;
432 }
433
434 static int oklist_add(
435 pool_handle_t *pool,
436 PropertyOkList **begin,
437 PropertyOkList **end,
438 WebdavProperty *property,
439 WebdavNSList *nsdef)
440 {
441 PropertyOkList *newelm = pool_malloc(pool,
sizeof(PropertyOkList));
442 if(!newelm) {
443 return 1;
444 }
445 newelm->property = property;
446 newelm->nsdef = nsdef;
447 newelm->next =
NULL;
448 if(*end) {
449 (*end)->next = newelm;
450 }
else {
451 *begin = newelm;
452 }
453 *end = newelm;
454 return 0;
455 }
456
457
458
459
460
461
462 static int msresponse_addproperror(
463 MSResponse *response,
464 WebdavProperty *property,
465 int statuscode)
466 {
467 pool_handle_t *pool = response->multistatus->sn->pool;
468
469 response->resource.err++;
470
471
472
473 PropertyErrorList *errlist =
NULL;
474 PropertyErrorList *list = response->errors;
475 PropertyErrorList *last =
NULL;
476 while(list) {
477 if(list->status == statuscode) {
478 errlist = list;
479 break;
480 }
481 last = list;
482 list = list->next;
483 }
484
485 if(!errlist) {
486
487 PropertyErrorList *newelm = pool_malloc(pool,
488 sizeof(PropertyErrorList));
489 if(!newelm) {
490 return 1;
491 }
492 newelm->begin =
NULL;
493 newelm->end =
NULL;
494 newelm->next =
NULL;
495 newelm->status = statuscode;
496
497 if(last) {
498 last->next = newelm;
499 }
else {
500 response->errors = newelm;
501 }
502 errlist = newelm;
503 }
504
505
506 if(webdav_plist_add(pool, &errlist->begin, &errlist->end, property)) {
507 return 1;
508 }
509 return 0;
510 }
511
512 static CxHashKey ms_property_key(
513 CxAllocator *a,
514 const xmlChar *href,
515 const char *property_name)
516 {
517 cxmutstr key_data = cx_strcat_a(a,
3, cx_str((
const char*)href), (cxstring){
"\0",
1 }, cx_str(property_name));
518 return cx_hash_key_bytes((
unsigned char*)key_data.ptr, key_data.length);
519 }
520
521 int msresponse_addproperty(
522 WebdavResource *res,
523 WebdavProperty *property,
524 int status)
525 {
526 MSResponse *response = (MSResponse*)res;
527 Session *sn = response->multistatus->sn;
528 if(response->resource.isclosed) {
529 log_ereport(
530 LOG_WARN,
531 "%s",
532 "webdav: cannot add property to closed response tag");
533 return 0;
534 }
535
536
537 if(!property->namespace || !property->namespace->href) {
538
539 log_ereport(
540 LOG_FAILURE,
541 "%s",
542 "webdav: property ''%s'' has no namespace",
543 property->name);
544 return 1;
545 }
546
547
548 CxAllocator *a = pool_allocator(sn->pool);
549 CxHashKey key = ms_property_key(a, property->namespace->href, property->name);
550 if(cxMapGet(response->properties, key)) {
551 cxFree(a, (
void*)key.data);
552 return 0;
553 }
554 if(cxMapPut(response->properties, key, property)) {
555 return 1;
556 }
557 cxFree(a, (
void*)key.data);
558
559
560 WebdavNSList *nsdef_begin =
NULL;
561 WebdavNSList *nsdef_end =
NULL;
562
563
564
565 if(property->namespace->prefix) {
566 WSNamespace *ns = cxMapGet(
567 response->multistatus->namespaces,
568 cx_hash_key_str((
const char*)property->namespace->prefix));
569 if(!ns) {
570
571 int err = cxMapPut(
572 response->multistatus->namespaces,
573 cx_hash_key_str((
const char*)property->namespace->prefix),
574 property->namespace);
575 if(err) {
576 return 1;
577 }
578 }
else if(
579 strcmp((
const char*)property->namespace->href,
580 (
const char*)ns->href))
581 {
582
583
584
585
586 if(webdav_nslist_add(
587 sn->pool,
588 &nsdef_begin,
589 &nsdef_end,
590 property->namespace))
591 {
592 return 1;
593 }
594 }
595 }
596
597 if(response->multistatus->proppatch && response->errors) {
598
599
600
601 status =
424;
602 }
603
604
605 if(status !=
200) {
606 return msresponse_addproperror(response, property, status);
607 }
608
609
610 WebdavNSList *nslist =
NULL;
611 if(property->vtype ==
WS_VALUE_XML_NODE) {
612
613 int err =
0;
614 nslist = wsxml_get_required_namespaces(
615 response->multistatus->sn->pool,
616 property->value.node,
617 &err);
618 if(err) {
619 return 1;
620 }
621 }
else if(property->vtype ==
WS_VALUE_XML_DATA) {
622
623 nslist = property->value.data.namespaces;
624 }
625
626 while(nslist) {
627
628
629
630 if(strcmp(
631 (
const char*)nslist->namespace->prefix,
632 (
const char*)property->namespace->prefix))
633 {
634
635 if(webdav_nslist_add(
636 sn->pool,
637 &nsdef_begin,
638 &nsdef_end,
639 nslist->namespace))
640 {
641 return 1;
642 }
643 }
644 nslist = nslist->next;
645 }
646
647
648 if(oklist_add(
649 sn->pool,
650 &response->plist_begin,
651 &response->plist_end,
652 property,
653 nsdef_begin))
654 {
655 return 1;
656 }
657 return 0;
658 }
659
660 int msresponse_close(WebdavResource *res) {
661 MSResponse *response = (MSResponse*)res;
662 if(response->closing) {
663 return 0;
664 }
665 response->closing =
TRUE;
666 Multistatus *ms = response->multistatus;
667
668 int ret =
REQ_PROCEED;
669
670
671
672
673 WebdavOperation *op = ms->response.op;
674 if(op->response_close(op, res)) {
675 ret =
REQ_ABORTED;
676 }
677
678
679 CxAllocator *a = pool_allocator(ms->sn->pool);
680 WebdavPList *pl = ms->response.op->reqprops;
681 while(pl) {
682 CxHashKey key = ms_property_key(a, pl->property->namespace->href, pl->property->name);
683 if(!cxMapGet(response->properties, key)) {
684
685 if(ms->proppatch) {
686 if(msresponse_addproperty(res, pl->property,
424)) {
687 ret =
REQ_ABORTED;
688 break;
689 }
690 }
else {
691 if(msresponse_addproperty(res, pl->property,
404)) {
692 ret =
REQ_ABORTED;
693 break;
694 }
695 }
696 }
697
698 pl = pl->next;
699 }
700
701 if(ms->proppatch && response->errors) {
702
703
704
705 PropertyOkList *elm = response->plist_begin;
706 PropertyOkList *nextelm;
707 while(elm) {
708 if(msresponse_addproperty(res, elm->property,
424)) {
709 return 1;
710 }
711 nextelm = elm->next;
712 pool_free(response->multistatus->sn->pool, elm);
713 elm = nextelm;
714 }
715 response->plist_begin =
NULL;
716 response->plist_end =
NULL;
717 }
718
719
720 cxMapDestroy(response->properties);
721
722 response->resource.isclosed =
TRUE;
723 return ret;
724 }
725