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