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