29 #include <stdio.h> |
29 #include <stdio.h> |
30 #include <stdlib.h> |
30 #include <stdlib.h> |
31 #include <string.h> |
31 #include <string.h> |
32 |
32 |
33 #include "webdav.h" |
33 #include "webdav.h" |
34 #include <ucx/list.h> |
|
35 #include <ucx/string.h> |
|
36 #include "../util/pool.h" |
|
37 #include "../util/pblock.h" |
|
38 #include "../util/date.h" |
|
39 #include "../util/util.h" |
|
40 |
|
41 #include "../daemon/vfs.h" |
|
42 #include "../daemon/protocol.h" |
|
43 |
|
44 #include "davparser.h" |
|
45 #include "parser.h" |
|
46 #include "persistence.h" |
|
47 |
|
48 static UcxMap *pmgr_map; // char*, PersistenceManager |
|
49 |
|
50 int webdav_init(pblock *pb, Session *sn, Request *rq) { |
|
51 pmgr_map = ucx_map_new(8); |
|
52 PersistenceManager *defaultmgr = create_property_backend(); |
|
53 ucx_map_cstr_put(pmgr_map, "default", defaultmgr); |
|
54 return REQ_PROCEED; |
|
55 } |
|
56 |
|
57 void webdav_add_persistence_manager(char *name, PersistenceManager *mgr) { |
|
58 if(!pmgr_map) { |
|
59 webdav_init(NULL, NULL, NULL); |
|
60 } |
|
61 ucx_map_cstr_put(pmgr_map, name, mgr); |
|
62 } |
|
63 |
|
64 int webdav_setcollection(pblock *pb, Session *sn, Request *rq) { |
|
65 //char *name = pblock_findkeyval(pb_key_name, pb); |
|
66 char *db = pblock_findval("db", pb); |
|
67 |
|
68 if(!db) { |
|
69 db = "default"; |
|
70 } |
|
71 |
|
72 // setup DavCollection |
|
73 DavCollection *dav = pool_malloc(sn->pool, sizeof(DavCollection)); |
|
74 dav->mgr = ucx_map_cstr_get(pmgr_map, db); |
|
75 rq->davCollection = dav; |
|
76 |
|
77 return REQ_NOACTION; |
|
78 } |
|
79 |
|
80 int webdav_service(pblock *pb, Session *sn, Request *rq) { |
|
81 char *method = pblock_findkeyval(pb_key_method, rq->reqpb); |
|
82 if(method == NULL) { |
|
83 return REQ_ABORTED; |
|
84 } |
|
85 |
|
86 if(!strcmp(method, "PROPFIND")) { |
|
87 return webdav_propfind(pb, sn, rq); |
|
88 } else if(!strcmp(method, "PROPPATCH")) { |
|
89 return webdav_proppatch(pb, sn, rq); |
|
90 } else if(!strcmp(method, "PUT")) { |
|
91 return webdav_put(pb, sn, rq); |
|
92 } else if(!strcmp(method, "DELETE")) { |
|
93 return webdav_delete(pb, sn, rq); |
|
94 } else if(!strcmp(method, "MKCOL")) { |
|
95 return webdav_mkcol(pb, sn, rq); |
|
96 } |
|
97 |
|
98 return REQ_NOACTION; |
|
99 } |
|
100 |
|
101 int webdav_put(pblock *pb, Session *sn, Request *rq) { |
|
102 int length = 0; |
|
103 |
|
104 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); |
|
105 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); |
|
106 if(ctlen) { |
|
107 length = atoi(ctlen); |
|
108 } else { |
|
109 /* invalid request */ |
|
110 printf("invalid request\n"); |
|
111 return REQ_ABORTED; |
|
112 } |
|
113 |
|
114 printf("PUT length: %d\n", length); |
|
115 |
|
116 //int status = 201; |
|
117 //FILE *out = fopen(ppath, "w"); |
|
118 |
|
119 VFSContext *vfs = vfs_request_context(sn, rq); |
|
120 SYS_FILE out = vfs_openWO(vfs, ppath); |
|
121 if(out == NULL) { |
|
122 fprintf(stderr, "vfs_openWO(%s, \"w\") failed\n", ppath); |
|
123 //protocol_status(sn, rq, 500, NULL); |
|
124 return REQ_ABORTED; |
|
125 } |
|
126 |
|
127 if(length > 0) { |
|
128 size_t len = (length > 4096) ? (4096) : (length); |
|
129 char *buffer = pool_malloc(sn->pool, len); |
|
130 |
|
131 int r; |
|
132 int r2 = 0; |
|
133 while(r2 < length) { |
|
134 r = netbuf_getbytes(sn->inbuf, buffer, len); |
|
135 if(r == NETBUF_EOF) { |
|
136 break; |
|
137 } |
|
138 system_fwrite(out, buffer, r); |
|
139 |
|
140 r2 += r; |
|
141 } |
|
142 |
|
143 pool_free(sn->pool, buffer); |
|
144 } else { |
|
145 |
|
146 } |
|
147 vfs_close(out); |
|
148 |
|
149 protocol_status(sn, rq, 201, NULL); |
|
150 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
|
151 pblock_nninsert("content-length", 0, rq->srvhdrs); |
|
152 http_start_response(sn, rq); |
|
153 |
|
154 return REQ_PROCEED; |
|
155 } |
|
156 |
|
157 int webdav_delete(pblock *pb, Session *sn, Request *rq) { |
|
158 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); |
|
159 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); |
|
160 |
|
161 VFSContext *vfs = vfs_request_context(sn, rq); |
|
162 |
|
163 int status = 204; |
|
164 |
|
165 struct stat st; |
|
166 if(vfs_stat(vfs, ppath, &st)) { |
|
167 return REQ_ABORTED; |
|
168 } |
|
169 |
|
170 if(!strcmp(uri, "/")) { |
|
171 status = 403; |
|
172 } else if((st.st_mode & S_IFDIR) == S_IFDIR) { |
|
173 if(rmdir(ppath) != 0) { |
|
174 /* ERROR */ |
|
175 status = 403; |
|
176 } |
|
177 } else { |
|
178 if(vfs_unlink(vfs, ppath)) { |
|
179 /* ERROR */ |
|
180 return REQ_ABORTED; |
|
181 } |
|
182 } |
|
183 |
|
184 protocol_status(sn, rq, status, NULL); |
|
185 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
|
186 pblock_nninsert("content-length", 0, rq->srvhdrs); |
|
187 http_start_response(sn, rq); |
|
188 |
|
189 return REQ_PROCEED; |
|
190 } |
|
191 |
|
192 int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { |
|
193 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); |
|
194 |
|
195 VFSContext *vfs = vfs_request_context(sn, rq); |
|
196 if(vfs_mkdir(vfs, ppath)) { |
|
197 return REQ_ABORTED; |
|
198 } |
|
199 |
|
200 protocol_status(sn, rq, 201, NULL); |
|
201 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
|
202 pblock_nninsert("content-length", 0, rq->srvhdrs); |
|
203 http_start_response(sn, rq); |
|
204 |
|
205 return REQ_ABORTED; |
|
206 } |
|
207 |
|
208 int webdav_copy(pblock *pb, Session *sn, Request *rq) { |
|
209 return REQ_ABORTED; |
|
210 } |
|
211 |
|
212 int webdav_move(pblock *pb, Session *sn, Request *rq) { |
|
213 return REQ_ABORTED; |
|
214 } |
|
215 |
|
216 int webdav_propfind(pblock *pb, Session *sn, Request *rq) { |
|
217 /* TODO: clean up if errors occurs */ |
|
218 |
|
219 /* Get request body which contains the webdav XML request */ |
|
220 char *xml_body; |
|
221 size_t xml_len = 0; |
|
222 |
|
223 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); |
|
224 if(ctlen) { |
|
225 xml_len = atoi(ctlen); |
|
226 } else { |
|
227 /* invalid request */ |
|
228 printf("invalid request\n"); |
|
229 return REQ_ABORTED; |
|
230 } |
|
231 |
|
232 xml_body = pool_malloc(sn->pool, xml_len + 1); |
|
233 if(xml_body == NULL) { |
|
234 return REQ_ABORTED; |
|
235 } |
|
236 xml_body[xml_len] = 0; |
|
237 if(!xml_body) { |
|
238 /* server error */ |
|
239 printf("server error\n"); |
|
240 return REQ_ABORTED; |
|
241 } |
|
242 |
|
243 /* get request body */ |
|
244 int r = 0; |
|
245 char *xb = xml_body; |
|
246 size_t xl = xml_len; |
|
247 while((r = netbuf_getbytes(sn->inbuf, xb, xl)) != NETBUF_EOF) { |
|
248 xb += r; |
|
249 xl -= r; |
|
250 } |
|
251 |
|
252 /* |
|
253 * get requested properties and initialize some stuff |
|
254 */ |
|
255 DavCollection *collection = rq->davCollection; |
|
256 if(!collection) { |
|
257 collection = pool_malloc(sn->pool, sizeof(DavCollection)); |
|
258 collection->mgr = ucx_map_cstr_get(pmgr_map, "default"); |
|
259 } |
|
260 |
|
261 PropfindRequest *davrq = dav_parse_propfind2(sn, rq, xml_body, xml_len); |
|
262 davrq->sn = sn; |
|
263 davrq->rq = rq; |
|
264 davrq->out = sbuf_new(512); |
|
265 davrq->persistencemgr = collection->mgr; |
|
266 |
|
267 /* begin multistatus response */ |
|
268 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); |
|
269 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); |
|
270 davrq->uri = uri; |
|
271 davrq->path = ppath; |
|
272 |
|
273 VFSContext *vfs = vfs_request_context(sn, rq); |
|
274 |
|
275 struct stat st; |
|
276 if(vfs_stat(vfs, ppath, &st) != 0) { |
|
277 return REQ_ABORTED; |
|
278 } |
|
279 |
|
280 // begin propfind |
|
281 davrq->isdir = S_ISDIR(st.st_mode); |
|
282 davrq->persistencemgr->propfind_begin(davrq->persistencemgr, davrq); |
|
283 |
|
284 // create the response for the requested resource |
|
285 dav_resource_response(davrq, sstr(ppath), sstr(uri)); |
|
286 |
|
287 /* |
|
288 * if the requested webdav resource(file) is a directory, we create |
|
289 * a response for every child |
|
290 */ |
|
291 if(S_ISDIR(st.st_mode)) { |
|
292 VFS_DIR dir = vfs_opendir(vfs, ppath); |
|
293 if(dir == NULL) { |
|
294 return REQ_ABORTED; |
|
295 } |
|
296 |
|
297 VFS_ENTRY entry; |
|
298 while(vfs_readdir(dir, &entry)) { |
|
299 sstr_t newpath = util_path_append(sn->pool, ppath, entry.name); |
|
300 sstr_t newuri = util_path_append(sn->pool, uri, entry.name); |
|
301 // child response |
|
302 dav_resource_response(davrq, newpath, newuri); |
|
303 } |
|
304 } |
|
305 |
|
306 // end propfind |
|
307 davrq->persistencemgr->propfind_begin(davrq->persistencemgr, davrq); |
|
308 |
|
309 // end xml |
|
310 sbuf_puts(davrq->out, "</D:multistatus>\n"); |
|
311 |
|
312 //printf("%s\n", davrq->out->ptr); |
|
313 |
|
314 // write xml response header |
|
315 sbuf_t *out = sbuf_new(256); |
|
316 sbuf_puts(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); |
|
317 sbuf_puts(out, "<D:multistatus"); |
|
318 UcxMapIterator nsiter = ucx_map_iterator(davrq->nsmap->map); |
|
319 XmlNs *ns; |
|
320 UCX_MAP_FOREACH(key, ns, nsiter) { |
|
321 sbuf_puts(out, " xmlns:"); |
|
322 sbuf_puts(out, ns->prefix); |
|
323 sbuf_puts(out, "=\""); |
|
324 sbuf_puts(out, ns->xmlns); |
|
325 sbuf_puts(out, "\""); |
|
326 } |
|
327 sbuf_puts(out, ">\n"); |
|
328 |
|
329 // send the xml response to the client |
|
330 protocol_status(sn, rq, 207, "Multi Status"); |
|
331 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
|
332 pblock_nvinsert("content-type", "text/xml", rq->srvhdrs); |
|
333 pblock_nninsert( |
|
334 "content-length", |
|
335 out->length + davrq->out->length, |
|
336 rq->srvhdrs); |
|
337 |
|
338 http_start_response(sn, rq); |
|
339 |
|
340 // write content |
|
341 size_t nr; |
|
342 nr = net_write(sn->csd, out->ptr, out->length); |
|
343 nr = net_write(sn->csd, davrq->out->ptr, davrq->out->length); |
|
344 |
|
345 sbuf_free(out); |
|
346 dav_free_propfind(davrq); |
|
347 |
|
348 return REQ_PROCEED; |
|
349 } |
|
350 |
|
351 int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { |
|
352 printf("webdav-proppatch\n"); |
|
353 /* TODO: clean up if errors occurs */ |
|
354 /* TODO: this is the same code as in propfind */ |
|
355 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); |
|
356 if(uri == NULL) { |
|
357 /* TODO: error */ |
|
358 return REQ_ABORTED; |
|
359 } |
|
360 |
|
361 // Get request body which contains the webdav XML request |
|
362 char *xml_body; |
|
363 size_t xml_len = 0; |
|
364 |
|
365 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); |
|
366 if(ctlen) { |
|
367 xml_len = atoi(ctlen); |
|
368 } else { |
|
369 // invalid request |
|
370 printf("invalid request\n"); |
|
371 return REQ_ABORTED; |
|
372 } |
|
373 |
|
374 xml_body = pool_malloc(sn->pool, xml_len + 1); |
|
375 if(xml_body == NULL) { |
|
376 return REQ_ABORTED; |
|
377 } |
|
378 xml_body[xml_len] = 0; |
|
379 if(!xml_body) { |
|
380 // server error |
|
381 printf("server error\n"); |
|
382 return REQ_ABORTED; |
|
383 } |
|
384 |
|
385 // get request body |
|
386 int r = 0; |
|
387 char *xb = xml_body; |
|
388 size_t xl = xml_len; |
|
389 while((r = netbuf_getbytes(sn->inbuf, xb, xl)) != NETBUF_EOF) { |
|
390 xb += r; |
|
391 xl -= r; |
|
392 } |
|
393 |
|
394 /* |
|
395 * parse the xml request and create the proppatch object |
|
396 */ |
|
397 DavCollection *collection = rq->davCollection; |
|
398 if(!collection) { |
|
399 collection = pool_malloc(sn->pool, sizeof(DavCollection)); |
|
400 collection->mgr = ucx_map_cstr_get(pmgr_map, "default"); |
|
401 } |
|
402 |
|
403 ProppatchRequest *davrq = dav_parse_proppatch(sn, rq, xml_body, xml_len); |
|
404 davrq->sn = sn; |
|
405 davrq->rq = rq; |
|
406 davrq->out = sbuf_new(512); |
|
407 davrq->backend = collection->mgr; |
|
408 davrq->propstat = propstat_create(sn->pool); |
|
409 |
|
410 |
|
411 /* |
|
412 * begin multistatus response |
|
413 * |
|
414 * The webdav backend does the most work. The backend->proppatch function |
|
415 * modifies the properties and adds status informations to the propstat |
|
416 * member of the ProppatchRequest. All we have to do here is to create |
|
417 * the xml response and send it to the client |
|
418 */ |
|
419 |
|
420 /* write xml response header */ |
|
421 sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); |
|
422 //sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n"); |
|
423 sbuf_puts(davrq->out, "<D:multistatus"); |
|
424 UcxMapIterator nsiter = ucx_map_iterator(davrq->nsmap->map); |
|
425 XmlNs *ns; |
|
426 UCX_MAP_FOREACH(key, ns, nsiter) { |
|
427 sbuf_puts(davrq->out, " xmlns:"); |
|
428 sbuf_puts(davrq->out, ns->prefix); |
|
429 sbuf_puts(davrq->out, "=\""); |
|
430 sbuf_puts(davrq->out, ns->xmlns); |
|
431 sbuf_puts(davrq->out, "\""); |
|
432 } |
|
433 sbuf_puts(davrq->out, ">\n"); |
|
434 |
|
435 sbuf_puts(davrq->out, "<D:response>\n<D:href>"); |
|
436 sbuf_puts(davrq->out, uri); |
|
437 sbuf_puts(davrq->out, "</D:href>\n"); |
|
438 |
|
439 /* do proppatch operation */ |
|
440 davrq->backend->proppatch(davrq->backend, davrq); |
|
441 |
|
442 propstat_write(davrq->propstat, davrq->out, 0); |
|
443 |
|
444 sbuf_puts(davrq->out, "</D:response>\n"); |
|
445 sbuf_puts(davrq->out, "</D:multistatus>\n"); |
|
446 |
|
447 |
|
448 /* send the xml response to the client */ |
|
449 protocol_status(sn, rq, 207, "Multi Status"); |
|
450 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
|
451 pblock_nvinsert("content-type", "text/xml", rq->srvhdrs); |
|
452 pblock_nninsert("content-length", davrq->out->length, rq->srvhdrs); |
|
453 |
|
454 //pblock_nvinsert("connection", "close", rq->srvhdrs); |
|
455 http_start_response(sn, rq); |
|
456 |
|
457 net_write(sn->csd, davrq->out->ptr, davrq->out->length); |
|
458 |
|
459 dav_free_proppatch(davrq); |
|
460 |
|
461 return REQ_PROCEED; |
|
462 } |
|
463 |
|
464 void dav_resource_response(PropfindRequest *davrq, sstr_t path, sstr_t uri) { |
|
465 printf("dav_resource_response %s %s\n", sstrdup(path).ptr, sstrdup(uri).ptr); |
|
466 |
|
467 sbuf_puts(davrq->out, "<D:response>\n"); |
|
468 sbuf_puts(davrq->out, "<D:href>"); |
|
469 sbuf_append(davrq->out, uri); |
|
470 sbuf_puts(davrq->out, "</D:href>\n"); |
|
471 |
|
472 if(davrq->persistencemgr->vfs_props) { |
|
473 // get some DAV properties from the file system |
|
474 dav_rq_propfind(davrq->persistencemgr, davrq, path.ptr); |
|
475 } |
|
476 davrq->persistencemgr->propfind(davrq->persistencemgr, davrq, path.ptr); |
|
477 |
|
478 if(davrq->prop) { |
|
479 /* |
|
480 * there are some properties written, so we close the |
|
481 * prop and propstat tag |
|
482 */ |
|
483 sbuf_puts(davrq->out, "</D:prop>\n"); |
|
484 sbuf_puts(davrq->out, "<D:status>HTTP/1.1 200 OK</D:status>\n"); |
|
485 sbuf_puts(davrq->out, "</D:propstat>\n"); |
|
486 } |
|
487 |
|
488 if(davrq->notFoundProps != NULL) { |
|
489 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); |
|
490 DAV_FOREACH(elm, davrq->notFoundProps) { |
|
491 DavProperty *prop = (DavProperty*)elm->data; |
|
492 sbuf_put(davrq->out, '<'); |
|
493 sbuf_puts(davrq->out, prop->xmlns->prefix); |
|
494 sbuf_put(davrq->out, ':'); |
|
495 |
|
496 sbuf_puts(davrq->out, prop->name); |
|
497 sbuf_puts(davrq->out, " />\n"); |
|
498 } |
|
499 sbuf_puts(davrq->out, "</D:prop>\n"); |
|
500 sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n"); |
|
501 sbuf_puts(davrq->out, "</D:propstat>\n"); |
|
502 } |
|
503 |
|
504 sbuf_puts(davrq->out, "</D:response>\n"); |
|
505 |
|
506 /* reset */ |
|
507 davrq->prop = 0; |
|
508 davrq->notFoundProps = NULL; |
|
509 davrq->forbiddenProps = NULL; |
|
510 |
|
511 } |
|
512 |
|
513 void dav_propfind_add_str_prop( |
|
514 PropfindRequest *davrq, |
|
515 DavProperty* prop, |
|
516 char *str, |
|
517 size_t len) |
|
518 { |
|
519 if(!davrq->prop) { |
|
520 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); |
|
521 davrq->prop = 1; |
|
522 } |
|
523 |
|
524 sbuf_put(davrq->out, '<'); |
|
525 sbuf_puts(davrq->out, prop->xmlns->prefix); |
|
526 sbuf_put(davrq->out, ':'); |
|
527 sbuf_puts(davrq->out, prop->name); |
|
528 sbuf_put(davrq->out, '>'); |
|
529 |
|
530 sbuf_append(davrq->out, sstrn(str, len)); |
|
531 |
|
532 sbuf_puts(davrq->out, "</"); |
|
533 sbuf_puts(davrq->out, prop->xmlns->prefix); |
|
534 sbuf_put(davrq->out, ':'); |
|
535 sbuf_puts(davrq->out, prop->name); |
|
536 sbuf_puts(davrq->out, ">\n"); |
|
537 } |
|
538 |
|
539 void dav_propfind_add_prop_error( |
|
540 PropfindRequest *davrq, |
|
541 DavProperty *prop, |
|
542 int error) |
|
543 { |
|
544 // TODO: different errors |
|
545 davrq->notFoundProps = ucx_list_append(davrq->notFoundProps, prop); |
|
546 } |
|
547 |
34 |
548 |
35 |
549 |
|
550 |
|
551 /* WebDAV Default Backend */ |
|
552 static PersistenceManager dav_file_backend = { |
|
553 dav_rq_propfind_begin, |
|
554 dav_rq_propfind_end, |
|
555 dav_rq_propfind, |
|
556 dav_rq_proppatch, |
|
557 0 |
|
558 }; |
|
559 |
|
560 PersistenceManager* create_property_backend() { |
|
561 return &dav_file_backend; |
|
562 } |
|
563 |
|
564 void dav_rq_propfind_begin(PersistenceManager *mgr, PropfindRequest *rq) { |
|
565 |
|
566 } |
|
567 |
|
568 void dav_rq_propfind_end(PersistenceManager *mgr, PropfindRequest *rq) { |
|
569 |
|
570 } |
|
571 |
|
572 void dav_rq_propfind(PersistenceManager *b, PropfindRequest *rq ,char *path) { |
|
573 struct stat st; |
|
574 if(stat(path, &st) != 0) { |
|
575 perror("dav_be_propfind"); |
|
576 fprintf(stderr, "Cannot get stat of file: %s\n", path); |
|
577 } |
|
578 |
|
579 if(rq->allprop) { |
|
580 DavProperty prop; |
|
581 prop.xmlns = xmlnsmap_get(rq->nsmap, "DAV:"); |
|
582 |
|
583 prop.name = "resourcetype"; |
|
584 if(S_ISDIR(st.st_mode)) { |
|
585 dav_propfind_add_str_prop(rq, &prop, "<D:collection/>", 15); |
|
586 } else { |
|
587 dav_propfind_add_str_prop(rq, &prop, NULL, 0); |
|
588 } |
|
589 |
|
590 if(!S_ISDIR(st.st_mode)) { |
|
591 prop.name = "getcontentlength"; |
|
592 char buf[32]; |
|
593 size_t n = snprintf(buf, 32, "%jd", st.st_size); |
|
594 dav_propfind_add_str_prop(rq, &prop, buf, n); |
|
595 } |
|
596 |
|
597 prop.name = "getlastmodified"; |
|
598 |
|
599 sstr_t s = date_format_http(st.st_mtime, rq->sn->pool); |
|
600 dav_propfind_add_str_prop(rq, &prop, s.ptr, s.length); |
|
601 |
|
602 prop.name = "creationdate"; |
|
603 s = date_format_iso8601(st.st_ctime, rq->sn->pool); |
|
604 dav_propfind_add_str_prop(rq, &prop, s.ptr, s.length); |
|
605 |
|
606 return; |
|
607 } |
|
608 |
|
609 DAV_FOREACH(elm, rq->properties) { |
|
610 DavProperty *prop = (DavProperty*)elm->data; |
|
611 |
|
612 char *s = prop->name; |
|
613 if(!strcmp(s, "resourcetype")) { |
|
614 if(S_ISDIR(st.st_mode)) { |
|
615 dav_propfind_add_str_prop(rq, prop, "<D:collection/>", 15); |
|
616 } else { |
|
617 dav_propfind_add_str_prop(rq, prop, NULL, 0); |
|
618 } |
|
619 } else if(!strcmp(s, "getcontentlength") && !S_ISDIR(st.st_mode)) { |
|
620 char buf[32]; |
|
621 size_t n = snprintf(buf, 32, "%jd", st.st_size); |
|
622 dav_propfind_add_str_prop(rq, prop, buf, n); |
|
623 } else if(!strcmp(s, "getlastmodified")) { |
|
624 sstr_t s = date_format_http(st.st_mtime, rq->sn->pool); |
|
625 dav_propfind_add_str_prop(rq, prop, s.ptr, s.length); |
|
626 } else if(!strcmp(s, "creationdate")) { |
|
627 sstr_t s = date_format_iso8601(st.st_ctime, rq->sn->pool); |
|
628 dav_propfind_add_str_prop(rq, prop, s.ptr, s.length); |
|
629 } else { |
|
630 dav_propfind_add_prop_error(rq, prop, 404); |
|
631 } |
|
632 } |
|
633 } |
|
634 |
|
635 void dav_rq_proppatch(PersistenceManager *b, ProppatchRequest *rq) { |
|
636 DAV_FOREACH(p, rq->setProps) { |
|
637 XmlElement *prop = (XmlElement*)p->data; |
|
638 propstat_add(rq->propstat, 403, prop); |
|
639 } |
|
640 |
|
641 DAV_FOREACH(p, rq->removeProps) { |
|
642 XmlElement *prop = (XmlElement*)p->data; |
|
643 propstat_add(rq->propstat, 403, prop); |
|
644 } |
|
645 } |
|
646 |
|
647 |
|
648 |
|
649 /*---------------------------------- utils ----------------------------------*/ |
|
650 |
|
651 /* XmlNsMap */ |
|
652 |
|
653 XmlNsMap* xmlnsmap_create(pool_handle_t *pool) { |
|
654 XmlNsMap *map = pool_malloc(pool, sizeof(XmlNsMap)); |
|
655 UcxMap *uxm = ucx_map_new(16); // TODO: use pool for map |
|
656 if(map == NULL || uxm == NULL) { |
|
657 return NULL; |
|
658 } |
|
659 map->map = uxm; |
|
660 map->pool = pool; |
|
661 map->num = 0; |
|
662 |
|
663 // create DAV: namespace |
|
664 XmlNs *ns = pool_malloc(map->pool, sizeof(XmlNs)); |
|
665 ns->xmlns = "DAV:"; |
|
666 ns->prefix = "D"; |
|
667 ns->nslen = 4; |
|
668 ns->prelen = 1; |
|
669 |
|
670 ucx_map_cstr_put(uxm, "DAV:", ns); |
|
671 |
|
672 return map; |
|
673 } |
|
674 |
|
675 void xmlnsmap_free(XmlNsMap *map) { |
|
676 ucx_map_free(map->map); |
|
677 } |
|
678 |
|
679 XmlNs* xmlnsmap_put(XmlNsMap *map, char *ns) { |
|
680 if(!ns) { |
|
681 return NULL; |
|
682 } |
|
683 |
|
684 XmlNs *xmlns = xmlnsmap_get(map, ns); |
|
685 if(xmlns != NULL) { |
|
686 return xmlns; |
|
687 } |
|
688 |
|
689 xmlns = pool_malloc(map->pool, sizeof(XmlNs)); |
|
690 if(xmlns == NULL) { |
|
691 return NULL; |
|
692 } |
|
693 |
|
694 sstr_t newns = sstrdup(sstr(ns)); |
|
695 |
|
696 xmlns->xmlns = newns.ptr; |
|
697 xmlns->nslen = newns.length; |
|
698 |
|
699 xmlns->prefix = pool_calloc(map->pool, 1, 8); |
|
700 xmlns->prelen = snprintf(xmlns->prefix, 7, "x%d", map->num); |
|
701 |
|
702 ucx_map_cstr_put(map->map, ns, xmlns); // TODO: check return value |
|
703 map->num++; |
|
704 return xmlns; |
|
705 } |
|
706 |
|
707 XmlNs* xmlnsmap_get(XmlNsMap *map, char *ns) { |
|
708 return ucx_map_cstr_get(map->map, ns); |
|
709 } |
|
710 |
|
711 |
|
712 /* XmlElement */ |
|
713 |
|
714 void xmlelm_add_child(XmlElement *parent, XmlElement *child) { |
|
715 if(parent->ctlen == 0) { |
|
716 parent->content = ucx_list_append(parent->content, child); |
|
717 } |
|
718 } |
|
719 |
|
720 void xmlelm_write(XmlElement *elm, Buffer *out, int wv) { |
|
721 sbuf_append(out, sstrn("<", 1)); |
|
722 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); |
|
723 sbuf_append(out, sstrn(":", 1)); |
|
724 sbuf_append(out, sstr(elm->name)); |
|
725 |
|
726 if(wv) { |
|
727 if(elm->ctlen == 0) { |
|
728 if(elm->content == NULL) { |
|
729 sbuf_append(out, sstrn(" />", 3)); |
|
730 } else { |
|
731 sbuf_append(out, sstrn(">", 1)); |
|
732 DAV_FOREACH(pr, (UcxList*)elm->content) { |
|
733 xmlelm_write((XmlElement*)pr->data, out, 1); |
|
734 } |
|
735 sbuf_append(out, sstrn("</", 2)); |
|
736 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); |
|
737 sbuf_append(out, sstrn(":", 1)); |
|
738 sbuf_append(out, sstr(elm->name)); |
|
739 sbuf_append(out, sstrn(">", 1)); |
|
740 } |
|
741 } else { |
|
742 sbuf_append(out, sstrn(" />", 3)); |
|
743 sbuf_append(out, sstrn((char*)elm->content, elm->ctlen)); |
|
744 sbuf_append(out, sstrn("</", 2)); |
|
745 sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); |
|
746 sbuf_append(out, sstrn(":", 1)); |
|
747 sbuf_append(out, sstr(elm->name)); |
|
748 sbuf_append(out, sstrn(">", 1)); |
|
749 } |
|
750 } else { |
|
751 sbuf_append(out, sstrn(" />", 3)); |
|
752 } |
|
753 } |
|
754 |
|
755 |
|
756 /* PropstatMap */ |
|
757 |
|
758 Propstat* propstat_create(pool_handle_t *pool) { |
|
759 Propstat *propstat = (Propstat*)pool_malloc(pool, sizeof(Propstat)); |
|
760 propstat->map = ucx_map_new(8); |
|
761 propstat->okprop = NULL; |
|
762 propstat->pool = pool; |
|
763 return propstat; |
|
764 } |
|
765 |
|
766 void propstat_add(Propstat *propstat, int status, XmlElement *prop) { |
|
767 if(status == 200) { |
|
768 propstat->okprop = ucx_list_append(propstat->okprop, prop); |
|
769 } else { |
|
770 UcxKey key; |
|
771 key.data = &status; |
|
772 key.len = sizeof(int); |
|
773 |
|
774 UcxList *list = ucx_map_get(propstat->map, key); |
|
775 list = ucx_list_append(list, prop); |
|
776 |
|
777 ucx_map_put(propstat->map, key, list); |
|
778 } |
|
779 } |
|
780 |
|
781 void propstat_write(Propstat *propstat, Buffer *out, int wv) { |
|
782 if(propstat->okprop) { |
|
783 sbuf_puts(out, "<D:propstat>\n<D:prop>\n"); |
|
784 |
|
785 DAV_FOREACH(prop, propstat->okprop) { |
|
786 xmlelm_write((XmlElement*)prop->data, out, wv); |
|
787 } |
|
788 |
|
789 sbuf_puts(out, "\n</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n"); |
|
790 sbuf_puts(out, "</D:propstat>\n"); |
|
791 } |
|
792 |
|
793 UcxMapIterator iter = ucx_map_iterator(propstat->map); |
|
794 UcxList *proplist; |
|
795 UCX_MAP_FOREACH(key, proplist, iter) { |
|
796 if(proplist) { |
|
797 sbuf_puts(out, "<D:propstat>\n<D:prop>\n"); |
|
798 |
|
799 DAV_FOREACH(prop, proplist) { |
|
800 xmlelm_write((XmlElement*)prop->data, out, wv); |
|
801 } |
|
802 |
|
803 sbuf_puts(out, "\n</D:prop>\n<D:status>"); |
|
804 |
|
805 int status = *(int*)iter.cur->key.data; |
|
806 if(status < 1000 && status > 0) { |
|
807 char buf[5]; |
|
808 buf[4] = 0; |
|
809 sprintf(buf, "%d ", status); |
|
810 sbuf_puts(out, "HTTP/1.1 "); |
|
811 sbuf_puts(out, buf); |
|
812 sbuf_puts(out, (char*)protocol_status_message(status)); |
|
813 } |
|
814 |
|
815 sbuf_puts(out, "</D:status>\n</D:propstat>\n"); |
|
816 } |
|
817 } |
|
818 } |
|