libidav/config.c

changeset 795
05647e862a17
child 796
81e0f67386a6
equal deleted inserted replaced
794:29d544c3c2b8 795:05647e862a17
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2023 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 "config.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <cx/hash_map.h>
36 #include <errno.h>
37 #include <libxml/tree.h>
38 #include "utils.h"
39
40 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
41 #define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b)
42
43 #define print_error(lineno, ...) \
44 do {\
45 fprintf(stderr, "Error (config.xml line %u): ", lineno); \
46 fprintf(stderr, __VA_ARGS__); \
47 fprintf(stderr, "Abort.\n"); \
48 } while(0);
49 #define print_warning(lineno, ...) \
50 do {\
51 fprintf(stderr, "Warning (config.xml line %u): ", lineno); \
52 fprintf(stderr, __VA_ARGS__); \
53 } while(0);
54
55 #ifdef _WIN32
56 #define ENV_HOME getenv("USERPROFILE")
57 #else
58 #define ENV_HOME getenv("HOME")
59 #endif /* _WIN32 */
60
61
62 static int load_repository(
63 DavConfig *config,
64 DavCfgRepository **list_begin,
65 DavCfgRepository **list_end,
66 xmlNode *reponode);
67 static int load_key(
68 DavConfig *config,
69 DavCfgKey **list_begin,
70 DavCfgKey **list_end,
71 xmlNode *keynode);
72 static int load_proxy(
73 DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type);
74 static int load_namespace(
75 DavConfig *config,
76 DavCfgNamespace **list_begin,
77 DavCfgNamespace **list_end,
78 xmlNode *node);
79 static int load_secretstore(DavConfig *config, xmlNode *node);
80
81
82 int dav_cfg_string_set_value(DavConfig *config, CfgString *str, xmlNode *node) {
83 str->node = node;
84 char *value = util_xml_get_text(node);
85 if(value) {
86 str->value = cx_strdup_a(config->mp->allocator, cx_str(value));
87 return 0;
88 } else {
89 str->value = (cxmutstr){NULL, 0};
90 return 1;
91 }
92 }
93
94 void dav_cfg_bool_set_value(DavConfig *config, CfgBool *cbool, xmlNode *node) {
95 cbool->node = node;
96 char *value = util_xml_get_text(node);
97 cbool->value = util_getboolean(value);
98 }
99
100
101 DavConfig* dav_config_load(cxmutstr xmlfilecontent, int *error) {
102 xmlDoc *doc = xmlReadMemory(xmlfilecontent.ptr, xmlfilecontent.length, NULL, NULL, 0);
103 if(!doc) {
104 if(error) {
105 *error = DAV_CONFIG_ERROR_XML;
106 }
107 return NULL;
108 }
109
110 CxMempool *cfg_mp = cxMempoolCreate(128, NULL);
111 cxMempoolRegister(cfg_mp, doc, (cx_destructor_func)xmlFreeDoc);
112 DavConfig *config = cxMalloc(cfg_mp->allocator, sizeof(DavConfig));
113 memset(config, 0, sizeof(DavConfig));
114 config->mp = cfg_mp;
115 config->doc = doc;
116
117 DavCfgRepository *repos_begin = NULL;
118 DavCfgRepository *repos_end = NULL;
119 DavCfgKey *keys_begin = NULL;
120 DavCfgKey *keys_end = NULL;
121 DavCfgNamespace *namespaces_begin = NULL;
122 DavCfgNamespace *namespaces_end = NULL;
123
124 xmlNode *xml_root = xmlDocGetRootElement(doc);
125 xmlNode *node = xml_root->children;
126 int ret = 0;
127 while(node && !ret) {
128 if(node->type == XML_ELEMENT_NODE) {
129 if(xstreq(node->name, "repository")) {
130 ret = load_repository(config, &repos_begin, &repos_end, node);
131 } else if(xstreq(node->name, "key")) {
132 ret = load_key(config, &keys_begin, &keys_end, node);
133 } else if (xstreq(node->name, "http-proxy")) {
134 config->http_proxy = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgProxy));
135 ret = load_proxy(config, config->http_proxy, node, DAV_HTTP_PROXY);
136 } else if (xstreq(node->name, "https-proxy")) {
137 config->https_proxy = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgProxy));
138 ret = load_proxy(config, config->https_proxy, node, DAV_HTTPS_PROXY);
139 } else if (xstreq(node->name, "namespace")) {
140 ret = load_namespace(config, &namespaces_begin, &namespaces_end, node);
141 } else if (xstreq(node->name, "secretstore")) {
142 ret = load_secretstore(config, node);
143 } else {
144 fprintf(stderr, "Unknown config element: %s\n", node->name);
145 ret = 1;
146 }
147 }
148 node = node->next;
149 }
150
151 config->repositories = repos_begin;
152 config->keys = keys_begin;
153 config->namespaces = namespaces_begin;
154
155 if(ret != 0 && error) {
156 *error = ret;
157 cxMempoolDestroy(cfg_mp);
158 }
159
160 return config;
161 }
162
163 CxBuffer* dav_config2buf(DavConfig *config) {
164 xmlChar* xmlText = NULL;
165 int textLen = 0;
166 xmlDocDumpFormatMemory(config->doc, &xmlText, &textLen, 1);
167
168 if(!xmlText) {
169 return NULL;
170 }
171
172 CxBuffer *buf = cxBufferCreate(NULL, textLen, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
173 cxBufferWrite(xmlText, 1, textLen, buf);
174 xmlFree(xmlText);
175 return buf;
176 }
177
178
179 static int repo_add_config(
180 DavConfig *config,
181 DavCfgRepository *repo,
182 xmlNode* node)
183 {
184 unsigned short lineno = node->line;
185 char *key = (char*)node->name;
186 char *value = util_xml_get_text(node);
187
188 /* every key needs a value */
189 if(!value) {
190 /* TODO: maybe this should only be reported, if the key is valid
191 * But this makes the code very ugly.
192 */
193 print_error(lineno, "missing value for config element: %s\n", key);
194 return 1;
195 }
196
197 if(xstreq(key, "name")) {
198 dav_cfg_string_set_value(config, &repo->name, node);
199 } else if(xstreq(key, "url")) {
200 dav_cfg_string_set_value(config, &repo->url, node);
201 } else if(xstreq(key, "user")) {
202 dav_cfg_string_set_value(config, &repo->user, node);
203 } else if(xstreq(key, "password")) {
204 dav_cfg_string_set_value(config, &repo->password, node);
205 } else if(xstreq(key, "stored-user")) {
206 dav_cfg_string_set_value(config, &repo->stored_user, node);
207 } else if(xstreq(key, "default-key")) {
208 dav_cfg_string_set_value(config, &repo->default_key, node);
209 } else if(xstreq(key, "full-encryption")) {
210 dav_cfg_bool_set_value(config, &repo->full_encryption, node);
211 } else if(xstreq(key, "content-encryption")) {
212 dav_cfg_bool_set_value(config, &repo->content_encryption, node);
213 } else if(xstreq(key, "decrypt-content")) {
214 dav_cfg_bool_set_value(config, &repo->decrypt_content, node);
215 } else if(xstreq(key, "decrypt-name")) {
216 dav_cfg_bool_set_value(config, &repo->decrypt_name, node);
217 } else if(xstreq(key, "cert")) {
218 dav_cfg_string_set_value(config, &repo->cert, node);
219 } else if(xstreq(key, "verification")) {
220 dav_cfg_bool_set_value(config, &repo->verification, node);
221 } else if(xstreq(key, "ssl-version")) {
222 repo->ssl_version.node = node;
223 if(xstrEQ(value, "TLSv1")) {
224 repo->ssl_version.value = CURL_SSLVERSION_TLSv1;
225 } else if(xstrEQ(value, "SSLv2")) {
226 repo->ssl_version.value = CURL_SSLVERSION_SSLv2;
227 } else if(xstrEQ(value, "SSLv3")) {
228 repo->ssl_version.value = CURL_SSLVERSION_SSLv3;
229 }
230 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7034
231 else if(xstrEQ(value, "TLSv1.0")) {
232 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_0;
233 } else if(xstrEQ(value, "TLSv1.1")) {
234 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_1;
235 } else if(xstrEQ(value, "TLSv1.2")) {
236 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_2;
237 }
238 #endif
239 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7052
240 else if(xstrEQ(value, "TLSv1.3")) {
241 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_3;
242 }
243 #endif
244 else {
245 print_warning(lineno, "unknown ssl version: %s\n", value);
246 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT;
247 }
248 } else if(xstreq(key, "authmethods")) {
249 repo->authmethods.node = node;
250 repo->authmethods.value = CURLAUTH_NONE;
251 const char *delims = " \t\r\n";
252 char *meths = strdup(value);
253 char *meth = strtok(meths, delims);
254 while (meth) {
255 if(xstrEQ(meth, "basic")) {
256 repo->authmethods.value |= CURLAUTH_BASIC;
257 } else if(xstrEQ(meth, "digest")) {
258 repo->authmethods.value |= CURLAUTH_DIGEST;
259 } else if(xstrEQ(meth, "negotiate")) {
260 repo->authmethods.value |= CURLAUTH_GSSNEGOTIATE;
261 } else if(xstrEQ(meth, "ntlm")) {
262 repo->authmethods.value |= CURLAUTH_NTLM;
263 } else if(xstrEQ(meth, "any")) {
264 repo->authmethods.value = CURLAUTH_ANY;
265 } else if(xstrEQ(meth, "none")) {
266 /* skip */
267 } else {
268 print_warning(lineno,
269 "unknown authentication method: %s\n", meth);
270 }
271 meth = strtok(NULL, delims);
272 }
273 free(meths);
274 } else {
275 print_error(lineno, "unkown repository config element: %s\n", key);
276 return 1;
277 }
278 return 0;
279 }
280
281 static int load_repository(
282 DavConfig *config,
283 DavCfgRepository **list_begin,
284 DavCfgRepository **list_end,
285 xmlNode *reponode)
286 {
287 DavCfgRepository *repo = dav_repository_new(config);
288 repo->node = reponode;
289
290 // add repo config from child nodes
291 xmlNode *node = reponode->children;
292 int ret = 0;
293 while(node && !ret) {
294 if(node->type == XML_ELEMENT_NODE) {
295 ret = repo_add_config(config, repo, node);
296 }
297 node = node->next;
298 }
299
300 // success: add repo to the configuration, error: free repo
301 if(ret) {
302 return 1;
303 } else {
304 cx_linked_list_add(
305 (void**)list_begin,
306 (void**)list_end,
307 offsetof(DavCfgRepository, prev),
308 offsetof(DavCfgRepository, next),
309 repo);
310 }
311
312 return 0;
313 }
314
315 static xmlNode* addXmlNode(xmlNode *node, const char *name, cxmutstr content) {
316 xmlNode *text1 = xmlNewDocText(node->doc, BAD_CAST "\t\t");
317 xmlAddChild(node, text1);
318
319 cxmutstr ctn = cx_strdup(cx_strcast(content));
320 xmlNode *newNode = xmlNewChild(node, NULL, BAD_CAST name, BAD_CAST ctn.ptr);
321 free(ctn.ptr);
322
323 xmlNode *text2 = xmlNewDocText(node->doc, BAD_CAST "\n");
324 xmlAddChild(node, text2);
325
326 return newNode;
327 }
328
329 void dav_config_add_repository(DavConfig *config, DavCfgRepository *repo) {
330 if(repo->node) {
331 fprintf(stderr, "Error: dav_config_add_repository: node already exists\n");
332 return;
333 }
334
335 xmlNode *repoNode = xmlNewNode(NULL, BAD_CAST "repository");
336 xmlNode *rtext1 = xmlNewDocText(config->doc, BAD_CAST "\n");
337 xmlAddChild(repoNode, rtext1);
338
339 if(repo->name.value.ptr) {
340 repo->name.node = addXmlNode(repoNode, "name", repo->name.value);
341 }
342 if(repo->url.value.ptr) {
343 repo->url.node = addXmlNode(repoNode, "url", repo->url.value);
344 }
345 if(repo->user.value.ptr) {
346 repo->user.node = addXmlNode(repoNode, "user", repo->user.value);
347 }
348 if(repo->password.value.ptr) {
349 repo->password.node = addXmlNode(repoNode, "password", repo->password.value);
350 }
351
352 if(repo->stored_user.value.ptr) {
353 repo->stored_user.node = addXmlNode(repoNode, "stored-user", repo->stored_user.value);
354 }
355 if(repo->default_key.value.ptr) {
356 repo->default_key.node = addXmlNode(repoNode, "default-key", repo->default_key.value);
357 }
358 if(repo->cert.value.ptr) {
359 repo->cert.node = addXmlNode(repoNode, "cert", repo->cert.value);
360 }
361
362 // TODO: implement booleans
363
364 // indent closing tag
365 xmlNode *rtext2 = xmlNewDocText(config->doc, BAD_CAST "\t");
366 xmlAddChild(repoNode, rtext2);
367
368 // add repository to internal list
369 DavCfgRepository **list_begin = &config->repositories;
370 cx_linked_list_add(
371 (void**)list_begin,
372 NULL,
373 offsetof(DavCfgRepository, prev),
374 offsetof(DavCfgRepository, next),
375 repo);
376
377 // add repository element to the xml document
378 xmlNode *xml_root = xmlDocGetRootElement(config->doc);
379
380 xmlNode *text1 = xmlNewDocText(config->doc, BAD_CAST "\n\t");
381 xmlAddChild(xml_root, text1);
382
383 xmlAddChild(xml_root, repoNode);
384
385 xmlNode *text2 = xmlNewDocText(config->doc, BAD_CAST "\n");
386 xmlAddChild(xml_root, text2);
387 }
388
389 DavCfgRepository* dav_repository_new(DavConfig *config) {
390 DavCfgRepository *repo = cxMalloc(config->mp->allocator, sizeof(DavCfgRepository));
391 repo->decrypt_name.value = false;
392 repo->decrypt_content.value = true;
393 repo->decrypt_properties.value = false;
394 repo->verification.value = true;
395 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT;
396 repo->authmethods.value = CURLAUTH_BASIC;
397 return repo;
398 }
399
400 void dav_repository_free(DavConfig *config, DavCfgRepository *repo) {
401 // TODO
402 }
403
404 void dav_repository_remove_and_free(DavConfig *config, DavCfgRepository *repo) {
405 if(repo->prev) {
406 repo->prev->next = repo->next;
407 }
408 if(repo->next) {
409 repo->next->prev = repo->prev;
410 }
411
412 if(repo->node) {
413 // TODO: remove newline after repo node
414
415 xmlUnlinkNode(repo->node);
416 xmlFreeNode(repo->node);
417 }
418 }
419
420 int dav_repository_get_flags(DavCfgRepository *repo) {
421 int flags = 0;
422
423 DavBool encrypt_content = FALSE;
424 DavBool encrypt_name = FALSE;
425 DavBool encrypt_properties = FALSE;
426 DavBool decrypt_content = FALSE;
427 DavBool decrypt_name = FALSE;
428 DavBool decrypt_properties = FALSE;
429 if(repo->full_encryption.value) {
430 encrypt_content = TRUE;
431 encrypt_name = TRUE;
432 encrypt_properties = TRUE;
433 decrypt_content = TRUE;
434 decrypt_name = TRUE;
435 decrypt_properties = TRUE;
436 } else if(repo->content_encryption.value) {
437 encrypt_content = TRUE;
438 decrypt_content = TRUE;
439 }
440
441 if(decrypt_content) {
442 flags |= DAV_SESSION_DECRYPT_CONTENT;
443 }
444 if(decrypt_name) {
445 flags |= DAV_SESSION_DECRYPT_NAME;
446 }
447 if(decrypt_properties) {
448 flags |= DAV_SESSION_DECRYPT_PROPERTIES;
449 }
450 if(encrypt_content) {
451 flags |= DAV_SESSION_ENCRYPT_CONTENT;
452 }
453 if(encrypt_name) {
454 flags |= DAV_SESSION_ENCRYPT_NAME;
455 }
456 if(encrypt_properties) {
457 flags |= DAV_SESSION_ENCRYPT_PROPERTIES;
458 }
459 return flags;
460 }
461
462 void dav_repository_set_url(DavConfig *config, DavCfgRepository *repo, cxstring newurl) {
463 if(repo->url.value.ptr) {
464 cxFree(config->mp->allocator, repo->url.value.ptr);
465 }
466 repo->url.value = cx_strdup_a(config->mp->allocator, newurl);
467 }
468
469 void dav_repository_set_auth(DavConfig *config, DavCfgRepository *repo, cxstring user, cxstring password) {
470 const CxAllocator *a = config->mp->allocator;
471 repo->user.value = cx_strdup_a(a, user);
472 char *pwenc = util_base64encode(password.ptr, password.length);
473 repo->password.value = cx_strdup_a(a, cx_str(pwenc));
474 free(pwenc);
475 }
476
477 cxmutstr dav_repository_get_decodedpassword(DavCfgRepository *repo) {
478 cxmutstr pw = { NULL, 0 };
479 if(repo->password.value.ptr) {
480 pw = cx_mutstr(util_base64decode(repo->password.value.ptr));
481 }
482 return pw;
483 }
484
485
486 static int load_key(
487 DavConfig *config,
488 DavCfgKey **list_begin,
489 DavCfgKey **list_end,
490 xmlNode *keynode)
491 {
492 xmlNode *node = keynode->children;
493 DavCfgKey *key = cxMalloc(config->mp->allocator, sizeof(DavCfgKey));
494 memset(key, 0, sizeof(DavCfgKey));
495 key->type = DAV_KEY_TYPE_AES256;
496
497 int error = 0;
498 while(node) {
499 if(node->type == XML_ELEMENT_NODE) {
500 if(xstreq(node->name, "name")) {
501 dav_cfg_string_set_value(config, &key->name, node);
502 } else if(xstreq(node->name, "file")) {
503 dav_cfg_string_set_value(config, &key->file, node);
504 } else if(xstreq(node->name, "type")) {
505 const char *value = util_xml_get_text(node);
506 key->type_node = node;
507 if(!strcmp(value, "aes128")) {
508 key->type = DAV_KEY_TYPE_AES128;
509 } else if(!strcmp(value, "aes256")) {
510 key->type = DAV_KEY_TYPE_AES256;
511 } else {
512 print_error(node->line, "unknown key type %s\n", value);
513 error = 1;
514 }
515 } else {
516 key->unknown_elements++;
517 }
518
519 }
520 node = node->next;
521 }
522
523 if(!key->name.value.ptr) {
524 error = 1;
525 }
526
527 if(!error) {
528 error = 0;
529 size_t expected_length = 0;
530 if(key->type == DAV_KEY_TYPE_AES128) {
531 expected_length = 16;
532 }
533 if(key->type == DAV_KEY_TYPE_AES256) {
534 expected_length = 32;
535 }
536 /*
537 if(key->length < expected_length) {
538 print_error(keynode->line, "key %s is too small (%zu < %zu)\n",
539 key->name,
540 key->length,
541 expected_length);
542 error = 1;
543 }
544
545 // add key to context
546 if(!error) {
547 cxMapPut(keys, cx_hash_key_str(key->name), key);
548 dav_context_add_key(context, key);
549 }
550 */
551 }
552
553 // cleanup
554 if(error) {
555 return 1;
556 } else {
557 // add key to the configuration
558 cx_linked_list_add(
559 (void**)list_begin,
560 (void**)list_end,
561 offsetof(DavCfgKey, prev),
562 offsetof(DavCfgKey, next),
563 key);
564
565 return 0;
566 }
567 }
568
569 static int load_proxy(
570 DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type)
571 {
572 const char *stype;
573 if(type == DAV_HTTPS_PROXY) {
574 stype = "https";
575 } else if(type == DAV_HTTP_PROXY) {
576 stype = "http";
577 }
578
579 if(!proxy) {
580 // no xml error - so report this directly via fprintf
581 fprintf(stderr, "no memory reserved for %s proxy.\n", stype);
582 return 1;
583 }
584
585 xmlNode *node = proxynode->children;
586 int ret = 0;
587 while(node && !ret) {
588 if(node->type == XML_ELEMENT_NODE) {
589 int reportmissingvalue = 0;
590 if(xstreq(node->name, "url")) {
591 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->url, node);
592 } else if(xstreq(node->name, "user")) {
593 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->user, node);
594 } else if(xstreq(node->name, "password")) {
595 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->password, node);
596 } else if(xstreq(node->name, "no")) {
597 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->noproxy, node);
598 } else {
599 proxy->unknown_elements++;
600 }
601
602 if (reportmissingvalue) {
603 print_error(node->line,
604 "missing value for proxy configuration element: %s\n",
605 node->name);
606 ret = 1;
607 break;
608 }
609 }
610 node = node->next;
611 }
612
613 if(!ret && !proxy->url.value.ptr) {
614 print_error(proxynode->line, "missing url for %s proxy.\n", stype);
615 return 1;
616 }
617
618 return ret;
619 }
620
621 static char* get_attr_content(xmlNode *node) {
622 // TODO: remove code duplication (util_xml_get_text)
623 while(node) {
624 if(node->type == XML_TEXT_NODE) {
625 return (char*)node->content;
626 }
627 node = node->next;
628 }
629 return NULL;
630 }
631
632 static int load_namespace(
633 DavConfig *config,
634 DavCfgNamespace **list_begin,
635 DavCfgNamespace **list_end,
636 xmlNode *node)
637 {
638 const char *prefix = NULL;
639 const char *uri = NULL;
640 xmlAttr *attr = node->properties;
641 while(attr) {
642 if(attr->type == XML_ATTRIBUTE_NODE) {
643 char *value = get_attr_content(attr->children);
644 if(!value) {
645 print_error(
646 node->line,
647 "missing value for attribute %s\n", (char*)attr->name);
648 return 1;
649 }
650 if(xstreq(attr->name, "prefix")) {
651 prefix = value;
652 } else if(xstreq(attr->name, "uri")) {
653 uri = value;
654 } else {
655 print_error(
656 node->line,
657 "unexpected attribute %s\n", (char*)attr->name);
658 return 1;
659 }
660 }
661 attr = attr->next;
662 }
663
664 if(!prefix) {
665 print_error(node->line, "missing prefix attribute\n");
666 return 1;
667 }
668 if(!uri) {
669 print_error(node->line, "missing uri attribute\n");
670 return 1;
671 }
672
673 DavCfgNamespace *ns = cxMalloc(config->mp->allocator, sizeof(DavCfgNamespace));
674 memset(ns, 0, sizeof(DavCfgNamespace));
675 ns->node = node;
676 ns->prefix = cx_strdup_a(config->mp->allocator, cx_str(prefix));
677 ns->uri = cx_strdup_a(config->mp->allocator, cx_str(uri));
678 cx_linked_list_add(
679 (void**)list_begin,
680 (void**)list_end,
681 offsetof(DavCfgNamespace, prev),
682 offsetof(DavCfgNamespace, next),
683 ns);
684
685 return 0;
686 }
687
688 static int load_secretstore(DavConfig *config, xmlNode *node) {
689 // currently only one secretstore is supported
690
691 if(config->secretstore) {
692 return 1;
693 }
694
695 config->secretstore = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgSecretStore));
696
697 node = node->children;
698 int error = 0;
699 while(node) {
700 if(node->type == XML_ELEMENT_NODE) {
701 if(xstreq(node->name, "unlock-command")) {
702 dav_cfg_string_set_value(config, &config->secretstore->unlock_cmd, node);
703 } else if(xstreq(node->name, "lock-command")) {
704 dav_cfg_string_set_value(config, &config->secretstore->lock_cmd, node);
705 }
706 }
707 node = node->next;
708 }
709
710 return error;
711 }
712
713
714
715
716
717 DavCfgRepository* dav_config_get_repository(DavConfig *config, cxstring name) {
718 DavCfgRepository *repo = config->repositories;
719 while(repo) {
720 if(!cx_strcmp(cx_strcast(repo->name.value), name)) {
721 return repo;
722 }
723 repo = repo->next;
724 }
725 return NULL;
726 }
727
728 DavCfgRepository* dav_config_url2repo(DavConfig *config, const char *url, char **path) {
729 cxmutstr p;
730 DavCfgRepository *repo = dav_config_url2repo_s(config, cx_str(url), &p);
731 *path = p.ptr;
732 return repo;
733 }
734
735 DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutstr *path) {
736 path->ptr = NULL;
737 path->length = 0;
738
739 int s;
740 if(cx_strprefix(url, CX_STR("http://"))) {
741 s = 7;
742 } else if(cx_strprefix(url, CX_STR("https://"))) {
743 s = 8;
744 } else {
745 s = 1;
746 }
747
748 // split URL into repository and path
749 cxstring r = cx_strsubs(url, s);
750 cxstring p = cx_strchr(r, '/');
751 r = cx_strsubsl(url, 0, url.length-p.length);
752 if(p.length == 0) {
753 p = cx_strn("/", 1);
754 }
755
756 DavCfgRepository *repo = dav_config_get_repository(config, r);
757 if(repo) {
758 *path = cx_strdup(p);
759 } else {
760 // TODO: who is responsible for freeing this repository?
761 // how can the callee know, if he has to call free()?
762 repo = dav_repository_new(config);
763 repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR(""));
764 if(url.ptr[url.length-1] == '/') {
765 repo->url.value = cx_strdup_a(config->mp->allocator, url);
766 *path = cx_strdup(CX_STR("/"));
767 } else if (cx_strchr(url, '/').length > 0) {
768 // TODO: fix the following workaround after
769 // fixing the inconsistent behavior of util_url_*()
770 cxstring repo_url = util_url_base_s(url);
771 repo->url.value = cx_strdup_a(config->mp->allocator, repo_url);
772 *path = cx_strdup(util_url_path_s(url));
773 } else {
774 repo->url.value = cx_strdup(url);
775 *path = cx_strdup(CX_STR("/"));
776 }
777 }
778
779 return repo;
780 }

mercurial