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