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