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