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