1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <libidav/utils.h>
34 #include <cx/hash_map.h>
35 #include <cx/utils.h>
36 #include <cx/linked_list.h>
37 #include <cx/printf.h>
38
39 #include "scfg.h"
40 #include "config.h"
41
42 #define xstreq(a,b) xmlStrEqual(
BAD_CAST a,
BAD_CAST b)
43
44
45 #define print_error(lineno, ...) \
46 do {\
47 fprintf(stderr,
"Error (sync.xml line %u): ", lineno); \
48 fprintf(stderr,
__VA_ARGS__); \
49 fprintf(stderr,
"Abort.\n"); \
50 }
while(
0);
51 #define print_warning(lineno, ...) \
52 do {\
53 fprintf(stderr,
"Warning (sync.xml line %u): ", lineno); \
54 fprintf(stderr,
__VA_ARGS__); \
55 }
while(
0);
56
57 #ifdef _WIN32
58 #define ENV_HOME getenv(
"USERPROFILE")
59 #else
60 #define ENV_HOME getenv(
"HOME")
61 #endif
62
63 static CxMap *directories;
64
65 CxIterator scfg_directory_iterator() {
66 return cxMapIteratorValues(directories);
67 }
68
69 static int create_default_sync_config(
char *file) {
70 FILE *out = fopen(file,
"w");
71 if(!out) {
72 perror(
"Cannot create config file");
73 return -
1;
74 }
75
76 fputs(
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n", out);
77 fputs(
"<configuration>\n", out);
78 fputs(
"</configuration>\n", out);
79 fclose(out);
80
81 return 0;
82 }
83
84 static void add_regex_pattern(CxList *list,
char *value,
85 unsigned short xmlline) {
86 regex_t regex;
87 if (regcomp(®ex, value,
REG_EXTENDED|
REG_NOSUB)) {
88 print_warning(xmlline,
89 "Invalid regular expression (%s) ... skipped\n", value);
90 }
else {
91 cxListAdd(list, ®ex);
92 }
93 }
94
95 static int scfg_load_filter(
96 xmlNode *node,
97 CxList *include,
98 CxList *exclude,
99 CxList *tags)
100 {
101 node = node->children;
102
103 while(node) {
104 if(node->type ==
XML_ELEMENT_NODE) {
105 char *value = util_xml_get_text(node);
106 if(xstreq(node->name,
"include")) {
107 if(value) {
108 add_regex_pattern(include, value, node->line);
109 }
110 }
else if(xstreq(node->name,
"exclude")) {
111 if(value) {
112 add_regex_pattern(exclude, value, node->line);
113 }
114 }
else if(xstreq(node->name,
"tags")) {
115 if(value) {
116 SyncTagFilter *tagfilter = parse_tagfilter_string(
117 value,
DAV_SYNC_TAGFILTER_SCOPE_RESOURCE);
118 if(!tagfilter) {
119 print_error(
120 node->line,
121 "malformed tag filter: %s\n",
122 value);
123 return 1;
124 }
else {
125
126 xmlChar *scope = xmlGetNoNsProp(node,
BAD_CAST "scope");
127 if(scope) {
128 if(xstreq(scope,
"resource"))
129 {
130 tagfilter->scope =
131 DAV_SYNC_TAGFILTER_SCOPE_RESOURCE;
132 }
else if(xstreq(scope,
"collection")) {
133 tagfilter->scope =
134 DAV_SYNC_TAGFILTER_SCOPE_COLLECTION;
135 }
else if(xstreq(scope,
"all")) {
136 tagfilter->scope =
137 DAV_SYNC_TAGFILTER_SCOPE_RESOURCE
138 |
DAV_SYNC_TAGFILTER_SCOPE_COLLECTION;
139 }
else {
140 tagfilter->scope =
141 DAV_SYNC_TAGFILTER_SCOPE_RESOURCE;
142 }
143 }
144 xmlFree(scope);
145
146 cxListAdd(tags, tagfilter);
147 }
148 }
149 }
else {
150 print_error(node->line,
151 "unknown filter config element: %s\n", node->name);
152 return 1;
153 }
154 if(!value) {
155 print_error(node->line,
156 "missing value for filter: %s\n", node->name);
157 return 1;
158 }
159 }
160
161 node = node->next;
162 }
163
164 return 0;
165 }
166
167 Filter* parse_filter(xmlNode *node) {
168 CxList *include = cxLinkedListCreate(cxDefaultAllocator,
NULL,
sizeof(
regex_t));
169 CxList *exclude = cxLinkedListCreate(cxDefaultAllocator,
NULL,
sizeof(
regex_t));
170 CxList *tags = cxLinkedListCreate(cxDefaultAllocator,
NULL,
CX_STORE_POINTERS);
171
172 cxDefineDestructor(include, regfree);
173 cxDefineDestructor(exclude, regfree);
174
175
176 if(scfg_load_filter(node, include, exclude, tags)) {
177 return NULL;
178 }
179
180 Filter *filter = malloc(
sizeof(Filter));
181 filter->include = include;
182 filter->exclude = exclude;
183 filter->tags = tags;
184 return filter;
185 }
186
187 void init_default_filter(Filter *filter) {
188 if(cxListSize(filter->include) ==
0) {
189 regex_t matchall;
190 regcomp(&matchall,
".*",
REG_NOSUB);
191 cxListAdd(filter->include, &matchall);
192 }
193
194
195
196
197
198
199
200 }
201
202 static TagFormat str2tagformat(
const char *str) {
203 if(!strcmp(str,
"text")) {
204 return TAG_FORMAT_TEXT;
205 }
else if(!strcmp(str,
"csv")) {
206 return TAG_FORMAT_CSV;
207 }
else if(!strcmp(str,
"xml")) {
208 return TAG_FORMAT_XML;
209 }
else if(!strcmp(str,
"macos")) {
210 return TAG_FORMAT_MACOS;
211 }
212 return TAG_FORMAT_UNKNOWN;
213 }
214
215 #define CHECK_VALUE_RET_NULL(element, value)
if(!(value)) \
216 {print_error(element->line,
"missing value element: %s\n", element->name);
return NULL;}
217
218 static TagConfig* parse_tagconfig(xmlNode *node) {
219 TagConfig conf;
220 conf.store =
TAG_STORE_XATTR;
221 conf.local_format =
TAG_FORMAT_TEXT;
222 conf.server_format =
TAG_FORMAT_XML;
223 conf.xattr_name =
NULL;
224 conf.detect_changes = false;
225 conf.conflict =
TAG_NO_CONFLICT;
226 xmlNode *c = node->children;
227
228
229 while(c) {
230 if(c->type ==
XML_ELEMENT_NODE) {
231 char *value = util_xml_get_text(c);
232 if(xstreq(c->name,
"local-store")) {
233 CHECK_VALUE_RET_NULL(c, value);
234 if(xstreq(value,
"xattr")) {
235 conf.store =
TAG_STORE_XATTR;
236 }
else {
237 return NULL;
238 }
239
240 xmlChar *format = xmlGetNoNsProp(node,
BAD_CAST "format");
241 if(format) {
242 conf.local_format = str2tagformat((
char*)format);
243 xmlFree(format);
244 }
245 }
else if(xstreq(c->name,
"detect-changes")) {
246 CHECK_VALUE_RET_NULL(c, value);
247 conf.detect_changes = util_getboolean(value);
248 }
else if(xstreq(c->name,
"xattr-name")) {
249 if(!value) {
250 return NULL;
251 }
252 conf.xattr_name = strdup(value);
253 }
else if(xstreq(c->name,
"on-conflict")) {
254 CHECK_VALUE_RET_NULL(c, value);
255 if(xstreq(value,
"no_conflict")) {
256 conf.conflict =
TAG_NO_CONFLICT;
257 }
else if(xstreq(value,
"keep_local")) {
258 conf.conflict =
TAG_KEEP_LOCAL;
259 }
else if(xstreq(value,
"keep_remote")) {
260 conf.conflict =
TAG_KEEP_REMOTE;
261 }
else if(xstreq(value,
"merge")) {
262 conf.conflict =
TAG_MERGE;
263 }
else {
264 fprintf(stderr,
"on-conflict: unknown value: %s\n", value);
265 return NULL;
266 }
267 }
268 }
269 c = c->next;
270 }
271
272 if(conf.store ==
TAG_STORE_XATTR && !conf.xattr_name) {
273 switch(conf.local_format) {
274 default:
275 case TAG_FORMAT_TEXT:
276 case TAG_FORMAT_CSV:
277 case TAG_FORMAT_XML: conf.xattr_name = strdup(
DEFAULT_TAG_XATTR);
break;
278 case TAG_FORMAT_MACOS: conf.xattr_name = strdup(
MACOS_TAG_XATTR);
break;
279 }
280 }
281
282 TagConfig *tagconfig = malloc(
sizeof(TagConfig));
283 *tagconfig = conf;
284 return tagconfig;
285 }
286
287 static SplitConfig* parse_split(xmlNode *node) {
288 Filter *filter =
NULL;
289 char *minsize =
NULL;
290 char *blocksize =
NULL;
291
292 xmlNode *c = node->children;
293 while(c) {
294 if(xstreq(c->name,
"filter")) {
295 filter = parse_filter(node);
296 if(filter->tags) {
297 fprintf(stderr,
"splitconfig: tag filter not supported\n");
298 free_filter(*filter);
299 free(filter);
300 return NULL;
301 }
302 }
else if(xstreq(c->name,
"minsize")) {
303 minsize = util_xml_get_text(c);
304 }
else if(xstreq(c->name,
"blocksize")) {
305 blocksize = util_xml_get_text(c);
306 }
307 c = c->next;
308 }
309
310 uint64_t sz =
0;
311 if(!blocksize) {
312 fprintf(stderr,
"splitconfig: no blocksize specified\n");
313 return NULL;
314 }
315 size_t bsz_len = strlen(blocksize);
316 if(bsz_len <
2) {
317 fprintf(stderr,
"splitconfig: blocksize too small\n");
318 return NULL;
319 }
320
321 if(!util_szstrtouint(blocksize, &sz)) {
322 fprintf(stderr,
"splitconfig: blocksize is not a number\n");
323 return NULL;
324 }
325
326 if(!filter && !minsize) {
327 fprintf(stderr,
"splitconfig: filter or minsize must be specified\n");
328 return NULL;
329 }
330
331 int64_t minsz = -
1;
332 if(minsize) {
333 uint64_t m;
334 if(!util_szstrtouint(minsize, &m)) {
335 fprintf(stderr,
"splitconfig: minsize is not a number\n");
336 return NULL;
337 }
338 minsz = (
int64_t)m;
339 }
340
341 SplitConfig *sc = calloc(
1,
sizeof(SplitConfig));
342 if(filter) {
343 init_default_filter(filter);
344 }
345 sc->minsize = minsz;
346 sc->blocksize = (
size_t)sz;
347 return sc;
348 }
349
350 static CxList* parse_splitconfig(xmlNode *node,
int *error) {
351 CxList *splitconfig = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
352 int err =
0;
353 xmlNode *c = node->children;
354 while(c) {
355 if(c->type ==
XML_ELEMENT_NODE && xstreq(c->name,
"split")) {
356 SplitConfig *sc = parse_split(c);
357 if(sc) {
358 cxListAdd(splitconfig, sc);
359 }
else {
360 err =
1;
361 break;
362 }
363 }
364 c = c->next;
365 }
366
367 if(error) {
368 *error = err;
369 }
370 return splitconfig;
371 }
372
373 static Versioning* parse_versioning_config(xmlNode *node) {
374 Versioning v;
375 v.always =
FALSE;
376 v.type =
VERSIONING_SIMPLE;
377 v.collection =
VERSIONING_DEFAULT_PATH;
378
379 int err =
0;
380
381 xmlChar *attr_type = xmlGetNoNsProp(node,
BAD_CAST "type");
382 xmlChar *attr_always = xmlGetNoNsProp(node,
BAD_CAST "always");
383
384 if(attr_type) {
385 if(xstreq(attr_type,
"simple")) {
386 v.type =
VERSIONING_SIMPLE;
387 }
else if(xstreq(attr_type,
"deltav")) {
388 v.type =
VERSIONING_DELTAV;
389 }
else {
390 print_error(node->line,
391 "type attribute: unknown value: %s\n",
392 attr_type);
393 err =
1;
394 }
395 xmlFree(attr_type);
396 }
397 if(attr_always) {
398 v.always = util_getboolean((
const char*)attr_always);
399 xmlFree(attr_always);
400 }
401
402 if(err) {
403 return NULL;
404 }
405
406 xmlNode *c = node->children;
407 while(c) {
408 if(c->type ==
XML_ELEMENT_NODE) {
409 char *value = util_xml_get_text(c);
410 if(xstreq(c->name,
"history")) {
411 CHECK_VALUE_RET_NULL(c, value);
412 v.collection = value;
413 }
414 }
415 c = c->next;
416 }
417
418 v.collection = strdup(v.collection);
419
420 Versioning *versioning = malloc(
sizeof(Versioning));
421 *versioning = v;
422 return versioning;
423 }
424
425 static int scfg_load_directory(xmlNode *node) {
426 char *name =
NULL;
427 char *path =
NULL;
428 char *trash =
NULL;
429 char *collection =
NULL;
430 char *repository =
NULL;
431 char *database =
NULL;
432 char *logfile =
NULL;
433 TagConfig *tagconfig =
NULL;
434 Versioning *versioning =
NULL;
435 CxList *include = cxLinkedListCreateSimple(
sizeof(
regex_t));
436 CxList *exclude = cxLinkedListCreateSimple(
sizeof(
regex_t));
437 CxList *tagfilter = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
438 CxList *splitconfig =
NULL;
439 int max_retry =
0;
440 int allow_cmd =
SYNC_CMD_PULL |
SYNC_CMD_PUSH
441 |
SYNC_CMD_ARCHIVE |
SYNC_CMD_RESTORE;
442 bool backuppull = false;
443 bool lockpull = false;
444 bool lockpush = false;
445 bool hashing = false;
446 bool store_hash = false;
447 bool pull_skip_hashing = false;
448
449 time_t lock_timeout =
0;
450 uint32_t metadata =
0;
451 uint32_t symlink =
0;
452 PushStrategy pushstrat =
PUSH_STRATEGY_METADATA;
453
454 unsigned short parentlineno = node->line;
455 node = node->children;
456 while(node) {
457 if(node->type ==
XML_ELEMENT_NODE) {
458 char *value = util_xml_get_text(node);
459
460 if(!value && !xstreq(node->name,
"versioning")) {
461
462 print_error(node->line,
463 "missing value for directory element: %s\n",
464 node->name);
465 return 1;
466 }
467 if(xstreq(node->name,
"name")) {
468 name = value;
469 }
else if(xstreq(node->name,
"path")) {
470 path = value;
471 }
else if(xstreq(node->name,
"trash")) {
472 trash = value;
473 }
else if(xstreq(node->name,
"collection")) {
474 collection = value;
475 }
else if(xstreq(node->name,
"repository")) {
476 repository = value;
477 }
else if(xstreq(node->name,
"filter")) {
478 if(scfg_load_filter(node, include, exclude, tagfilter)) {
479 return 1;
480 }
481 }
else if(xstreq(node->name,
"database")) {
482 database = value;
483 }
else if(xstreq(node->name,
"logfile")) {
484 logfile = value;
485 }
else if(xstreq(node->name,
"tagconfig")) {
486 tagconfig = parse_tagconfig(node);
487 }
else if(xstreq(node->name,
"splitconfig")) {
488 int err =
0;
489 splitconfig = parse_splitconfig(node, &err);
490 if(err) {
491 return 1;
492 }
493 }
else if(xstreq(node->name,
"metadata")) {
494 uint32_t md =
0;
495
496 const char *delims =
" ,\t\r\n";
497 char *metadatastr = strdup(value);
498 char *m = strtok(metadatastr, delims);
499 while(m) {
500 if(!strcmp(m,
"mtime")) {
501 md |=
FINFO_MTIME;
502 }
else if(!strcmp(m,
"mode")) {
503 md |=
FINFO_MODE;
504 }
else if(!strcmp(m,
"owner")) {
505 md |=
FINFO_OWNER;
506 }
else if(!strcmp(m,
"xattr")) {
507 md |=
FINFO_XATTR;
508 }
else if(!strcmp(m,
"all")) {
509 md |=
FINFO_MTIME |
FINFO_MODE |
FINFO_OWNER |
FINFO_XATTR;
510 }
511 m = strtok(
NULL, delims);
512 }
513 free(metadatastr);
514
515 metadata = md;
516 }
else if(xstreq(node->name,
"versioning")) {
517 versioning = parse_versioning_config(node);
518 }
else if(xstreq(node->name,
"max-retry")) {
519 int64_t i;
520 if(util_strtoint(value, &i) && i >=
0) {
521 max_retry = (
int)i;
522 }
else {
523 print_warning(node->line,
"unsigned integer value "
524 "expected in <max-retry> element\n");
525 }
526 }
else if(xstreq(node->name,
"allow-cmd")) {
527 int cmds =
0;
528 const char *delims =
" ,\t\r\n";
529 char *cmdstr = strdup(value);
530 char *cmd = strtok(cmdstr, delims);
531 while(cmd) {
532 if(!strcmp(cmd,
"pull")) {
533 cmds |=
SYNC_CMD_PULL;
534 }
else if(!strcmp(cmd,
"push")) {
535 cmds |=
SYNC_CMD_PUSH;
536 }
else if(!strcmp(cmd,
"archive")) {
537 cmds |=
SYNC_CMD_ARCHIVE;
538 }
else if(!strcmp(cmd,
"restore")) {
539 cmds |=
SYNC_CMD_RESTORE;
540 }
541 cmd = strtok(
NULL, delims);
542 }
543 free(cmdstr);
544 allow_cmd = cmds;
545
546 }
else if(xstreq(node->name,
"backup-on-pull")) {
547 backuppull = util_getboolean(value);
548 }
else if(xstreq(node->name,
"lock-pull")) {
549 lockpull = util_getboolean(value);
550 }
else if(xstreq(node->name,
"lock-push")) {
551 lockpush = util_getboolean(value);
552 }
else if(xstreq(node->name,
"lock-timeout")) {
553 int64_t t =
0;
554 if(util_strtoint(value, &t)) {
555 lock_timeout = (
time_t)t;
556 }
else {
557 print_warning(node->line,
"integer value "
558 "expected in <lock-timeout> element\n");
559 }
560 }
else if(xstreq(node->name,
"hashing")) {
561 hashing = util_getboolean(value);
562 store_hash = hashing;
563 }
else if(xstreq(node->name,
"push-strategy")) {
564 if(value) {
565 if(xstreq(value,
"metadata")) {
566 pushstrat =
PUSH_STRATEGY_METADATA;
567 }
else if(xstreq(value,
"hash")) {
568 pushstrat =
PUSH_STRATEGY_HASH;
569 }
570 }
571 }
else if(xstreq(node->name,
"symlink-intern")) {
572 if(!value) {
573 print_error(node->line,
"missing value");
574 }
else if(xstreq(value,
"sync")) {
575 symlink |=
SYNC_SYMLINK_SYNC;
576 }
else if(xstreq(value,
"follow")) {
577
578 }
else if(xstreq(value,
"ignore")) {
579 symlink |=
SYNC_SYMLINK_IGNORE_INTERN;
580 }
else {
581 print_error(node->line,
582 "unknown value: %s\n", value);
583 }
584 }
else if(xstreq(node->name,
"symlink-extern")) {
585 if(!value) {
586 print_error(node->line,
"missing value");
587 }
else if(xstreq(value,
"follow")) {
588
589 }
else if(xstreq(value,
"ignore")) {
590 symlink |=
SYNC_SYMLINK_IGNORE_EXTERN;
591 }
else {
592 print_error(node->line,
593 "unknown value: %s\n", value);
594 }
595 }
else {
596 print_error(node->line,
597 "unknown directory config element: %s\n", node->name);
598 return 1;
599 }
600 }
601 node = node->next;
602 }
603
604 if(!name) {
605 print_error(parentlineno,
"missing name element for directory\n");
606 return 1;
607 }
608 if(!path) {
609 print_error(parentlineno,
610 "missing path element for directory %s\n", name);
611 return 1;
612 }
613 if(!repository) {
614 print_error(parentlineno,
615 "missing repository element for directory %s\n", name);
616 return 1;
617 }
618 if(!database) {
619 print_error(parentlineno,
620 "missing database element for directory %s\n", name);
621 return 1;
622 }
623
624 SyncDirectory *dir = calloc(
1,
sizeof(SyncDirectory));
625 dir->name = strdup(name);
626 dir->path = scfg_create_path(path);
627 dir->collection = collection ? strdup(collection) :
NULL;
628 dir->repository = strdup(repository);
629 dir->database = strdup(database);
630 dir->logfile = logfile ? strdup(logfile) :
NULL;
631 dir->tagconfig = tagconfig;
632 dir->versioning = versioning;
633 dir->max_retry = max_retry;
634 dir->allow_cmd = allow_cmd;
635 dir->backuppull = backuppull;
636 dir->lockpull = lockpull;
637 dir->lockpush = lockpush;
638 dir->hashing = hashing;
639 dir->store_hash = store_hash;
640 dir->pull_skip_hashing = pull_skip_hashing;
641 dir->lock_timeout = lock_timeout;
642 dir->metadata = metadata;
643 dir->splitconfig = splitconfig;
644 dir->symlink = symlink;
645 dir->push_strategy = pushstrat;
646 if((metadata &
FINFO_MODE) ==
FINFO_MODE) {
647 dir->db_settings =
DB_STORE_MODE;
648 }
649 if((metadata &
FINFO_OWNER) ==
FINFO_OWNER) {
650 dir->db_settings |=
DB_STORE_OWNER;
651 }
652
653 dir->filter.include = include;
654 dir->filter.exclude = exclude;
655 dir->filter.tags = tagfilter;
656 init_default_filter(&dir->filter);
657
658 if (trash && cx_strtrim(cx_str(trash)).length >
0) {
659 if (trash[
0] ==
'/' || trash[
0] ==
'$') {
660 dir->trash = scfg_create_path(trash);
661 }
else {
662 char *t = util_concat_path(dir->path, trash);
663 dir->trash = util_concat_path(t,
"/");
664 free(t);
665 }
666
667 if(dir->trash[strlen(dir->trash)-
1] !=
'/') {
668 char *t = dir->trash;
669 dir->trash = util_concat_path(t,
"/");
670 free(t);
671 }
672 }
else {
673 dir->trash =
NULL;
674 }
675
676 cxMapPut(directories, cx_hash_key_str(name), dir);
677
678 return 0;
679 }
680
681 int load_sync_config() {
682 directories = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
683
684 if(check_config_dir()) {
685 fprintf(stderr,
"Cannot create .dav directory\n");
686 return 1;
687 }
688
689 char *file = util_concat_path(
ENV_HOME,
".dav/sync.xml");
690
691 struct stat s;
692 if(stat(file, &s)) {
693 switch(errno) {
694 case ENOENT: {
695 if(create_default_sync_config(file)) {
696 return 1;
697 }
698 break;
699 }
700 default: {
701 perror(
"Cannot load sync.xml");
702 }
703 }
704 free(file);
705 return 0;
706 }
707
708 xmlDoc *doc = xmlReadFile(file,
NULL,
0);
709 if(!doc) {
710 fprintf(stderr,
"Cannot load sync.xml\n");
711 free(file);
712 return -
1;
713 }
714
715 int ret =
0;
716 xmlNode *node = xmlDocGetRootElement(doc)->children;
717 while(node && !ret) {
718 if(node->type ==
XML_ELEMENT_NODE) {
719 if(xstreq(node->name,
"directory")) {
720 ret = scfg_load_directory(node);
721 }
else {
722 print_error(node->line,
723 "unknown config element: %s\n", node->name);
724 ret =
1;
725 }
726 }
727 node = node->next;
728 }
729
730 xmlFreeDoc(doc);
731 free(file);
732 return ret;
733 }
734
735 SyncDirectory* scfg_get_dir(
const char *name) {
736 return cxMapGet(directories, cx_hash_key_str(name));
737 }
738
739 int scfg_check_dir(SyncDirectory *dir) {
740 struct stat s;
741 if(stat(dir->path, &s)) {
742 int err = errno;
743 if(err ==
ENOENT) {
744 fprintf(stderr,
"directory %s does not exist.\n", dir->path);
745 }
else {
746 fprintf(stderr,
"Cannot stat directory %s.\n", dir->path);
747 perror(
NULL);
748 }
749 fprintf(stderr,
"Abort.\n");
750 return -
1;
751 }
752
753 if(dir->trash) {
754
755 mode_t mode =
S_IRWXU |
S_IRGRP |
S_IXGRP |
S_IROTH |
S_IXOTH;
756 if (util_mkdir(dir->trash, mode)) {
757 if (errno !=
EEXIST) {
758 fprintf(stderr,
"Cannot create trash directory: %s\nAbort.\n", dir->trash);
759 return -
1;
760 }
761 }
762 }
763
764 return 0;
765 }
766
767 char* scfg_create_path(
const char *cfg) {
768 if(!cfg) {
769 return NULL;
770 }
771 if(cfg[
0] !=
'$') {
772 return strdup(cfg);
773 }
774
775 cxstring s = cx_str(cfg);
776 cxstring path = cx_strchr(cx_str(cfg),
'/');
777 char *localpath =
NULL;
778 if(path.length >
0) {
779
780
781 cxstring var = cx_strsubsl(s,
1, path.ptr - s.ptr -
1);
782 if(var.length >
0) {
783 char *env = cx_strdup(var).ptr;
784 char *envval = getenv(env);
785 free(env);
786 if(envval) {
787 localpath = util_concat_path(envval, path.ptr);
788 }
else {
789 fprintf(
790 stderr,
791 "Environment variable %.*s not set.\nAbort.\n",
792 (
int)var.length,
793 var.ptr);
794 exit(-
1);
795 }
796 }
else {
797 localpath = cx_strdup(path).ptr;
798 }
799 }
else {
800
801 char *envval = getenv(cfg +
1);
802 if(envval) {
803 localpath = strdup(envval);
804 }
else {
805 fprintf(
806 stderr,
807 "Environment variable %s not set.\nAbort.\n",
808 cfg);
809 exit(-
1);
810 }
811 }
812 return localpath;
813 }
814
815
816 int add_directory(SyncDirectory *dir) {
817 char *file = util_concat_path(
ENV_HOME,
".dav/sync.xml");
818 xmlDoc *doc = xmlReadFile(file,
NULL,
0);
819 if(!doc) {
820 free(file);
821 fprintf(stderr,
"Cannot load config.xml\n");
822 return 1;
823 }
824
825 xmlNode *root = xmlDocGetRootElement(doc);
826
827 xmlNode *dirNode = xmlNewNode(
NULL,
BAD_CAST "directory");
828 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t\t");
829
830 xmlNewTextChild(dirNode,
NULL,
BAD_CAST "name",
BAD_CAST dir->name);
831 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t\t");
832
833 xmlNewTextChild(dirNode,
NULL,
BAD_CAST "path",
BAD_CAST dir->path);
834 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t\t");
835
836 xmlNewTextChild(dirNode,
NULL,
BAD_CAST "repository",
BAD_CAST dir->repository);
837 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t\t");
838
839 xmlNewTextChild(dirNode,
NULL,
BAD_CAST "collection",
BAD_CAST dir->collection);
840 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t\t");
841
842 if(dir->trash) {
843 xmlNewTextChild(dirNode,
NULL,
BAD_CAST "trash",
BAD_CAST dir->trash);
844 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t\t");
845 }
846 xmlNewTextChild(dirNode,
NULL,
BAD_CAST "database",
BAD_CAST dir->database);
847 xmlNodeAddContent(dirNode,
BAD_CAST "\n\t");
848
849 xmlNodeAddContent(root,
BAD_CAST "\n\t");
850 xmlAddChild(root, dirNode);
851 xmlNodeAddContent(root,
BAD_CAST "\n");
852
853 int ret =
0;
854 if(xmlSaveFormatFileEnc(file, doc,
"UTF-8",
1) == -
1) {
855 ret =
1;
856 }
857 xmlFreeDoc(doc);
858 free(file);
859
860 return ret;
861 }
862
863 char* generate_db_name(
const char *basename) {
864 char *dbname =
NULL;
865 int count = -
1;
866 while(!dbname) {
867 cxmutstr name = count <
0 ?
868 cx_asprintf(
"%s-db.xml", basename) :
869 cx_asprintf(
"%s%d-db.xml", basename, count);
870 count++;
871
872 CxIterator i = cxMapIteratorValues(directories);
873 bool unique = true;
874 cx_foreach(SyncDirectory *, dir, i) {
875 if(!cx_strcmp(cx_strcast(name), cx_str(dir->database))) {
876 unique = false;
877 break;
878 }
879 }
880 if(unique) {
881 dbname = name.ptr;
882 break;
883 }
884 }
885 return dbname;
886 }
887
888 void free_filter(Filter filter) {
889 cxListDestroy(filter.include);
890 cxListDestroy(filter.exclude);
891 cxListDestroy(filter.tags);
892 }
893
894 void free_sync_config() {
895 if(directories) {
896 CxIterator i = cxMapIteratorValues(directories);
897 cx_foreach(SyncDirectory *, dir, i) {
898 free(dir->name);
899 free(dir->path);
900 free(dir->repository);
901 free(dir->database);
902
903 if(dir->collection) {
904 free(dir->collection);
905 }
906 if(dir->trash) {
907 free(dir->trash);
908 }
909
910 free_filter(dir->filter);
911
912 free(dir);
913 }
914
915 cxMapDestroy(directories);
916 }
917 }
918