src/server/webdav/multistatus.c

branch
webdav
changeset 231
4714468b9b7e
parent 230
ca50e1ebdc4d
child 233
c5985d2fc19a
equal deleted inserted replaced
230:ca50e1ebdc4d 231:4714468b9b7e
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2019 Olaf Wintermann. All rights reserved. 4 * Copyright 2020 Olaf Wintermann. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
28 28
29 #include <stdio.h> 29 #include <stdio.h>
30 #include <stdlib.h> 30 #include <stdlib.h>
31 31
32 #include "../daemon/session.h" 32 #include "../daemon/session.h"
33 #include "../util/platform.h"
34
35 #include <ucx/string.h>
33 36
34 #include "operation.h" 37 #include "operation.h"
35 38
36 #include "multistatus.h" 39 #include "multistatus.h"
37 40
48 ms->rq = rq; 51 ms->rq = rq;
49 ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8); 52 ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8);
50 if(!ms->namespaces) { 53 if(!ms->namespaces) {
51 return NULL; 54 return NULL;
52 } 55 }
53 if(ucx_map_cstr_put(ms->namespaces, "D", "DAV:")) { 56 if(ucx_map_cstr_put(ms->namespaces, "D", webdav_dav_namespace())) {
54 return NULL; 57 return NULL;
55 } 58 }
56 return ms; 59 return ms;
60 }
61
62 static int send_xml_root(Multistatus *ms, Writer *out) {
63 writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
64 "<D:multistatus"));
65
66 // write the namespaces definitions
67 // key is the namespace prefix
68 // the map always contains the "DAV:" namespace with the prefix "D"
69 UcxMapIterator i = ucx_map_iterator(ms->namespaces);
70 WSNamespace *ns;
71 UCX_MAP_FOREACH(key, ns, i) {
72 writer_puts(out, S(" xmlns:"));
73 writer_put(out, key.data, key.len);
74 writer_puts(out, S("=\""));
75 writer_puts(out, sstr((char*)ns->href));
76 writer_puts(out, S("\""));
77 }
78
79 writer_puts(out, S(">\n"));
80
81 return out->error;
82 }
83
84 static void send_nsdef(WSNamespace *ns, Writer *out) {
85 writer_puts(out, S(" xmlns:"));
86 writer_puts(out, sstr((char*)ns->prefix));
87 writer_puts(out, S("=\""));
88 writer_puts(out, sstr((char*)ns->href));
89 writer_putc(out, '\"');
90 }
91
92 static int send_property(
93 Multistatus *ms,
94 WebdavProperty *property,
95 WebdavNSList *nsdef,
96 WSBool writeContent,
97 Writer *out)
98 {
99 // write: "<prefix:name"
100 writer_puts(out, S(" <"));
101 writer_puts(out, sstr((char*)property->namespace->prefix));
102 writer_putc(out, ':');
103 writer_puts(out, sstr((char*)property->name));
104
105 // check if the namespace is already defined
106 WSBool need_nsdef = TRUE;
107 WSNamespace *ns = ucx_map_cstr_get(
108 ms->namespaces,
109 (char*)property->namespace->prefix);
110 if(ns && !strcmp(
111 (const char*)ns->href,
112 (const char*)property->namespace->href))
113 {
114 need_nsdef = FALSE; // prefix and href are the same, no need for nsdef
115 }
116
117 // send definition for the element's namespace
118 if(need_nsdef) {
119 send_nsdef(property->namespace, out);
120 }
121
122 // send additional namespace definitions required for the value
123 WebdavNSList *def = nsdef;
124 while(def) {
125 send_nsdef(def->namespace, out);
126 def = def->next;
127 }
128
129 // send xml lang attribute
130 if(property->lang) {
131 writer_puts(out, S(" xml:lang=\""));
132 writer_puts(out, sstr((char*)property->lang));
133 writer_putc(out, '\"');
134 }
135
136 // end property tag and write content
137 if(writeContent) {
138 writer_putc(out, '>');
139
140 // content
141 switch(property->vtype) {
142 case WS_VALUE_NO_TYPE: break;
143 case WS_VALUE_XML_NODE: {
144 // TODO
145 break;
146 }
147 case WS_VALUE_XML_DATA: {
148 // only write data, data->namespaces is already handled
149 writer_put(
150 out,
151 property->value.data->data,
152 property->value.data->length);
153 break;
154 }
155 case WS_VALUE_TEXT: {
156 // asume the text is already escaped
157 writer_put(
158 out,
159 property->value.text.str,
160 property->value.text.length);
161 break;
162 }
163 }
164
165 // end tag
166 writer_putc(out, '<');
167 writer_puts(out, sstr((char*)property->namespace->prefix));
168 writer_putc(out, ':');
169 writer_puts(out, sstr((char*)property->name));
170 writer_putc(out, '>');
171 } else {
172 writer_putc(out, '/>');
173 }
174
175 return out->error;
176 }
177
178 static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) {
179 writer_puts(out, S(" <D:response>\n"
180 " <D:href>"));
181 writer_puts(out, sstr(rp->resource.href));
182 writer_puts(out, S("</href>\n"));
183
184 if(rp->plist_begin) {
185 writer_puts(out, S(" <D:propstat>"
186 " <D:prop>\n"));
187 // send properties
188 PropertyOkList *p = rp->plist_begin;
189 while(p) {
190 if(send_property(ms, p->property, p->nsdef, TRUE, out)) {
191 return out->error;
192 }
193 p = p->next;
194 }
195
196 writer_puts(out, S(" </D:prop>\n"
197 " <D:status>HTTP/1.1 200 OK</D:status>"
198 " </D:propstat>\n"));
199 }
200
201 // send error properties
202 PropertyErrorList *error = rp->errors;
203 while(error) {
204 WebdavPList *errprop = error->begin;
205 while(errprop) {
206 if(send_property(ms, errprop->property, NULL, FALSE, out)) {
207 return out->error;
208 }
209 errprop = errprop->next;
210 }
211 error = error->next;
212 }
213
214 return out->error;
57 } 215 }
58 216
59 int multistatus_send(Multistatus *ms, SYS_NETFD net) { 217 int multistatus_send(Multistatus *ms, SYS_NETFD net) {
60 char buffer[MULTISTATUS_BUFFER_LENGTH]; 218 char buffer[MULTISTATUS_BUFFER_LENGTH];
61 // create a writer, that flushes the buffer when it is filled 219 // create a writer, that flushes the buffer when it is filled
62 Writer writer; 220 Writer writer;
63 Writer *out = &writer; 221 Writer *out = &writer;
64 writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH); 222 writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH);
65 223
66 224 // send the xml root element with namespace defs
225 if(send_xml_root(ms, out)) {
226 return 1;
227 }
228
229 // send response tags
230 MSResponse *response = ms->first;
231 while(response) {
232 if(send_response_tag(ms, response, out)) {
233 return 1;
234 }
235 response = response->next;
236 }
67 237
68 return 0; 238 return 0;
69 } 239 }
70 240
71 WebdavResource * multistatus_addresource( 241 WebdavResource * multistatus_addresource(
178 if(ucx_map_sstr_put(response->properties, key, property)) { 348 if(ucx_map_sstr_put(response->properties, key, property)) {
179 return 1; // OOM 349 return 1; // OOM
180 } 350 }
181 a->free(a->pool, key.ptr); 351 a->free(a->pool, key.ptr);
182 352
353 // list of namespace definitions for this property
354 WebdavNSList *nsdef_begin = NULL;
355 WebdavNSList *nsdef_end = NULL;
356
357 // add namespace of this property to the namespace map
358 // the namespace map will be used for global namespace definitions
359 if(property->namespace->prefix) {
360 WSNamespace *ns = ucx_map_cstr_get(
361 response->multistatus->namespaces,
362 (const char*)property->namespace->prefix);
363 if(!ns) {
364 // prefix is not in use -> we can add the namespace to the ns map
365 int err = ucx_map_cstr_put(
366 response->multistatus->namespaces,
367 (const char*)property->namespace->prefix,
368 property->namespace);
369 if(err) {
370 return 1; // OOM
371 }
372 } else if(
373 strcmp((const char*)property->namespace->href,
374 (const char*)ns->href))
375 {
376 // global namespace != local namespace
377 // therefore we need a namespace definition in this element
378
379 // ns-prefix != property-prefix -> add ns to nsdef
380 if(webdav_nslist_add(
381 sn->pool,
382 &nsdef_begin,
383 &nsdef_end,
384 property->namespace))
385 {
386 return 1; // OOM
387 }
388 }
389 }
390
183 // error properties will be added to a separate list 391 // error properties will be added to a separate list
184 if(status != 200) { 392 if(status != 200) {
185 return msresponse_addproperror(response, property, status); 393 return msresponse_addproperror(response, property, status);
186 } 394 }
187 395
200 } else if(property->vtype == WS_VALUE_XML_DATA) { 408 } else if(property->vtype == WS_VALUE_XML_DATA) {
201 // xml data contains a list of all used namespaces 409 // xml data contains a list of all used namespaces
202 nslist = property->value.data->namespaces; 410 nslist = property->value.data->namespaces;
203 } // other value types don't contain xml namespaces 411 } // other value types don't contain xml namespaces
204 412
205 WebdavNSList *nsdef_begin = NULL;
206 WebdavNSList *nsdef_end = NULL;
207 while(nslist) { 413 while(nslist) {
208 // only add the namespace to the definitions list, if it isn't a 414 // only add the namespace to the definitions list, if it isn't a
209 // property namespace, because the prop ns is already added 415 // property namespace, because the prop ns is already added
210 // to the element's def list or global definitions list 416 // to the element's def list or global definitions list
211 if(strcmp( 417 if(strcmp(
218 &nsdef_begin, 424 &nsdef_begin,
219 &nsdef_end, 425 &nsdef_end,
220 nslist->namespace)) 426 nslist->namespace))
221 { 427 {
222 return 1; // OOM 428 return 1; // OOM
223 } 429 }
224 } 430 }
225 nslist = nslist->next; 431 nslist = nslist->next;
226 } 432 }
227 433
228 // add property to the list 434 // add property to the list

mercurial