UNIXworkcode

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 (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 memset(repo, 0, sizeof(DavCfgRepository)); 396 repo->decrypt_name.value = false; 397 repo->decrypt_content.value = true; 398 repo->decrypt_properties.value = false; 399 repo->verification.value = true; 400 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT; 401 repo->authmethods.value = CURLAUTH_BASIC; 402 return repo; 403 } 404 405 void dav_repository_free(DavConfig *config, DavCfgRepository *repo) { 406 // TODO 407 } 408 409 void dav_repository_remove_and_free(DavConfig *config, DavCfgRepository *repo) { 410 if(repo->prev) { 411 repo->prev->next = repo->next; 412 } 413 if(repo->next) { 414 repo->next->prev = repo->prev; 415 } 416 417 if(repo->node) { 418 // TODO: remove newline after repo node 419 420 xmlUnlinkNode(repo->node); 421 xmlFreeNode(repo->node); 422 } 423 } 424 425 int dav_repository_get_flags(DavCfgRepository *repo) { 426 int flags = 0; 427 428 DavBool encrypt_content = FALSE; 429 DavBool encrypt_name = FALSE; 430 DavBool encrypt_properties = FALSE; 431 DavBool decrypt_content = FALSE; 432 DavBool decrypt_name = FALSE; 433 DavBool decrypt_properties = FALSE; 434 if(repo->full_encryption.value) { 435 encrypt_content = TRUE; 436 encrypt_name = TRUE; 437 encrypt_properties = TRUE; 438 decrypt_content = TRUE; 439 decrypt_name = TRUE; 440 decrypt_properties = TRUE; 441 } else if(repo->content_encryption.value) { 442 encrypt_content = TRUE; 443 decrypt_content = TRUE; 444 } 445 446 if(decrypt_content) { 447 flags |= DAV_SESSION_DECRYPT_CONTENT; 448 } 449 if(decrypt_name) { 450 flags |= DAV_SESSION_DECRYPT_NAME; 451 } 452 if(decrypt_properties) { 453 flags |= DAV_SESSION_DECRYPT_PROPERTIES; 454 } 455 if(encrypt_content) { 456 flags |= DAV_SESSION_ENCRYPT_CONTENT; 457 } 458 if(encrypt_name) { 459 flags |= DAV_SESSION_ENCRYPT_NAME; 460 } 461 if(encrypt_properties) { 462 flags |= DAV_SESSION_ENCRYPT_PROPERTIES; 463 } 464 return flags; 465 } 466 467 void dav_repository_set_url(DavConfig *config, DavCfgRepository *repo, cxstring newurl) { 468 if(repo->url.value.ptr) { 469 cxFree(config->mp->allocator, repo->url.value.ptr); 470 } 471 repo->url.value = cx_strdup_a(config->mp->allocator, newurl); 472 } 473 474 void dav_repository_set_auth(DavConfig *config, DavCfgRepository *repo, cxstring user, cxstring password) { 475 const CxAllocator *a = config->mp->allocator; 476 repo->user.value = cx_strdup_a(a, user); 477 char *pwenc = util_base64encode(password.ptr, password.length); 478 repo->password.value = cx_strdup_a(a, cx_str(pwenc)); 479 free(pwenc); 480 } 481 482 cxmutstr dav_repository_get_decodedpassword(DavCfgRepository *repo) { 483 cxmutstr pw = { NULL, 0 }; 484 if(repo->password.value.ptr) { 485 pw = cx_mutstr(util_base64decode(repo->password.value.ptr)); 486 } 487 return pw; 488 } 489 490 491 static int load_key( 492 DavConfig *config, 493 DavCfgKey **list_begin, 494 DavCfgKey **list_end, 495 xmlNode *keynode) 496 { 497 xmlNode *node = keynode->children; 498 DavCfgKey *key = cxMalloc(config->mp->allocator, sizeof(DavCfgKey)); 499 memset(key, 0, sizeof(DavCfgKey)); 500 key->type = DAV_KEY_TYPE_AES256; 501 502 int error = 0; 503 while(node) { 504 if(node->type == XML_ELEMENT_NODE) { 505 if(xstreq(node->name, "name")) { 506 dav_cfg_string_set_value(config, &key->name, node); 507 } else if(xstreq(node->name, "file")) { 508 dav_cfg_string_set_value(config, &key->file, node); 509 } else if(xstreq(node->name, "type")) { 510 const char *value = util_xml_get_text(node); 511 key->type_node = node; 512 if(!strcmp(value, "aes128")) { 513 key->type = DAV_KEY_TYPE_AES128; 514 } else if(!strcmp(value, "aes256")) { 515 key->type = DAV_KEY_TYPE_AES256; 516 } else { 517 print_error(node->line, "unknown key type %s\n", value); 518 error = 1; 519 } 520 } else { 521 key->unknown_elements++; 522 } 523 524 } 525 node = node->next; 526 } 527 528 if(!key->name.value.ptr) { 529 error = < an class="c2html-macroconst">1; 530 } 531 532 if(!error) { 533 error = 0; 534 size_t expected_length = 0; 535 if(key->type == DAV_KEY_TYPE_AES128) { 536 expected_length = 16; 537 } 538 if(key->type == DAV_KEY_TYPE_AES256) { 539 expected_length = 32; 540 } 541 /* 542 if(key->length < expected_length) { 543 print_error(keynode->line, "key %s is too small (%zu < %zu)\n", 544 key->name, 545 key->length, 546 expected_length); 547 error = 1; 548 } 549 550 // add key to context 551 if(!error) { 552 cxMapPut(keys, cx_hash_key_str(key->name), key); 553 dav_context_add_key(context, key); 554 } 555 */ 556 } 557 558 // cleanup 559 if(error) { 560 return 1; 561 } else { 562 // add key to the configuration 563 cx_linked_list_add( 564 (void**)list_begin, 565 (void**)list_end, 566 offsetof(DavCfgKey, prev), 567 offsetof(DavCfgKey, next), 568 key); 569 570 return 0; 571 } 572 } 573 574 static int load_proxy( 575 DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type) 576 { 577 const char *stype; 578 if(type == DAV_HTTPS_PROXY) { 579 stype = "https"; 580 } else if(type == DAV_HTTP_PROXY) { 581 stype = "http"; 582 } else { 583 fprintf(stderr, "unknown proxy type\n"); 584 return 1; 585 } 586 587 if(!proxy) { 588 // no xml error - so report this directly via fprintf 589 fprintf(stderr, "no memory reserved for %s proxy.\n", stype); 590 return 1; 591 } 592 593 xmlNode *node = proxynode->children; 594 int ret = 0; 595 while(node && !ret) { 596 if(node->type == XML_ELEMENT_NODE) { 597 int reportmissingvalue = 0; 598 if(xstreq(node->name, "url")) { 599 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->url, node); 600 } else if(xstreq(node->name, "user")) { 601 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->user, node); 602 } else if(xstreq(node->name, "password")) { 603 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->password, node); 604 } else if(xstreq(node->name, "no")) { 605 reportmissingvalue = dav_cfg_string_set_value(config, &proxy->noproxy, node); 606 } else { 607 proxy->unknown_elements++; 608 } 609 610 if (reportmissingvalue) { 611 print_error(node->line, 612 "missing value for proxy configuration element: %s\n", 613 node->name); 614 ret = 1; 615 break; 616 } 617 } 618 node = node->next; 619 } 620 621 if(!ret && !proxy->url.value.ptr) { 622 print_error(proxynode->line, "missing url for %s proxy.\n", stype); 623 return 1; 624 } 625 626 return ret; 627 } 628 629 static char* get_attr_content(xmlNode *node) { 630 // TODO: remove code duplication (util_xml_get_text) 631 while(node) { 632 if(node->type == XML_TEXT_NODE) { 633 return (char*)node->content; 634 } 635 node = node->next; 636 } 637 return NULL; 638 } 639 640 static int load_namespace( 641 DavConfig *config, 642 DavCfgNamespace **list_begin, 643 DavCfgNamespace **list_end, 644 xmlNode *node) 645 { 646 const char *prefix = NULL; 647 const char *uri = NULL; 648 xmlAttr *attr = node->properties; 649 while(attr) { 650 if(attr->type == XML_ATTRIBUTE_NODE) { 651 char *value = get_attr_content(attr->children); 652 if(!value) { 653 print_error( 654 node->line, 655 "missing value for attribute %s\n", (char*)attr->name); 656 return 1; 657 } 658 if(xstreq(attr->name, "prefix")) { 659 prefix = value; 660 } else if(xstreq(attr->name, "uri")) { 661 uri = value; 662 } else { 663 print_error( 664 node->line, 665 "unexpected attribute %s\n", (char*)attr->name); 666 return 1; 667 } 668 } 669 attr = attr->next; 670 } 671 672 if(!prefix) { 673 print_error(node->line, "missing prefix attribute\n"); 674 return 1; 675 } 676 if(!uri) { 677 print_error(node->line, "missing uri attribute\n"); 678 return 1; 679 } 680 681 DavCfgNamespace *ns = cxMalloc(config->mp->allocator, sizeof(DavCfgNamespace)); 682 memset(ns, 0, sizeof(DavCfgNamespace)); 683 ns->node = node; 684 ns->prefix = cx_strdup_a(config->mp->allocator, cx_str(prefix)); 685 ns->uri = cx_strdup_a(config->mp->allocator, cx_str(uri)); 686 cx_linked_list_add( 687 (void**)list_begin, 688 (void**)list_end, 689 offsetof(DavCfgNamespace, prev), 690 offsetof(DavCfgNamespace, next), 691 ns); 692 693 return 0; 694 } 695 696 static int load_secretstore(DavConfig *config, xmlNode *node) { 697 // currently only one secretstore is supported 698 699 if(config->secretstore) { 700 return 1; 701 } 702 703 config->secretstore = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgSecretStore)); 704 705 node = node->children; 706 int error = 0; 707 while(node) { 708 if(node->type == XML_ELEMENT_NODE) { 709 if(xstreq(node->name, "unlock-command")) { 710 dav_cfg_string_set_value(config, &config->secretstore->unlock_cmd, node); 711 } else if(xstreq(node->name, "lock-command")) { 712 dav_cfg_string_set_value(config, &config->secretstore->lock_cmd, node); 713 } 714 } 715 node = node->next; 716 } 717 718 return error; 719 } 720 721 722 723 724 725 DavCfgRepository* dav_config_get_repository(DavConfig *config, cxstring name) { 726 DavCfgRepository *repo = config->repositories; 727 while(repo) { 728 if(!cx_strcmp(cx_strcast(repo->name.value), name)) { 729 return repo; 730 } 731 repo = repo->next; 732 } 733 return NULL; 734 } 735 736 DavCfgRepository* dav_config_url2repo(DavConfig *config, const char *url, char **path) { 737 cxmutstr p; 738 DavCfgRepository *repo = dav_config_url2repo_s(config, cx_str(url), &p); 739 *path = p.ptr; 740 return repo; 741 } 742 743 DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutstr *path) { 744 path->ptr = NULL; 745 path->length = 0; 746 747 int s; 748 if(cx_strprefix(url, CX_STR("http://"))) { 749 s = 7; 750 } else if(cx_strprefix(url, CX_STR("https://"))) { 751 s = 8; 752 } else { 753 s = 1; 754 } 755 756 // split URL into repository and path 757 cxstring r = cx_strsubs(url, s); 758 cxstring p = cx_strchr(r, '/'); 759 r = cx_strsubsl(url, 0, url.length-p.length); 760 if(p.length == 0) { 761 p = cx_strn("/", 1); 762 } 763 764 DavCfgRepository *repo = dav_config_get_repository(config, r); 765 if(repo) { 766 *path = cx_strdup(p); 767 } else { 768 // TODO: who is responsible for freeing this repository? 769 // how can the callee know, if he has to call free()? 770 repo = dav_repository_new(config); 771 repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR("")); 772 if(url.ptr[url.length-1] == '/') { 773 repo->url.value = cx_strdup_a(config->mp->allocator, url); 774 *path = cx_strdup(CX_STR("/")); 775 } else if (cx_strchr(url, '/').length > 0) { 776 // TODO: fix the following workaround after 777 // fixing the inconsistent behavior of util_url_*() 778 cxstring repo_url = util_url_base_s(url); 779 repo->url.value = cx_strdup_a(config->mp->allocator, repo_url); 780 *path = cx_strdup(util_url_path_s(url)); 781 } else { 782 repo->url.value = cx_strdup(url); 783 *path = cx_strdup(CX_STR("/")); 784 } 785 } 786 787 return repo; 788 } 789 790 int dav_config_keytype(DavCfgKeyType type) { 791 switch(type) { 792 default: break; 793 case DAV_KEY_TYPE_AES256: return DAV_KEY_AES256; 794 case DAV_KEY_TYPE_AES128: return DAV_KEY_AES128; 795 } 796 return 0; 797 } 798 799 int dav_config_register_keys(DavConfig *config, DavContext *ctx, dav_loadkeyfile_func loadkey) { 800 for(DavCfgKey *key=config->keys;key;key=key->next) { 801 char *file = cx_strdup_m(key->file.value).ptr; 802 cxmutstr keycontent = loadkey(file); 803 free(file); 804 805 // TODO: check key length 806 807 if(!keycontent.ptr) { 808 return 1; 809 } 810 811 DavKey *davkey = calloc(1, sizeof(DavKey)); 812 davkey->name = cx_strdup_m(key->name.value).ptr; 813 davkey->type = dav_config_keytype(key->type); 814 davkey->data = keycontent.ptr; 815 davkey->length = keycontent.length; 816 817 dav_context_add_key(ctx, davkey); 818 } 819 return 0; 820 } 821