dav/config.c

changeset 796
81e0f67386a6
parent 795
05647e862a17
child 798
d7f5067a27ce
equal deleted inserted replaced
795:05647e862a17 796:81e0f67386a6
143 } 143 }
144 } 144 }
145 return 1; 145 return 1;
146 } 146 }
147 147
148 xmlDoc *doc = xmlReadFile(file, NULL, 0);
149
150 cxmutstr config_content = config_load_file(file); 148 cxmutstr config_content = config_load_file(file);
151 int config_error; 149 int config_error;
152 davconfig = dav_config_load(config_content, &config_error); 150 davconfig = dav_config_load(config_content, &config_error);
153 free(config_content.ptr); 151 free(config_content.ptr);
154
155 free(file); 152 free(file);
156 if(!doc) { 153
154 if(!davconfig) {
157 fprintf(stderr, "Cannot load config.xml\n"); 155 fprintf(stderr, "Cannot load config.xml\n");
158 return 1; 156 return 1;
159 } 157 }
160 158
161 xmlNode *xml_root = xmlDocGetRootElement(doc); 159 return dav_config_register_keys(davconfig, ctx, load_key_file);
162 xmlNode *node = xml_root->children;
163 int ret = 0;
164 while(node && !ret) {
165 if(node->type == XML_ELEMENT_NODE) {
166 if(xstreq(node->name, "repository")) {
167 ret = load_repository(node);
168 } else if(xstreq(node->name, "key")) {
169 ret = load_key(node);
170 } else if (xstreq(node->name, "http-proxy")) {
171 ret = load_proxy(ctx->http_proxy, node, HTTP_PROXY);
172 } else if (xstreq(node->name, "https-proxy")) {
173 ret = load_proxy(ctx->https_proxy, node, HTTPS_PROXY);
174 } else if (xstreq(node->name, "namespace")) {
175 ret = load_namespace(node);
176 } else if (xstreq(node->name, "secretstore")) {
177 ret = load_secretstore(node);
178 } else {
179 fprintf(stderr, "Unknown config element: %s\n", node->name);
180 ret = 1;
181 }
182 }
183 node = node->next;
184 }
185
186 xmlFreeDoc(doc);
187 return ret;
188 } 160 }
189 161
190 DavConfig* get_config(void) { 162 DavConfig* get_config(void) {
191 return davconfig; 163 return davconfig;
192 } 164 }
219 191
220 return 0; 192 return 0;
221 } 193 }
222 194
223 void free_config(void) { 195 void free_config(void) {
224 if(repos) { 196 if(davconfig) {
225 CxIterator i = cxMapIteratorValues(repos); 197 dav_config_free(davconfig);
226 Repository *repo;
227 cx_foreach(Repository*, repo, i) {
228 if(repo->default_key) {
229 free(repo->default_key);
230 }
231 if(repo->name) {
232 free(repo->name);
233 }
234 if(repo->password) {
235 free(repo->password);
236 }
237 if(repo->url) {
238 free(repo->url);
239 }
240 if(repo->user) {
241 free(repo->user);
242 }
243 if(repo->cert) {
244 free(repo->cert);
245 }
246 free(repo);
247 }
248 cxMapDestroy(repos);
249 }
250 if(keys) {
251 cxMapDestroy(keys);
252 }
253 }
254
255 Repository* repository_new(void) {
256 Repository *repo = calloc(1, sizeof(Repository));
257 repo->encrypt_name = false;
258 repo->encrypt_content = false;
259 repo->encrypt_properties = false;
260 repo->decrypt_name = false;
261 repo->decrypt_content = true;
262 repo->decrypt_properties = false;
263 repo->verification = true;
264 repo->ssl_version = CURL_SSLVERSION_DEFAULT;
265 repo->authmethods = CURLAUTH_BASIC;
266 return repo;
267 }
268
269 static int repo_add_config(Repository *repo, const xmlNode* node) {
270 unsigned short lineno = node->line;
271 char *key = (char*)node->name;
272 char *value = util_xml_get_text(node);
273
274 /* every key needs a value */
275 if(!value) {
276 /* TODO: maybe this should only be reported, if the key is valid
277 * But this makes the code very ugly.
278 */
279 print_error(lineno, "missing value for config element: %s\n", key);
280 return 1;
281 }
282
283 if(xstreq(key, "name")) {
284 repo->name = strdup(value);
285 } else if(xstreq(key, "url")) {
286 repo->url = strdup(value);
287 } else if(xstreq(key, "user")) {
288 repo->user = strdup(value);
289 } else if(xstreq(key, "password")) {
290 repo->password = util_base64decode(value);
291 } else if(xstreq(key, "stored-user")) {
292 repo->stored_user = strdup(value);
293 } else if(xstreq(key, "default-key")) {
294 repo->default_key = strdup(value);
295 } else if(xstreq(key, "full-encryption")) {
296 if(util_getboolean(value)) {
297 repo->encrypt_name = true;
298 repo->encrypt_content = true;
299 repo->encrypt_properties = true;
300 repo->decrypt_name = true;
301 repo->decrypt_content = true;
302 repo->decrypt_properties = true;
303 }
304 } else if(xstreq(key, "content-encryption")) {
305 if(util_getboolean(value)) {
306 repo->encrypt_content = true;
307 repo->decrypt_content = true;
308 } else {
309 repo->encrypt_content = false;
310 }
311 } else if(xstreq(key, "decrypt-content")) {
312 repo->decrypt_content = util_getboolean(value);
313 } else if(xstreq(key, "decrypt-name")) {
314 repo->decrypt_name = util_getboolean(value);
315 } else if(xstreq(key, "cert")) {
316 char *configdir = util_concat_path(ENV_HOME, ".dav");
317 char *certfile = util_concat_path(configdir, value);
318 repo->cert = certfile;
319 free(configdir);
320 } else if(xstreq(key, "verification")) {
321 repo->verification = util_getboolean(value);
322 } else if(xstreq(key, "ssl-version")) {
323 if(xstrEQ(value, "TLSv1")) {
324 repo->ssl_version = CURL_SSLVERSION_TLSv1;
325 } else if(xstrEQ(value, "SSLv2")) {
326 repo->ssl_version = CURL_SSLVERSION_SSLv2;
327 } else if(xstrEQ(value, "SSLv3")) {
328 repo->ssl_version = CURL_SSLVERSION_SSLv3;
329 }
330 #if LIBCURL_VERSION_MAJOR >= 7
331 #if LIBCURL_VERSION_MINOR >= 34
332 else if(xstrEQ(value, "TLSv1.0")) {
333 repo->ssl_version = CURL_SSLVERSION_TLSv1_0;
334 } else if(xstrEQ(value, "TLSv1.1")) {
335 repo->ssl_version = CURL_SSLVERSION_TLSv1_1;
336 } else if(xstrEQ(value, "TLSv1.2")) {
337 repo->ssl_version = CURL_SSLVERSION_TLSv1_2;
338 }
339 #endif
340 #if LIBCURL_VERSION_MINOR >= 52
341 else if(xstrEQ(value, "TLSv1.3")) {
342 repo->ssl_version = CURL_SSLVERSION_TLSv1_3;
343 }
344 #endif
345 #endif
346 else {
347 print_warning(lineno, "unknown ssl version: %s\n", value);
348 }
349 } else if(xstreq(key, "authmethods")) {
350 repo->authmethods = CURLAUTH_NONE;
351 const char *delims = " \t\r\n";
352 char *meths = strdup(value);
353 char *meth = strtok(meths, delims);
354 while (meth) {
355 if(xstrEQ(meth, "basic")) {
356 repo->authmethods |= CURLAUTH_BASIC;
357 } else if(xstrEQ(meth, "digest")) {
358 repo->authmethods |= CURLAUTH_DIGEST;
359 } else if(xstrEQ(meth, "negotiate")) {
360 repo->authmethods |= CURLAUTH_GSSNEGOTIATE;
361 } else if(xstrEQ(meth, "ntlm")) {
362 repo->authmethods |= CURLAUTH_NTLM;
363 } else if(xstrEQ(meth, "any")) {
364 repo->authmethods = CURLAUTH_ANY;
365 } else if(xstrEQ(meth, "none")) {
366 /* skip */
367 } else {
368 print_warning(lineno,
369 "unknown authentication method: %s\n", meth);
370 }
371 meth = strtok(NULL, delims);
372 }
373 free(meths);
374 } else {
375 print_error(lineno, "unkown repository config element: %s\n", key);
376 return 1;
377 }
378 return 0;
379 }
380
381 int load_repository(const xmlNode *reponode) {
382 Repository *repo = repository_new();
383 {
384 xmlNode *node = reponode->children;
385 int ret = 0;
386 while(node && !ret) {
387 if(node->type == XML_ELEMENT_NODE) {
388 ret = repo_add_config(repo, node);
389 }
390 node = node->next;
391 }
392 if(ret) {
393 free(repo);
394 return 1;
395 }
396 }
397
398 if(!repo->name) {
399 print_error(reponode->line, "missing name for repository.\n");
400 return 1;
401 }
402 if(!repo->url) {
403 print_error(reponode->line,
404 "missing url for repository '%s'.\n", repo->name);
405 return 1;
406 }
407
408 cxMapPut(repos, cx_hash_key_str(repo->name), repo);
409 return 0;
410 }
411
412 int load_proxy(DavProxy *proxy, const xmlNode *proxynode, int type) {
413 const char *stype;
414 if(type == HTTPS_PROXY) {
415 stype = "https";
416 } else if(type == HTTP_PROXY) {
417 stype = "http";
418 }
419
420 if(!proxy) {
421 // no xml error - so report this directly via fprintf
422 fprintf(stderr, "no memory reserved for %s proxy.\n", stype);
423 return 1;
424 }
425
426 xmlNode *node = proxynode->children;
427 int ret = 0;
428 while(node && !ret) {
429 if(node->type == XML_ELEMENT_NODE) {
430 char *value = util_xml_get_text(node);
431 int reportmissingvalue = 0;
432 if(xstreq(node->name, "url")) {
433 if(!(reportmissingvalue = !value)) {
434 proxy->url = strdup(value);
435 }
436 } else if(xstreq(node->name, "user")) {
437 if(!(reportmissingvalue = !value)) {
438 proxy->username = strdup(value);
439 }
440 } else if(xstreq(node->name, "password")) {
441 if(!(reportmissingvalue = !value)) {
442 proxy->password = util_base64decode(value);
443 }
444 } else if(xstreq(node->name, "no")) {
445 if(!(reportmissingvalue = !value)) {
446 proxy->no_proxy = strdup(value);
447 }
448 } else {
449 print_error(node->line,
450 "invalid element for proxy config: %s\n", node->name);
451 ret = 1;
452 }
453 if (reportmissingvalue) {
454 print_error(node->line,
455 "missing value for proxy configuration element: %s\n",
456 node->name);
457 ret = 1;
458 }
459 }
460 node = node->next;
461 }
462
463 if(!ret && !proxy->url) {
464 print_error(proxynode->line, "missing url for %s proxy.\n", stype);
465 return 1;
466 }
467
468 return ret;
469 }
470
471 int load_key(const xmlNode *keynode) {
472 xmlNode *node = keynode->children;
473 Key *key = calloc(1, sizeof(Key));
474 key->type = KEY_AES256;
475
476 int error = 0;
477 while(node) {
478 if(node->type == XML_ELEMENT_NODE) {
479 char *value = util_xml_get_text(node);
480 if(!value) {
481 // next
482 } else if(xstreq(node->name, "name")) {
483 key->name = strdup(value);
484 } else if(xstreq(node->name, "file")) {
485 // load key file
486 cxmutstr key_data = load_key_file(value);
487 if(key_data.length > 0) {
488 key->data = key_data.ptr;
489 key->length = key_data.length;
490 } else {
491 print_error(node->line,
492 "cannot get key from file: %s\n", value);
493 error = 1;
494 }
495 } else if(xstreq(node->name, "type")) {
496 if(!strcmp(value, "aes128")) {
497 key->type = KEY_AES128;
498 } else if(!strcmp(value, "aes256")) {
499 key->type = KEY_AES256;
500 } else {
501 print_error(node->line, "unknown key type %s\n", value);
502 error = 1;
503 }
504 }
505
506 }
507 node = node->next;
508 }
509
510 if(!error && key->name) {
511 error = 0;
512 size_t expected_length = 0;
513 if(key->type == KEY_AES128) {
514 expected_length = 16;
515 }
516 if(key->type == KEY_AES256) {
517 expected_length = 32;
518 }
519 if(key->length < expected_length) {
520 print_error(keynode->line, "key %s is too small (%zu < %zu)\n",
521 key->name,
522 key->length,
523 expected_length);
524 error = 1;
525 }
526
527 // add key to context
528 if(!error) {
529 cxMapPut(keys, cx_hash_key_str(key->name), key);
530 dav_context_add_key(context, key);
531 }
532 }
533
534 // cleanup
535 if(error) {
536 if(key->data) {
537 free(key->data);
538 }
539 free(key);
540 return 1;
541 } else {
542 return 0;
543 } 198 }
544 } 199 }
545 200
546 cxmutstr load_key_file(const char *filename) { 201 cxmutstr load_key_file(const char *filename) {
547 cxmutstr k; 202 cxmutstr k;
548 k.ptr = NULL; 203 k.ptr = NULL;
549 k.length = 0; 204 k.length = 0;
550 205
551 FILE *file = NULL; 206 FILE *file = NULL;
552 if(filename[0] == '/') { 207 if(filename[0] == '/') {
553 file = fopen(filename, "r"); 208 file = sys_fopen(filename, "r");
554 } else { 209 } else {
555 char *path = util_concat_path(ENV_HOME, ".dav/"); 210 char *path = util_concat_path(ENV_HOME, ".dav/");
556 char *p2 = util_concat_path(path, filename); 211 char *p2 = util_concat_path(path, filename);
557 file = fopen(p2, "r"); 212 file = sys_fopen(p2, "r");
558 free(path); 213 free(path);
559 free(p2); 214 free(p2);
560 } 215 }
561 216
562 if(!file) { 217 if(!file) {
218 fprintf(stderr, "Error: cannot load keyfile %s\n", filename);
563 return k; 219 return k;
564 } 220 }
565 221
566 char *data = malloc(256); 222 char *data = malloc(256);
567 size_t r = fread(data, 1, 256, file); 223 size_t r = fread(data, 1, 256, file);
568 k.ptr = data; 224 k.ptr = data;
569 k.length = r; 225 k.length = r;
570 226
571 fclose(file); 227 fclose(file);
572 return k; 228 return k;
229 }
230
231 int list_repositories(void) {
232 if(!davconfig) {
233 return 1;
234 }
235 for(DavCfgRepository *repo=davconfig->repositories;repo;repo=repo->next) {
236 printf("%.*s\n", (int)repo->name.value.length, repo->name.value.ptr);
237 }
238 return 0;
573 } 239 }
574 240
575 static char* get_attr_content(xmlNode *node) { 241 static char* get_attr_content(xmlNode *node) {
576 // TODO: remove code duplication (util_xml_get_text) 242 // TODO: remove code duplication (util_xml_get_text)
577 while(node) { 243 while(node) {
650 } 316 }
651 node = node->next; 317 node = node->next;
652 } 318 }
653 319
654 return error; 320 return error;
655 }
656
657 Repository* get_repository(cxstring name) {
658 if(!name.ptr) {
659 return NULL;
660 }
661 return cxMapGet(repos, cx_hash_key(name.ptr, name.length));
662 }
663
664 int get_repository_flags(Repository *repo) {
665 int flags = 0;
666 if(repo->decrypt_content) {
667 flags |= DAV_SESSION_DECRYPT_CONTENT;
668 }
669 if(repo->decrypt_name) {
670 flags |= DAV_SESSION_DECRYPT_NAME;
671 }
672 if(repo->decrypt_properties) {
673 flags |= DAV_SESSION_DECRYPT_PROPERTIES;
674 }
675 if(repo->encrypt_content) {
676 flags |= DAV_SESSION_ENCRYPT_CONTENT;
677 }
678 if(repo->encrypt_name) {
679 flags |= DAV_SESSION_ENCRYPT_NAME;
680 }
681 if(repo->encrypt_properties) {
682 flags |= DAV_SESSION_ENCRYPT_PROPERTIES;
683 }
684 return flags;
685 }
686
687
688 Key* get_key(const char *name) {
689 if(!name) {
690 return NULL;
691 }
692 return cxMapGet(keys, cx_hash_key_str(name));
693 }
694
695 int add_repository(Repository *repo) {
696 if(check_config_dir()) {
697 fprintf(stderr, "Cannot create .dav directory\n");
698 return 1;
699 }
700
701 char *file = util_concat_path(ENV_HOME, ".dav/config.xml");
702 struct stat s;
703 if(stat(file, &s)) {
704 switch(errno) {
705 case ENOENT: {
706 create_default_config(file);
707 break;
708 }
709 default: {
710 perror("Cannot load config.xml");
711 free(file);
712 return 1;
713 }
714 }
715 }
716
717 xmlDoc *doc = xmlReadFile(file, NULL, 0);
718 if(!doc) {
719 free(file);
720 fprintf(stderr, "Cannot load config.xml\n");
721 return 1;
722 }
723
724 xmlNode *root = xmlDocGetRootElement(doc);
725 if(!root) {
726 fprintf(stderr, "Missing root node in config.xml\n");
727 xmlFreeDoc(doc);
728 free(file);
729 return 1;
730 }
731
732 xmlNode *repoNode = xmlNewNode(NULL, BAD_CAST "repository");
733 xmlNodeAddContent(repoNode, BAD_CAST "\n\t\t");
734 xmlNewTextChild(repoNode, NULL, BAD_CAST "name", BAD_CAST repo->name);
735 xmlNodeAddContent(repoNode, BAD_CAST "\n\t\t");
736 xmlNewTextChild(repoNode, NULL, BAD_CAST "url", BAD_CAST repo->url);
737 xmlNodeAddContent(repoNode, BAD_CAST "\n");
738 if(repo->user) {
739 xmlNodeAddContent(repoNode, BAD_CAST "\t\t");
740 xmlNewChild(repoNode, NULL, BAD_CAST "user", BAD_CAST repo->user);
741 xmlNodeAddContent(repoNode, BAD_CAST "\n");
742 if(repo->password) {
743 char *pwenc = util_base64encode(
744 repo->password,
745 strlen(repo->password));
746 xmlNodeAddContent(repoNode, BAD_CAST "\t\t");
747 xmlNewTextChild(repoNode, NULL, BAD_CAST "password", BAD_CAST pwenc);
748 free(pwenc);
749 xmlNodeAddContent(repoNode, BAD_CAST "\n");
750 }
751 }
752 xmlNodeAddContent(repoNode, BAD_CAST "\t");
753
754 xmlNodeAddContent(root, BAD_CAST "\n\t");
755 xmlAddChild(root, repoNode);
756 xmlNodeAddContent(root, BAD_CAST "\n");
757
758 int ret = (xmlSaveFormatFileEnc(file, doc, "UTF-8", 1) == -1) ? 1 : 0;
759 xmlFreeDoc(doc);
760 free(file);
761
762 return ret;
763 }
764
765 int remove_repository(Repository *repo) {
766 char *file = util_concat_path(ENV_HOME, ".dav/config.xml");
767 struct stat s;
768 if(stat(file, &s)) {
769 perror("Cannot access config.xml");
770 free(file);
771 return 1;
772 }
773
774 xmlDoc *doc = xmlReadFile(file, NULL, 0);
775 if(!doc) {
776 free(file);
777 fprintf(stderr, "Cannot load config.xml\n");
778 return 1;
779 }
780
781 xmlNodePtr root = xmlDocGetRootElement(doc);
782 if(!root) {
783 fprintf(stderr, "Missing root node in config.xml\n");
784 xmlFreeDoc(doc);
785 free(file);
786 return 1;
787 }
788
789 xmlNodePtr repoNode = root->children;
790 xmlNodePtr matchedRepoNode = NULL;
791 while(!matchedRepoNode && repoNode) {
792 if(repoNode->type == XML_ELEMENT_NODE
793 && xstreq(repoNode->name, "repository")) {
794 xmlNodePtr nameNode = repoNode->children;
795 while(!matchedRepoNode && nameNode) {
796 if (nameNode->type == XML_ELEMENT_NODE
797 && xstreq(nameNode->name, "name")) {
798 char *reponame = util_xml_get_text(nameNode);
799 if(!strcmp(repo->name, reponame)) {
800 matchedRepoNode = repoNode;
801 }
802 }
803 nameNode = nameNode->next;
804 }
805 }
806 repoNode = repoNode->next;
807 }
808
809 if(matchedRepoNode) {
810 xmlNodePtr prev = matchedRepoNode->prev;
811 xmlNodePtr next = matchedRepoNode->next;
812 if(prev && prev->type == XML_TEXT_NODE) {
813 cxstring content = cx_str((char*)prev->content);
814 cxstring lf = cx_strrchr(content, '\n');
815 if(lf.length > 0) {
816 content.length = lf.ptr - content.ptr;
817 char* newcontent = cx_strdup(content).ptr;
818 xmlNodeSetContent(prev, (xmlChar*)newcontent);
819 free(newcontent);
820 }
821 }
822 if(next && next->type == XML_TEXT_NODE) {
823 cxstring lf = cx_strchr(cx_str((char*)next->content), '\n');
824 if(lf.length > 0) {
825 char* newcontent = malloc(lf.length);
826 memcpy(newcontent, lf.ptr+1, lf.length-1);
827 newcontent[lf.length-1] = '\0';
828 xmlNodeSetContent(next, (xmlChar*)newcontent);
829 free(newcontent);
830 }
831 }
832
833 xmlUnlinkNode(matchedRepoNode);
834 xmlFreeNode(matchedRepoNode);
835 }
836
837 int ret = (xmlSaveFormatFileEnc(file, doc, "UTF-8", 1) == -1) ? 1 : 0;
838 xmlFreeDoc(doc);
839 free(file);
840
841 return ret;
842 }
843
844 int list_repositories(void) {
845 CxIterator i = cxMapIteratorValues(repos);
846 Repository *repo;
847 cx_foreach(Repository *, repo, i) {
848 printf("%s\n", repo->name);
849 }
850 return 0;
851 }
852
853 CxIterator get_repositories(void) {
854 return cxMapIteratorValues(repos);
855 } 321 }
856 322
857 PwdStore* get_pwdstore(void) { 323 PwdStore* get_pwdstore(void) {
858 return pstore; 324 return pstore;
859 } 325 }
1127 } 593 }
1128 return sn; 594 return sn;
1129 } 595 }
1130 596
1131 int request_auth(DavSession *sn, void *userdata) { 597 int request_auth(DavSession *sn, void *userdata) {
1132 Repository *repo = userdata; 598 DavCfgRepository *repo = userdata;
1133 599
1134 char *user = NULL; 600 cxstring user = {NULL, 0};
1135 char ubuf[256]; 601 char ubuf[256];
1136 if(repo->user) { 602 if(repo->user.value.ptr) {
1137 user = repo->user; 603 user = cx_strcast(repo->user.value);
1138 } else { 604 } else {
1139 fprintf(stderr, "User: "); 605 fprintf(stderr, "User: ");
1140 fflush(stderr); 606 fflush(stderr);
1141 user = fgets(ubuf, 256, stdin); 607 user = cx_str(fgets(ubuf, 256, stdin));
1142 } 608 }
1143 if(!user) { 609 if(!user.ptr) {
1144 return 0; 610 return 0;
1145 } 611 }
1146 612
1147 char *password = util_password_input("Password: "); 613 char *password = util_password_input("Password: ");
1148 if(!password || strlen(password) == 0) { 614 if(!password || strlen(password) == 0) {
1149 return 0; 615 return 0;
1150 } 616 }
1151 617
1152 size_t ulen = strlen(user); 618 dav_session_set_auth_s(sn, user, cx_str(password));
1153 if(user[ulen-1] == '\n') {
1154 user[ulen-1] = '\0';
1155 }
1156
1157 dav_session_set_auth(sn, user, password);
1158 free(password); 619 free(password);
1159 620
1160 return 0; 621 return 0;
1161 } 622 }

mercurial