1 /* cfg_driver.c -- Main driver for Mailutils configuration files
2 Copyright (C) 2007-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 3, or (at
7 your option) any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16 */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <mailutils/argcv.h>
29 #include <mailutils/nls.h>
30 #include <mailutils/cfg.h>
31 #include <mailutils/errno.h>
32 #include <mailutils/error.h>
33 #include <mailutils/util.h>
34 #include <mailutils/monitor.h>
35 #include <mailutils/refcount.h>
36 #include <mailutils/list.h>
37 #include <mailutils/iterator.h>
38 #include <mailutils/stream.h>
39 #include <mailutils/assoc.h>
40 #include <mailutils/alloc.h>
41 #include <mailutils/cstr.h>
42
43 static mu_assoc_t section_tab;
44
45 static void
alloc_section_tab()46 alloc_section_tab ()
47 {
48 if (!section_tab)
49 mu_assoc_create (§ion_tab, MU_ASSOC_COPY_KEY);
50 }
51
52 int
mu_create_canned_section(char * name,struct mu_cfg_section ** psection)53 mu_create_canned_section (char *name, struct mu_cfg_section **psection)
54 {
55 int rc;
56 struct mu_cfg_cont **pcont;
57 alloc_section_tab ();
58 rc = mu_assoc_install_ref (section_tab, name, &pcont);
59 if (rc == 0)
60 {
61 mu_config_create_container (pcont, mu_cfg_cont_section);
62 *psection = &(*pcont)->v.section;
63 (*psection)->ident = name;
64 }
65 else if (rc == MU_ERR_EXISTS)
66 *psection = &(*pcont)->v.section;
67 return rc;
68 }
69
70 int
mu_create_canned_param(char * name,struct mu_cfg_param ** pparam)71 mu_create_canned_param (char *name, struct mu_cfg_param **pparam)
72 {
73 int rc;
74 struct mu_cfg_cont **pcont;
75 alloc_section_tab ();
76 rc = mu_assoc_install_ref (section_tab, name, &pcont);
77 if (rc == 0)
78 {
79 mu_config_create_container (pcont, mu_cfg_cont_param);
80 *pparam = &(*pcont)->v.param;
81 (*pparam)->ident = name;
82 }
83 else if (rc == MU_ERR_EXISTS)
84 *pparam = &(*pcont)->v.param;
85 return rc;
86 }
87
88 struct mu_cfg_cont *
mu_get_canned_container(const char * name)89 mu_get_canned_container (const char *name)
90 {
91 return mu_assoc_get (section_tab, name);
92 }
93
94
95 static struct mu_cfg_cont *root_container;
96
97 int
mu_config_create_container(struct mu_cfg_cont ** pcont,enum mu_cfg_cont_type type)98 mu_config_create_container (struct mu_cfg_cont **pcont,
99 enum mu_cfg_cont_type type)
100 {
101 struct mu_cfg_cont *cont;
102 int rc;
103
104 cont = calloc (1, sizeof (*cont));
105 if (!cont)
106 return ENOMEM;
107 rc = mu_refcount_create (&cont->refcount);
108 if (rc)
109 free (cont);
110 else
111 {
112 cont->type = type;
113 *pcont = cont;
114 }
115 return rc;
116 }
117
118
119 struct dup_data
120 {
121 struct mu_cfg_cont *cont;
122 };
123
124 static int dup_container (struct mu_cfg_cont **pcont);
125
126 static int
_dup_cont_action(void * item,void * cbdata)127 _dup_cont_action (void *item, void *cbdata)
128 {
129 int rc;
130 struct mu_cfg_cont *cont = item;
131 struct dup_data *pdd = cbdata;
132
133 rc = dup_container (&cont);
134 if (rc)
135 return rc;
136
137 if (!pdd->cont->v.section.children)
138 {
139 int rc = mu_list_create (&pdd->cont->v.section.children);
140 if (rc)
141 return rc;
142 }
143 return mu_list_append (pdd->cont->v.section.children, cont);
144 }
145
146 static int
dup_container(struct mu_cfg_cont ** pcont)147 dup_container (struct mu_cfg_cont **pcont)
148 {
149 int rc;
150 struct mu_cfg_cont *newcont, *oldcont = *pcont;
151 struct dup_data dd;
152
153 rc = mu_config_create_container (&newcont, oldcont->type);
154 if (rc)
155 return rc;
156
157 dd.cont = newcont;
158 switch (oldcont->type)
159 {
160 case mu_cfg_cont_section:
161 newcont->v.section.ident = oldcont->v.section.ident;
162 newcont->v.section.label = oldcont->v.section.label;
163 newcont->v.section.parser = oldcont->v.section.parser;
164 newcont->v.section.data = oldcont->v.section.data;
165 newcont->v.section.offset = oldcont->v.section.offset;
166 newcont->v.section.docstring = oldcont->v.section.docstring;
167 newcont->v.section.children = NULL;
168 rc = mu_list_foreach (oldcont->v.section.children, _dup_cont_action, &dd);
169 if (rc)
170 {
171 mu_diag_funcall (MU_DIAG_ERROR, "_dup_cont_action",
172 oldcont->v.section.ident, rc);
173 abort ();
174 }
175 break;
176
177 case mu_cfg_cont_param:
178 newcont->v.param = oldcont->v.param;
179 break;
180 }
181 *pcont = newcont;
182 return 0;
183 }
184
185
186 static void
destroy_list(mu_list_t * plist)187 destroy_list (mu_list_t *plist)
188 {
189 mu_list_t list = *plist;
190 mu_iterator_t itr = NULL;
191
192 if (!list)
193 return;
194
195 mu_list_get_iterator (list, &itr);
196 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
197 mu_iterator_next (itr))
198 {
199 struct mu_cfg_cont *cont, *p;
200 mu_iterator_current (itr, (void**)&cont);
201 p = cont;
202 mu_config_destroy_container (&p);
203 if (!p)
204 mu_list_remove (list, cont);
205 }
206 mu_iterator_destroy (&itr);
207 if (mu_list_is_empty (list))
208 mu_list_destroy (plist);
209 }
210
211 void
mu_config_destroy_container(struct mu_cfg_cont ** pcont)212 mu_config_destroy_container (struct mu_cfg_cont **pcont)
213 {
214 struct mu_cfg_cont *cont = *pcont;
215 unsigned refcount = mu_refcount_dec (cont->refcount);
216 /* printf ("destr %p-%s: %d\n", cont, cont->v.section.ident, refcount); */
217 // FIXME: Shouldn't it be done inside the conditional below?
218 switch (cont->type)
219 {
220 case mu_cfg_cont_section:
221 destroy_list (&cont->v.section.children);
222 break;
223
224 case mu_cfg_cont_param:
225 break;
226 }
227
228 if (refcount == 0)
229 {
230 mu_refcount_destroy (&cont->refcount);
231 free (cont);
232 *pcont = 0;
233 }
234 }
235
236
237 int
mu_cfg_section_add_container(struct mu_cfg_section * sect,struct mu_cfg_cont * cont)238 mu_cfg_section_add_container (struct mu_cfg_section *sect,
239 struct mu_cfg_cont *cont)
240 {
241 if (!cont)
242 return 0;
243 if (!sect->children)
244 mu_list_create (§->children);
245 return mu_list_append (sect->children, cont);
246 }
247
248 int
mu_cfg_section_add_params(struct mu_cfg_section * sect,struct mu_cfg_param * param)249 mu_cfg_section_add_params (struct mu_cfg_section *sect,
250 struct mu_cfg_param *param)
251 {
252 if (!param)
253 return 0;
254
255 for (; param->ident; param++)
256 {
257 int rc;
258 struct mu_cfg_cont *container;
259
260 if (param->type == mu_cfg_section)
261 {
262 container = mu_get_canned_container (param->ident);
263 if (!container)
264 {
265 mu_error (_("INTERNAL ERROR: Requested unknown canned "
266 "section %s"),
267 param->ident);
268 abort ();
269 }
270 if (param->ident[0] == '.')
271 {
272 mu_iterator_t itr;
273 mu_list_get_iterator (container->v.section.children, &itr);
274 for (mu_iterator_first (itr);
275 !mu_iterator_is_done (itr);
276 mu_iterator_next (itr))
277 {
278 struct mu_cfg_cont *c;
279 mu_iterator_current (itr, (void**)&c);
280 mu_config_clone_container (c);
281 if (mu_refcount_value (c->refcount) > 1)
282 dup_container (&c);
283 switch (c->type)
284 {
285 case mu_cfg_cont_section:
286 if (param->data)
287 {
288 c->v.section.data = param->data;
289 c->v.section.offset = param->offset;
290 }
291 else if (c->v.section.data)
292 /* Keep data & offset */;
293 else
294 c->v.section.offset += param->offset;
295 break;
296
297 case mu_cfg_cont_param:
298 if (param->data)
299 {
300 container->v.param.data = param->data;
301 container->v.param.offset = param->offset;
302 }
303 else if (container->v.param.data)
304 /* Keep data & offset */;
305 else
306 container->v.param.offset += param->offset;
307 break;
308 }
309 mu_cfg_section_add_container (sect, c);
310 }
311 mu_iterator_destroy (&itr);
312 continue;
313 }
314 else
315 {
316 mu_config_clone_container (container);
317 if (mu_refcount_value (container->refcount) > 1)
318 dup_container (&container);
319 container->v.section.data = param->data;
320 container->v.section.offset = param->offset;
321 }
322 }
323 else
324 {
325 rc = mu_config_create_container (&container, mu_cfg_cont_param);
326 if (rc)
327 return rc;
328 container->v.param = *param;
329 }
330 mu_cfg_section_add_container (sect, container);
331 }
332 return 0;
333 }
334
335 static int
_clone_action(void * item,void * cbdata)336 _clone_action (void *item, void *cbdata)
337 {
338 struct mu_cfg_cont *cont = item;
339 return mu_config_clone_container (cont);
340 }
341
342 int
mu_config_clone_container(struct mu_cfg_cont * cont)343 mu_config_clone_container (struct mu_cfg_cont *cont)
344 {
345 if (!cont)
346 return 0;
347 mu_refcount_inc (cont->refcount);
348 /* printf("clone %p-%s: %d\n", cont, cont->v.section.ident, n); */
349 switch (cont->type)
350 {
351 case mu_cfg_cont_section:
352 return mu_list_foreach (cont->v.section.children, _clone_action, NULL);
353
354 case mu_cfg_cont_param:
355 break;
356 }
357 return 0;
358 }
359
360
361 int
mu_config_container_register_section(struct mu_cfg_cont ** proot,const char * parent_path,const char * ident,const char * label,mu_cfg_section_fp parser,struct mu_cfg_param * param,struct mu_cfg_section ** psection)362 mu_config_container_register_section (struct mu_cfg_cont **proot,
363 const char *parent_path,
364 const char *ident,
365 const char *label,
366 mu_cfg_section_fp parser,
367 struct mu_cfg_param *param,
368 struct mu_cfg_section **psection)
369 {
370 int rc;
371 struct mu_cfg_section *root_section;
372 struct mu_cfg_section *parent;
373
374 if (!*proot)
375 {
376 rc = mu_config_create_container (proot, mu_cfg_cont_section);
377 if (rc)
378 return rc;
379 memset (&(*proot)->v.section, 0, sizeof (*proot)->v.section);
380 }
381
382 root_section = &(*proot)->v.section;
383
384 if (parent_path)
385 {
386 if (mu_cfg_find_section (root_section, parent_path, &parent))
387 return MU_ERR_NOENT;
388 }
389 else
390 parent = root_section;
391
392 if (mu_refcount_value ((*proot)->refcount) > 1)
393 {
394 /* It is a clone, do copy-on-write */
395 rc = dup_container (proot);
396 if (rc)
397 return rc;
398
399 root_section = &(*proot)->v.section;
400
401 if (parent_path)
402 {
403 if (mu_cfg_find_section (root_section, parent_path, &parent))
404 return MU_ERR_NOENT;
405 }
406 else
407 parent = root_section;
408 }
409
410 if (ident)
411 {
412 struct mu_cfg_cont *container;
413 struct mu_cfg_section *s;
414
415 if (!parent->children)
416 mu_list_create (&parent->children);
417 mu_config_create_container (&container, mu_cfg_cont_section);
418 mu_list_append (parent->children, container);
419 s = &container->v.section;
420
421 s->ident = strdup (ident);
422 s->label = label ? strdup (label) : NULL;
423 s->parser = parser;
424 s->children = NULL;
425 mu_cfg_section_add_params (s, param);
426 if (psection)
427 *psection = s;
428 }
429 else
430 {
431 mu_cfg_section_add_params (parent, param);
432 /* FIXME: */
433 if (!parent->parser)
434 parent->parser = parser;
435 if (psection)
436 *psection = parent;
437 }
438 return 0;
439 }
440
441 int
mu_config_root_register_section(const char * parent_path,const char * ident,const char * label,mu_cfg_section_fp parser,struct mu_cfg_param * param)442 mu_config_root_register_section (const char *parent_path,
443 const char *ident,
444 const char *label,
445 mu_cfg_section_fp parser,
446 struct mu_cfg_param *param)
447 {
448 return mu_config_container_register_section (&root_container,
449 parent_path,
450 ident, label,
451 parser, param, NULL);
452 }
453
454 int
mu_config_register_plain_section(const char * parent_path,const char * ident,struct mu_cfg_param * params)455 mu_config_register_plain_section (const char *parent_path, const char *ident,
456 struct mu_cfg_param *params)
457 {
458 return mu_config_root_register_section (parent_path, ident, NULL, NULL,
459 params);
460 }
461
462 struct mu_cfg_cont *
mu_config_clone_root_container(void)463 mu_config_clone_root_container (void)
464 {
465 struct mu_cfg_cont *cont = root_container;
466 mu_config_clone_container (cont);
467 return cont;
468 }
469
470 static struct mu_cfg_cont *
mu_build_container(struct mu_cfg_param * param)471 mu_build_container (struct mu_cfg_param *param)
472 {
473 struct mu_cfg_cont *cont = mu_config_clone_root_container ();
474 mu_config_container_register_section (&cont, NULL, NULL, NULL, NULL,
475 param, NULL);
476 return cont;
477 }
478
479 int
mu_cfg_tree_reduce(mu_cfg_tree_t * parse_tree,struct mu_cfg_parse_hints * hints,struct mu_cfg_param * progparam,void * target_ptr)480 mu_cfg_tree_reduce (mu_cfg_tree_t *parse_tree,
481 struct mu_cfg_parse_hints *hints,
482 struct mu_cfg_param *progparam,
483 void *target_ptr)
484 {
485 int rc = 0;
486 struct mu_cfg_cont *cont;
487 if (!parse_tree)
488 return 0;
489 if (hints && (hints->flags & MU_CF_DUMP))
490 {
491 int yes = 1;
492 mu_stream_t stream;
493
494 mu_stdio_stream_create (&stream, MU_STDERR_FD, 0);
495 mu_stream_ioctl (stream, MU_IOCTL_FD, MU_IOCTL_FD_SET_BORROW, &yes);
496 mu_cfg_format_parse_tree (stream, parse_tree, MU_CF_FMT_LOCUS);
497 mu_stream_destroy (&stream);
498 }
499
500 cont = mu_build_container (progparam);
501 rc = mu_cfg_scan_tree (parse_tree, &cont->v.section, target_ptr, NULL);
502 mu_config_destroy_container (&cont);
503
504 return rc;
505 }
506
507 void
mu_format_config_tree(mu_stream_t stream,struct mu_cfg_param * progparam)508 mu_format_config_tree (mu_stream_t stream, struct mu_cfg_param *progparam)
509 {
510 struct mu_cfg_cont *cont = mu_build_container (progparam);
511 mu_cfg_format_container (stream, cont);
512 mu_config_destroy_container (&cont);
513 }
514
515 static const char *
_first_value_ptr(mu_config_value_t * val)516 _first_value_ptr (mu_config_value_t *val)
517 {
518 switch (val->type)
519 {
520 case MU_CFG_STRING:
521 return val->v.string;
522
523 case MU_CFG_ARRAY:
524 return _first_value_ptr (val->v.arg.v);
525
526 case MU_CFG_LIST:
527 mu_list_get (val->v.list, 0, (void**) &val);
528 return _first_value_ptr (val);
529 }
530 return "";
531 }
532
533 int
mu_cfg_assert_value_type(mu_config_value_t * val,int type)534 mu_cfg_assert_value_type (mu_config_value_t *val, int type)
535 {
536 if (!val)
537 {
538 mu_error (_("required argument missing"));
539 return 1;
540 }
541
542 if (type == MU_CFG_ARRAY)
543 {
544 if (val->type == MU_CFG_STRING)
545 {
546 mu_config_value_t *arr = mu_calloc (1, sizeof arr[0]);
547 arr[0] = *val;
548 val->v.arg.c = 1;
549 val->v.arg.v = arr;
550 val->type = MU_CFG_ARRAY;
551 }
552 }
553
554 if (val->type != type)
555 {
556 /* FIXME */
557 mu_error (_("unexpected value: %s"), _first_value_ptr (val));
558 return 1;
559 }
560 return 0;
561 }
562
563 int
mu_cfg_string_value_cb(mu_config_value_t * val,int (* fun)(const char *,void *),void * data)564 mu_cfg_string_value_cb (mu_config_value_t *val,
565 int (*fun) (const char *, void *),
566 void *data)
567 {
568 int rc = 0;
569
570 switch (val->type)
571 {
572 case MU_CFG_STRING:
573 return fun (val->v.string, data);
574 break;
575
576 case MU_CFG_ARRAY:
577 {
578 int i;
579
580 for (i = 0; i < val->v.arg.c; i++)
581 {
582 if (mu_cfg_assert_value_type (&val->v.arg.v[i],
583 MU_CFG_STRING))
584 return 1;
585 fun (val->v.arg.v[i].v.string, data);
586 }
587 }
588 break;
589
590 case MU_CFG_LIST:
591 {
592 mu_iterator_t itr;
593 mu_list_get_iterator (val->v.list, &itr);
594 for (mu_iterator_first (itr);
595 !mu_iterator_is_done (itr); mu_iterator_next (itr))
596 {
597 mu_config_value_t *pval;
598 mu_iterator_current (itr, (void*) &pval);
599 if (mu_cfg_assert_value_type (pval, MU_CFG_STRING))
600 {
601 rc = 1;
602 break;
603 }
604 fun (pval->v.string, data);
605 }
606 mu_iterator_destroy (&itr);
607 }
608 }
609 return rc;
610 }
611
612 struct mapping_closure
613 {
614 mu_assoc_t assoc;
615 char *err_term;
616 };
617
618 static int
parse_mapping_str(void * item,void * data)619 parse_mapping_str (void *item, void *data)
620 {
621 struct mapping_closure *clos = data;
622 char const *str = item;
623 size_t len;
624 char *key, *val;
625 int rc;
626
627 len = strcspn (str, "=");
628 if (str[len] == 0)
629 {
630 clos->err_term = mu_strdup (str);
631 return MU_ERR_PARSE;
632 }
633 key = mu_alloc (len + 1);
634 memcpy (key, str, len);
635 key[len] = 0;
636 val = mu_strdup (str + len + 1);
637 if (!val)
638 return ENOMEM;
639 rc = mu_assoc_install (clos->assoc, key, val);
640 free (key);
641 return rc;
642 }
643
644 static int
parse_mapping_val(void * item,void * data)645 parse_mapping_val (void *item, void *data)
646 {
647 struct mu_config_value *cval = item;
648
649 if (mu_cfg_assert_value_type (cval, MU_CFG_STRING))
650 return MU_ERR_PARSE;
651 return parse_mapping_str ((void*) cval->v.string, data);
652 }
653
654 int
mu_cfg_field_map(struct mu_config_value const * val,mu_assoc_t * passoc,char ** err_term)655 mu_cfg_field_map (struct mu_config_value const *val, mu_assoc_t *passoc,
656 char **err_term)
657 {
658 int rc;
659 struct mapping_closure clos;
660 mu_list_t list = NULL;
661
662 rc = mu_assoc_create (&clos.assoc, 0);
663 if (rc)
664 return rc;
665 mu_assoc_set_destroy_item (clos.assoc, mu_list_free_item);
666 clos.err_term = NULL;
667
668 switch (val->type)
669 {
670 case MU_CFG_STRING:
671 mu_list_create (&list);
672 mu_list_set_destroy_item (list, mu_list_free_item);
673 rc = mu_string_split (val->v.string, ":", list);
674 if (rc == 0)
675 rc = mu_list_foreach (list, parse_mapping_str, &clos);
676 mu_list_destroy (&list);
677 break;
678
679 case MU_CFG_LIST:
680 rc = mu_list_foreach (val->v.list, parse_mapping_val, &clos);
681 break;
682
683 case MU_CFG_ARRAY:
684 rc = EINVAL;
685 }
686
687 if (rc)
688 {
689 if (rc == MU_ERR_PARSE)
690 {
691 if (err_term)
692 *err_term = clos.err_term;
693 else
694 free (clos.err_term);
695 }
696 else
697 mu_error ("%s:%d: %s", __FILE__, __LINE__, mu_strerror (rc));
698 mu_assoc_destroy (&clos.assoc);
699 }
700 else
701 *passoc = clos.assoc;
702
703 return rc;
704 }
705
706
707