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