chiark / gitweb /
Merge up to 5.0.2
[disorder] / lib / configuration.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004-2010 Richard Kettlewell
4  * Portions copyright (C) 2007 Mark Wooding
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /** @file lib/configuration.c
20  * @brief Configuration file support
21  */
22
23 #include "common.h"
24
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <stddef.h>
31 #include <pwd.h>
32 #include <langinfo.h>
33 #include <pcre.h>
34 #include <signal.h>
35
36 #include "rights.h"
37 #include "configuration.h"
38 #include "mem.h"
39 #include "log.h"
40 #include "split.h"
41 #include "syscalls.h"
42 #include "table.h"
43 #include "inputline.h"
44 #include "charset.h"
45 #include "defs.h"
46 #include "printf.h"
47 #include "regsub.h"
48 #include "signame.h"
49 #include "authhash.h"
50 #include "vector.h"
51 #include "uaudio.h"
52
53 /** @brief Path to config file 
54  *
55  * set_configfile() sets the default if it is null.
56  */
57 char *configfile;
58
59 /** @brief Read user configuration
60  *
61  * If clear, the user-specific configuration is not read.
62  */
63 int config_per_user = 1;
64
65 /** @brief Table of audio APIs
66  *
67  * Only set in server processes.
68  */
69 const struct uaudio *const *config_uaudio_apis;
70
71 /** @brief Config file parser state */
72 struct config_state {
73   /** @brief Filename */
74   const char *path;
75
76   /** @brief Line number */
77   int line;
78
79   /** @brief Configuration object under construction */
80   struct config *config;
81 };
82
83 /** @brief Current configuration */
84 struct config *config;
85
86 /** @brief One configuration item */
87 struct conf {
88   /** @brief Name as it appears in the config file */
89   const char *name;
90
91   /** @brief Offset in @ref config structure */
92   size_t offset;
93
94   /** @brief Pointer to item type */
95   const struct conftype *type;
96
97   /** @brief Pointer to item-specific validation routine
98    * @param cs Configuration state
99    * @param nvec Length of (proposed) new value
100    * @param vec Elements of new value
101    * @return 0 on success, non-0 on error
102    *
103    * The validate function should report any error it detects.
104    */
105   int (*validate)(const struct config_state *cs,
106                   int nvec, char **vec);
107 };
108
109 /** @brief Type of a configuration item */
110 struct conftype {
111   /** @brief Pointer to function to set item
112    * @param cs Configuration state
113    * @param whoami Configuration item to set
114    * @param nvec Length of new value
115    * @param vec New value
116    * @return 0 on success, non-0 on error
117    */
118   int (*set)(const struct config_state *cs,
119              const struct conf *whoami,
120              int nvec, char **vec);
121
122   /** @brief Pointer to function to free item
123    * @param c Configuration structure to free an item of
124    * @param whoami Configuration item to free
125    */
126   void (*free)(struct config *c, const struct conf *whoami);
127 };
128
129 /** @brief Compute the address of an item */
130 #define ADDRESS(C, TYPE) ((TYPE *)((char *)(C) + whoami->offset))
131 /** @brief Return the value of an item */
132 #define VALUE(C, TYPE) (*ADDRESS(C, TYPE))
133
134 static int stringlist_compare(const struct stringlist *a,
135                               const struct stringlist *b);
136 static int namepartlist_compare(const struct namepartlist *a,
137                                 const struct namepartlist *b);
138
139 static int set_signal(const struct config_state *cs,
140                       const struct conf *whoami,
141                       int nvec, char **vec) {
142   int n;
143   
144   if(nvec != 1) {
145     disorder_error(0, "%s:%d: '%s' requires one argument",
146           cs->path, cs->line, whoami->name);
147     return -1;
148   }
149   if((n = find_signal(vec[0])) == -1) {
150     disorder_error(0, "%s:%d: unknown signal '%s'",
151           cs->path, cs->line, vec[0]);
152     return -1;
153   }
154   VALUE(cs->config, int) = n;
155   return 0;
156 }
157
158 static int set_collections(const struct config_state *cs,
159                            const struct conf *whoami,
160                            int nvec, char **vec) {
161   struct collectionlist *cl;
162   const char *root, *encoding, *module;
163   
164   switch(nvec) {
165   case 1:
166     module = 0;
167     encoding = 0;
168     root = vec[0];
169     break;
170   case 2:
171     module = vec[0];
172     encoding = 0;
173     root = vec[1];
174     break;
175   case 3:
176     module = vec[0];
177     encoding = vec[1];
178     root = vec[2];
179     break;
180   case 0:
181     disorder_error(0, "%s:%d: '%s' requires at least one argument",
182                    cs->path, cs->line, whoami->name);
183     return -1;
184   default:
185     disorder_error(0, "%s:%d: '%s' requires at most three arguments",
186                    cs->path, cs->line, whoami->name);
187     return -1;
188   }
189   /* Sanity check root */
190   if(root[0] != '/') {
191     disorder_error(0, "%s:%d: collection root must start with '/'",
192                    cs->path, cs->line);
193     return -1;
194   }
195   if(root[1] && root[strlen(root)-1] == '/') {
196     disorder_error(0, "%s:%d: collection root must not end with '/'",
197                    cs->path, cs->line);
198     return -1;
199   }
200   /* Defaults */
201   if(!module)
202     module = "fs";
203   if(!encoding)
204     encoding = nl_langinfo(CODESET);
205   cl = ADDRESS(cs->config, struct collectionlist);
206   ++cl->n;
207   cl->s = xrealloc(cl->s, cl->n * sizeof (struct collection));
208   cl->s[cl->n - 1].module = xstrdup(module);
209   cl->s[cl->n - 1].encoding = xstrdup(encoding);
210   cl->s[cl->n - 1].root = xstrdup(root);
211   return 0;
212 }
213
214 static int set_boolean(const struct config_state *cs,
215                        const struct conf *whoami,
216                        int nvec, char **vec) {
217   int state;
218   
219   if(nvec != 1) {
220     disorder_error(0, "%s:%d: '%s' takes only one argument",
221                    cs->path, cs->line, whoami->name);
222     return -1;
223   }
224   if(!strcmp(vec[0], "yes")) state = 1;
225   else if(!strcmp(vec[0], "no")) state = 0;
226   else {
227     disorder_error(0, "%s:%d: argument to '%s' must be 'yes' or 'no'",
228                    cs->path, cs->line, whoami->name);
229     return -1;
230   }
231   VALUE(cs->config, int) = state;
232   return 0;
233 }
234
235 static int set_string(const struct config_state *cs,
236                       const struct conf *whoami,
237                       int nvec, char **vec) {
238   if(nvec != 1) {
239     disorder_error(0, "%s:%d: '%s' takes only one argument",
240                    cs->path, cs->line, whoami->name);
241     return -1;
242   }
243   xfree(VALUE(cs->config, char *));
244   VALUE(cs->config, char *) = xstrdup(vec[0]);
245   return 0;
246 }
247
248 static int set_stringlist(const struct config_state *cs,
249                           const struct conf *whoami,
250                           int nvec, char **vec) {
251   int n;
252   struct stringlist *sl;
253
254   sl = ADDRESS(cs->config, struct stringlist);
255   sl->n = 0;
256   for(n = 0; n < nvec; ++n) {
257     sl->n++;
258     sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
259     sl->s[sl->n - 1] = xstrdup(vec[n]);
260   }
261   return 0;
262 }
263
264 static int set_integer(const struct config_state *cs,
265                        const struct conf *whoami,
266                        int nvec, char **vec) {
267   char *e;
268
269   if(nvec != 1) {
270     disorder_error(0, "%s:%d: '%s' takes only one argument",
271                    cs->path, cs->line, whoami->name);
272     return -1;
273   }
274   if(xstrtol(ADDRESS(cs->config, long), vec[0], &e, 0)) {
275     disorder_error(errno, "%s:%d: converting integer", cs->path, cs->line);
276     return -1;
277   }
278   if(*e) {
279     disorder_error(0, "%s:%d: invalid integer syntax", cs->path, cs->line);
280     return -1;
281   }
282   return 0;
283 }
284
285 static int set_stringlist_accum(const struct config_state *cs,
286                                 const struct conf *whoami,
287                                 int nvec, char **vec) {
288   int n;
289   struct stringlist *s;
290   struct stringlistlist *sll;
291
292   sll = ADDRESS(cs->config, struct stringlistlist);
293   if(nvec == 0) {
294     sll->n = 0;
295     return 0;
296   }
297   sll->n++;
298   sll->s = xrealloc(sll->s, (sll->n * sizeof (struct stringlist)));
299   s = &sll->s[sll->n - 1];
300   s->n = nvec;
301   s->s = xmalloc((nvec + 1) * sizeof (char *));
302   for(n = 0; n < nvec; ++n)
303     s->s[n] = xstrdup(vec[n]);
304   return 0;
305 }
306
307 static int set_string_accum(const struct config_state *cs,
308                             const struct conf *whoami,
309                             int nvec, char **vec) {
310   int n;
311   struct stringlist *sl;
312
313   sl = ADDRESS(cs->config, struct stringlist);
314   if(nvec == 0) {
315     sl->n = 0;
316     return 0;
317   }
318   for(n = 0; n < nvec; ++n) {
319     sl->n++;
320     sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
321     sl->s[sl->n - 1] = xstrdup(vec[n]);
322   }
323   return 0;
324 }
325
326 static int set_restrict(const struct config_state *cs,
327                         const struct conf *whoami,
328                         int nvec, char **vec) {
329   unsigned r = 0;
330   int n, i;
331   
332   static const struct restriction {
333     const char *name;
334     unsigned bit;
335   } restrictions[] = {
336     { "remove", RESTRICT_REMOVE },
337     { "scratch", RESTRICT_SCRATCH },
338     { "move", RESTRICT_MOVE },
339   };
340
341   for(n = 0; n < nvec; ++n) {
342     if((i = TABLE_FIND(restrictions, name, vec[n])) < 0) {
343       disorder_error(0, "%s:%d: invalid restriction '%s'",
344                      cs->path, cs->line, vec[n]);
345       return -1;
346     }
347     r |= restrictions[i].bit;
348   }
349   VALUE(cs->config, unsigned) = r;
350   return 0;
351 }
352
353 static int parse_sample_format(const struct config_state *cs,
354                                struct stream_header *format,
355                                int nvec, char **vec) {
356   char *p = vec[0];
357   long t;
358
359   if(nvec != 1) {
360     disorder_error(0, "%s:%d: wrong number of arguments", cs->path, cs->line);
361     return -1;
362   }
363   if(xstrtol(&t, p, &p, 0)) {
364     disorder_error(errno, "%s:%d: converting bits-per-sample",
365                    cs->path, cs->line);
366     return -1;
367   }
368   if(t != 8 && t != 16) {
369     disorder_error(0, "%s:%d: bad bits-per-sample (%ld)",
370                    cs->path, cs->line, t);
371     return -1;
372   }
373   if(format) format->bits = t;
374   switch (*p) {
375     case 'l': case 'L': t = ENDIAN_LITTLE; p++; break;
376     case 'b': case 'B': t = ENDIAN_BIG; p++; break;
377     default: t = ENDIAN_NATIVE; break;
378   }
379   if(format) format->endian = t;
380   if(*p != '/') {
381     disorder_error(errno, "%s:%d: expected `/' after bits-per-sample",
382           cs->path, cs->line);
383     return -1;
384   }
385   p++;
386   if(xstrtol(&t, p, &p, 0)) {
387     disorder_error(errno, "%s:%d: converting sample-rate", cs->path, cs->line);
388     return -1;
389   }
390   if(t < 1 || t > INT_MAX) {
391     disorder_error(0, "%s:%d: silly sample-rate (%ld)", cs->path, cs->line, t);
392     return -1;
393   }
394   if(format) format->rate = t;
395   if(*p != '/') {
396     disorder_error(0, "%s:%d: expected `/' after sample-rate",
397                    cs->path, cs->line);
398     return -1;
399   }
400   p++;
401   if(xstrtol(&t, p, &p, 0)) {
402     disorder_error(errno, "%s:%d: converting channels", cs->path, cs->line);
403     return -1;
404   }
405   if(t < 1 || t > 8) {
406     disorder_error(0, "%s:%d: silly number (%ld) of channels",
407                    cs->path, cs->line, t);
408     return -1;
409   }
410   if(format) format->channels = t;
411   if(*p) {
412     disorder_error(0, "%s:%d: junk after channels", cs->path, cs->line);
413     return -1;
414   }
415   return 0;
416 }
417
418 static int set_sample_format(const struct config_state *cs,
419                              const struct conf *whoami,
420                              int nvec, char **vec) {
421   return parse_sample_format(cs, ADDRESS(cs->config, struct stream_header),
422                              nvec, vec);
423 }
424
425 static int set_namepart(const struct config_state *cs,
426                         const struct conf *whoami,
427                         int nvec, char **vec) {
428   struct namepartlist *npl = ADDRESS(cs->config, struct namepartlist);
429   unsigned reflags;
430   const char *errstr;
431   int erroffset, n;
432   pcre *re;
433
434   if(nvec < 3) {
435     disorder_error(0, "%s:%d: namepart needs at least 3 arguments",
436                    cs->path, cs->line);
437     return -1;
438   }
439   if(nvec > 5) {
440     disorder_error(0, "%s:%d: namepart needs at most 5 arguments",
441                    cs->path, cs->line);
442     return -1;
443   }
444   reflags = nvec >= 5 ? regsub_flags(vec[4]) : 0;
445   if(!(re = pcre_compile(vec[1],
446                          PCRE_UTF8
447                          |regsub_compile_options(reflags),
448                          &errstr, &erroffset, 0))) {
449     disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
450                    cs->path, cs->line, vec[1], errstr, erroffset);
451     return -1;
452   }
453   npl->s = xrealloc(npl->s, (npl->n + 1) * sizeof (struct namepart));
454   npl->s[npl->n].part = xstrdup(vec[0]);
455   npl->s[npl->n].re = re;
456   npl->s[npl->n].res = xstrdup(vec[1]);
457   npl->s[npl->n].replace = xstrdup(vec[2]);
458   npl->s[npl->n].context = xstrdup(vec[3]);
459   npl->s[npl->n].reflags = reflags;
460   ++npl->n;
461   /* XXX a bit of a bodge; relies on there being very few parts. */
462   for(n = 0; (n < cs->config->nparts
463               && strcmp(cs->config->parts[n], vec[0])); ++n)
464     ;
465   if(n >= cs->config->nparts) {
466     cs->config->parts = xrealloc(cs->config->parts,
467                                  (cs->config->nparts + 1) * sizeof (char *));
468     cs->config->parts[cs->config->nparts++] = xstrdup(vec[0]);
469   }
470   return 0;
471 }
472
473 static int set_transform(const struct config_state *cs,
474                          const struct conf *whoami,
475                          int nvec, char **vec) {
476   struct transformlist *tl = ADDRESS(cs->config, struct transformlist);
477   pcre *re;
478   unsigned reflags;
479   const char *errstr;
480   int erroffset;
481
482   if(nvec < 3) {
483     disorder_error(0, "%s:%d: transform needs at least 3 arguments",
484                    cs->path, cs->line);
485     return -1;
486   }
487   if(nvec > 5) {
488     disorder_error(0, "%s:%d: transform needs at most 5 arguments",
489                    cs->path, cs->line);
490     return -1;
491   }
492   reflags = (nvec >= 5 ? regsub_flags(vec[4]) : 0);
493   if(!(re = pcre_compile(vec[1],
494                          PCRE_UTF8
495                          |regsub_compile_options(reflags),
496                          &errstr, &erroffset, 0))) {
497     disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
498                    cs->path, cs->line, vec[1], errstr, erroffset);
499     return -1;
500   }
501   tl->t = xrealloc(tl->t, (tl->n + 1) * sizeof (struct namepart));
502   tl->t[tl->n].type = xstrdup(vec[0]);
503   tl->t[tl->n].context = xstrdup(vec[3] ? vec[3] : "*");
504   tl->t[tl->n].re = re;
505   tl->t[tl->n].replace = xstrdup(vec[2]);
506   tl->t[tl->n].flags = reflags;
507   ++tl->n;
508   return 0;
509 }
510
511 static int set_rights(const struct config_state *cs,
512                       const struct conf *whoami,
513                       int nvec, char **vec) {
514   if(nvec != 1) {
515     disorder_error(0, "%s:%d: '%s' requires one argument",
516                    cs->path, cs->line, whoami->name);
517     return -1;
518   }
519   if(parse_rights(vec[0], 0, 1)) {
520     disorder_error(0, "%s:%d: invalid rights string '%s'",
521                    cs->path, cs->line, vec[0]);
522     return -1;
523   }
524   return set_string(cs, whoami, nvec, vec);
525 }
526
527 static int set_netaddress(const struct config_state *cs,
528                           const struct conf *whoami,
529                           int nvec, char **vec) {
530   struct netaddress *na = ADDRESS(cs->config, struct netaddress);
531
532   if(netaddress_parse(na, nvec, vec)) {
533     disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
534     return -1;
535   }
536   return 0;
537 }
538
539 /* free functions */
540
541 static void free_none(struct config attribute((unused)) *c,
542                       const struct conf attribute((unused)) *whoami) {
543 }
544
545 static void free_string(struct config *c,
546                         const struct conf *whoami) {
547   xfree(VALUE(c, char *));
548   VALUE(c, char *) = 0;
549 }
550
551 static void free_stringlist(struct config *c,
552                             const struct conf *whoami) {
553   int n;
554   struct stringlist *sl = ADDRESS(c, struct stringlist);
555
556   for(n = 0; n < sl->n; ++n)
557     xfree(sl->s[n]);
558   xfree(sl->s);
559 }
560
561 static void free_stringlistlist(struct config *c,
562                                 const struct conf *whoami) {
563   int n, m;
564   struct stringlistlist *sll = ADDRESS(c, struct stringlistlist);
565   struct stringlist *sl;
566
567   for(n = 0; n < sll->n; ++n) {
568     sl = &sll->s[n];
569     for(m = 0; m < sl->n; ++m)
570       xfree(sl->s[m]);
571     xfree(sl->s);
572   }
573   xfree(sll->s);
574 }
575
576 static void free_collectionlist(struct config *c,
577                                 const struct conf *whoami) {
578   struct collectionlist *cll = ADDRESS(c, struct collectionlist);
579   struct collection *cl;
580   int n;
581
582   for(n = 0; n < cll->n; ++n) {
583     cl = &cll->s[n];
584     xfree(cl->module);
585     xfree(cl->encoding);
586     xfree(cl->root);
587   }
588   xfree(cll->s);
589 }
590
591 static void free_namepartlist(struct config *c,
592                               const struct conf *whoami) {
593   struct namepartlist *npl = ADDRESS(c, struct namepartlist);
594   struct namepart *np;
595   int n;
596
597   for(n = 0; n < npl->n; ++n) {
598     np = &npl->s[n];
599     xfree(np->part);
600     pcre_free(np->re);                  /* ...whatever pcre_free is set to. */
601     xfree(np->res);
602     xfree(np->replace);
603     xfree(np->context);
604   }
605   xfree(npl->s);
606 }
607
608 static void free_transformlist(struct config *c,
609                                const struct conf *whoami) {
610   struct transformlist *tl = ADDRESS(c, struct transformlist);
611   struct transform *t;
612   int n;
613
614   for(n = 0; n < tl->n; ++n) {
615     t = &tl->t[n];
616     xfree(t->type);
617     pcre_free(t->re);                   /* ...whatever pcre_free is set to. */
618     xfree(t->replace);
619     xfree(t->context);
620   }
621   xfree(tl->t);
622 }
623
624 static void free_netaddress(struct config *c,
625                             const struct conf *whoami) {
626   struct netaddress *na = ADDRESS(c, struct netaddress);
627
628   xfree(na->address);
629 }
630
631 /* configuration types */
632
633 static const struct conftype
634   type_signal = { set_signal, free_none },
635   type_collections = { set_collections, free_collectionlist },
636   type_boolean = { set_boolean, free_none },
637   type_string = { set_string, free_string },
638   type_stringlist = { set_stringlist, free_stringlist },
639   type_integer = { set_integer, free_none },
640   type_stringlist_accum = { set_stringlist_accum, free_stringlistlist },
641   type_string_accum = { set_string_accum, free_stringlist },
642   type_sample_format = { set_sample_format, free_none },
643   type_restrict = { set_restrict, free_none },
644   type_namepart = { set_namepart, free_namepartlist },
645   type_transform = { set_transform, free_transformlist },
646   type_netaddress = { set_netaddress, free_netaddress },
647   type_rights = { set_rights, free_string };
648
649 /* specific validation routine */
650
651 /** @brief Perform a test on a filename
652  * @param test Test function to call on mode bits
653  * @param what Type of file sought
654  *
655  * If @p test returns 0 then the file is not a @p what and an error
656  * is reported and -1 is returned.
657  */
658 #define VALIDATE_FILE(test, what) do {                  \
659   struct stat sb;                                       \
660   int n;                                                \
661                                                         \
662   for(n = 0; n < nvec; ++n) {                           \
663     if(stat(vec[n], &sb) < 0) {                         \
664       disorder_error(errno, "%s:%d: %s",                \
665                      cs->path, cs->line, vec[n]);       \
666       return -1;                                        \
667     }                                                   \
668     if(!test(sb.st_mode)) {                             \
669       disorder_error(0, "%s:%d: %s is not a %s",        \
670                      cs->path, cs->line, vec[n], what); \
671       return -1;                                        \
672     }                                                   \
673   }                                                     \
674 } while(0)
675
676 /** @brief Validate an absolute path
677  * @param cs Configuration state
678  * @param nvec Length of (proposed) new value
679  * @param vec Elements of new value
680  * @return 0 on success, non-0 on error
681  */
682 static int validate_isabspath(const struct config_state *cs,
683                               int nvec, char **vec) {
684   int n;
685
686   for(n = 0; n < nvec; ++n)
687     if(vec[n][0] != '/') {
688       disorder_error(errno, "%s:%d: %s: not an absolute path", 
689                      cs->path, cs->line, vec[n]);
690       return -1;
691     }
692   return 0;
693 }
694
695 /** @brief Validate an existing directory
696  * @param cs Configuration state
697  * @param nvec Length of (proposed) new value
698  * @param vec Elements of new value
699  * @return 0 on success, non-0 on error
700  */
701 static int validate_isdir(const struct config_state *cs,
702                           int nvec, char **vec) {
703   VALIDATE_FILE(S_ISDIR, "directory");
704   return 0;
705 }
706
707 /** @brief Validate an existing regular file
708  * @param cs Configuration state
709  * @param nvec Length of (proposed) new value
710  * @param vec Elements of new value
711  * @return 0 on success, non-0 on error
712  */
713 static int validate_isreg(const struct config_state *cs,
714                           int nvec, char **vec) {
715   VALIDATE_FILE(S_ISREG, "regular file");
716   return 0;
717 }
718
719 /** @brief Validate a player pattern
720  * @param cs Configuration state
721  * @param nvec Length of (proposed) new value
722  * @param vec Elements of new value
723  * @return 0 on success, non-0 on error
724  */
725 static int validate_player(const struct config_state *cs,
726                            int nvec,
727                            char attribute((unused)) **vec) {
728   if(nvec < 2) {
729     disorder_error(0, "%s:%d: should be at least 'player PATTERN MODULE'",
730                    cs->path, cs->line);
731     return -1;
732   }
733   return 0;
734 }
735
736 /** @brief Validate a track length pattern
737  * @param cs Configuration state
738  * @param nvec Length of (proposed) new value
739  * @param vec Elements of new value
740  * @return 0 on success, non-0 on error
741  */
742 static int validate_tracklength(const struct config_state *cs,
743                                 int nvec,
744                                 char attribute((unused)) **vec) {
745   if(nvec < 2) {
746     disorder_error(0, "%s:%d: should be at least 'tracklength PATTERN MODULE'",
747                    cs->path, cs->line);
748     return -1;
749   }
750   return 0;
751 }
752
753 /** @brief Validate an allow directive
754  * @param cs Configuration state
755  * @param nvec Length of (proposed) new value
756  * @param vec Elements of new value
757  * @return 0 on success, non-0 on error
758  *
759  * Obsolete - only used for parsing legacy configuration.
760  */
761 static int validate_allow(const struct config_state *cs,
762                           int nvec,
763                           char attribute((unused)) **vec) {
764   if(nvec != 2) {
765     disorder_error(0, "%s:%d: must be 'allow NAME PASS'", cs->path, cs->line);
766     return -1;
767   }
768   return 0;
769 }
770
771 /** @brief Validate a non-negative (@c long) integer
772  * @param cs Configuration state
773  * @param nvec Length of (proposed) new value
774  * @param vec Elements of new value
775  * @return 0 on success, non-0 on error
776  */
777 static int validate_non_negative(const struct config_state *cs,
778                                  int nvec, char **vec) {
779   long n;
780
781   if(nvec < 1) {
782     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
783     return -1;
784   }
785   if(nvec > 1) {
786     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
787     return -1;
788   }
789   if(xstrtol(&n, vec[0], 0, 0)) {
790     disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
791     return -1;
792   }
793   if(n < 0) {
794     disorder_error(0, "%s:%d: must not be negative", cs->path, cs->line);
795     return -1;
796   }
797   return 0;
798 }
799
800 /** @brief Validate a positive (@c long) integer
801  * @param cs Configuration state
802  * @param nvec Length of (proposed) new value
803  * @param vec Elements of new value
804  * @return 0 on success, non-0 on error
805  */
806 static int validate_positive(const struct config_state *cs,
807                           int nvec, char **vec) {
808   long n;
809
810   if(nvec < 1) {
811     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
812     return -1;
813   }
814   if(nvec > 1) {
815     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
816     return -1;
817   }
818   if(xstrtol(&n, vec[0], 0, 0)) {
819     disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
820     return -1;
821   }
822   if(n <= 0) {
823     disorder_error(0, "%s:%d: must be positive", cs->path, cs->line);
824     return -1;
825   }
826   return 0;
827 }
828
829 /** @brief Validate a system username
830  * @param cs Configuration state
831  * @param nvec Length of (proposed) new value
832  * @param vec Elements of new value
833  * @return 0 on success, non-0 on error
834  */
835 static int validate_isauser(const struct config_state *cs,
836                             int attribute((unused)) nvec,
837                             char **vec) {
838   if(!getpwnam(vec[0])) {
839     disorder_error(0, "%s:%d: no such user as '%s'", cs->path, cs->line, vec[0]);
840     return -1;
841   }
842   return 0;
843 }
844
845 /** @brief Validate a sample format string
846  * @param cs Configuration state
847  * @param nvec Length of (proposed) new value
848  * @param vec Elements of new value
849  * @return 0 on success, non-0 on error
850  */
851 static int validate_sample_format(const struct config_state *cs,
852                                   int attribute((unused)) nvec,
853                                   char **vec) {
854   return parse_sample_format(cs, 0, nvec, vec);
855 }
856
857 /** @brief Validate anything
858  * @param cs Configuration state
859  * @param nvec Length of (proposed) new value
860  * @param vec Elements of new value
861  * @return 0
862  */
863 static int validate_any(const struct config_state attribute((unused)) *cs,
864                         int attribute((unused)) nvec,
865                         char attribute((unused)) **vec) {
866   return 0;
867 }
868
869 /** @brief Validate a URL
870  * @param cs Configuration state
871  * @param nvec Length of (proposed) new value
872  * @param vec Elements of new value
873  * @return 0 on success, non-0 on error
874  *
875  * Rather cursory.
876  */
877 static int validate_url(const struct config_state attribute((unused)) *cs,
878                         int attribute((unused)) nvec,
879                         char **vec) {
880   const char *s;
881   int n;
882   /* absoluteURI   = scheme ":" ( hier_part | opaque_part )
883      scheme        = alpha *( alpha | digit | "+" | "-" | "." ) */
884   s = vec[0];
885   n = strspn(s, ("abcdefghijklmnopqrstuvwxyz"
886                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
887                  "0123456789"));
888   if(s[n] != ':') {
889     disorder_error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
890     return -1;
891   }
892   if(!strncmp(s, "http:", 5)
893      || !strncmp(s, "https:", 6)) {
894     s += n + 1;
895     /* we only do a rather cursory check */
896     if(strncmp(s, "//", 2)) {
897       disorder_error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
898       return -1;
899     }
900   }
901   return 0;
902 }
903
904 /** @brief Validate an alias pattern
905  * @param cs Configuration state
906  * @param nvec Length of (proposed) new value
907  * @param vec Elements of new value
908  * @return 0 on success, non-0 on error
909  */
910 static int validate_alias(const struct config_state *cs,
911                           int nvec,
912                           char **vec) {
913   const char *s;
914   int in_brackets = 0, c;
915
916   if(nvec < 1) {
917     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
918     return -1;
919   }
920   if(nvec > 1) {
921     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
922     return -1;
923   }
924   s = vec[0];
925   while((c = (unsigned char)*s++)) {
926     if(in_brackets) {
927       if(c == '}')
928         in_brackets = 0;
929       else if(!isalnum(c)) {
930         disorder_error(0, "%s:%d: invalid part name in alias expansion in '%s'",
931                        cs->path, cs->line, vec[0]);
932           return -1;
933       }
934     } else {
935       if(c == '{') {
936         in_brackets = 1;
937         if(*s == '/')
938           ++s;
939       } else if(c == '\\') {
940         if(!(c = (unsigned char)*s++)) {
941           disorder_error(0, "%s:%d: unterminated escape in alias expansion in '%s'",
942                          cs->path, cs->line, vec[0]);
943           return -1;
944         } else if(c != '\\' && c != '{') {
945           disorder_error(0, "%s:%d: invalid escape in alias expansion in '%s'",
946                          cs->path, cs->line, vec[0]);
947           return -1;
948         }
949       }
950     }
951     ++s;
952   }
953   if(in_brackets) {
954     disorder_error(0,
955                    "%s:%d: unterminated part name in alias expansion in '%s'",
956                    cs->path, cs->line, vec[0]);
957     return -1;
958   }
959   return 0;
960 }
961
962 /** @brief Validate a hash algorithm name
963  * @param cs Configuration state
964  * @param nvec Length of (proposed) new value
965  * @param vec Elements of new value
966  * @return 0 on success, non-0 on error
967  */
968 static int validate_algo(const struct config_state attribute((unused)) *cs,
969                          int nvec,
970                          char **vec) {
971   if(nvec != 1) {
972     disorder_error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
973     return -1;
974   }
975   if(!valid_authhash(vec[0])) {
976     disorder_error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
977     return -1;
978   }
979   return 0;
980 }
981
982 /** @brief Validate a playback backend name
983  * @param cs Configuration state
984  * @param nvec Length of (proposed) new value
985  * @param vec Elements of new value
986  * @return 0 on success, non-0 on error
987  */
988 static int validate_backend(const struct config_state attribute((unused)) *cs,
989                             int nvec,
990                             char **vec) {
991   int n;
992   if(nvec != 1) {
993     disorder_error(0, "%s:%d: invalid sound API specification", cs->path, cs->line);
994     return -1;
995   }
996   if(!strcmp(vec[0], "network")) {
997     disorder_error(0, "'api network' is deprecated; use 'api rtp'");
998     return 0;
999   }
1000   if(config_uaudio_apis) {
1001     for(n = 0; config_uaudio_apis[n]; ++n)
1002       if(!strcmp(vec[0], config_uaudio_apis[n]->name))
1003         return 0;
1004     disorder_error(0, "%s:%d: unrecognized sound API '%s'", cs->path, cs->line, vec[0]);
1005     return -1;
1006   }
1007   /* In non-server processes we have no idea what's valid */
1008   return 0;
1009 }
1010
1011 /** @brief Validate a pause mode string
1012  * @param cs Configuration state
1013  * @param nvec Length of (proposed) new value
1014  * @param vec Elements of new value
1015  * @return 0 on success, non-0 on error
1016  */
1017 static int validate_pausemode(const struct config_state attribute((unused)) *cs,
1018                               int nvec,
1019                               char **vec) {
1020   if(nvec == 1 && (!strcmp(vec[0], "silence") || !strcmp(vec[0], "suspend")))
1021     return 0;
1022   disorder_error(0, "%s:%d: invalid pause mode", cs->path, cs->line);
1023   return -1;
1024 }
1025
1026 /** @brief Validate a destination network address
1027  * @param cs Configuration state
1028  * @param nvec Length of (proposed) new value
1029  * @param vec Elements of new value
1030  * @return 0 on success, non-0 on error
1031  *
1032  * By a destination address, it is meant that it must not be a wildcard
1033  * address.
1034  */
1035 static int validate_destaddr(const struct config_state attribute((unused)) *cs,
1036                              int nvec,
1037                              char **vec) {
1038   struct netaddress na[1];
1039
1040   if(netaddress_parse(na, nvec, vec)) {
1041     disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
1042     return -1;
1043   }
1044   if(!na->address) {
1045     disorder_error(0, "%s:%d: destination address required", cs->path, cs->line);
1046     return -1;
1047   }
1048   xfree(na->address);
1049   return 0;
1050 }
1051
1052 /** @brief Item name and and offset */
1053 #define C(x) #x, offsetof(struct config, x)
1054 /** @brief Item name and and offset */
1055 #define C2(x,y) #x, offsetof(struct config, y)
1056
1057 /** @brief All configuration items */
1058 static const struct conf conf[] = {
1059   { C(alias),            &type_string,           validate_alias },
1060   { C(allow),            &type_stringlist_accum, validate_allow },
1061   { C(api),              &type_string,           validate_backend },
1062   { C(authorization_algorithm), &type_string,    validate_algo },
1063   { C(broadcast),        &type_netaddress,       validate_destaddr },
1064   { C(broadcast_from),   &type_netaddress,       validate_any },
1065   { C(channel),          &type_string,           validate_any },
1066   { C(checkpoint_kbyte), &type_integer,          validate_non_negative },
1067   { C(checkpoint_min),   &type_integer,          validate_non_negative },
1068   { C(collection),       &type_collections,      validate_any },
1069   { C(connect),          &type_netaddress,       validate_destaddr },
1070   { C(cookie_key_lifetime),  &type_integer,      validate_positive },
1071   { C(cookie_login_lifetime),  &type_integer,    validate_positive },
1072   { C(dbversion),        &type_integer,          validate_positive },
1073   { C(default_rights),   &type_rights,           validate_any },
1074   { C(device),           &type_string,           validate_any },
1075   { C(gap),              &type_integer,          validate_non_negative },
1076   { C(history),          &type_integer,          validate_positive },
1077   { C(home),             &type_string,           validate_isabspath },
1078   { C(listen),           &type_netaddress,       validate_any },
1079   { C(lock),             &type_boolean,          validate_any },
1080   { C(mail_sender),      &type_string,           validate_any },
1081   { C(mixer),            &type_string,           validate_any },
1082   { C(mount_rescan),     &type_boolean,          validate_any },
1083   { C(multicast_loop),   &type_boolean,          validate_any },
1084   { C(multicast_ttl),    &type_integer,          validate_non_negative },
1085   { C(namepart),         &type_namepart,         validate_any },
1086   { C(new_bias),         &type_integer,          validate_positive },
1087   { C(new_bias_age),     &type_integer,          validate_positive },
1088   { C(new_max),          &type_integer,          validate_positive },
1089   { C2(nice, nice_rescan), &type_integer,        validate_non_negative },
1090   { C(nice_rescan),      &type_integer,          validate_non_negative },
1091   { C(nice_server),      &type_integer,          validate_any },
1092   { C(nice_speaker),     &type_integer,          validate_any },
1093   { C(noticed_history),  &type_integer,          validate_positive },
1094   { C(password),         &type_string,           validate_any },
1095   { C(pause_mode),       &type_string,           validate_pausemode },
1096   { C(player),           &type_stringlist_accum, validate_player },
1097   { C(playlist_lock_timeout), &type_integer,     validate_positive },
1098   { C(playlist_max) ,    &type_integer,          validate_positive },
1099   { C(plugins),          &type_string_accum,     validate_isdir },
1100   { C(prefsync),         &type_integer,          validate_positive },
1101   { C(queue_pad),        &type_integer,          validate_positive },
1102   { C(refresh),          &type_integer,          validate_positive },
1103   { C(refresh_min),      &type_integer,          validate_non_negative },
1104   { C(reminder_interval), &type_integer,         validate_positive },
1105   { C(remote_userman),   &type_boolean,          validate_any },
1106   { C(replay_min),       &type_integer,          validate_non_negative },
1107   { C2(restrict, restrictions),         &type_restrict,         validate_any },
1108   { C(rtp_delay_threshold), &type_integer,       validate_positive },
1109   { C(rtp_verbose),      &type_boolean,          validate_any },
1110   { C(sample_format),    &type_sample_format,    validate_sample_format },
1111   { C(scratch),          &type_string_accum,     validate_isreg },
1112   { C(sendmail),         &type_string,           validate_isabspath },
1113   { C(short_display),    &type_integer,          validate_positive },
1114   { C(signal),           &type_signal,           validate_any },
1115   { C(smtp_server),      &type_string,           validate_any },
1116   { C(sox_generation),   &type_integer,          validate_non_negative },
1117   { C2(speaker_backend, api),  &type_string,     validate_backend },
1118   { C(speaker_command),  &type_string,           validate_any },
1119   { C(stopword),         &type_string_accum,     validate_any },
1120   { C(templates),        &type_string_accum,     validate_isdir },
1121   { C(tracklength),      &type_stringlist_accum, validate_tracklength },
1122   { C(transform),        &type_transform,        validate_any },
1123   { C(trust),            &type_string_accum,     validate_any },
1124   { C(url),              &type_string,           validate_url },
1125   { C(user),             &type_string,           validate_isauser },
1126   { C(username),         &type_string,           validate_any },
1127 };
1128
1129 /** @brief Find a configuration item's definition by key */
1130 static const struct conf *find(const char *key) {
1131   int n;
1132
1133   if((n = TABLE_FIND(conf, name, key)) < 0)
1134     return 0;
1135   return &conf[n];
1136 }
1137
1138 /** @brief Set a new configuration value
1139  * @param cs Configuration state
1140  * @param nvec Length of @p vec
1141  * @param vec Name and new value
1142  * @return 0 on success, non-0 on error.
1143  *
1144  * @c vec[0] is the name, the rest is the value.
1145  */
1146 static int config_set(const struct config_state *cs,
1147                       int nvec, char **vec) {
1148   const struct conf *which;
1149
1150   D(("config_set %s", vec[0]));
1151   if(!(which = find(vec[0]))) {
1152     disorder_error(0, "%s:%d: unknown configuration key '%s'",
1153           cs->path, cs->line, vec[0]);
1154     return -1;
1155   }
1156   return (which->validate(cs, nvec - 1, vec + 1)
1157           || which->type->set(cs, which, nvec - 1, vec + 1));
1158 }
1159
1160 /** @brief Set a configuration item from parameters
1161  * @param cs Configuration state
1162  * @param which Item to set
1163  * @param ... Value as strings, terminated by (char *)NULL
1164  * @return 0 on success, non-0 on error
1165  */
1166 static int config_set_args(const struct config_state *cs,
1167                            const char *which, ...) {
1168   va_list ap;
1169   struct vector v[1];
1170   char *s;
1171
1172   vector_init(v);
1173   vector_append(v, (char *)which);
1174   va_start(ap, which);
1175   while((s = va_arg(ap, char *)))
1176     vector_append(v, s);
1177   va_end(ap);
1178   vector_terminate(v);
1179   int rc = config_set(cs, v->nvec, v->vec);
1180   xfree(v->vec);
1181   return rc;
1182 }
1183
1184 /** @brief Error callback used by config_include()
1185  * @param msg Error message
1186  * @param u User data (@ref config_state)
1187  */
1188 static void config_error(const char *msg, void *u) {
1189   const struct config_state *cs = u;
1190
1191   disorder_error(0, "%s:%d: %s", cs->path, cs->line, msg);
1192 }
1193
1194 /** @brief Include a file by name
1195  * @param c Configuration to update
1196  * @param path Path to read
1197  * @return 0 on success, non-0 on error
1198  */
1199 static int config_include(struct config *c, const char *path) {
1200   FILE *fp;
1201   char *buffer, *inputbuffer, **vec;
1202   int n, ret = 0;
1203   struct config_state cs;
1204
1205   cs.path = path;
1206   cs.line = 0;
1207   cs.config = c;
1208   D(("%s: reading configuration", path));
1209   if(!(fp = fopen(path, "r"))) {
1210     disorder_error(errno, "error opening %s", path);
1211     return -1;
1212   }
1213   while(!inputline(path, fp, &inputbuffer, '\n')) {
1214     ++cs.line;
1215     if(!(buffer = mb2utf8(inputbuffer))) {
1216       disorder_error(errno, "%s:%d: cannot convert to UTF-8", cs.path, cs.line);
1217       ret = -1;
1218       xfree(inputbuffer);
1219       continue;
1220     }
1221     xfree(inputbuffer);
1222     if(!(vec = split(buffer, &n, SPLIT_COMMENTS|SPLIT_QUOTES,
1223                      config_error, &cs))) {
1224       ret = -1;
1225       xfree(buffer);
1226       continue;
1227     }
1228     if(n) {
1229       /* 'include' is special-cased */
1230       if(!strcmp(vec[0], "include")) {
1231         if(n != 2) {
1232           disorder_error(0, "%s:%d: must be 'include PATH'", cs.path, cs.line);
1233           ret = -1;
1234         } else
1235           config_include(c, vec[1]);
1236       } else
1237         ret |= config_set(&cs, n, vec);
1238     }
1239     for(n = 0; vec[n]; ++n) xfree(vec[n]);
1240     xfree(vec);
1241     xfree(buffer);
1242   }
1243   if(ferror(fp)) {
1244     disorder_error(errno, "error reading %s", path);
1245     ret = -1;
1246   }
1247   fclose(fp);
1248   return ret;
1249 }
1250
1251 /** @brief Default stopword setting */
1252 static const char *const default_stopwords[] = {
1253   "stopword",
1254
1255   "01",
1256   "02",
1257   "03",
1258   "04",
1259   "05",
1260   "06",
1261   "07",
1262   "08",
1263   "09",
1264   "1",
1265   "10",
1266   "11",
1267   "12",
1268   "13",
1269   "14",
1270   "15",
1271   "16",
1272   "17",
1273   "18",
1274   "19",
1275   "2",
1276   "20",
1277   "21",
1278   "22",
1279   "23",
1280   "24",
1281   "25",
1282   "26",
1283   "27",
1284   "28",
1285   "29",
1286   "3",
1287   "30",
1288   "4",
1289   "5",
1290   "6",
1291   "7",
1292   "8",
1293   "9",
1294   "a",
1295   "am",
1296   "an",
1297   "and",
1298   "as",
1299   "for",
1300   "i",
1301   "im",
1302   "in",
1303   "is",
1304   "of",
1305   "on",
1306   "the",
1307   "to",
1308   "too",
1309   "we",
1310 };
1311 #define NDEFAULT_STOPWORDS (sizeof default_stopwords / sizeof *default_stopwords)
1312
1313 /** @brief Default player patterns */
1314 static const char *const default_players[] = {
1315   "*.ogg",
1316   "*.flac",
1317   "*.mp3",
1318   "*.wav",
1319 };
1320 #define NDEFAULT_PLAYERS (sizeof default_players / sizeof *default_players)
1321
1322 /** @brief Make a new default configuration
1323  * @return New configuration
1324  */
1325 static struct config *config_default(void) {
1326   struct config *c = xmalloc(sizeof *c);
1327   const char *logname;
1328   struct passwd *pw;
1329   struct config_state cs;
1330   size_t n;
1331
1332   cs.path = "<internal>";
1333   cs.line = 0;
1334   cs.config = c;
1335   /* Strings had better be xstrdup'd as they will get freed at some point. */
1336   c->gap = 0;
1337   c->history = 60;
1338   c->home = xstrdup(pkgstatedir);
1339   if(!(pw = getpwuid(getuid())))
1340     disorder_fatal(0, "cannot determine our username");
1341   logname = pw->pw_name;
1342   c->username = xstrdup(logname);
1343   c->refresh = 15;
1344   c->refresh_min = 1;
1345   c->prefsync = 0;
1346   c->signal = SIGKILL;
1347   c->alias = xstrdup("{/artist}{/album}{/title}{ext}");
1348   c->lock = 1;
1349   c->device = xstrdup("default");
1350   c->nice_rescan = 10;
1351   c->speaker_command = 0;
1352   c->sample_format.bits = 16;
1353   c->sample_format.rate = 44100;
1354   c->sample_format.channels = 2;
1355   c->sample_format.endian = ENDIAN_NATIVE;
1356   c->queue_pad = 10;
1357   c->replay_min = 8 * 3600;
1358   c->api = NULL;
1359   c->multicast_ttl = 1;
1360   c->multicast_loop = 1;
1361   c->authorization_algorithm = xstrdup("sha1");
1362   c->noticed_history = 31;
1363   c->short_display = 32;
1364   c->mixer = 0;
1365   c->channel = 0;
1366   c->dbversion = 2;
1367   c->cookie_login_lifetime = 86400;
1368   c->cookie_key_lifetime = 86400 * 7;
1369   if(sendmail_binary[0] && strcmp(sendmail_binary, "none"))
1370     c->sendmail = xstrdup(sendmail_binary);
1371   c->smtp_server = xstrdup("127.0.0.1");
1372   c->new_max = 100;
1373   c->reminder_interval = 600;           /* 10m */
1374   c->new_bias_age = 7 * 86400;          /* 1 week */
1375   c->new_bias = 4500000;                /* 50 times the base weight */
1376   c->sox_generation = DEFAULT_SOX_GENERATION;
1377   c->playlist_max = INT_MAX;            /* effectively no limit */
1378   c->playlist_lock_timeout = 10;        /* 10s */
1379   c->mount_rescan = 1;
1380   /* Default stopwords */
1381   if(config_set(&cs, (int)NDEFAULT_STOPWORDS, (char **)default_stopwords))
1382     exit(1);
1383   /* Default player configuration */
1384   for(n = 0; n < NDEFAULT_PLAYERS; ++n) {
1385     if(config_set_args(&cs, "player",
1386                        default_players[n], "execraw", "disorder-decode", (char *)0))
1387       exit(1);
1388     if(config_set_args(&cs, "tracklength",
1389                        default_players[n], "disorder-tracklength", (char *)0))
1390       exit(1);
1391   }
1392   c->broadcast.af = -1;
1393   c->broadcast_from.af = -1;
1394   c->listen.af = -1;
1395   c->connect.af = -1;
1396   return c;
1397 }
1398
1399 /** @brief Construct a filename
1400  * @param c Configuration
1401  * @param name Base filename
1402  * @return Full filename
1403  *
1404  * Usually use config_get_file() instead.
1405  */
1406 char *config_get_file2(struct config *c, const char *name) {
1407   char *s;
1408
1409   byte_xasprintf(&s, "%s/%s", c->home, name);
1410   return s;
1411 }
1412
1413 /** @brief Set the default configuration file */
1414 static void set_configfile(void) {
1415   if(!configfile)
1416     byte_xasprintf(&configfile, "%s/config", pkgconfdir);
1417 }
1418
1419 /** @brief Free a configuration object
1420  * @param c Configuration to free
1421  *
1422  * @p c is indeterminate after this function is called.
1423  */
1424 void config_free(struct config *c) {
1425   int n;
1426
1427   if(c) {
1428     for(n = 0; n < (int)(sizeof conf / sizeof *conf); ++n)
1429       conf[n].type->free(c, &conf[n]);
1430     for(n = 0; n < c->nparts; ++n)
1431       xfree(c->parts[n]);
1432     xfree(c->parts);
1433     xfree(c);
1434   }
1435 }
1436
1437 /** @brief Set post-parse defaults
1438  * @param c Configuration to update
1439  * @param server True when running in the server
1440  *
1441  * If @p server is set then certain parts of the configuration are more
1442  * strictly validated.
1443  */
1444 static void config_postdefaults(struct config *c,
1445                                 int server) {
1446   struct config_state cs;
1447   const struct conf *whoami;
1448   int n;
1449
1450   static const char *namepart[][4] = {
1451     { "title",  "/([0-9]+ *[-:]? *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
1452     { "title",  "/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort" },
1453     { "album",  "/([^/]+)/[^/]+$",                    "$1", "*" },
1454     { "artist", "/([^/]+)/[^/]+/[^/]+$",              "$1", "*" },
1455     { "ext",    "(\\.[a-zA-Z0-9]+)$",                 "$1", "*" },
1456   };
1457 #define NNAMEPART (int)(sizeof namepart / sizeof *namepart)
1458
1459   static const char *transform[][5] = {
1460     { "track", "^.*/([0-9]+ *[-:]? *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display", "" },
1461     { "track", "^.*/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort", "" },
1462     { "dir",   "^.*/([^/]+)$",                          "$1", "*", "" },
1463     { "dir",   "^(the) ([^/]*)",                        "$2, $1", "sort", "i", },
1464     { "dir",   "[[:punct:]]",                           "", "sort", "g", }
1465   };
1466 #define NTRANSFORM (int)(sizeof transform / sizeof *transform)
1467
1468   cs.path = "<internal>";
1469   cs.line = 0;
1470   cs.config = c;
1471   if(!c->namepart.n) {
1472     whoami = find("namepart");
1473     for(n = 0; n < NNAMEPART; ++n)
1474       set_namepart(&cs, whoami, 4, (char **)namepart[n]);
1475   }
1476   if(!c->transform.n) {
1477     whoami = find("transform");
1478     for(n = 0; n < NTRANSFORM; ++n)
1479       set_transform(&cs, whoami, 5, (char **)transform[n]);
1480   }
1481   if(!c->api) {
1482     if(c->speaker_command)
1483       c->api = xstrdup("command");
1484     else if(c->broadcast.af != -1)
1485       c->api = xstrdup("rtp");
1486     else if(config_uaudio_apis)
1487       c->api = xstrdup(config_uaudio_apis[0]->name);
1488     else
1489       c->api = xstrdup("<none>");
1490   }
1491   if(!strcmp(c->api, "network"))
1492     c->api = xstrdup("rtp");
1493   if(server) {
1494     if(!strcmp(c->api, "command") && !c->speaker_command)
1495       disorder_fatal(0, "'api command' but speaker_command is not set");
1496     if((!strcmp(c->api, "rtp")) && c->broadcast.af == -1)
1497       disorder_fatal(0, "'api rtp' but broadcast is not set");
1498   }
1499   /* Override sample format */
1500   if(!strcmp(c->api, "rtp")) {
1501     c->sample_format.rate = 44100;
1502     c->sample_format.channels = 2;
1503     c->sample_format.bits = 16;
1504     c->sample_format.endian = ENDIAN_NATIVE;
1505   }
1506   if(!strcmp(c->api, "coreaudio")) {
1507     c->sample_format.rate = 44100;
1508     c->sample_format.channels = 2;
1509     c->sample_format.bits = 16;
1510     c->sample_format.endian = ENDIAN_NATIVE;
1511   }
1512   if(!c->default_rights) {
1513     rights_type r = RIGHTS__MASK & ~(RIGHT_ADMIN|RIGHT_REGISTER
1514                                      |RIGHT_MOVE__MASK
1515                                      |RIGHT_SCRATCH__MASK
1516                                      |RIGHT_REMOVE__MASK);
1517     /* The idea is to approximate the meaning of the old 'restrict' directive
1518      * in the default rights if they are not overridden. */
1519     if(c->restrictions & RESTRICT_SCRATCH)
1520       r |= RIGHT_SCRATCH_MINE|RIGHT_SCRATCH_RANDOM;
1521     else
1522       r |= RIGHT_SCRATCH_ANY;
1523     if(!(c->restrictions & RESTRICT_MOVE))
1524       r |= RIGHT_MOVE_ANY;
1525     if(c->restrictions & RESTRICT_REMOVE)
1526       r |= RIGHT_REMOVE_MINE;
1527     else
1528       r |= RIGHT_REMOVE_ANY;
1529     c->default_rights = rights_string(r);
1530   }
1531 }
1532
1533 /** @brief (Re-)read the config file
1534  * @param server If set, do extra checking
1535  * @param oldconfig Old configuration for compatibility check
1536  * @return 0 on success, non-0 on error
1537  *
1538  * If @p oldconfig is set, then certain compatibility checks are done between
1539  * the old and new configurations.
1540  */
1541 int config_read(int server,
1542                 const struct config *oldconfig) {
1543   struct config *c;
1544   char *privconf;
1545   struct passwd *pw;
1546
1547   set_configfile();
1548   c = config_default();
1549   /* standalone Disobedience installs might not have a global config file */
1550   if(access(configfile, F_OK) == 0)
1551     if(config_include(c, configfile))
1552       return -1;
1553   /* if we can read the private config file, do */
1554   if((privconf = config_private())
1555      && access(privconf, R_OK) == 0
1556      && config_include(c, privconf))
1557     return -1;
1558   xfree(privconf);
1559   /* if there's a per-user system config file for this user, read it */
1560   if(config_per_user) {
1561     if(!(pw = getpwuid(getuid())))
1562       disorder_fatal(0, "cannot determine our username");
1563     if((privconf = config_usersysconf(pw))
1564        && access(privconf, F_OK) == 0
1565        && config_include(c, privconf))
1566       return -1;
1567     xfree(privconf);
1568     /* if we have a password file, read it */
1569     if((privconf = config_userconf(0, pw))
1570        && access(privconf, F_OK) == 0
1571        && config_include(c, privconf))
1572       return -1;
1573     xfree(privconf);
1574   }
1575   /* install default namepart and transform settings */
1576   config_postdefaults(c, server);
1577   if(oldconfig)  {
1578     int failed = 0;
1579     if(strcmp(c->home, oldconfig->home)) {
1580       disorder_error(0, "'home' cannot be changed without a restart");
1581       failed = 1;
1582     }
1583     if(strcmp(c->alias, oldconfig->alias)) {
1584       disorder_error(0, "'alias' cannot be changed without a restart");
1585       failed = 1;
1586     }
1587     if(strcmp(c->user, oldconfig->user)) {
1588       disorder_error(0, "'user' cannot be changed without a restart");
1589       failed = 1;
1590     }
1591     if(c->nice_speaker != oldconfig->nice_speaker) {
1592       disorder_error(0, "'nice_speaker' cannot be changed without a restart");
1593       /* ...but we accept the new config anyway */
1594     }
1595     if(c->nice_server != oldconfig->nice_server) {
1596       disorder_error(0, "'nice_server' cannot be changed without a restart");
1597       /* ...but we accept the new config anyway */
1598     }
1599     if(namepartlist_compare(&c->namepart, &oldconfig->namepart)) {
1600       disorder_error(0, "'namepart' settings cannot be changed without a restart");
1601       failed = 1;
1602     }
1603     if(stringlist_compare(&c->stopword, &oldconfig->stopword)) {
1604       disorder_error(0, "'stopword' settings cannot be changed without a restart");
1605       failed = 1;
1606     }
1607     if(failed) {
1608       disorder_error(0, "not installing incompatible new configuration");
1609       return -1;
1610     }
1611   }
1612   /* everything is good so we shall use the new config */
1613   config_free(config);
1614   /* warn about obsolete directives */
1615   if(c->restrictions)
1616     disorder_error(0, "'restrict' will be removed in a future version");
1617   if(c->allow.n)
1618     disorder_error(0, "'allow' will be removed in a future version");
1619   if(c->trust.n)
1620     disorder_error(0, "'trust' will be removed in a future version");
1621   if(!c->lock)
1622     disorder_error(0, "'lock' will be removed in a future version");
1623   if(c->gap)
1624     disorder_error(0, "'gap' will be removed in a future version");
1625   if(c->prefsync)
1626     disorder_error(0, "'prefsync' will be removed in a future version");
1627   config = c;
1628   return 0;
1629 }
1630
1631 /** @brief Return the path to the private configuration file */
1632 char *config_private(void) {
1633   char *s;
1634
1635   set_configfile();
1636   byte_xasprintf(&s, "%s.private", configfile);
1637   return s;
1638 }
1639
1640 /** @brief Return the path to user's personal configuration file */
1641 char *config_userconf(const char *home, const struct passwd *pw) {
1642   char *s;
1643
1644   if(!home && !pw && !(pw = getpwuid(getuid())))
1645     disorder_fatal(0, "cannot determine our username");
1646   byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir);
1647   return s;
1648 }
1649
1650 /** @brief Return the path to user-specific system configuration */
1651 char *config_usersysconf(const struct passwd *pw) {
1652   char *s;
1653
1654   set_configfile();
1655   if(!strchr(pw->pw_name, '/')) {
1656     byte_xasprintf(&s, "%s.%s", configfile, pw->pw_name);
1657     return s;
1658   } else
1659     return 0;
1660 }
1661
1662 /** @brief Get a filename within the home directory
1663  * @param name Relative name
1664  * @return Full path
1665  */
1666 char *config_get_file(const char *name) {
1667   return config_get_file2(config, name);
1668 }
1669
1670 /** @brief Order two stringlists
1671  * @param a First stringlist
1672  * @param b Second stringlist
1673  * @return <0, 0 or >0 if a<b, a=b or a>b
1674  */
1675 static int stringlist_compare(const struct stringlist *a,
1676                               const struct stringlist *b) {
1677   int n = 0, c;
1678
1679   while(n < a->n && n < b->n) {
1680     if((c = strcmp(a->s[n], b->s[n])))
1681       return c;
1682     ++n;
1683   }
1684   if(a->n < b->n)
1685     return -1;
1686   else if(a->n > b->n)
1687     return 1;
1688   else
1689     return 0;
1690 }
1691
1692 /** @brief Order two namepart definitions
1693  * @param a First namepart definition
1694  * @param b Second namepart definition
1695  * @return <0, 0 or >0 if a<b, a=b or a>b
1696  */
1697 static int namepart_compare(const struct namepart *a,
1698                             const struct namepart *b) {
1699   int c;
1700
1701   if((c = strcmp(a->part, b->part)))
1702     return c;
1703   if((c = strcmp(a->res, b->res)))
1704     return c;
1705   if((c = strcmp(a->replace, b->replace)))
1706     return c;
1707   if((c = strcmp(a->context, b->context)))
1708     return c;
1709   if(a->reflags > b->reflags)
1710     return 1;
1711   if(a->reflags < b->reflags)
1712     return -1;
1713   return 0;
1714 }
1715
1716 /** @brief Order two lists of namepart definitions
1717  * @param a First list of namepart definitions
1718  * @param b Second list of namepart definitions
1719  * @return <0, 0 or >0 if a<b, a=b or a>b
1720  */
1721 static int namepartlist_compare(const struct namepartlist *a,
1722                                 const struct namepartlist *b) {
1723   int n = 0, c;
1724
1725   while(n < a->n && n < b->n) {
1726     if((c = namepart_compare(&a->s[n], &b->s[n])))
1727       return c;
1728     ++n;
1729   }
1730   if(a->n > b->n)
1731     return 1;
1732   else if(a->n < b->n)
1733     return -1;
1734   else
1735     return 0;
1736 }
1737
1738 /** @brief Verify configuration table.
1739  * @return The number of problems found
1740 */
1741 int config_verify(void) {
1742   int fails = 0;
1743   for(size_t n = 1; n < sizeof conf / sizeof *conf; ++n)
1744     if(strcmp(conf[n-1].name, conf[n].name) >= 0) {
1745       fprintf(stderr, "%s >= %s\n", conf[n-1].name, conf[n].name);
1746       ++fails;
1747     }
1748   return fails;
1749 }
1750
1751 /*
1752 Local Variables:
1753 c-basic-offset:2
1754 comment-column:40
1755 fill-column:79
1756 End:
1757 */