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