X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/04e1fa7cf5163aa784baaf3292ccb3b49e296410..05b75f8d50b83e943af3be4071449304d82dbdcd:/lib/configuration.c diff --git a/lib/configuration.c b/lib/configuration.c index 0f1dc96..c66f66b 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell + * Copyright (C) 2004-2008 Richard Kettlewell * Portions copyright (C) 2007 Mark Wooding * * This program is free software; you can redistribute it and/or modify @@ -22,12 +22,8 @@ * @brief Configuration file support */ -#include -#include "types.h" +#include "common.h" -#include -#include -#include #include #include #include @@ -54,6 +50,7 @@ #include "regsub.h" #include "signame.h" #include "authhash.h" +#include "vector.h" /** @brief Path to config file * @@ -131,28 +128,55 @@ static int set_collections(const struct config_state *cs, const struct conf *whoami, int nvec, char **vec) { struct collectionlist *cl; + const char *root, *encoding, *module; - if(nvec != 3) { - error(0, "%s:%d: '%s' requires three arguments", + switch(nvec) { + case 1: + module = 0; + encoding = 0; + root = vec[0]; + break; + case 2: + module = vec[0]; + encoding = 0; + root = vec[1]; + break; + case 3: + module = vec[0]; + encoding = vec[1]; + root = vec[2]; + break; + case 0: + error(0, "%s:%d: '%s' requires at least one argument", + cs->path, cs->line, whoami->name); + return -1; + default: + error(0, "%s:%d: '%s' requires at most three arguments", cs->path, cs->line, whoami->name); return -1; } - if(vec[2][0] != '/') { + /* Sanity check root */ + if(root[0] != '/') { error(0, "%s:%d: collection root must start with '/'", cs->path, cs->line); return -1; } - if(vec[2][1] && vec[2][strlen(vec[2])-1] == '/') { + if(root[1] && root[strlen(root)-1] == '/') { error(0, "%s:%d: collection root must not end with '/'", cs->path, cs->line); return -1; } + /* Defaults */ + if(!module) + module = "fs"; + if(!encoding) + encoding = nl_langinfo(CODESET); cl = ADDRESS(cs->config, struct collectionlist); ++cl->n; cl->s = xrealloc(cl->s, cl->n * sizeof (struct collection)); - cl->s[cl->n - 1].module = xstrdup(vec[0]); - cl->s[cl->n - 1].encoding = xstrdup(vec[1]); - cl->s[cl->n - 1].root = xstrdup(vec[2]); + cl->s[cl->n - 1].module = xstrdup(module); + cl->s[cl->n - 1].encoding = xstrdup(encoding); + cl->s[cl->n - 1].root = xstrdup(root); return 0; } @@ -283,7 +307,7 @@ static int set_restrict(const struct config_state *cs, }; for(n = 0; n < nvec; ++n) { - if((i = TABLE_FIND(restrictions, struct restriction, name, vec[n])) < 0) { + if((i = TABLE_FIND(restrictions, name, vec[n])) < 0) { error(0, "%s:%d: invalid restriction '%s'", cs->path, cs->line, vec[n]); return -1; @@ -493,19 +517,17 @@ static int set_backend(const struct config_state *cs, static int set_rights(const struct config_state *cs, const struct conf *whoami, int nvec, char **vec) { - rights_type r; - if(nvec != 1) { error(0, "%s:%d: '%s' requires one argument", cs->path, cs->line, whoami->name); return -1; } - if(parse_rights(vec[0], &r)) { + if(parse_rights(vec[0], 0, 1)) { error(0, "%s:%d: invalid rights string '%s'", cs->path, cs->line, vec[0]); return -1; } - *ADDRESS(cs->config, rights_type) = r; + *ADDRESS(cs->config, char *) = vec[0]; return 0; } @@ -654,12 +676,6 @@ static int validate_isreg(const struct config_state *cs, return 0; } -static int validate_ischr(const struct config_state *cs, - int nvec, char **vec) { - VALIDATE_FILE(S_ISCHR, "character device"); - return 0; -} - static int validate_player(const struct config_state *cs, int nvec, char attribute((unused)) **vec) { @@ -756,16 +772,6 @@ static int validate_sample_format(const struct config_state *cs, return parse_sample_format(cs, 0, nvec, vec); } -static int validate_channel(const struct config_state *cs, - int attribute((unused)) nvec, - char **vec) { - if(mixer_channel(vec[0]) == -1) { - error(0, "%s:%d: invalid channel '%s'", cs->path, cs->line, vec[0]); - return -1; - } - return 0; -} - static int validate_any(const struct config_state attribute((unused)) *cs, int attribute((unused)) nvec, char attribute((unused)) **vec) { @@ -912,10 +918,11 @@ static int validate_algo(const struct config_state attribute((unused)) *cs, static const struct conf conf[] = { { C(alias), &type_string, validate_alias }, { C(allow), &type_stringlist_accum, validate_allow }, + { C(api), &type_backend, validate_any }, { C(authorization_algorithm), &type_string, validate_algo }, { C(broadcast), &type_stringlist, validate_addrport }, { C(broadcast_from), &type_stringlist, validate_addrport }, - { C(channel), &type_string, validate_channel }, + { C(channel), &type_string, validate_any }, { C(checkpoint_kbyte), &type_integer, validate_non_negative }, { C(checkpoint_min), &type_integer, validate_non_negative }, { C(collection), &type_collections, validate_any }, @@ -930,10 +937,14 @@ static const struct conf conf[] = { { C(home), &type_string, validate_isabspath }, { C(listen), &type_stringlist, validate_port }, { C(lock), &type_boolean, validate_any }, - { C(mixer), &type_string, validate_ischr }, + { C(mail_sender), &type_string, validate_any }, + { C(mixer), &type_string, validate_any }, { C(multicast_loop), &type_boolean, validate_any }, { C(multicast_ttl), &type_integer, validate_non_negative }, { C(namepart), &type_namepart, validate_any }, + { C(new_bias), &type_integer, validate_positive }, + { C(new_bias_age), &type_integer, validate_positive }, + { C(new_max), &type_integer, validate_positive }, { C2(nice, nice_rescan), &type_integer, validate_non_negative }, { C(nice_rescan), &type_integer, validate_non_negative }, { C(nice_server), &type_integer, validate_any }, @@ -944,14 +955,18 @@ static const struct conf conf[] = { { C(plugins), &type_string_accum, validate_isdir }, { C(prefsync), &type_integer, validate_positive }, { C(queue_pad), &type_integer, validate_positive }, + { C(replay_min), &type_integer, validate_non_negative }, { C(refresh), &type_integer, validate_positive }, + { C(reminder_interval), &type_integer, validate_positive }, + { C(remote_userman), &type_boolean, validate_any }, { C2(restrict, restrictions), &type_restrict, validate_any }, { C(sample_format), &type_sample_format, validate_sample_format }, { C(scratch), &type_string_accum, validate_isreg }, { C(short_display), &type_integer, validate_positive }, { C(signal), &type_signal, validate_any }, + { C(smtp_server), &type_string, validate_any }, { C(sox_generation), &type_integer, validate_non_negative }, - { C(speaker_backend), &type_backend, validate_any }, + { C2(speaker_backend, api), &type_backend, validate_any }, { C(speaker_command), &type_string, validate_any }, { C(stopword), &type_string_accum, validate_any }, { C(templates), &type_string_accum, validate_isdir }, @@ -967,7 +982,7 @@ static const struct conf conf[] = { static const struct conf *find(const char *key) { int n; - if((n = TABLE_FIND(conf, struct conf, name, key)) < 0) + if((n = TABLE_FIND(conf, name, key)) < 0) return 0; return &conf[n]; } @@ -987,6 +1002,22 @@ static int config_set(const struct config_state *cs, || which->type->set(cs, which, nvec - 1, vec + 1)); } +static int config_set_args(const struct config_state *cs, + const char *which, ...) { + va_list ap; + struct vector v[1]; + char *s; + + vector_init(v); + vector_append(v, (char *)which); + va_start(ap, which); + while((s = va_arg(ap, char *))) + vector_append(v, s); + va_end(ap); + vector_terminate(v); + return config_set(cs, v->nvec, v->vec); +} + /** @brief Error callback used by config_include() */ static void config_error(const char *msg, void *u) { const struct config_state *cs = u; @@ -1046,14 +1077,88 @@ static int config_include(struct config *c, const char *path) { return ret; } +static const char *const default_stopwords[] = { + "stopword", + + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "1", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "2", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "3", + "30", + "4", + "5", + "6", + "7", + "8", + "9", + "a", + "am", + "an", + "and", + "as", + "for", + "i", + "im", + "in", + "is", + "of", + "on", + "the", + "to", + "too", + "we", +}; +#define NDEFAULT_STOPWORDS (sizeof default_stopwords / sizeof *default_stopwords) + +static const char *const default_players[] = { + "*.ogg", + "*.flac", + "*.mp3", + "*.wav", +}; +#define NDEFAULT_PLAYERS (sizeof default_players / sizeof *default_players) + /** @brief Make a new default configuration */ static struct config *config_default(void) { struct config *c = xmalloc(sizeof *c); const char *logname; struct passwd *pw; + struct config_state cs; + size_t n; + cs.path = ""; + cs.line = 0; + cs.config = c; /* Strings had better be xstrdup'd as they will get freed at some point. */ - c->gap = 2; + c->gap = 0; c->history = 60; c->home = xstrdup(pkgstatedir); if(!(pw = getpwuid(getuid()))) @@ -1073,17 +1178,35 @@ static struct config *config_default(void) { c->sample_format.channels = 2; c->sample_format.endian = ENDIAN_NATIVE; c->queue_pad = 10; - c->speaker_backend = -1; + c->replay_min = 8 * 3600; + c->api = -1; c->multicast_ttl = 1; c->multicast_loop = 1; c->authorization_algorithm = xstrdup("sha1"); c->noticed_history = 31; c->short_display = 32; - c->mixer = xstrdup("/dev/mixer"); - c->channel = xstrdup("pcm"); + c->mixer = 0; + c->channel = 0; c->dbversion = 2; c->cookie_login_lifetime = 86400; c->cookie_key_lifetime = 86400 * 7; + c->smtp_server = xstrdup("127.0.0.1"); + c->new_max = 100; + c->reminder_interval = 600; /* 10m */ + c->new_bias_age = 7 * 86400; /* 1 week */ + c->new_bias = 9000000; /* 100 times the base weight */ + /* Default stopwords */ + if(config_set(&cs, (int)NDEFAULT_STOPWORDS, (char **)default_stopwords)) + exit(1); + /* Default player configuration */ + for(n = 0; n < NDEFAULT_PLAYERS; ++n) { + if(config_set_args(&cs, "player", + default_players[n], "execraw", "disorder-decode", (char *)0)) + exit(1); + if(config_set_args(&cs, "tracklength", + default_players[n], "disorder-tracklength", (char *)0)) + exit(1); + } return c; } @@ -1152,31 +1275,22 @@ static void config_postdefaults(struct config *c, for(n = 0; n < NTRANSFORM; ++n) set_transform(&cs, whoami, 5, (char **)transform[n]); } - if(c->speaker_backend == -1) { + if(c->api == -1) { if(c->speaker_command) - c->speaker_backend = BACKEND_COMMAND; + c->api = BACKEND_COMMAND; else if(c->broadcast.n) - c->speaker_backend = BACKEND_NETWORK; - else { -#if HAVE_ALSA_ASOUNDLIB_H - c->speaker_backend = BACKEND_ALSA; -#elif HAVE_SYS_SOUNDCARD_H - c->speaker_backend = BACKEND_OSS; -#elif HAVE_COREAUDIO_AUDIOHARDWARE_H - c->speaker_backend = BACKEND_COREAUDIO; -#else - c->speaker_backend = BACKEND_COMMAND; -#endif - } + c->api = BACKEND_NETWORK; + else + c->api = DEFAULT_BACKEND; } if(server) { - if(c->speaker_backend == BACKEND_COMMAND && !c->speaker_command) - fatal(0, "speaker_backend is command but speaker_command is not set"); - if(c->speaker_backend == BACKEND_NETWORK && !c->broadcast.n) - fatal(0, "speaker_backend is network but broadcast is not set"); + if(c->api == BACKEND_COMMAND && !c->speaker_command) + fatal(0, "'api command' but speaker_command is not set"); + if(c->api == BACKEND_NETWORK && !c->broadcast.n) + fatal(0, "'api network' but broadcast is not set"); } /* Override sample format */ - switch(c->speaker_backend) { + switch(c->api) { case BACKEND_NETWORK: c->sample_format.rate = 44100; c->sample_format.channels = 2; @@ -1207,7 +1321,7 @@ static void config_postdefaults(struct config *c, r |= RIGHT_REMOVE_MINE; else r |= RIGHT_REMOVE_ANY; - c->default_rights = r; + c->default_rights = rights_string(r); } } @@ -1241,7 +1355,7 @@ int config_read(int server) { return -1; xfree(privconf); /* if we have a password file, read it */ - if((privconf = config_userconf(getenv("HOME"), pw)) + if((privconf = config_userconf(0, pw)) && access(privconf, F_OK) == 0 && config_include(c, privconf)) return -1;