src/server/webdav/multistatus.c

branch
webdav
changeset 211
2160585200ac
child 217
8ed14d76db42
equal deleted inserted replaced
210:21274e5950af 211:2160585200ac
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2019 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
34 #include "multistatus.h"
35
36 #define MULTISTATUS_BUFFER_LENGTH 2048
37
38 Multistatus* multistatus_response(Session *sn, Request *rq) {
39 Multistatus *ms = pool_malloc(sn->pool, sizeof(Multistatus));
40 if(!ms) {
41 return NULL;
42 }
43 ZERO(ms, sizeof(Multistatus));
44 ms->response.addresource = multistatus_addresource;
45 ms->sn = sn;
46 ms->rq = rq;
47 ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8);
48 if(!ms->namespaces) {
49 return NULL;
50 }
51 if(ucx_map_cstr_put(ms->namespaces, "D", "DAV:")) {
52 return NULL;
53 }
54 return ms;
55 }
56
57 static int send_xml_root(Multistatus *ms, Writer *out) {
58 writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
59 "<D:multistatus"));
60
61 // write the namespaces definitions
62 // key is the namespace prefix
63 // the map always contains the "DAV:" namespace with the prefix "D"
64 UcxMapIterator i = ucx_map_iterator(ms->namespaces);
65 char *href;
66 UCX_MAP_FOREACH(key, href, i) {
67 writer_puts(out, S(" xmlns:"));
68 writer_put(out, key.data, key.len);
69 writer_puts(out, S("=\""));
70 writer_puts(out, sstr(href));
71 writer_puts(out, S("\""));
72 }
73
74 writer_puts(out, S(">\n"));
75
76 return out->error;
77 }
78
79 static int send_prop_name(
80 Multistatus *ms,
81 WebdavProperty *p,
82 Writer *out,
83 char *prefixbuf,
84 int *prefixbuflen)
85 {
86 int in_prefix_len = *prefixbuflen;
87 *prefixbuflen = 0;
88
89 writer_putc(out, '<');
90
91 const char *prefix = NULL;
92 const char *href = NULL;
93
94 if(p->namespace && p->namespace->href) {
95 href = (const char*)p->namespace->href;
96
97 if(p->namespace->prefix) {
98 // check if there is a namespace with this prefix already defined
99 // and has the same namespace uri
100 prefix = (const char*)p->namespace->prefix;
101 char *nshref = ucx_map_cstr_get(
102 ms->namespaces,
103 (const char*)p->namespace->prefix);
104 if(!strcmp(nshref, href)) {
105 href = NULL; // we don't need a new xmlns def
106 }
107 } else {
108 // generate new prefix
109 for(int i=0;i<1024;i++) {
110 int len = snprintf(prefixbuf, in_prefix_len, "x%d\0", i);
111 char *test = ucx_map_cstr_get(ms->namespaces, prefixbuf);
112 if(!test) {
113 prefix = prefixbuf;
114 *prefixbuflen = len;
115 break; // found an unused prefix
116 }
117 if(!prefix) {
118 // What? Can't find a free prefix?
119 return 1;
120 }
121 }
122 }
123 }
124
125 if(prefix) {
126 writer_put(out, prefix, strlen(prefix));
127 writer_put(out, ":", 1);
128 }
129
130 // write xml element name
131 writer_put(out, (const char*)p->name, strlen((const char*)p->name));
132
133 if(href) {
134 writer_puts(out, S(" xmlns:"));
135 writer_put(out, prefix, strlen(prefix));
136 writer_puts(out, S("=\""));
137 writer_put(out, href, strlen(href));
138 writer_putc(out, '\"');
139 }
140
141 if(p->lang) {
142 writer_puts(out, S(" lang=\""));
143 writer_puts(out, sstr(p->lang));
144 writer_putc(out, '\"');
145 }
146
147 writer_putc(out, '>');
148
149 return out->error;
150 }
151
152
153
154 #define MAX_XML_TREE_DEPTH 128
155 static int send_xml(Multistatus *ms, Writer *out, WSXmlNode *node, WSNamespace *rootns, int depth) {
156 int ret = 0;
157 const char *s;
158 while(node) {
159 switch(node->type) {
160 case XML_ELEMENT_NODE: {
161 writer_putc(out, '<');
162 if(node->ns && node->ns->prefix) {
163 s = (const char*)node->ns->prefix;
164 writer_put(out, s, strlen(s));
165 writer_putc(out, ':');
166 }
167 s = (const char*)node->name;
168 writer_put(out, s, strlen(s));
169
170
171 }
172
173 }
174 node = node->next;
175 }
176
177 return ret;
178 }
179
180 static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) {
181 writer_puts(out, S(" <D:response>\n"
182 " <D:href>"));
183 writer_puts(out, sstr(rp->resource.href));
184 writer_puts(out, S("</href>\n"));
185
186 if(rp->plist_begin) {
187 writer_puts(out, S(" <D:propstat>"
188 " <D:prop>\n"));
189 WebdavPList *p = rp->plist_begin;
190 char prefix[16];
191 while(p) {
192 WebdavProperty *prop = p->property;
193 int prefixlen = 16;
194 if(send_prop_name(ms, prop, out, prefix, &prefixlen)) {
195 return 1;
196 }
197
198 // send content
199
200
201 // send end tag
202 writer_put(out, "<", 1);
203 if(prop->namespace && prop->namespace->href) {
204 const char *pre = NULL;
205 if(prop->namespace->prefix) {
206 pre = (const char*)prop->namespace->prefix;
207 } else if(prefixlen > 0) {
208 pre = prefix;
209 }
210
211 if(pre) {
212 writer_put(out, pre, strlen(pre));
213 writer_put(out, ":", 1);
214 }
215 }
216 writer_put(out, prop->name, strlen(prop->name));
217 writer_put(out, ">", 1);
218
219 if(out->error) {
220 return 1;
221 }
222
223 p = p->next;
224 }
225 writer_puts(out, S(" </D:prop>\n"
226 " <D:status>HTTP/1.1 200 OK</D:status>"
227 " </D:propstat>\n"));
228 }
229
230 return out->error;
231 }
232
233 int multistatus_send(Multistatus *ms, SYS_NETFD net) {
234 char buffer[MULTISTATUS_BUFFER_LENGTH];
235 Writer writer;
236 Writer *out = &writer;
237 writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH);
238
239 // send the xml root element with namespace defs
240 if(send_xml_root(ms, out)) {
241 return 1;
242 }
243
244 // send response tags
245 MSResponse *response = ms->first;
246 while(response) {
247 if(send_response_tag(ms, response, out)) {
248 return 1;
249 }
250 response = response->next;
251 }
252
253 return 0;
254 }
255
256
257 WebdavResource * multistatus_addresource(
258 WebdavResponse *response,
259 const char *path)
260 {
261 Multistatus *ms = (Multistatus*)response;
262 MSResponse *res = pool_malloc(ms->sn->pool, sizeof(MSResponse));
263 if(!res) {
264 return NULL;
265 }
266 ZERO(res, sizeof(MSResponse));
267
268 res->resource.addproperty = msresponse_addproperty;
269
270 res->multistatus = ms;
271 res->errors = NULL;
272 res->end = 0;
273
274 if(ms->current) {
275 ms->current->end = 1;
276 ms->current->next = res;
277 } else {
278 ms->first = res;
279 }
280 ms->current = res;
281
282 return (WebdavResource*)res;
283 }
284
285 int msresponse_addproperty(
286 WebdavResource *res,
287 WebdavProperty *property,
288 int status)
289 {
290 MSResponse *response = (MSResponse*)res;
291 if(response->end) {
292 log_ereport(
293 LOG_WARN,
294 "%s",
295 "webdav: cannot add property to closed response tag");
296 return 0;
297 }
298
299 // add namespace of this property to the namespace map
300 if(property->namespace && property->namespace->prefix) {
301 char *ns = ucx_map_cstr_get(
302 response->multistatus->namespaces,
303 (const char*)property->namespace->prefix);
304 if(!ns) {
305 int err = ucx_map_cstr_put(
306 response->multistatus->namespaces,
307 (const char*)property->namespace->prefix,
308 property->namespace->href);
309 if(err) {
310 return 1;
311 }
312 }
313 }
314
315 if(status != 200) {
316 return msresponse_addproperror(response, property, status);
317 }
318
319 // add property to the list
320 WebdavPList *listelm = pool_malloc(
321 response->multistatus->sn->pool,
322 sizeof(WebdavPList));
323 if(!listelm) {
324 return 1;
325 }
326
327 listelm->property = property;
328 listelm->next = NULL;
329
330 if(response->plist_end) {
331 response->plist_end->next = listelm;
332 } else {
333 response->plist_begin = listelm;
334 }
335 response->plist_end = listelm;
336 return 0;
337 }
338
339 int msresponse_addproperror(
340 MSResponse *response,
341 WebdavProperty *property,
342 int statuscode)
343 {
344 pool_handle_t *pool = response->multistatus->sn->pool;
345 UcxAllocator *a = session_get_allocator(response->multistatus->sn);
346
347 // MSResponse contains a list of properties for each status code
348 // at first find the list for this status code
349 PropertyErrorList *errlist = NULL;
350 PropertyErrorList *list = response->errors;
351 PropertyErrorList *last = NULL;
352 while(list) {
353 if(list->status == statuscode) {
354 errlist = list;
355 break;
356 }
357 last = list;
358 list = list->next;
359 }
360
361 if(!errlist) {
362 // no list available for this statuscode
363 PropertyErrorList *newelm = pool_malloc(pool,
364 sizeof(PropertyErrorList));
365 if(!newelm) {
366 return 1;
367 }
368 newelm->begin = NULL;
369 newelm->end = NULL;
370 newelm->next = NULL;
371 newelm->status = statuscode;
372
373 if(last) {
374 last->next = newelm;
375 } else {
376 response->errors = newelm;
377 }
378 errlist = newelm;
379 }
380
381 // we have the list -> add the new element
382 UcxList *newlistelm = ucx_list_append_a(a, errlist->end, property);
383 if(!newlistelm) {
384 return 1;
385 }
386 errlist->end = newlistelm;
387 if(!errlist->begin) {
388 errlist->begin = newlistelm;
389 }
390 return 0;
391 }

mercurial