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_node_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_node_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 static void set_xml_content(xmlNode *node, const char *content) { 101 xmlNode *child = node->children; 102 while(child) { 103 xmlNode *next = child->next; 104 xmlUnlinkNode(child); 105 xmlFreeNode(child); 106 child = next; 107 } 108 109 if(content) { 110 xmlChar *encoded = xmlEncodeSpecialChars(node->doc, (const xmlChar*)content); 111 if(encoded) { 112 xmlNodeSetContent(node, encoded); 113 xmlFree(encoded); 114 } 115 } 116 } 117 118 void dav_cfg_string_set_value(DavConfig *config, CfgString *str, xmlNode *parent, cxstring new_value, const char *nodename) { 119 if(str->value.ptr) { 120 cxFree(config->mp->allocator, str->value.ptr); 121 } 122 if(new_value.ptr) { 123 str->value = cx_strdup_a(config->mp->allocator, new_value); 124 } else { 125 str->value = cx_mutstrn(NULL, 0); 126 } 127 128 if(!str->node) { 129 str->node = xmlNewNode(NULL, (const xmlChar*) nodename); 130 xmlAddChild(parent, str->node); 131 } 132 set_xml_content(str->node, new_value.ptr); 133 } 134 135 void dav_cfg_bool_set_value(DavConfig *config, CfgBool *cbool, xmlNode *parent, DavBool new_value, const char *nodename) { 136 const char *content = new_value ? "true" : "false"; 137 cbool->value = new_value; 138 if(!cbool->node) { 139 cbool->node = xmlNewNode(NULL, (const xmlChar*) nodename); 140 xmlAddChild(parent, cbool->node); 141 } 142 set_xml_content(cbool->node, content); 143 } 144 145 void dav_cfg_int_set_value(DavConfig *config, CfgInt *cint, xmlNode *parent, int64_t new_value, const char *nodename) { 146 char content[32]; 147 snprintf(content, 32, "%" PRId64, new_value); 148 cint->value = new_value; 149 if(!cint->node) { 150 cint->node = xmlNewNode(NULL, (const xmlChar*) nodename); 151 xmlAddChild(parent, cint->node); 152 } 153 set_xml_content(cint->node, content); 154 } 155 156 void dav_cfg_uint_set_value(DavConfig *config, CfgUInt *cint, xmlNode *parent, uint64_t new_value, const char *nodename) { 157 char content[32]; 158 snprintf(content, 32, "%" PRIu64, new_value); 159 cint->value = new_value; 160 if(!cint->node) { 161 cint->node = xmlNewNode(NULL, (const xmlChar*) nodename); 162 xmlAddChild(parent, cint->node); 163 } 164 set_xml_content(cint->node, content); 165 } 166 167 void dav_cfg_string_remove(CfgString *str) { 168 if(str->node) { 169 xmlUnlinkNode(str->node); 170 xmlFreeNode(str->node); 171 str->node = NULL; 172 } 173 } 174 175 void dav_cfg_bool_remove(CfgBool *cbool) { 176 if(cbool->node) { 177 xmlUnlinkNode(cbool->node); 178 xmlFreeNode(cbool->node); 179 cbool->node = NULL; 180 } 181 } 182 183 void dav_cfg_int_remove(CfgInt *cint) { 184 if(cint->node) { 185 xmlUnlinkNode(cint->node); 186 xmlFreeNode(cint->node); 187 cint->node = NULL; 188 } 189 } 190 191 void dav_cfg_uint_remove(CfgUInt *cint) { 192 if(cint->node) { 193 xmlUnlinkNode(cint->node); 194 xmlFreeNode(cint->node); 195 cint->node = NULL; 196 } 197 } 198 199 200 DavConfig* dav_config_new(xmlDoc *doc) { 201 CxMempool *cfg_mp = cxMempoolCreate(128, NULL); 202 DavConfig *config = cxMalloc(cfg_mp->allocator, sizeof(DavConfig)); 203 memset(config, 0, sizeof(DavConfig)); 204 config->mp = cfg_mp; 205 206 if(!doc) { 207 doc = xmlNewDoc(BAD_CAST "1.0"); 208 xmlNode *root = xmlNewNode(NULL, BAD_CAST "configuration"); 209 xmlDocSetRootElement(doc, root); 210 } 211 config->doc = doc; 212 213 return config; 214 } 215 216 DavConfig* dav_config_load(cxmutstr xmlfilecontent, int *error) { 217 xmlDoc *doc = xmlReadMemory(xmlfilecontent.ptr, xmlfilecontent.length, NULL, NULL, 0); 218 if(!doc) { 219 if(error) { 220 *error = DAV_CONFIG_ERROR_XML; 221 } 222 return NULL; 223 } 224 225 DavConfig *config = dav_config_new(doc); 226 CxMempool *cfg_mp = config->mp; 227 cxMempoolRegister(cfg_mp, doc, (cx_destructor_func)xmlFreeDoc); 228 229 DavCfgRepository *repos_begin = NULL; 230 DavCfgRepository *repos_end = NULL; 231 DavCfgKey *keys_begin = NULL; 232 DavCfgKey *keys_end = NULL; 233 DavCfgNamespace *namespaces_begin = NULL; 234 DavCfgNamespace *namespaces_end = NULL; 235 236 xmlNode *xml_root = xmlDocGetRootElement(doc); 237 xmlNode *node = xml_root->children; 238 int ret = 0; 239 while(node && !ret) { 240 if(node->type == XML_ELEMENT_NODE) { 241 if(xstreq(node->name, "repository")) { 242 ret = load_repository(config, &repos_begin, &repos_end, node); 243 } else if(xstreq(node->name, "key")) { 244 ret = load_key(config, &keys_begin, &keys_end, node); 245 } else if (xstreq(node->name, "http-proxy")) { 246 config->http_proxy = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgProxy)); 247 ret = load_proxy(config, config->http_proxy, node, DAV_HTTP_PROXY); 248 } else if (xstreq(node->name, "https-proxy")) { 249 config->https_proxy = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgProxy)); 250 ret = load_proxy(config, config->https_proxy, node, DAV_HTTPS_PROXY); 251 } else if (xstreq(node->name, "namespace")) { 252 ret = load_namespace(config, &namespaces_begin, &namespaces_end, node); 253 } else if (xstreq(node->name, "secretstore")) { 254 ret = load_secretstore(config, node); 255 } else { 256 fprintf(stderr, "Unknown config element: %s\n", node->name); 257 ret = 1; 258 } 259 } 260 node = node->next; 261 } 262 263 config->repositories = repos_begin; 264 config->keys = keys_begin; 265 config->namespaces = namespaces_begin; 266 267 if(ret != 0 && error) { 268 *error = ret; 269 cxMempoolDestroy(cfg_mp); 270 } 271 272 return config; 273 } 274 275 void dav_config_free(DavConfig *config) { 276 cxMempoolDestroy(config->mp); 277 } 278 279 CxBuffer* dav_config2buf(DavConfig *config) { 280 xmlChar* xmlText = NULL; 281 int textLen = 0; 282 xmlDocDumpFormatMemory(config->doc, &xmlText, &textLen, 1); 283 284 if(!xmlText) { 285 return NULL; 286 } 287 288 CxBuffer *buf = cxBufferCreate(NULL, textLen, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 289 cxBufferWrite(xmlText, 1, textLen, buf); 290 xmlFree(xmlText); 291 return buf; 292 } 293 294 295 static int repo_add_config( 296 DavConfig *config, 297 DavCfgRepository *repo, 298 xmlNode* node) 299 { 300 unsigned short lineno = node->line; 301 char *key = (char*)node->name; 302 char *value = util_xml_get_text(node); 303 304 /* every key needs a value */ 305 if(!value) { 306 /* TODO: maybe this should only be reported, if the key is valid 307 * But this makes the code very ugly. 308 */ 309 print_error(lineno, "missing value for config element: %s\n", key); 310 return 1; 311 } 312 313 if(xstreq(key, "name")) { 314 dav_cfg_string_set_node_value(config, &repo->name, node); 315 } else if(xstreq(key, "url")) { 316 dav_cfg_string_set_node_value(config, &repo->url, node); 317 } else if(xstreq(key, "user")) { 318 dav_cfg_string_set_node_value(config, &repo->user, node); 319 } else if(xstreq(key, "password")) { 320 dav_cfg_string_set_node_value(config, &repo->password, node); 321 } else if(xstreq(key, "stored-user")) { 322 dav_cfg_string_set_node_value(config, &repo->stored_user, node); 323 } else if(xstreq(key, "default-key")) { 324 dav_cfg_string_set_node_value(config, &repo->default_key, node); 325 } else if(xstreq(key, "full-encryption")) { 326 dav_cfg_bool_set_node_value(config, &repo->full_encryption, node); 327 } else if(xstreq(key, "content-encryption")) { 328 dav_cfg_bool_set_node_value(config, &repo->content_encryption, node); 329 } else if(xstreq(key, "decrypt-content")) { 330 dav_cfg_bool_set_node_value(config, &repo->decrypt_content, node); 331 } else if(xstreq(key, "decrypt-name")) { 332 dav_cfg_bool_set_node_value(config, &repo->decrypt_name, node); 333 } else if(xstreq(key, "cert")) { 334 dav_cfg_string_set_node_value(config, &repo->cert, node); 335 } else if(xstreq(key, "verification")) { 336 dav_cfg_bool_set_node_value(config, &repo->verification, node); 337 } else if(xstreq(key, "ssl-version")) { 338 repo->ssl_version.node = node; 339 int ssl_version = dav_str2ssl_version((const char*)value); 340 if(ssl_version == -1) { 341 print_warning(lineno, "unknown ssl version: %s\n", value); 342 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT; 343 } else { 344 repo->ssl_version.value = ssl_version; 345 } 346 } else if(xstreq(key, "authmethods")) { 347 repo->authmethods.node = node; 348 repo->authmethods.value = CURLAUTH_NONE; 349 const char *delims = " \t\r\n"; 350 char *meths = strdup(value); 351 char *meth = strtok(meths, delims); 352 while (meth) { 353 if(xstrEQ(meth, "basic")) { 354 repo->authmethods.value |= CURLAUTH_BASIC; 355 } else if(xstrEQ(meth, "digest")) { 356 repo->authmethods.value |= CURLAUTH_DIGEST; 357 } else if(xstrEQ(meth, "negotiate")) { 358 repo->authmethods.value |= CURLAUTH_GSSNEGOTIATE; 359 } else if(xstrEQ(meth, "ntlm")) { 360 repo->authmethods.value |= CURLAUTH_NTLM; 361 } else if(xstrEQ(meth, "any")) { 362 repo->authmethods.value = CURLAUTH_ANY; 363 } else if(xstrEQ(meth, "none")) { 364 /* skip */ 365 } else { 366 print_warning(lineno, 367 "unknown authentication method: %s\n", meth); 368 } 369 meth = strtok(NULL, delims); 370 } 371 free(meths); 372 } else { 373 print_error(lineno, "unkown repository config element: %s\n", key); 374 return 1; 375 } 376 return 0; 377 } 378 379 int dav_str2ssl_version(const char *value) { 380 if(xstrEQ(value, "TLSv1")) { 381 return CURL_SSLVERSION_TLSv1; 382 } else if(xstrEQ(value, "SSLv2")) { 383 return CURL_SSLVERSION_SSLv2; 384 } else if(xstrEQ(value, "SSLv3")) { 385 return CURL_SSLVERSION_SSLv3; 386 } 387 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7034 388 else if(xstrEQ(value, "TLSv1.0")) { 389 return CURL_SSLVERSION_TLSv1_0; 390 } else if(xstrEQ(value, "TLSv1.1")) { 391 return CURL_SSLVERSION_TLSv1_1; 392 } else if(xstrEQ(value, "TLSv1.2")) { 393 return CURL_SSLVERSION_TLSv1_2; 394 } 395 #endif 396 #if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7052 397 else if(xstrEQ(value, "TLSv1.3")) { 398 return CURL_SSLVERSION_TLSv1_3; 399 } 400 #endif 401 return -1; 402 } 403 404 static int load_repository( 405 DavConfig *config, 406 DavCfgRepository **list_begin, 407 DavCfgRepository **list_end, 408 xmlNode *reponode) 409 { 410 DavCfgRepository *repo = dav_repository_new(config); 411 repo->node = reponode; 412 413 // add repo config from child nodes 414 xmlNode *node = reponode->children; 415 int ret = 0; 416 while(node && !ret) { 417 if(node->type == XML_ELEMENT_NODE) { 418 ret = repo_add_config(config, repo, node); 419 } 420 node = node->next; 421 } 422 423 // success: add repo to the configuration, error: free repo 424 if(ret) { 425 return 1; 426 } else { 427 cx_linked_list_add( 428 (void**)list_begin, 429 (void**)list_end, 430 offsetof(DavCfgRepository, prev), 431 offsetof(DavCfgRepository, next), 432 repo); 433 } 434 435 return 0; 436 } 437 438 static xmlNode* addXmlNode(xmlNode *node, const char *name, cxmutstr content) { 439 xmlNode *text1 = xmlNewDocText(node->doc, BAD_CAST "\t\t"); 440 xmlAddChild(node, text1); 441 442 cxmutstr ctn = cx_strdup(cx_strcast(content)); 443 xmlNode *newNode = xmlNewChild(node, NULL, BAD_CAST name, BAD_CAST ctn.ptr); 444 free(ctn.ptr); 445 446 xmlNode *text2 = xmlNewDocText(node->doc, BAD_CAST "\n"); 447 xmlAddChild(node, text2); 448 449 return newNode; 450 } 451 452 void dav_config_add_repository(DavConfig *config, DavCfgRepository *repo) { 453 if(repo->node) { 454 fprintf(stderr, "Error: dav_config_add_repository: node already exists\n"); 455 return; 456 } 457 458 xmlNode *repoNode = xmlNewNode(NULL, BAD_CAST "repository"); 459 xmlNode *rtext1 = xmlNewDocText(config->doc, BAD_CAST "\n"); 460 xmlAddChild(repoNode, rtext1); 461 repo->node = repoNode; 462 if(repo->name.value.ptr) { 463 repo->name.node = addXmlNode(repoNode, "name", repo->name.value); 464 } 465 if(repo->url.value.ptr) { 466 repo->url.node = addXmlNode(repoNode, "url", repo->url.value); 467 } 468 if(repo->user.value.ptr) { 469 repo->user.node = addXmlNode(repoNode, "user", repo->user.value); 470 } 471 if(repo->password.value.ptr) { 472 repo->password.node = addXmlNode(repoNode, "password", repo->password.value); 473 } 474 475 if(repo->stored_user.value.ptr) { 476 repo->stored_user.node = addXmlNode(repoNode, "stored-user", repo->stored_user.value); 477 } 478 if(repo->default_key.value.ptr) { 479 repo->default_key.node = addXmlNode(repoNode, "default-key", repo->default_key.value); 480 } 481 if(repo->cert.value.ptr) { 482 repo->cert.node = addXmlNode(repoNode, "cert", repo->cert.value); 483 } 484 485 // TODO: implement booleans 486 487 // indent closing tag 488 xmlNode *rtext2 = xmlNewDocText(config->doc, BAD_CAST "\t"); 489 xmlAddChild(repoNode, rtext2); 490 491 // add repository to internal list 492 DavCfgRepository **list_begin = &config->repositories; 493 cx_linked_list_add( 494 (void**)list_begin, 495 NULL, 496 offsetof(DavCfgRepository, prev), 497 offsetof(DavCfgRepository, next), 498 repo); 499 500 // add repository element to the xml document 501 xmlNode *xml_root = xmlDocGetRootElement(config->doc); 502 503 xmlNode *text1 = xmlNewDocText(config->doc, BAD_CAST "\n\t"); 504 xmlAddChild(xml_root, text1); 505 506 xmlAddChild(xml_root, repoNode); 507 508 xmlNode *text2 = xmlNewDocText(config->doc, BAD_CAST "\n"); 509 xmlAddChild(xml_root, text2); 510 } 511 512 DavCfgRepository* dav_repository_new(DavConfig *config) { 513 DavCfgRepository *repo = cxMalloc(config->mp->allocator, sizeof(DavCfgRepository)); 514 memset(repo, 0, sizeof(DavCfgRepository)); 515 repo->decrypt_name.value = false; 516 repo->decrypt_content.value = true; 517 repo->decrypt_properties.value = false; 518 repo->verification.value = true; 519 repo->ssl_version.value = CURL_SSLVERSION_DEFAULT; 520 repo->authmethods.value = CURLAUTH_BASIC; 521 return repo; 522 } 523 524 void dav_repository_free(DavConfig *config, DavCfgRepository *repo) { 525 // TODO 526 } 527 528 void dav_repository_remove_and_free(DavConfig *config, DavCfgRepository *repo) { 529 if(repo->prev) { 530 repo->prev->next = repo->next; 531 } 532 if(repo->next) { 533 repo->next->prev = repo->prev; 534 } 535 536 if(repo->node) { 537 // TODO: remove newline after repo node 538 539 xmlUnlinkNode(repo->node); 540 xmlFreeNode(repo->node); 541 } 542 } 543 544 int dav_repository_get_flags(DavCfgRepository *repo) { 545 int flags = 0; 546 547 DavBool encrypt_content = FALSE; 548 DavBool encrypt_name = FALSE; 549 DavBool encrypt_properties = FALSE; 550 DavBool decrypt_content = FALSE; 551 DavBool decrypt_name = FALSE; 552 DavBool decrypt_properties = FALSE; 553 if(repo->full_encryption.value) { 554 encrypt_content = TRUE; 555 encrypt_name = TRUE; 556 encrypt_properties = TRUE; 557 decrypt_content = TRUE; 558 decrypt_name = TRUE; 559 decrypt_properties = TRUE; 560 } else if(repo->content_encryption.value) { 561 encrypt_content = TRUE; 562 decrypt_content = TRUE; 563 } 564 565 if(decrypt_content) { 566 flags |= DAV_SESSION_DECRYPT_CONTENT; 567 } 568 if(decrypt_name) { 569 flags |= DAV_SESSION_DECRYPT_NAME; 570 } 571 if(decrypt_properties) { 572 flags |= DAV_SESSION_DECRYPT_PROPERTIES; 573 } 574 if(encrypt_content) { 575 flags |= DAV_SESSION_ENCRYPT_CONTENT; 576 } 577 if(encrypt_name) { 578 flags |= DAV_SESSION_ENCRYPT_NAME; 579 } 580 if(encrypt_properties) { 581 flags |= DAV_SESSION_ENCRYPT_PROPERTIES; 582 } 583 return flags; 584 } 585 586 void dav_repository_set_url(DavConfig *config, DavCfgRepository *repo, cxstring newurl) { 587 if(repo->url.value.ptr) { 588 cxFree(config->mp->allocator, repo->url.value.ptr); 589 } 590 repo->url.value = cx_strdup_a(config->mp->allocator, newurl); 591 } 592 593 void dav_repository_set_auth(DavConfig *config, DavCfgRepository *repo, cxstring user, cxstring password) { 594 const CxAllocator *a = config->mp->allocator; 595 if(user.length > 0) { 596 repo->user.value = cx_strdup_a(a, user); 597 } 598 if(password.length > 0) { 599 char *pwenc = util_base64encode(password.ptr, password.length); 600 repo->password.value = cx_strdup_a(a, cx_str(pwenc)); 601 free(pwenc); 602 } 603 } 604 605 cxmutstr dav_repository_get_decodedpassword(DavCfgRepository *repo) { 606 cxmutstr pw = { NULL, 0 }; 607 if(repo->password.value.ptr) { 608 pw = cx_mutstr(util_base64decode(repo->password.value.ptr)); 609 } 610 return pw; 611 } 612 613 614 void dav_config_add_key(DavConfig *config, DavCfgKey *key) { 615 cx_linked_list_add( 616 (void**)&config->keys, 617 NULL, 618 offsetof(DavCfgKey, prev), 619 offsetof(DavCfgKey, next), 620 key); 621 622 if(key->node) { 623 fprintf(stderr, "Error: dav_config_add_key: node already exists\n"); 624 return; 625 } 626 627 xmlNode *keyNode = xmlNewNode(NULL, BAD_CAST "key"); 628 xmlNode *rtext1 = xmlNewDocText(config->doc, BAD_CAST "\n"); 629 xmlAddChild(keyNode, rtext1); 630 key->node = keyNode; 631 632 if(key->name.value.ptr) { 633 key->name.node = addXmlNode(keyNode, "name", key->name.value); 634 } 635 const char *type = dav_config_keytype_str(key->type); 636 if(type) { 637 key->type_node = addXmlNode(keyNode, "type", cx_mutstr((char*)type)); 638 } 639 if(key->file.value.ptr) { 640 key->file.node = addXmlNode(keyNode, "file", key->file.value); 641 } 642 643 // indent closing tag 644 xmlNode *rtext2 = xmlNewDocText(config->doc, BAD_CAST "\t"); 645 xmlAddChild(keyNode, rtext2); 646 647 // add key element to the xml document 648 xmlNode *xml_root = xmlDocGetRootElement(config->doc); 649 650 xmlNode *text1 = xmlNewDocText(config->doc, BAD_CAST "\n\t"); 651 xmlAddChild(xml_root, text1); 652 653 xmlAddChild(xml_root, keyNode); 654 655 xmlNode *text2 = xmlNewDocText(config->doc, BAD_CAST "\n"); 656 xmlAddChild(xml_root, text2); 657 } 658 659 DavCfgKey* dav_key_new(DavConfig *config) { 660 DavCfgKey *key = cxMalloc(config->mp->allocator, sizeof(DavCfgKey)); 661 memset(key, 0, sizeof(DavCfgKey)); 662 key->type = DAV_KEY_TYPE_AES256; 663 return key; 664 } 665 666 void dav_key_remove_and_free(DavConfig *config, DavCfgKey *key) { 667 cx_linked_list_remove( 668 (void**)&config->keys, 669 NULL, 670 offsetof(DavCfgKey, prev), 671 offsetof(DavCfgKey, next), 672 key); 673 if(key->node) { 674 // TODO: remove newline after key node 675 676 xmlUnlinkNode(key->node); 677 xmlFreeNode(key->node); 678 } 679 } 680 681 682 static int load_key( 683 DavConfig *config, 684 DavCfgKey **list_begin, 685 DavCfgKey **list_end, 686 xmlNode *keynode) 687 { 688 xmlNode *node = keynode->children; 689 DavCfgKey *key = cxMalloc(config->mp->allocator, sizeof(DavCfgKey)); 690 memset(key, 0, sizeof(DavCfgKey)); 691 key->type = DAV_KEY_TYPE_AES256; 692 key->node = keynode; 693 694 int error = 0; 695 while(node) { 696 if(node->type == XML_ELEMENT_NODE) { 697 if(xstreq(node->name, "name")) { 698 dav_cfg_string_set_node_value(config, &key->name, node); 699 } else if(xstreq(node->name, "file")) { 700 dav_cfg_string_set_node_value(config, &key->file, node); 701 } else if(xstreq(node->name, "type")) { 702 const char *value = util_xml_get_text(node); 703 key->type_node = node; 704 if(!strcmp(value, "aes128")) { 705 key->type = DAV_KEY_TYPE_AES128; 706 } else if(!strcmp(value, "aes256")) { 707 key->type = DAV_KEY_TYPE_AES256; 708 } else { 709 print_error(node->line, "unknown key type %s\n", value); 710 error = 1; 711 } 712 } else { 713 key->unknown_elements++; 714 } 715 716 } 717 node = node->next; 718 } 719 720 if(!key->name.value.ptr) { 721 error = 1; 722 } 723 724 if(!error) { 725 error = 0; 726 size_t expected_length = 0; 727 if(key->type == DAV_KEY_TYPE_AES128) { 728 expected_length = 16; 729 } 730 if(key->type == DAV_KEY_TYPE_AES256) { 731 expected_length = 32; 732 } 733 /* 734 if(key->length < expected_length) { 735 print_error(keynode->line, "key %s is too small (%zu < %zu)\n", 736 key->name, 737 key->length, 738 expected_length); 739 error = 1; 740 } 741 742 // add key to context 743 if(!error) { 744 cxMapPut(keys, cx_hash_key_str(key->name), key); 745 dav_context_add_key(context, key); 746 } 747 */ 748 } 749 750 // cleanup 751 if(error) { 752 return 1; 753 } else { 754 // add key to the configuration 755 cx_linked_list_add( 756 (void**)list_begin, 757 (void**)list_end, 758 offsetof(DavCfgKey, prev), 759 offsetof(DavCfgKey, next), 760 key); 761 762 return 0; 763 } 764 } 765 766 static int load_proxy( 767 DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type) 768 { 769 const char *stype; 770 if(type == DAV_HTTPS_PROXY) { 771 stype = "https"; 772 } else if(type == DAV_HTTP_PROXY) { 773 stype = "http"; 774 } else { 775 fprintf(stderr, "unknown proxy type\n"); 776 return 1; 777 } 778 779 if(!proxy) { 780 // no xml error - so report this directly via fprintf 781 fprintf(stderr, "no memory reserved for %s proxy.\n", stype); 782 return 1; 783 } 784 785 xmlNode *node = proxynode->children; 786 int ret = 0; 787 while(node && !ret) { 788 if(node->type == XML_ELEMENT_NODE) { 789 int reportmissingvalue = 0; 790 if(xstreq(node->name, "url")) { 791 reportmissingvalue = dav_cfg_string_set_node_value(config, &proxy->url, node); 792 } else if(xstreq(node->name, "user")) { 793 reportmissingvalue = dav_cfg_string_set_node_value(config, &proxy->user, node); 794 } else if(xstreq(node->name, "password")) { 795 reportmissingvalue = dav_cfg_string_set_node_value(config, &proxy->password, node); 796 } else if(xstreq(node->name, "no")) { 797 reportmissingvalue = dav_cfg_string_set_node_value(config, &proxy->noproxy, node); 798 } else { 799 proxy->unknown_elements++; 800 } 801 802 if (reportmissingvalue) { 803 print_error(node->line, 804 "missing value for proxy configuration element: %s\n", 805 node->name); 806 ret = 1; 807 break; 808 } 809 } 810 node = node->next; 811 } 812 813 if(!ret && !proxy->url.value.ptr) { 814 print_error(proxynode->line, "missing url for %s proxy.\n", stype); 815 return 1; 816 } 817 818 return ret; 819 } 820 821 static char* get_attr_content(xmlNode *node) { 822 // TODO: remove code duplication (util_xml_get_text) 823 while(node) { 824 if(node->type == XML_TEXT_NODE) { 825 return (char*)node->content; 826 } 827 node = node->next; 828 } 829 return NULL; 830 } 831 832 static int load_namespace( 833 DavConfig *config, 834 DavCfgNamespace **list_begin, 835 DavCfgNamespace **list_end, 836 xmlNode *node) 837 { 838 const char *prefix = NULL; 839 const char *uri = NULL; 840 xmlAttr *attr = node->properties; 841 while(attr) { 842 if(attr->type == XML_ATTRIBUTE_NODE) { 843 char *value = get_attr_content(attr->children); 844 if(!value) { 845 print_error( 846 node->line, 847 "missing value for attribute %s\n", (char*)attr->name); 848 return 1; 849 } 850 if(xstreq(attr->name, "prefix")) { 851 prefix = value; 852 } else if(xstreq(attr->name, "uri")) { 853 uri = value; 854 } else { 855 print_error( 856 node->line, 857 "unexpected attribute %s\n", (char*)attr->name); 858 return 1; 859 } 860 } 861 attr = attr->next; 862 } 863 864 if(!prefix) { 865 print_error(node->line, "missing prefix attribute\n"); 866 return 1; 867 } 868 if(!uri) { 869 print_error(node->line, "missing uri attribute\n"); 870 return 1; 871 } 872 873 DavCfgNamespace *ns = cxMalloc(config->mp->allocator, sizeof(DavCfgNamespace)); 874 memset(ns, 0, sizeof(DavCfgNamespace)); 875 ns->node = node; 876 ns->prefix = cx_strdup_a(config->mp->allocator, cx_str(prefix)); 877 ns->uri = cx_strdup_a(config->mp->allocator, cx_str(uri)); 878 cx_linked_list_add( 879 (void**)list_begin, 880 (void**)list_end, 881 offsetof(DavCfgNamespace, prev), 882 offsetof(DavCfgNamespace, next), 883 ns); 884 885 return 0; 886 } 887 888 static int load_secretstore(DavConfig *config, xmlNode *node) { 889 // currently only one secretstore is supported 890 891 if(config->secretstore) { 892 return 1; 893 } 894 895 config->secretstore = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgSecretStore)); 896 897 node = node->children; 898 int error = 0; 899 while(node) { 900 if(node->type == XML_ELEMENT_NODE) { 901 if(xstreq(node->name, "unlock-command")) { 902 dav_cfg_string_set_node_value(config, &config->secretstore->unlock_cmd, node); 903 } else if(xstreq(node->name, "lock-command")) { 904 dav_cfg_string_set_node_value(config, &config->secretstore->lock_cmd, node); 905 } 906 } 907 node = node->next; 908 } 909 910 return error; 911 } 912 913 914 915 916 917 DavCfgRepository* dav_config_get_repository(DavConfig *config, cxstring name) { 918 if(!config) { 919 return NULL; 920 } 921 DavCfgRepository *repo = config->repositories; 922 while(repo) { 923 if(!cx_strcmp(cx_strcast(repo->name.value), name)) { 924 return repo; 925 } 926 repo = repo->next; 927 } 928 return NULL; 929 } 930 931 DavCfgRepository* dav_config_url2repo(DavConfig *config, const char *url, char **path) { 932 cxmutstr p; 933 DavCfgRepository *repo = dav_config_url2repo_s(config, cx_str(url), &p); 934 *path = p.ptr; 935 return repo; 936 } 937 938 DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutstr *path) { 939 path->ptr = NULL; 940 path->length = 0; 941 942 int s; 943 if(cx_strprefix(url, CX_STR("http://"))) { 944 s = 7; 945 } else if(cx_strprefix(url, CX_STR("https://"))) { 946 s = 8; 947 } else { 948 s = 1; 949 } 950 951 // split URL into repository and path 952 cxstring r = cx_strsubs(url, s); 953 cxstring p = cx_strchr(r, '/'); 954 r = cx_strsubsl(url, 0, url.length-p.length); 955 if(p.length == 0) { 956 p = cx_strn("/", 1); 957 } 958 959 DavCfgRepository *repo = dav_config_get_repository(config, r); 960 if(repo) { 961 *path = cx_strdup(p); 962 } else { 963 // TODO: who is responsible for freeing this repository? 964 // how can the callee know, if he has to call free()? 965 repo = dav_repository_new(config); 966 repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR("")); 967 if(url.ptr[url.length-1] == '/') { 968 repo->url.value = cx_strdup_a(config->mp->allocator, url); 969 *path = cx_strdup(CX_STR("/")); 970 } else if (cx_strchr(url, '/').length > 0) { 971 // TODO: fix the following workaround after 972 // fixing the inconsistent behavior of util_url_*() 973 cxstring repo_url = util_url_base_s(url); 974 repo->url.value = cx_strdup_a(config->mp->allocator, repo_url); 975 *path = cx_strdup(util_url_path_s(url)); 976 } else { 977 repo->url.value = cx_strdup(url); 978 *path = cx_strdup(CX_STR("/")); 979 } 980 } 981 982 return repo; 983 } 984 985 int dav_config_keytype(DavCfgKeyType type) { 986 switch(type) { 987 default: break; 988 case DAV_KEY_TYPE_AES256: return DAV_KEY_AES256; 989 case DAV_KEY_TYPE_AES128: return DAV_KEY_AES128; 990 } 991 return 0; 992 } 993 994 const char* dav_config_keytype_str(DavCfgKeyType type) { 995 switch(type) { 996 default: break; 997 case DAV_KEY_TYPE_AES256: return "aes256"; 998 case DAV_KEY_TYPE_AES128: return "aes128"; 999 } 1000 return NULL; 1001 } 1002 1003 int dav_config_register_keys(DavConfig *config, DavContext *ctx, dav_loadkeyfile_func loadkey) { 1004 for(DavCfgKey *key=config->keys;key;key=key->next) { 1005 char *file = cx_strdup_m(key->file.value).ptr; 1006 cxmutstr keycontent = loadkey(file); 1007 free(file); 1008 1009 // TODO: check key length 1010 1011 if(!keycontent.ptr) { 1012 return 1; 1013 } 1014 1015 DavKey *davkey = calloc(1, sizeof(DavKey)); 1016 davkey->name = cx_strdup_m(key->name.value).ptr; 1017 davkey->type = dav_config_keytype(key->type); 1018 davkey->data = keycontent.ptr; 1019 davkey->length = keycontent.length; 1020 1021 dav_context_add_key(ctx, davkey); 1022 } 1023 return 0; 1024 } 1025 1026 int dav_config_register_namespaces(DavConfig *config, DavContext *ctx) { 1027 DavCfgNamespace *ns = config->namespaces; 1028 while(ns) { 1029 dav_add_namespace(ctx, ns->prefix.ptr, ns->uri.ptr); 1030 ns = ns->next; 1031 } 1032 return 0; 1033 } 1034