|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2020 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 |
|
32 #include "../daemon/session.h" |
|
33 #include "../daemon/protocol.h" |
|
34 #include "../util/platform.h" |
|
35 |
|
36 #include <ucx/string.h> |
|
37 |
|
38 #include "multistatus.h" |
|
39 |
|
40 #include "operation.h" |
|
41 #include "xml.h" |
|
42 |
|
43 #define MULTISTATUS_BUFFER_LENGTH 2048 |
|
44 |
|
45 Multistatus* multistatus_response(Session *sn, Request *rq) { |
|
46 Multistatus *ms = pool_malloc(sn->pool, sizeof(Multistatus)); |
|
47 if(!ms) { |
|
48 return NULL; |
|
49 } |
|
50 ZERO(ms, sizeof(Multistatus)); |
|
51 ms->response.addresource = multistatus_addresource; |
|
52 ms->sn = sn; |
|
53 ms->rq = rq; |
|
54 ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8); |
|
55 ms->proppatch = FALSE; |
|
56 if(!ms->namespaces) { |
|
57 return NULL; |
|
58 } |
|
59 if(ucx_map_cstr_put(ms->namespaces, "D", webdav_dav_namespace())) { |
|
60 return NULL; |
|
61 } |
|
62 return ms; |
|
63 } |
|
64 |
|
65 static int send_xml_root(Multistatus *ms, Writer *out) { |
|
66 writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" |
|
67 "<D:multistatus")); |
|
68 |
|
69 // write the namespaces definitions |
|
70 // key is the namespace prefix |
|
71 // the map always contains the "DAV:" namespace with the prefix "D" |
|
72 UcxMapIterator i = ucx_map_iterator(ms->namespaces); |
|
73 WSNamespace *ns; |
|
74 UCX_MAP_FOREACH(key, ns, i) { |
|
75 writer_puts(out, S(" xmlns:")); |
|
76 writer_put(out, key.data, key.len); |
|
77 writer_puts(out, S("=\"")); |
|
78 writer_puts(out, sstr((char*)ns->href)); |
|
79 writer_puts(out, S("\"")); |
|
80 } |
|
81 |
|
82 writer_puts(out, S(">\n")); |
|
83 |
|
84 return out->error; |
|
85 } |
|
86 |
|
87 static void send_nsdef(WSNamespace *ns, Writer *out) { |
|
88 writer_puts(out, S(" xmlns:")); |
|
89 writer_puts(out, sstr((char*)ns->prefix)); |
|
90 writer_puts(out, S("=\"")); |
|
91 writer_puts(out, sstr((char*)ns->href)); |
|
92 writer_putc(out, '\"'); |
|
93 } |
|
94 |
|
95 static void send_string_escaped(Writer *out, sstr_t str) { |
|
96 char *begin = str.ptr; |
|
97 char *end = begin; |
|
98 char *escape = NULL; |
|
99 int esclen; |
|
100 for(size_t i=0;i<str.length;i++) { |
|
101 char c = str.ptr[i]; |
|
102 end = str.ptr + i; |
|
103 switch(c) { |
|
104 case '"': { |
|
105 escape = """; |
|
106 esclen = 6; |
|
107 break; |
|
108 } |
|
109 case '&': { |
|
110 escape = "&"; |
|
111 esclen = 5; |
|
112 break; |
|
113 } |
|
114 case '\'': { |
|
115 escape = "'"; |
|
116 esclen = 6; |
|
117 break; |
|
118 } |
|
119 case '<': { |
|
120 escape = "<"; |
|
121 esclen = 4; |
|
122 break; |
|
123 } |
|
124 case '>': { |
|
125 escape = ">"; |
|
126 esclen = 4; |
|
127 break; |
|
128 } |
|
129 default: continue; |
|
130 } |
|
131 ptrdiff_t len = end - begin; |
|
132 if(len > 0) { |
|
133 writer_put(out, begin, len); |
|
134 begin = end + 1; |
|
135 } |
|
136 writer_put(out, escape, esclen); |
|
137 } |
|
138 ptrdiff_t len = end - begin; |
|
139 if(len > 0) { |
|
140 writer_put(out, begin, len + 1); |
|
141 begin = end + 1; |
|
142 } |
|
143 } |
|
144 |
|
145 static int send_property( |
|
146 Multistatus *ms, |
|
147 WebdavProperty *property, |
|
148 WebdavNSList *nsdef, |
|
149 WSBool writeContent, |
|
150 Writer *out) |
|
151 { |
|
152 // write: "<prefix:name" |
|
153 writer_putc(out, '<'); |
|
154 writer_puts(out, sstr((char*)property->namespace->prefix)); |
|
155 writer_putc(out, ':'); |
|
156 writer_puts(out, sstr((char*)property->name)); |
|
157 |
|
158 // check if the namespace is already defined |
|
159 WSBool need_nsdef = TRUE; |
|
160 WSNamespace *ns = ucx_map_cstr_get( |
|
161 ms->namespaces, |
|
162 (char*)property->namespace->prefix); |
|
163 if(ns && !strcmp( |
|
164 (const char*)ns->href, |
|
165 (const char*)property->namespace->href)) |
|
166 { |
|
167 need_nsdef = FALSE; // prefix and href are the same, no need for nsdef |
|
168 } |
|
169 |
|
170 // send definition for the element's namespace |
|
171 if(need_nsdef) { |
|
172 send_nsdef(property->namespace, out); |
|
173 } |
|
174 |
|
175 // send additional namespace definitions required for the value |
|
176 WebdavNSList *def = nsdef; |
|
177 while(def) { |
|
178 send_nsdef(def->namespace, out); |
|
179 def = def->next; |
|
180 } |
|
181 |
|
182 // send xml lang attribute |
|
183 if(property->lang) { |
|
184 writer_puts(out, S(" xml:lang=\"")); |
|
185 writer_puts(out, sstr((char*)property->lang)); |
|
186 writer_putc(out, '\"'); |
|
187 } |
|
188 |
|
189 // end property tag and write content |
|
190 if(writeContent) { |
|
191 writer_putc(out, '>'); |
|
192 |
|
193 // content |
|
194 switch(property->vtype) { |
|
195 case WS_VALUE_NO_TYPE: break; |
|
196 case WS_VALUE_XML_NODE: { |
|
197 wsxml_write_nodes_without_nsdef( |
|
198 ms->sn->pool, |
|
199 out, |
|
200 property->value.node); |
|
201 break; |
|
202 } |
|
203 case WS_VALUE_XML_DATA: { |
|
204 // only write data, data->namespaces is already handled |
|
205 writer_put( |
|
206 out, |
|
207 property->value.data.data, |
|
208 property->value.data.length); |
|
209 break; |
|
210 } |
|
211 case WS_VALUE_TEXT: { |
|
212 // asume the text is already escaped |
|
213 writer_put( |
|
214 out, |
|
215 property->value.text.str, |
|
216 property->value.text.length); |
|
217 break; |
|
218 } |
|
219 } |
|
220 |
|
221 // end tag |
|
222 writer_puts(out, S("</")); |
|
223 writer_puts(out, sstr((char*)property->namespace->prefix)); |
|
224 writer_putc(out, ':'); |
|
225 writer_puts(out, sstr((char*)property->name)); |
|
226 writer_putc(out, '>'); |
|
227 } else { |
|
228 writer_puts(out, S("/>")); |
|
229 } |
|
230 |
|
231 return out->error; |
|
232 } |
|
233 |
|
234 static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) { |
|
235 writer_puts(out, S(" <D:response>\n" |
|
236 " <D:href>")); |
|
237 //writer_puts(out, sstr(rp->resource.href)); |
|
238 send_string_escaped(out, sstr(rp->resource.href)); |
|
239 writer_puts(out, S("</D:href>\n")); |
|
240 |
|
241 WSBool writeContent = ms->proppatch ? FALSE : TRUE; |
|
242 |
|
243 if(rp->plist_begin) { |
|
244 writer_puts(out, S(" <D:propstat>\n" |
|
245 " <D:prop>\n")); |
|
246 // send properties |
|
247 PropertyOkList *p = rp->plist_begin; |
|
248 while(p) { |
|
249 writer_puts(out, S(" ")); |
|
250 if(send_property(ms, p->property, p->nsdef, writeContent, out)) { |
|
251 return out->error; |
|
252 } |
|
253 writer_puts(out, S("\n")); |
|
254 p = p->next; |
|
255 } |
|
256 |
|
257 writer_puts(out, S(" </D:prop>\n" |
|
258 " <D:status>HTTP/1.1 200 OK</D:status>\n" |
|
259 " </D:propstat>\n")); |
|
260 } |
|
261 |
|
262 // send error properties |
|
263 PropertyErrorList *error = rp->errors; |
|
264 while(error) { |
|
265 writer_puts(out, S(" <D:propstat>\n" |
|
266 " <D:prop>\n")); |
|
267 |
|
268 WebdavPList *errprop = error->begin; |
|
269 while(errprop) { |
|
270 writer_puts(out, S(" ")); |
|
271 if(send_property(ms, errprop->property, NULL, FALSE, out)) { |
|
272 return out->error; |
|
273 } |
|
274 writer_putc(out, '\n'); |
|
275 errprop = errprop->next; |
|
276 } |
|
277 |
|
278 char statuscode[8]; |
|
279 int sclen = snprintf(statuscode, 8, "%d ", error->status); |
|
280 if(sclen > 4) { |
|
281 statuscode[0] = '5'; |
|
282 statuscode[1] = '0'; |
|
283 statuscode[2] = '0'; |
|
284 statuscode[3] = ' '; |
|
285 sclen = 4; |
|
286 } |
|
287 writer_puts(out, S(" </D:prop>\n" |
|
288 " <D:status>HTTP/1.1 ")); |
|
289 writer_put(out, statuscode, sclen); |
|
290 const char *status_msg = protocol_status_message(error->status); |
|
291 if(status_msg) { |
|
292 writer_put(out, status_msg, strlen(status_msg)); |
|
293 } else { |
|
294 writer_puts(out, S("Server Error")); |
|
295 } |
|
296 writer_puts(out, S("</D:status>\n" |
|
297 " </D:propstat>\n")); |
|
298 |
|
299 |
|
300 error = error->next; |
|
301 } |
|
302 |
|
303 // end response tag |
|
304 writer_puts(out, S(" </D:response>\n")); |
|
305 |
|
306 return out->error; |
|
307 } |
|
308 |
|
309 int multistatus_send(Multistatus *ms, SYS_NETFD net) { |
|
310 // make sure every resource is closed |
|
311 if(ms->current && !ms->current->resource.isclosed) { |
|
312 if(msresponse_close((WebdavResource*)ms->current)) { |
|
313 return 1; |
|
314 } |
|
315 } |
|
316 |
|
317 // start http response |
|
318 protocol_status(ms->sn, ms->rq, 207, NULL); |
|
319 protocol_start_response(ms->sn, ms->rq); |
|
320 |
|
321 char buffer[MULTISTATUS_BUFFER_LENGTH]; |
|
322 // create a writer, that flushes the buffer when it is filled |
|
323 Writer writer; |
|
324 Writer *out = &writer; |
|
325 writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH); |
|
326 |
|
327 // send the xml root element with namespace defs |
|
328 if(send_xml_root(ms, out)) { |
|
329 return 1; |
|
330 } |
|
331 |
|
332 // send response tags |
|
333 MSResponse *response = ms->first; |
|
334 while(response) { |
|
335 if(send_response_tag(ms, response, out)) { |
|
336 return 1; |
|
337 } |
|
338 response = response->next; |
|
339 } |
|
340 |
|
341 // end multistatus |
|
342 writer_puts(out, S("</D:multistatus>\n")); |
|
343 |
|
344 //printf("\n\n"); |
|
345 //fflush(stdout); |
|
346 |
|
347 writer_flush(out); |
|
348 |
|
349 return 0; |
|
350 } |
|
351 |
|
352 WebdavResource * multistatus_addresource( |
|
353 WebdavResponse *response, |
|
354 const char *path) |
|
355 { |
|
356 Multistatus *ms = (Multistatus*)response; |
|
357 MSResponse *res = pool_malloc(ms->sn->pool, sizeof(MSResponse)); |
|
358 if(!res) { |
|
359 return NULL; |
|
360 } |
|
361 ZERO(res, sizeof(MSResponse)); |
|
362 |
|
363 // set href |
|
364 res->resource.href = pool_strdup(ms->sn->pool, path); |
|
365 if(!res->resource.href) { |
|
366 return NULL; |
|
367 } |
|
368 |
|
369 res->resource.err = 0; |
|
370 |
|
371 // add resource funcs |
|
372 res->resource.addproperty = msresponse_addproperty; |
|
373 res->resource.close = msresponse_close; |
|
374 |
|
375 res->properties = ucx_map_new_a(session_get_allocator(ms->sn), 32); |
|
376 if(!res->properties) { |
|
377 return NULL; |
|
378 } |
|
379 |
|
380 res->multistatus = ms; |
|
381 res->errors = NULL; |
|
382 res->resource.isclosed = 0; |
|
383 res->closing = 0; |
|
384 |
|
385 // add new resource to the resource list |
|
386 if(ms->current) { |
|
387 // before adding a new resource, the current resource must be closed |
|
388 if(!ms->current->resource.isclosed) { |
|
389 msresponse_close((WebdavResource*)ms->current); |
|
390 } |
|
391 ms->current->next = res; |
|
392 } else { |
|
393 ms->first = res; |
|
394 } |
|
395 ms->current = res; |
|
396 |
|
397 return (WebdavResource*)res; |
|
398 } |
|
399 |
|
400 static int oklist_add( |
|
401 pool_handle_t *pool, |
|
402 PropertyOkList **begin, |
|
403 PropertyOkList **end, |
|
404 WebdavProperty *property, |
|
405 WebdavNSList *nsdef) |
|
406 { |
|
407 PropertyOkList *newelm = pool_malloc(pool, sizeof(PropertyOkList)); |
|
408 if(!newelm) { |
|
409 return 1; |
|
410 } |
|
411 newelm->property = property; |
|
412 newelm->nsdef = nsdef; |
|
413 newelm->next = NULL; |
|
414 if(*end) { |
|
415 (*end)->next = newelm; |
|
416 } else { |
|
417 *begin = newelm; |
|
418 } |
|
419 *end = newelm; |
|
420 return 0; |
|
421 } |
|
422 |
|
423 int msresponse_addproperty( |
|
424 WebdavResource *res, |
|
425 WebdavProperty *property, |
|
426 int status) |
|
427 { |
|
428 MSResponse *response = (MSResponse*)res; |
|
429 Session *sn = response->multistatus->sn; |
|
430 if(response->resource.isclosed) { |
|
431 log_ereport( |
|
432 LOG_WARN, |
|
433 "%s", |
|
434 "webdav: cannot add property to closed response tag"); |
|
435 return 0; |
|
436 } |
|
437 |
|
438 // some WebdavProperty checks to make sure nothing explodes |
|
439 if(!property->namespace || !property->namespace->href) { |
|
440 // error: namespace is required |
|
441 log_ereport( |
|
442 LOG_FAILURE, |
|
443 "%s", |
|
444 "webdav: property '%s' has no namespace", |
|
445 property->name); |
|
446 return 1; |
|
447 } |
|
448 |
|
449 // check if the property was already added to the resource |
|
450 UcxAllocator *a = session_get_allocator(sn); |
|
451 sstr_t key = sstrcat_a( |
|
452 a, |
|
453 3, |
|
454 sstr((char*)property->namespace->href), |
|
455 S("\0"), |
|
456 sstr((char*)property->name)); |
|
457 if(ucx_map_sstr_get(response->properties, key)) { |
|
458 a->free(a->pool, key.ptr); |
|
459 return 0; |
|
460 } |
|
461 if(ucx_map_sstr_put(response->properties, key, property)) { |
|
462 return 1; // OOM |
|
463 } |
|
464 a->free(a->pool, key.ptr); |
|
465 |
|
466 // list of namespace definitions for this property |
|
467 WebdavNSList *nsdef_begin = NULL; |
|
468 WebdavNSList *nsdef_end = NULL; |
|
469 |
|
470 // add namespace of this property to the namespace map |
|
471 // the namespace map will be used for global namespace definitions |
|
472 if(property->namespace->prefix) { |
|
473 WSNamespace *ns = ucx_map_cstr_get( |
|
474 response->multistatus->namespaces, |
|
475 (const char*)property->namespace->prefix); |
|
476 if(!ns) { |
|
477 // prefix is not in use -> we can add the namespace to the ns map |
|
478 int err = ucx_map_cstr_put( |
|
479 response->multistatus->namespaces, |
|
480 (const char*)property->namespace->prefix, |
|
481 property->namespace); |
|
482 if(err) { |
|
483 return 1; // OOM |
|
484 } |
|
485 } else if( |
|
486 strcmp((const char*)property->namespace->href, |
|
487 (const char*)ns->href)) |
|
488 { |
|
489 // global namespace != local namespace |
|
490 // therefore we need a namespace definition in this element |
|
491 |
|
492 // ns-prefix != property-prefix -> add ns to nsdef |
|
493 if(webdav_nslist_add( |
|
494 sn->pool, |
|
495 &nsdef_begin, |
|
496 &nsdef_end, |
|
497 property->namespace)) |
|
498 { |
|
499 return 1; // OOM |
|
500 } |
|
501 } |
|
502 } |
|
503 |
|
504 if(response->multistatus->proppatch && response->errors) { |
|
505 // in a proppatch request all operations must succeed |
|
506 // if we have an error, the property update status code must be |
|
507 // 424 Failed Dependency |
|
508 status = 424; |
|
509 } |
|
510 |
|
511 // error properties will be added to a separate list |
|
512 if(status != 200) { |
|
513 return msresponse_addproperror(response, property, status); |
|
514 } |
|
515 |
|
516 // add all namespaces used by this property to the nsdef list |
|
517 WebdavNSList *nslist = NULL; |
|
518 if(property->vtype == WS_VALUE_XML_NODE) { |
|
519 // iterate over xml tree and collect all namespaces |
|
520 int err = 0; |
|
521 nslist = wsxml_get_required_namespaces( |
|
522 response->multistatus->sn->pool, |
|
523 property->value.node, |
|
524 &err); |
|
525 if(err) { |
|
526 return 1; // OOM |
|
527 } |
|
528 } else if(property->vtype == WS_VALUE_XML_DATA) { |
|
529 // xml data contains a list of all used namespaces |
|
530 nslist = property->value.data.namespaces; |
|
531 } // other value types don't contain xml namespaces |
|
532 |
|
533 while(nslist) { |
|
534 // only add the namespace to the definitions list, if it isn't a |
|
535 // property namespace, because the prop ns is already added |
|
536 // to the element's def list or global definitions list |
|
537 if(strcmp( |
|
538 (const char*)nslist->namespace->prefix, |
|
539 (const char*)property->namespace->prefix)) |
|
540 { |
|
541 // ns-prefix != property-prefix -> add ns to nsdef |
|
542 if(webdav_nslist_add( |
|
543 sn->pool, |
|
544 &nsdef_begin, |
|
545 &nsdef_end, |
|
546 nslist->namespace)) |
|
547 { |
|
548 return 1; // OOM |
|
549 } |
|
550 } |
|
551 nslist = nslist->next; |
|
552 } |
|
553 |
|
554 // add property to the list |
|
555 if(oklist_add( |
|
556 sn->pool, |
|
557 &response->plist_begin, |
|
558 &response->plist_end, |
|
559 property, |
|
560 nsdef_begin)) |
|
561 { |
|
562 return 1; |
|
563 } |
|
564 return 0; |
|
565 } |
|
566 |
|
567 int msresponse_addproperror( |
|
568 MSResponse *response, |
|
569 WebdavProperty *property, |
|
570 int statuscode) |
|
571 { |
|
572 pool_handle_t *pool = response->multistatus->sn->pool; |
|
573 UcxAllocator *a = session_get_allocator(response->multistatus->sn); |
|
574 |
|
575 response->resource.err++; |
|
576 |
|
577 // MSResponse contains a list of properties for each status code |
|
578 // at first find the list for this status code |
|
579 PropertyErrorList *errlist = NULL; |
|
580 PropertyErrorList *list = response->errors; |
|
581 PropertyErrorList *last = NULL; |
|
582 while(list) { |
|
583 if(list->status == statuscode) { |
|
584 errlist = list; |
|
585 break; |
|
586 } |
|
587 last = list; |
|
588 list = list->next; |
|
589 } |
|
590 |
|
591 if(!errlist) { |
|
592 // no list available for this statuscode |
|
593 PropertyErrorList *newelm = pool_malloc(pool, |
|
594 sizeof(PropertyErrorList)); |
|
595 if(!newelm) { |
|
596 return 1; |
|
597 } |
|
598 newelm->begin = NULL; |
|
599 newelm->end = NULL; |
|
600 newelm->next = NULL; |
|
601 newelm->status = statuscode; |
|
602 |
|
603 if(last) { |
|
604 last->next = newelm; |
|
605 } else { |
|
606 response->errors = newelm; |
|
607 } |
|
608 errlist = newelm; |
|
609 } |
|
610 |
|
611 // we have the list -> add the new element |
|
612 if(webdav_plist_add(pool, &errlist->begin, &errlist->end, property)) { |
|
613 return 1; |
|
614 } |
|
615 return 0; |
|
616 } |
|
617 |
|
618 int msresponse_close(WebdavResource *res) { |
|
619 MSResponse *response = (MSResponse*)res; |
|
620 if(response->closing) { |
|
621 return 0; // close already in progress |
|
622 } |
|
623 response->closing = TRUE; |
|
624 Multistatus *ms = response->multistatus; |
|
625 |
|
626 int ret = REQ_PROCEED; |
|
627 |
|
628 // PROPFIND: |
|
629 // response_close will execute propfind_do of all remaining backends |
|
630 // after that we will have all available properties |
|
631 WebdavOperation *op = ms->response.op; |
|
632 if(op->response_close(op, res)) { |
|
633 ret = REQ_ABORTED; |
|
634 } |
|
635 |
|
636 // add missing properties with status code 404 |
|
637 UcxAllocator *a = session_get_allocator(ms->sn); |
|
638 WebdavPList *pl = ms->response.op->reqprops; |
|
639 while(pl) { |
|
640 sstr_t key = sstrcat_a( |
|
641 a, |
|
642 3, |
|
643 sstr((char*)pl->property->namespace->href), |
|
644 S("\0"), |
|
645 sstr((char*)pl->property->name)); |
|
646 if(!ucx_map_sstr_get(response->properties, key)) { |
|
647 // property was not added to this response |
|
648 if(ms->proppatch) { |
|
649 if(msresponse_addproperror(response, pl->property, 424)) { |
|
650 ret = REQ_ABORTED; |
|
651 break; |
|
652 } |
|
653 } else { |
|
654 if(msresponse_addproperror(response, pl->property, 404)) { |
|
655 ret = REQ_ABORTED; |
|
656 break; |
|
657 } |
|
658 } |
|
659 } |
|
660 |
|
661 pl = pl->next; |
|
662 } |
|
663 |
|
664 if(ms->proppatch && response->errors) { |
|
665 // a proppatch response must succeed entirely |
|
666 // if we have a single error prop, move all props with status 200 |
|
667 // to the error list |
|
668 PropertyOkList *elm = response->plist_begin; |
|
669 PropertyOkList *nextelm; |
|
670 while(elm) { |
|
671 if(msresponse_addproperror(response, elm->property, 424)) { |
|
672 return 1; |
|
673 } |
|
674 nextelm = elm->next; |
|
675 pool_free(response->multistatus->sn->pool, elm); |
|
676 elm = nextelm; |
|
677 } |
|
678 response->plist_begin = NULL; |
|
679 response->plist_end = NULL; |
|
680 } |
|
681 |
|
682 // we don't need the properties anymore |
|
683 ucx_map_free(response->properties); |
|
684 |
|
685 response->resource.isclosed = TRUE; |
|
686 return ret; |
|
687 } |