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 } |