libidav/config.c

changeset 2
fbdfaacc4182
child 6
09ac07345656
equal deleted inserted replaced
1:b5bb7b3cd597 2:fbdfaacc4182
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 void dav_config_free(DavConfig *config) {
164 cxMempoolDestroy(config->mp);
165 }
166
167 CxBuffer* dav_config2buf(DavConfig *config) {
168 xmlChar* xmlText = NULL;
169 int textLen = 0;
170 xmlDocDumpFormatMemory(config->doc, &xmlText, &textLen, 1);
171
172 if(!xmlText) {
173 return NULL;
174 }
175
176 CxBuffer *buf = cxBufferCreate(NULL, textLen, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
177 cxBufferWrite(xmlText, 1, textLen, buf);
178 xmlFree(xmlText);
179 return buf;
180 }
181
182
183 static int repo_add_config(
184 DavConfig *config,
185 DavCfgRepository *repo,
186 xmlNode* node)
187 {
188 unsigned short lineno = node->line;
189 char *key = (char*)node->name;
190 char *value = util_xml_get_text(node);
191
192 /* every key needs a value */
193 if(!value) {
194 /* TODO: maybe this should only be reported, if the key is valid
195 * But this makes the code very ugly.
196 */
197 print_error(lineno, "missing value for config element: %s\n", key);
198 return 1;
199 }
200
201 if(xstreq(key, "name")) {
202 dav_cfg_string_set_value(config, &repo->name, node);
203 } else if(xstreq(key, "url")) {
204 dav_cfg_string_set_value(config, &repo->url, node);
205 } else if(xstreq(key, "user")) {
206 dav_cfg_string_set_value(config, &repo->user, node);
207 } else if(xstreq(key, "password")) {
208 dav_cfg_string_set_value(config, &repo->password, node);
209 } else if(xstreq(key, "stored-user")) {
210 dav_cfg_string_set_value(config, &repo->stored_user, node);
211 } else if(xstreq(key, "default-key")) {
212 dav_cfg_string_set_value(config, &repo->default_key, node);
213 } else if(xstreq(key, "full-encryption")) {
214 dav_cfg_bool_set_value(config, &repo->full_encryption, node);
215 } else if(xstreq(key, "content-encryption")) {
216 dav_cfg_bool_set_value(config, &repo->content_encryption, node);
217 } else if(xstreq(key, "decrypt-content")) {
218 dav_cfg_bool_set_value(config, &repo->decrypt_content, node);
219 } else if(xstreq(key, "decrypt-name")) {
220 dav_cfg_bool_set_value(config, &repo->decrypt_name, node);
221 } else if(xstreq(key, "cert")) {
222 dav_cfg_string_set_value(config, &repo->cert, node);
223 } else if(xstreq(key, "verification")) {
224 dav_cfg_bool_set_value(config, &repo->verification, node);
225 } else if(xstreq(key, "ssl-version")) {
226 repo->ssl_version.node = node;
227 if(xstrEQ(value, "TLSv1")) {
228 repo->ssl_version.value = CURL_SSLVERSION_TLSv1;
229 } else if(xstrEQ(value, "SSLv2")) {
230 repo->ssl_version.value = CURL_SSLVERSION_SSLv2;
231 } else if(xstrEQ(value, "SSLv3")) {
232 repo->ssl_version.value = CURL_SSLVERSION_SSLv3;
233 }
234 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7034
235 else if(xstrEQ(value, "TLSv1.0")) {
236 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_0;
237 } else if(xstrEQ(value, "TLSv1.1")) {
238 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_1;
239 } else if(xstrEQ(value, "TLSv1.2")) {
240 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_2;
241 }
242 #endif
243 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7052
244 else if(xstrEQ(value, "TLSv1.3")) {
245 repo->ssl_version.value = CURL_SSLVERSION_TLSv1_3;
246 }
247 #endif
248 else {
249 print_warning(lineno, "unknown ssl version: %s\n", value);
250 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT;
251 }
252 } else if(xstreq(key, "authmethods")) {
253 repo->authmethods.node = node;
254 repo->authmethods.value = CURLAUTH_NONE;
255 const char *delims = " \t\r\n";
256 char *meths = strdup(value);
257 char *meth = strtok(meths, delims);
258 while (meth) {
259 if(xstrEQ(meth, "basic")) {
260 repo->authmethods.value |= CURLAUTH_BASIC;
261 } else if(xstrEQ(meth, "digest")) {
262 repo->authmethods.value |= CURLAUTH_DIGEST;
263 } else if(xstrEQ(meth, "negotiate")) {
264 repo->authmethods.value |= CURLAUTH_GSSNEGOTIATE;
265 } else if(xstrEQ(meth, "ntlm")) {
266 repo->authmethods.value |= CURLAUTH_NTLM;
267 } else if(xstrEQ(meth, "any")) {
268 repo->authmethods.value = CURLAUTH_ANY;
269 } else if(xstrEQ(meth, "none")) {
270 /* skip */
271 } else {
272 print_warning(lineno,
273 "unknown authentication method: %s\n", meth);
274 }
275 meth = strtok(NULL, delims);
276 }
277 free(meths);
278 } else {
279 print_error(lineno, "unkown repository config element: %s\n", key);
280 return 1;
281 }
282 return 0;
283 }
284
285 static int load_repository(
286 DavConfig *config,
287 DavCfgRepository **list_begin,
288 DavCfgRepository **list_end,
289 xmlNode *reponode)
290 {
291 DavCfgRepository *repo = dav_repository_new(config);
292 repo->node = reponode;
293
294 // add repo config from child nodes
295 xmlNode *node = reponode->children;
296 int ret = 0;
297 while(node && !ret) {
298 if(node->type == XML_ELEMENT_NODE) {
299 ret = repo_add_config(config, repo, node);
300 }
301 node = node->next;
302 }
303
304 // success: add repo to the configuration, error: free repo
305 if(ret) {
306 return 1;
307 } else {
308 cx_linked_list_add(
309 (void**)list_begin,
310 (void**)list_end,
311 offsetof(DavCfgRepository, prev),
312 offsetof(DavCfgRepository, next),
313 repo);
314 }
315
316 return 0;
317 }
318
319 static xmlNode* addXmlNode(xmlNode *node, const char *name, cxmutstr content) {
320 xmlNode *text1 = xmlNewDocText(node->doc, BAD_CAST "\t\t");
321 xmlAddChild(node, text1);
322
323 cxmutstr ctn = cx_strdup(cx_strcast(content));
324 xmlNode *newNode = xmlNewChild(node, NULL, BAD_CAST name, BAD_CAST ctn.ptr);
325 free(ctn.ptr);
326
327 xmlNode *text2 = xmlNewDocText(node->doc, BAD_CAST "\n");
328 xmlAddChild(node, text2);
329
330 return newNode;
331 }
332
333 void dav_config_add_repository(DavConfig *config, DavCfgRepository *repo) {
334 if(repo->node) {
335 fprintf(stderr, "Error: dav_config_add_repository: node already exists\n");
336 return;
337 }
338
339 xmlNode *repoNode = xmlNewNode(NULL, BAD_CAST "repository");
340 xmlNode *rtext1 = xmlNewDocText(config->doc, BAD_CAST "\n");
341 xmlAddChild(repoNode, rtext1);
342
343 if(repo->name.value.ptr) {
344 repo->name.node = addXmlNode(repoNode, "name", repo->name.value);
345 }
346 if(repo->url.value.ptr) {
347 repo->url.node = addXmlNode(repoNode, "url", repo->url.value);
348 }
349 if(repo->user.value.ptr) {
350 repo->user.node = addXmlNode(repoNode, "user", repo->user.value);
351 }
352 if(repo->password.value.ptr) {
353 repo->password.node = addXmlNode(repoNode, "password", repo->password.value);
354 }
355
356 if(repo->stored_user.value.ptr) {
357 repo->stored_user.node = addXmlNode(repoNode, "stored-user", repo->stored_user.value);
358 }
359 if(repo->default_key.value.ptr) {
360 repo->default_key.node = addXmlNode(repoNode, "default-key", repo->default_key.value);
361 }
362 if(repo->cert.value.ptr) {
363 repo->cert.node = addXmlNode(repoNode, "cert", repo->cert.value);
364 }
365
366 // TODO: implement booleans
367
368 // indent closing tag
369 xmlNode *rtext2 = xmlNewDocText(config->doc, BAD_CAST "\t");
370 xmlAddChild(repoNode, rtext2);
371
372 // add repository to internal list
373 DavCfgRepository **list_begin = &config->repositories;
374 cx_linked_list_add(
375 (void**)list_begin,
376 NULL,
377 offsetof(DavCfgRepository, prev),
378 offsetof(DavCfgRepository, next),
379 repo);
380
381 // add repository element to the xml document
382 xmlNode *xml_root = xmlDocGetRootElement(config->doc);
383
384 xmlNode *text1 = xmlNewDocText(config->doc, BAD_CAST "\n\t");
385 xmlAddChild(xml_root, text1);
386
387 xmlAddChild(xml_root, repoNode);
388
389 xmlNode *text2 = xmlNewDocText(config->doc, BAD_CAST "\n");
390 xmlAddChild(xml_root, text2);
391 }
392
393 DavCfgRepository* dav_repository_new(DavConfig *config) {
394 DavCfgRepository *repo = cxMalloc(config->mp->allocator, sizeof(DavCfgRepository));
395 repo->decrypt_name.value = false;
396 repo->decrypt_content.value = true;
397 repo->decrypt_properties.value = false;
398 repo->verification.value = true;
399 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT;
400 repo->authmethods.value = CURLAUTH_BASIC;
401 return repo;
402 }
403
404 void dav_repository_free(DavConfig *config, DavCfgRepository *repo) {
405 // TODO
406 }
407
408 void dav_repository_remove_and_free(DavConfig *config, DavCfgRepository *repo) {
409 if(repo->prev) {
410 repo->prev->next = repo->next;
411 }
412 if(repo->next) {
413 repo->next->prev = repo->prev;
414 }
415
416 if(repo->node) {
417 // TODO: remove newline after repo node
418
419 xmlUnlinkNode(repo->node);
420 xmlFreeNode(repo->node);
421 }
422 }
423
424 int dav_repository_get_flags(DavCfgRepository *repo) {
425 int flags = 0;
426
427 DavBool encrypt_content = FALSE;
428 DavBool encrypt_name = FALSE;
429 DavBool encrypt_properties = FALSE;
430 DavBool decrypt_content = FALSE;
431 DavBool decrypt_name = FALSE;
432 DavBool decrypt_properties = FALSE;
433 if(repo->full_encryption.value) {
434 encrypt_content = TRUE;
435 encrypt_name = TRUE;
436 encrypt_properties = TRUE;
437 decrypt_content = TRUE;
438 decrypt_name = TRUE;
439 decrypt_properties = TRUE;
440 } else if(repo->content_encryption.value) {
441 encrypt_content = TRUE;
442 decrypt_content = TRUE;
443 }
444
445 if(decrypt_content) {
446 flags |= DAV_SESSION_DECRYPT_CONTENT;
447 }
448 if(decrypt_name) {
449 flags |= DAV_SESSION_DECRYPT_NAME;
450 }
451 if(decrypt_properties) {
452 flags |= DAV_SESSION_DECRYPT_PROPERTIES;
453 }
454 if(encrypt_content) {
455 flags |= DAV_SESSION_ENCRYPT_CONTENT;
456 }
457 if(encrypt_name) {
458 flags |= DAV_SESSION_ENCRYPT_NAME;
459 }
460 if(encrypt_properties) {
461 flags |= DAV_SESSION_ENCRYPT_PROPERTIES;
462 }
463 return flags;
464 }
465
466 void dav_repository_set_url(DavConfig *config, DavCfgRepository *repo, cxstring newurl) {
467 if(repo->url.value.ptr) {
468 cxFree(config->mp->allocator, repo->url.value.ptr);
469 }
470 repo->url.value = cx_strdup_a(config->mp->allocator, newurl);
471 }
472
473 void dav_repository_set_auth(DavConfig *config, DavCfgRepository *repo, cxstring user, cxstring password) {
474 const CxAllocator *a = config->mp->allocator;
475 repo->user.value = cx_strdup_a(a, user);
476 char *pwenc = util_base64encode(password.ptr, password.length);
477 repo->password.value = cx_strdup_a(a, cx_str(pwenc));
478 free(pwenc);
479 }
480
481 cxmutstr dav_repository_get_decodedpassword(DavCfgRepository *repo) {
482 cxmutstr pw = { NULL, 0 };
483 if(repo->password.value.ptr) {
484 pw = cx_mutstr(util_base64decode(repo->password.value.ptr));
485 }
486 return pw;
487 }
488
489
490 static int load_key(
491 DavConfig *config,
492 DavCfgKey **list_begin,
493 DavCfgKey **list_end,
494 xmlNode *keynode)
495 {
496 xmlNode *node = keynode->children;
497 DavCfgKey *key = cxMalloc(config->mp->allocator, sizeof(DavCfgKey));
498 memset(key, 0, sizeof(DavCfgKey));
499 key->type = DAV_KEY_TYPE_AES256;
500
501 int error = 0;
502 while(node) {
503 if(node->type == XML_ELEMENT_NODE) {
504 if(xstreq(node->name, "name")) {
505 dav_cfg_string_set_value(config, &key->name, node);
506 } else if(xstreq(node->name, "file")) {
507 dav_cfg_string_set_value(config, &key->file, node);
508 } else if(xstreq(node->name, "type")) {
509 const char *value = util_xml_get_text(node);
510 key->type_node = node;
511 if(!strcmp(value, "aes128")) {
512 key->type = DAV_KEY_TYPE_AES128;
513 } else if(!strcmp(value, "aes256")) {
514 key->type = DAV_KEY_TYPE_AES256;
515 } else {
516 print_error(node->line, "unknown key type %s\n", value);
517 error = 1;
518 }
519 } else {
520 key->unknown_elements++;
521 }
522
523 }
524 node = node->next;
525 }
526
527 if(!key->name.value.ptr) {
528 error = 1;
529 }
530
531 if(!error) {
532 error = 0;
533 size_t expected_length = 0;
534 if(key->type == DAV_KEY_TYPE_AES128) {
535 expected_length = 16;
536 }
537 if(key->type == DAV_KEY_TYPE_AES256) {
538 expected_length = 32;
539 }
540 /*
541 if(key->length < expected_length) {
542 print_error(keynode->line, "key %s is too small (%zu < %zu)\n",
543 key->name,
544 key->length,
545 expected_length);
546 error = 1;
547 }
548
549 // add key to context
550 if(!error) {
551 cxMapPut(keys, cx_hash_key_str(key->name), key);
552 dav_context_add_key(context, key);
553 }
554 */
555 }
556
557 // cleanup
558 if(error) {
559 return 1;
560 } else {
561 // add key to the configuration
562 cx_linked_list_add(
563 (void**)list_begin,
564 (void**)list_end,
565 offsetof(DavCfgKey, prev),
566 offsetof(DavCfgKey, next),
567 key);
568
569 return 0;
570 }
571 }
572
573 static int load_proxy(
574 DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type)
575 {
576 const char *stype;
577 if(type == DAV_HTTPS_PROXY) {
578 stype = "https";
579 } else if(type == DAV_HTTP_PROXY) {
580 stype = "http";
581 }
582
583 if(!proxy) {
584 // no xml error - so report this directly via fprintf
585 fprintf(stderr, "no memory reserved for %s proxy.\n", stype);
586 return 1;
587 }
588
589 xmlNode *node = proxynode->children;
590 int ret = 0;
591 while(node && !ret) {
592 if(node->type == XML_ELEMENT_NODE) {
593 int reportmissingvalue = 0;
594 if(xstreq(node->name, "url")) {
595 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->url, node);
596 } else if(xstreq(node->name, "user")) {
597 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->user, node);
598 } else if(xstreq(node->name, "password")) {
599 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->password, node);
600 } else if(xstreq(node->name, "no")) {
601 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->noproxy, node);
602 } else {
603 proxy->unknown_elements++;
604 }
605
606 if (reportmissingvalue) {
607 print_error(node->line,
608 "missing value for proxy configuration element: %s\n",
609 node->name);
610 ret = 1;
611 break;
612 }
613 }
614 node = node->next;
615 }
616
617 if(!ret && !proxy->url.value.ptr) {
618 print_error(proxynode->line, "missing url for %s proxy.\n", stype);
619 return 1;
620 }
621
622 return ret;
623 }
624
625 static char* get_attr_content(xmlNode *node) {
626 // TODO: remove code duplication (util_xml_get_text)
627 while(node) {
628 if(node->type == XML_TEXT_NODE) {
629 return (char*)node->content;
630 }
631 node = node->next;
632 }
633 return NULL;
634 }
635
636 static int load_namespace(
637 DavConfig *config,
638 DavCfgNamespace **list_begin,
639 DavCfgNamespace **list_end,
640 xmlNode *node)
641 {
642 const char *prefix = NULL;
643 const char *uri = NULL;
644 xmlAttr *attr = node->properties;
645 while(attr) {
646 if(attr->type == XML_ATTRIBUTE_NODE) {
647 char *value = get_attr_content(attr->children);
648 if(!value) {
649 print_error(
650 node->line,
651 "missing value for attribute %s\n", (char*)attr->name);
652 return 1;
653 }
654 if(xstreq(attr->name, "prefix")) {
655 prefix = value;
656 } else if(xstreq(attr->name, "uri")) {
657 uri = value;
658 } else {
659 print_error(
660 node->line,
661 "unexpected attribute %s\n", (char*)attr->name);
662 return 1;
663 }
664 }
665 attr = attr->next;
666 }
667
668 if(!prefix) {
669 print_error(node->line, "missing prefix attribute\n");
670 return 1;
671 }
672 if(!uri) {
673 print_error(node->line, "missing uri attribute\n");
674 return 1;
675 }
676
677 DavCfgNamespace *ns = cxMalloc(config->mp->allocator, sizeof(DavCfgNamespace));
678 memset(ns, 0, sizeof(DavCfgNamespace));
679 ns->node = node;
680 ns->prefix = cx_strdup_a(config->mp->allocator, cx_str(prefix));
681 ns->uri = cx_strdup_a(config->mp->allocator, cx_str(uri));
682 cx_linked_list_add(
683 (void**)list_begin,
684 (void**)list_end,
685 offsetof(DavCfgNamespace, prev),
686 offsetof(DavCfgNamespace, next),
687 ns);
688
689 return 0;
690 }
691
692 static int load_secretstore(DavConfig *config, xmlNode *node) {
693 // currently only one secretstore is supported
694
695 if(config->secretstore) {
696 return 1;
697 }
698
699 config->secretstore = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgSecretStore));
700
701 node = node->children;
702 int error = 0;
703 while(node) {
704 if(node->type == XML_ELEMENT_NODE) {
705 if(xstreq(node->name, "unlock-command")) {
706 dav_cfg_string_set_value(config, &config->secretstore->unlock_cmd, node);
707 } else if(xstreq(node->name, "lock-command")) {
708 dav_cfg_string_set_value(config, &config->secretstore->lock_cmd, node);
709 }
710 }
711 node = node->next;
712 }
713
714 return error;
715 }
716
717
718
719
720
721 DavCfgRepository* dav_config_get_repository(DavConfig *config, cxstring name) {
722 DavCfgRepository *repo = config->repositories;
723 while(repo) {
724 if(!cx_strcmp(cx_strcast(repo->name.value), name)) {
725 return repo;
726 }
727 repo = repo->next;
728 }
729 return NULL;
730 }
731
732 DavCfgRepository* dav_config_url2repo(DavConfig *config, const char *url, char **path) {
733 cxmutstr p;
734 DavCfgRepository *repo = dav_config_url2repo_s(config, cx_str(url), &p);
735 *path = p.ptr;
736 return repo;
737 }
738
739 DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutstr *path) {
740 path->ptr = NULL;
741 path->length = 0;
742
743 int s;
744 if(cx_strprefix(url, CX_STR("http://"))) {
745 s = 7;
746 } else if(cx_strprefix(url, CX_STR("https://"))) {
747 s = 8;
748 } else {
749 s = 1;
750 }
751
752 // split URL into repository and path
753 cxstring r = cx_strsubs(url, s);
754 cxstring p = cx_strchr(r, '/');
755 r = cx_strsubsl(url, 0, url.length-p.length);
756 if(p.length == 0) {
757 p = cx_strn("/", 1);
758 }
759
760 DavCfgRepository *repo = dav_config_get_repository(config, r);
761 if(repo) {
762 *path = cx_strdup(p);
763 } else {
764 // TODO: who is responsible for freeing this repository?
765 // how can the callee know, if he has to call free()?
766 repo = dav_repository_new(config);
767 repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR(""));
768 if(url.ptr[url.length-1] == '/') {
769 repo->url.value = cx_strdup_a(config->mp->allocator, url);
770 *path = cx_strdup(CX_STR("/"));
771 } else if (cx_strchr(url, '/').length > 0) {
772 // TODO: fix the following workaround after
773 // fixing the inconsistent behavior of util_url_*()
774 cxstring repo_url = util_url_base_s(url);
775 repo->url.value = cx_strdup_a(config->mp->allocator, repo_url);
776 *path = cx_strdup(util_url_path_s(url));
777 } else {
778 repo->url.value = cx_strdup(url);
779 *path = cx_strdup(CX_STR("/"));
780 }
781 }
782
783 return repo;
784 }
785
786 int dav_config_keytype(DavCfgKeyType type) {
787 switch(type) {
788 default: break;
789 case DAV_KEY_TYPE_AES256: return DAV_KEY_AES256;
790 case DAV_KEY_TYPE_AES128: return DAV_KEY_AES128;
791 }
792 return 0;
793 }
794
795 int dav_config_register_keys(DavConfig *config, DavContext *ctx, dav_loadkeyfile_func loadkey) {
796 for(DavCfgKey *key=config->keys;key;key=key->next) {
797 char *file = cx_strdup_m(key->file.value).ptr;
798 cxmutstr keycontent = loadkey(file);
799 free(file);
800
801 // TODO: check key length
802
803 if(!keycontent.ptr) {
804 return 1;
805 }
806
807 DavKey *davkey = calloc(1, sizeof(DavKey));
808 davkey->name = cx_strdup_m(key->name.value).ptr;
809 davkey->type = dav_config_keytype(key->type);
810 davkey->data = keycontent.ptr;
811 davkey->length = keycontent.length;
812
813 dav_context_add_key(ctx, davkey);
814 }
815 return 0;
816 }

mercurial