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