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 (&section_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 (&sect->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