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