176 WebdavResource *response, |
192 WebdavResource *response, |
177 VFSFile *file, |
193 VFSFile *file, |
178 WebdavPList **setInOut, |
194 WebdavPList **setInOut, |
179 WebdavPList **removeInOut) |
195 WebdavPList **removeInOut) |
180 { |
196 { |
181 return 0; |
197 Session *sn = request->sn; |
|
198 Request *rq = request->rq; |
|
199 CxAllocator *a = pool_allocator(sn->pool); |
|
200 |
|
201 WebdavXAttrBackend *xdav = request->dav->instance; |
|
202 WebdavXAttrRepository *repo = xdav->repo; |
|
203 |
|
204 XAttrProppatch *xprop = pool_malloc(sn->pool, sizeof(XAttrProppatch)); |
|
205 if(!xprop) { |
|
206 return 1; |
|
207 } |
|
208 request->userdata = xprop; |
|
209 |
|
210 char *path = pblock_findkeyval(pb_key_path, rq->vars); |
|
211 |
|
212 // get existing property data |
|
213 ssize_t xattr_data_len = 0; |
|
214 char *xattr_data = xattr_get_alloc( |
|
215 sn->pool, |
|
216 (libxattr_malloc_func)pool_malloc, |
|
217 (libxattr_free_func)pool_free, |
|
218 path, |
|
219 repo->xattr_name, |
|
220 &xattr_data_len); |
|
221 |
|
222 CxMap *pmap; |
|
223 if(xattr_data) { |
|
224 pmap = webdav_xattr_parse_data(a, xattr_data, xattr_data_len, path); |
|
225 } else { |
|
226 // empty map |
|
227 pmap = cxHashMapCreate(a, request->setcount + 8); |
|
228 } |
|
229 if(!pmap) { |
|
230 return 1; |
|
231 } |
|
232 |
|
233 // remove properties |
|
234 WebdavPListIterator i = webdav_plist_iterator(removeInOut); |
|
235 WebdavPList *cur; |
|
236 while(webdav_plist_iterator_next(&i, &cur)) { |
|
237 WebdavProperty *prop = cur->property; |
|
238 if(!prop->namespace || !prop->namespace->prefix) { |
|
239 // not sure if this check is required |
|
240 log_ereport(LOG_WARN, "webdav_xattr_proppatch_do: property %s has missing namespace infos", prop->name); |
|
241 continue; |
|
242 } |
|
243 |
|
244 CxHashKey key = webdav_property_key_a( |
|
245 a, |
|
246 (const char*)prop->namespace->href, |
|
247 (const char*)prop->name); |
|
248 if(!key.data.str) { |
|
249 cxMapDestroy(pmap); |
|
250 return 1; |
|
251 } |
|
252 void *rmprop = cxMapRemove(pmap, key); |
|
253 cxFree(a, key.data.str); |
|
254 |
|
255 // TODO: free rmprop |
|
256 |
|
257 if(rmprop) { |
|
258 webdav_plist_iterator_remove_current(&i); |
|
259 } |
|
260 } |
|
261 |
|
262 i = webdav_plist_iterator(setInOut); |
|
263 while(webdav_plist_iterator_next(&i, &cur)) { |
|
264 WebdavProperty *prop = cur->property; |
|
265 if(!prop->namespace || !prop->namespace->prefix) { |
|
266 // not sure if this check is required |
|
267 log_ereport(LOG_WARN, "webdav_xattr_proppatch_do: property %s has missing namespace infos", prop->name); |
|
268 continue; |
|
269 } |
|
270 |
|
271 if(webdav_xattr_put_prop(pmap, prop)) { |
|
272 cxMapDestroy(pmap); |
|
273 return 1; |
|
274 } |
|
275 |
|
276 webdav_plist_iterator_remove_current(&i); |
|
277 } |
|
278 |
|
279 //printf("\n\n%.*s\n\n", (int)buf.size, buf.space); |
|
280 //fflush(stdout); |
|
281 |
|
282 int ret = 0; |
|
283 cxmutstr new_data = webdav_xattr_serialze_map(a, pmap); |
|
284 if(new_data.ptr) { |
|
285 xattr_set(path, repo->xattr_name, new_data.ptr, new_data.length); |
|
286 } else { |
|
287 cxMapDestroy(pmap); |
|
288 ret = 1; |
|
289 } |
|
290 |
|
291 return ret; |
182 } |
292 } |
183 |
293 |
184 int webdav_xattr_proppatch_finish( |
294 int webdav_xattr_proppatch_finish( |
185 WebdavProppatchRequest *request, |
295 WebdavProppatchRequest *request, |
186 WebdavResource *response, |
296 WebdavResource *response, |
187 VFSFile *file, |
297 VFSFile *file, |
188 WSBool commit) |
298 WSBool commit) |
189 { |
299 { |
190 return 0; |
300 return 0; |
191 } |
301 } |
|
302 |
|
303 |
|
304 /* ----------------------- properties xattr data ----------------------- */ |
|
305 |
|
306 static int get_next_line(cxstring data, size_t *pos, cxstring *line) { |
|
307 size_t p = *pos; |
|
308 cxstring str = cx_strsubs(data, p); |
|
309 size_t i; |
|
310 int skip = 0; |
|
311 for(i=0;i<str.length;i++) { |
|
312 if(str.ptr[i] == '\n') { |
|
313 skip = 1; |
|
314 break; |
|
315 } |
|
316 } |
|
317 p += i; |
|
318 *pos = p + skip; |
|
319 *line = cx_strsubsl(str, 0, i); |
|
320 return i > 0 ? TRUE : FALSE; |
|
321 } |
|
322 |
|
323 static int webdav_xattr_parse_elm(cxstring line, cxstring *name, cxstring *prefix, cxstring *xmlns, cxstring *lang) { |
|
324 cxstring s_xmlns = CX_STR("xmlns:"); |
|
325 |
|
326 // check if line starts with 'xmlns:' |
|
327 if(!cx_strprefix(line, s_xmlns)) { |
|
328 return 1; |
|
329 } |
|
330 line.ptr += 6; |
|
331 line.length -= 6; |
|
332 |
|
333 |
|
334 // format: <prefix>="<href>" |
|
335 // find '=' |
|
336 size_t i; |
|
337 size_t token_end = 0; |
|
338 for(i=0;i<line.length;i++) { |
|
339 if(line.ptr[i] == '=') { |
|
340 token_end = i; |
|
341 break; |
|
342 } |
|
343 } |
|
344 if(token_end == 0) { |
|
345 return 1; |
|
346 } |
|
347 *prefix = cx_strn(line.ptr, token_end); |
|
348 |
|
349 // make sure the line length is big enough |
|
350 if(token_end + 4 > line.length) { |
|
351 return 1; |
|
352 } |
|
353 if(line.ptr[token_end + 1] != '\"') { |
|
354 return 1; |
|
355 } |
|
356 line.ptr += token_end + 2; |
|
357 line.length -= token_end + 2; |
|
358 |
|
359 // get <href> |
|
360 int escape = 0; |
|
361 token_end = 0; |
|
362 for(i=0;i<line.length;i++) { |
|
363 if(line.ptr[i] == '\\') { |
|
364 escape = 1; |
|
365 continue; |
|
366 } else if(!escape && line.ptr[i] == '\"') { |
|
367 token_end = i; |
|
368 break; |
|
369 } |
|
370 escape = 0; |
|
371 } |
|
372 if(token_end == 0) { |
|
373 return 1; |
|
374 } |
|
375 *xmlns = cx_strn(line.ptr, token_end); |
|
376 |
|
377 // check length |
|
378 if(token_end + 2 > line.length) { |
|
379 return 1; |
|
380 } |
|
381 line.ptr += token_end + 2; |
|
382 line.length -= token_end + 2; |
|
383 *name = cx_strtrim(line); |
|
384 |
|
385 if(name->length == 0) { |
|
386 return 1; |
|
387 } |
|
388 if(prefix->length == 0) { |
|
389 return 1; |
|
390 } |
|
391 if(xmlns->length == 0) { |
|
392 return 1; |
|
393 } |
|
394 |
|
395 return 0; |
|
396 } |
|
397 |
|
398 int webdav_xattr_put_prop(CxMap *pmap, WebdavProperty *prop) { |
|
399 CxHashKey key = webdav_property_key_a( |
|
400 pmap->allocator, |
|
401 (const char*)prop->namespace->href, |
|
402 (const char*)prop->name); |
|
403 if(!key.data.str) { |
|
404 return 1; |
|
405 } |
|
406 int ret = cxMapPut(pmap, key, prop); |
|
407 cxFree(pmap->allocator, key.data.str); |
|
408 return ret; |
|
409 } |
|
410 |
|
411 CxMap* webdav_xattr_parse_data(CxAllocator *a, const char *data, size_t len, const char *path) { |
|
412 CxMap *pmap = cxHashMapCreate(a, 32); |
|
413 if(!pmap) { |
|
414 return NULL; |
|
415 } |
|
416 cxstring dat = cx_strn(data, len); |
|
417 |
|
418 printf("\n\n"); |
|
419 |
|
420 cxstring s_elm = CX_STR("prop "); |
|
421 cxstring s_ns = CX_STR("ns "); |
|
422 cxstring s_data = CX_STR("data "); |
|
423 |
|
424 WebdavProperty *prop = NULL; |
|
425 |
|
426 int error = 0; |
|
427 |
|
428 size_t elmno = 0; |
|
429 cxstring line; |
|
430 size_t pos = 0; |
|
431 while(get_next_line(dat, &pos, &line)) { |
|
432 if(cx_strprefix(line, s_elm)) { |
|
433 if(prop) { |
|
434 if(webdav_xattr_put_prop(pmap, prop)) { |
|
435 error = 1; |
|
436 break; |
|
437 } |
|
438 } |
|
439 |
|
440 line = cx_strsubs(line, 5); |
|
441 |
|
442 // get prop name, namespace and lang |
|
443 cxstring name; |
|
444 cxstring prefix; |
|
445 cxstring xmlns; |
|
446 cxstring lang; |
|
447 if(webdav_xattr_parse_elm(line, &name, &prefix, &xmlns, &lang)) { |
|
448 log_ereport( |
|
449 LOG_FAILURE, |
|
450 "webdav xattr backend: %file %s: invalid xattr format[%d]: cannot parse elm line", |
|
451 path, |
|
452 elmno); |
|
453 error = 1; |
|
454 break; |
|
455 } |
|
456 |
|
457 // create property |
|
458 prop = cxMalloc(a, sizeof(WebdavProperty)); |
|
459 if(!prop) { |
|
460 error = 1; |
|
461 break; |
|
462 } |
|
463 ZERO(prop, sizeof(WebdavProperty)); |
|
464 |
|
465 WSNamespace *ns = cxMalloc(a, sizeof(WSNamespace)); |
|
466 if(!ns) { |
|
467 error = 1; |
|
468 break; |
|
469 } |
|
470 |
|
471 char *name_str = cx_strdup_a(a, name).ptr; |
|
472 char *prefix_str = cx_strdup_a(a, prefix).ptr; |
|
473 char *xmlns_str = cx_strdup_a(a, xmlns).ptr; |
|
474 if(!(name_str && prefix_str && xmlns_str)) { |
|
475 error = 1; |
|
476 break; |
|
477 } |
|
478 |
|
479 ns->prefix = (const xmlChar*)prefix_str; |
|
480 ns->href = (const xmlChar*)xmlns_str; |
|
481 |
|
482 prop->name = name_str; |
|
483 prop->namespace = ns; |
|
484 |
|
485 elmno++; |
|
486 } else if(prop) { |
|
487 if(cx_strprefix(line, s_ns)) { |
|
488 // TODO |
|
489 } else if(cx_strprefix(line, s_data)) { |
|
490 line = cx_strsubs(line, 5); |
|
491 |
|
492 // util_strtoint just works here, because the line ends with \n |
|
493 // the xattr content data is also garanteed to be 0-terminated |
|
494 int64_t data_len = 0; |
|
495 if(!util_strtoint(line.ptr, &data_len)) { |
|
496 log_ereport( |
|
497 LOG_FAILURE, |
|
498 "webdav xattr backend: %file %s: invalid xattr format[%d]: number expected after data", |
|
499 path, |
|
500 elmno); |
|
501 error = 1; |
|
502 break; |
|
503 } |
|
504 |
|
505 // get data |
|
506 if(data_len > 0) { |
|
507 if(data_len > dat.length - pos) { |
|
508 log_ereport( |
|
509 LOG_FAILURE, |
|
510 "webdav xattr backend: %file %s: invalid data length %" PRId64 " in prop %d", |
|
511 path, |
|
512 data_len, |
|
513 elmno); |
|
514 error = 1; |
|
515 break; |
|
516 } |
|
517 |
|
518 cxstring propdata; |
|
519 propdata.ptr = dat.ptr + pos; |
|
520 propdata.length = data_len; |
|
521 pos += data_len; |
|
522 |
|
523 cxmutstr propdata_cp = cx_strdup_a(a, propdata); |
|
524 if(!propdata_cp.ptr) { |
|
525 error = 1; |
|
526 break; |
|
527 } |
|
528 prop->vtype = WS_VALUE_XML_DATA; |
|
529 prop->value.data.namespaces = NULL; |
|
530 prop->value.data.data = propdata_cp.ptr; |
|
531 prop->value.data.length = propdata_cp.length; |
|
532 |
|
533 if(pos < dat.length && dat.ptr[pos] == '\n') { |
|
534 pos++; |
|
535 } |
|
536 } |
|
537 } else { |
|
538 log_ereport( |
|
539 LOG_FAILURE, |
|
540 "webdav xattr backend: %file %s: invalid xattr format[%d]: unknown element", |
|
541 path, |
|
542 elmno); |
|
543 error = 1; |
|
544 break; |
|
545 } |
|
546 } |
|
547 |
|
548 //printf("line: {%.*s}\n", (int)line.length, line.ptr); |
|
549 //fflush(stdout); |
|
550 } |
|
551 |
|
552 // add last property |
|
553 if(prop) { |
|
554 if(webdav_xattr_put_prop(pmap, prop)) { |
|
555 error = 1; |
|
556 } |
|
557 } |
|
558 |
|
559 if(error) { |
|
560 // TODO: free pmap content |
|
561 cxMapDestroy(pmap); |
|
562 pmap = NULL; |
|
563 } |
|
564 |
|
565 //printf("\n\n"); |
|
566 //fflush(stdout); |
|
567 |
|
568 |
|
569 return pmap; |
|
570 } |
|
571 |
|
572 cxmutstr webdav_xattr_serialze_map(CxAllocator *a, CxMap *pmap) { |
|
573 pool_handle_t *pool = a->data; |
|
574 |
|
575 CxBuffer buf; |
|
576 if(cxBufferInit(&buf, NULL, 8192, a, CX_BUFFER_AUTO_EXTEND)) { |
|
577 return (cxmutstr){NULL,0}; |
|
578 } |
|
579 |
|
580 CxIterator i = cxMapIteratorValues(pmap); |
|
581 cx_foreach(WebdavProperty*, prop, i) { |
|
582 WSXmlData *property_value = NULL; |
|
583 if(prop->vtype == WS_VALUE_XML_NODE) { |
|
584 property_value = wsxml_node2data(pool, prop->value.node); |
|
585 } else if(prop->vtype == WS_VALUE_XML_DATA) { |
|
586 property_value = &prop->value.data; |
|
587 } |
|
588 |
|
589 cx_bprintf(&buf, "prop xmlns:%s=\"%s\" %s\n", prop->namespace->prefix, prop->namespace->href, prop->name); |
|
590 if(property_value) { |
|
591 WebdavNSList *ns = property_value->namespaces; |
|
592 while(ns) { |
|
593 cx_bprintf(&buf, "ns %s:%s\n", prop->namespace->prefix, prop->namespace->href); |
|
594 ns = ns->next; |
|
595 } |
|
596 |
|
597 cx_bprintf(&buf, "data %zu\n", property_value->length); |
|
598 cxBufferWrite(property_value->data, 1, property_value->length, &buf); |
|
599 cxBufferPut(&buf, '\n'); |
|
600 } |
|
601 } |
|
602 |
|
603 return (cxmutstr){buf.space, buf.size}; |
|
604 } |
|
605 |