diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-02-14 16:05:56 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-02-14 16:12:13 +0200 |
commit | 5c691054bb7e8623b203fcbae31e468781597607 (patch) | |
tree | 3a42d647ea1795fec67192fa075a995d662ec8b5 | |
parent | 0531ff6127b03a917f99b8eb2cff1fa74a230cc7 (diff) | |
download | fileserv-5c691054bb7e8623b203fcbae31e468781597607.tar.gz fileserv-5c691054bb7e8623b203fcbae31e468781597607.tar.bz2 |
Rewrite configuration support. Introduce global (system-wide) configuration file.
* src/Makefile.am (AM_CPPFLAGS): Define SYSCONFDIR.
* src/dirconfig.c: Rename to src/config.c. Implement
global configuration file.
* src/fileserv.c: Remove unnecessary options.
* src/fileserv.h: Rewrite configuration-related prototypes and
declarations.
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/config.c | 596 | ||||
-rw-r--r-- | src/dirconfig.c | 340 | ||||
-rw-r--r-- | src/fileserv.c | 58 | ||||
-rw-r--r-- | src/fileserv.h | 44 | ||||
-rw-r--r-- | src/idx.c | 6 | ||||
-rw-r--r-- | src/logger.c | 9 |
7 files changed, 653 insertions, 404 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index f7b02da..657e47b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS=fileserv fileserv_SOURCES=fileserv.c runas.c fileserv.h logger.c pidfile.c\ - wordsplit.c wordsplit.h catfile.c dirconfig.c idx.c defidx.h\ + wordsplit.c wordsplit.h catfile.c config.c idx.c defidx.h\ mem.c remoteip.c BUILT_SOURCES=defidx.h if FSRV_WRAP @@ -8,7 +8,7 @@ if FSRV_WRAP endif dist_man_MANS=fileserv.8 LDADD = ../mimetypes/libmimetypes.a -AM_CPPFLAGS = -I $(top_srcdir)/mimetypes +AM_CPPFLAGS = -I $(top_srcdir)/mimetypes -DSYSCONFDIR=\"$(sysconfdir)\" EXTRA_DIST=ftoc.sed defidx.html .html.h: $(AM_V_GEN)sed -f $(srcdir)/ftoc.sed $< > $@ diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..874bd29 --- /dev/null +++ b/src/config.c @@ -0,0 +1,596 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include "fileserv.h" +#include "wordsplit.h" + +#ifndef SYSCONFDIR +# define SYSCONFDIR "/etc" +#endif + +#define DEFAULT_CONF_FILE "fileserv.conf" +char *config_file; +char *dotfile = ".fileserv"; + +static CONFIG *global_config; + +void +readconfig(void) +{ + if (!config_file) { + config_file = catfile(SYSCONFDIR, DEFAULT_CONF_FILE); + if (access(config_file, F_OK)) { + if (verbose) + error("warning: no configuration file %s", + config_file); + free(config_file); + config_file = NULL; + return; + } + } + global_config = config_init(); + if (config_parse(config_file, global_config, CTXGLOB)) + exit(1); +} + +CONFIG * +dirconfig(char const *path, size_t prefix_len) +{ + CONFIG *conf = config_clone(global_config); + char *tmp = NULL; + struct stat st; + + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { + tmp = catfile(path, "."); + path = tmp; + } + while (path[prefix_len]) { + char *name = catfile_n(path, prefix_len, dotfile); + config_parse(name, conf, CTXDIR); + free(name); + if (path[prefix_len] == '/') + prefix_len++; + while (path[prefix_len] && path[prefix_len] != '/') + prefix_len++; + } + free(tmp); + return conf; +} + +static void +index_files_free(CONFIG *conf) +{ + size_t i; + + for (i = conf->index_files_n; i < conf->index_files_c; i++) + free(conf->index_files_v[i]); + conf->index_files_c = 0; + conf->index_files_n = 0; + free(conf->index_files_v); + conf->index_files_v = NULL; +} + +static void +hidden_files_free(CONFIG *conf) +{ + size_t i; + + for (i = conf->hidden_files_rxn; i < conf->hidden_files_rxc; i++) + regfree(&conf->hidden_files_rxv[i]); + conf->hidden_files_rxc = 0; + conf->hidden_files_rxn = 0; + free(conf->hidden_files_rxv); + conf->hidden_files_rxv = NULL; +} + +CONFIG * +config_init(void) +{ + CONFIG *p = xcalloc(1, sizeof(*p)); + p->list_unreadable = 1; + return p; +} + +CONFIG * +config_clone(CONFIG const *source) +{ + size_t i; + CONFIG *p = config_init(); + + if (!source) + return p; + p->follow = source->follow; + p->listing = source->listing; + p->list_unreadable = source->list_unreadable; + + if (source->index_files_c) { + p->index_files_v = xcalloc(source->index_files_c, + sizeof(p->index_files_v[0])); + for (i = 0; i < source->index_files_c; i++) + p->index_files_v[i] = source->index_files_v[i]; + p->index_files_v[i] = NULL; + } + p->index_files_c = source->index_files_c; + p->index_files_n = source->index_files_c; + + if (source->hidden_files_rxc) { + p->hidden_files_rxv = xcalloc(source->hidden_files_rxc, + sizeof(p->hidden_files_rxv[0])); + for (i = 0; i < source->hidden_files_rxc; i++) + p->hidden_files_rxv[i] = source->hidden_files_rxv[i]; + } + p->hidden_files_rxc = source->hidden_files_rxc; + p->hidden_files_rxn = source->hidden_files_rxc; + + return p; +} + +void +config_free(CONFIG *conf) +{ + if (conf) { + index_files_free(conf); + hidden_files_free(conf); + free(conf); + } +} + +static int +bool_decode(char const *s) +{ + static char *yes[] = { + "1", "yes", "true", "on", NULL + }; + static char *no[] = { + "0", "no", "false", "off", NULL + }; + int i; + for (i = 0; yes[i]; i++) { + if (strcasecmp(s, yes[i]) == 0) + return 1; + } + for (i = 0; no[i]; i++) { + if (strcasecmp(s, no[i]) == 0) + return 0; + } + return -1; +} + +static int +bad_argc(const char *file, int line, char const *id) +{ + error("%s:%d: bad number of arguments to '%s'", file, line, id); + return 1; +} + +static int +not_a_bool(const char *file, int line) +{ + error("%s:%d: invalid boolean value", file, line); + return 1; +} + +static int +set_follow(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + int n; + if (argc != 2) + return bad_argc(file, line, argv[0]); + n = bool_decode(argv[1]); + if (n == -1) + return not_a_bool(file, line); + conf->follow = n; + return 0; +} + +static int +set_listing(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + int n; + if (argc != 2) + return bad_argc(file, line, argv[0]); + n = bool_decode(argv[1]); + if (n == -1) + return not_a_bool(file, line); + conf->listing = n; + return 0; +} + +static int +set_list_unreadable(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + int n; + if (argc != 2) + return bad_argc(file, line, argv[0]); + n = bool_decode(argv[1]); + if (n == -1) + return not_a_bool(file, line); + conf->list_unreadable = n; + return 0; +} + +static int +set_index_files(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + size_t i; + + if (argc < 2) + return bad_argc(file, line, argv[0]); + + i = 1; + if (strcmp(argv[i], "+") == 0) + i++; + else + index_files_free(conf); + + conf->index_files_v = xnrealloc(conf->index_files_v, + argc + conf->index_files_c, + sizeof(conf->index_files_v[0])); + for (; argv[i]; i++) + conf->index_files_v[conf->index_files_c++] = xstrdup(argv[i]); + conf->index_files_v[conf->index_files_c] = NULL; + return 0; +} + +static int +set_hidden_files(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + size_t i; + + if (argc < 2) + return bad_argc(file, line, argv[0]); + + i = 1; + if (strcmp(argv[i], "+") == 0) + i++; + else + hidden_files_free(conf); + + conf->hidden_files_rxv = xnrealloc(conf->hidden_files_rxv, + argc + conf->hidden_files_rxc, + sizeof(conf->hidden_files_rxv[0])); + for (; argv[i]; i++) { + int ec; + ec = regcomp(&conf->hidden_files_rxv[conf->hidden_files_rxc], + argv[i], REG_EXTENDED|REG_NOSUB); + if (ec) { + char buf[512]; + regerror(ec, + &conf->hidden_files_rxv[conf->hidden_files_rxc], + buf, sizeof buf); + error("%s: %s", argv[i], buf); + } else + conf->hidden_files_rxc++; + } + return 0; +} + +static int +set_user(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + user = xstrdup(argv[1]); + return 0; +} + +static int +set_group(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + group = xstrdup(argv[1]); + return 0; +} + +static int +set_listen(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + address = xstrdup(argv[1]); + return 0; +} + +static int +set_index_template(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + parse_template_file(argv[1]); + return 0; +} + +static int +set_dotfile(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + dotfile = xstrdup(argv[1]); + return 0; +} + +static int +set_forwarded_header(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + forwarded_header = xstrdup(argv[1]); + return 0; +} + +static int +set_temp_dir(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + tmpdir = xstrdup(argv[1]); + return 0; +} + +static int +set_mime_magic_file(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + mime_types_file = xstrdup(argv[1]); + return 0; +} + +static int +set_trusted_proxy(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + size_t i; + + if (argc == 1) { + error("%s:%d: expected one or more arguments", file, line); + return 1; + } + for (i = 1; i < argc; i++) + trusted_ip_add(argv[i]); + return 0; +} + +static int +set_pidfile(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + pidfile = xstrdup(argv[1]); + return 0; +} + +static int +set_syslog(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + if (argc != 2) { + error("%s:%d: expected exactly one argument", file, line); + return 1; + } + if (set_log_facility(argv[1])) { + error("%s:%d: not a valid syslog facility", file, line); + return 1; + } + return 0; +} + +static int +set_mapping(size_t argc, char **argv, CONFIG *conf, + char const *file, int line) +{ + size_t i; + + if (argc == 1) { + error("%s:%d: expected one or more arguments", file, line); + return 1; + } + for (i = 1; i < argc; i++) + urimap_add(argv[i]); + return 0; +} + + + +struct configkeyword { + int context; + char const *ident; + int (*setter)(size_t argc, char **argv, CONFIG *conf, + char const *file, int line); +}; + +static struct configkeyword keywords[] = { + { CTXGLOB, "user", set_user }, + { CTXGLOB, "group", set_group }, + { CTXGLOB, "listen", set_listen }, + { CTXGLOB, "index-template", set_index_template }, + { CTXGLOB, "access-file-name", set_dotfile }, + { CTXGLOB, "forwarded-header", set_forwarded_header }, + { CTXGLOB, "trusted-proxy", set_trusted_proxy }, + { CTXGLOB, "temp-dir", set_temp_dir }, + { CTXGLOB, "mime-magic-file", set_mime_magic_file }, + { CTXGLOB, "pid-file", set_pidfile }, + { CTXGLOB, "syslog", set_syslog }, + { CTXGLOB, "mapping", set_mapping }, + { CTXGLOB | CTXDIR, "directory-index", set_index_files }, + { CTXGLOB | CTXDIR, "follow", set_follow }, + { CTXGLOB | CTXDIR, "listing", set_listing }, + { CTXGLOB | CTXDIR, "list-unreadable", set_list_unreadable }, + { CTXGLOB | CTXDIR, "hidden-files", set_hidden_files }, + { CTXNONE, NULL } +}; + +static int +line_interpret(struct wordsplit const *ws, + CONFIG *conf, int ctx, char const *file, int line) +{ + struct configkeyword *kw; + for (kw = keywords; kw->ident; kw++) { + if ((kw->context & ctx) + && strcmp(kw->ident, ws->ws_wordv[0]) == 0) + return kw->setter(ws->ws_wordc, ws->ws_wordv, + conf, file, line); + } + error("%s:%d: unrecognized keyword", file, line); + return 1; +} + +int +config_parse(const char *file, CONFIG *conf, int ctx) +{ + FILE *fp; + char buf[1024]; + unsigned ln = 0; + int wsflags = WRDSF_DEFFLAGS; + struct wordsplit ws; + int err = 0; + + fp = fopen(file, "r"); + if (!fp) { + if (ctx == CTXGLOB || errno != ENOENT) + error("can't open file %s: %s", file, + strerror(errno)); + return 1; + } + + ws.ws_comment = "#"; + wsflags |= WRDSF_COMMENT; + + while (fgets(buf, sizeof(buf), fp)) { + int len; + + ln++; + len = strlen(buf); + if (len == 0) + continue; + if (buf[len-1] == '\n') + buf[--len] = 0; + else if (!feof(fp)) { + error("%s:%d: line too long", file, ln); + err = 1; + break; + } + + if (wordsplit(buf, &ws, wsflags)) { + error("%s:%d: %s", file, ln, wordsplit_strerror(&ws)); + err = 1; + break; + } + + wsflags |= WRDSF_REUSE; + if (ws.ws_wordc == 0) + continue; + if (line_interpret(&ws, conf, ctx, file, ln)) { + err = 1; + break; + } + } + + if (wsflags & WRDSF_REUSE) + wordsplit_free(&ws); + + if (ferror(fp)) { + err = 1; + error("%s: %s", file, strerror(errno)); + } + fclose(fp); + + return err; +} + +int +filename_is_valid(char const *name) +{ + enum { a_chr, a_sla, a_dot, a_end, A_MAX }; + enum { s_ini, s_cur, s_sla, s_dot, s_dt2, s_err, s_end, S_MAX = s_end } + state = s_ini; + static char trans[A_MAX][S_MAX] = { + /* ini cur sla dot dt2 err */ + /* chr */ { s_cur, s_cur, s_cur, s_cur, s_cur, s_err }, + /* sla */ { s_sla, s_sla, s_err, s_err, s_err, s_err }, + /* dot */ { s_dot, s_cur, s_dot, s_dt2, s_cur, s_err }, + /* end */ { s_end, s_end, s_end, s_err, s_err, s_err }, + }; + + int c; + while (state != s_err && state != s_end) { + switch (*name++) { + case '/': + c = a_sla; + break; + case '.': + c = a_dot; + break; + case 0: + c = a_end; + break; + default: + c = a_chr; + } + state = trans[c][state]; + } + return state == s_end; +} + +int +filename_is_special(char const *name) +{ + return (name[0] == '.' + && (name[1] == 0 + || (name[1] == '.' && name[2] == 0))); +} + +int +filename_is_hidden(char const *name, CONFIG const *conf) +{ + if (filename_is_special(name) || strcmp(name, dotfile) == 0) + return 1; + + if (conf && conf->hidden_files_rxv) { + size_t i; + for (i = 0; i < conf->hidden_files_rxc; i++) + if (regexec(&conf->hidden_files_rxv[i], name, + 0, NULL, 0) == 0) + return 1; + } + return 0; +} + + + diff --git a/src/dirconfig.c b/src/dirconfig.c deleted file mode 100644 index d40475d..0000000 --- a/src/dirconfig.c +++ /dev/null @@ -1,340 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <sys/stat.h> -#include "fileserv.h" -#include "wordsplit.h" - -char *dotfile = ".fileserv"; - -DIRCONFIG * -dirconfig(char const *path, size_t prefix_len) -{ - DIRCONFIG *conf = dirconfig_init(); - char *tmp = NULL; - struct stat st; - - if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { - tmp = catfile(path, "."); - path = tmp; - } - while (path[prefix_len]) { - char *name = catfile_n(path, prefix_len, dotfile); - dirconfig_parse(name, conf); - free(name); - if (path[prefix_len] == '/') - prefix_len++; - while (path[prefix_len] && path[prefix_len] != '/') - prefix_len++; - } - free(tmp); - return conf; -} - -static int -bool_decode(char const *s) -{ - static char *yes[] = { - "1", "yes", "true", "on", NULL - }; - static char *no[] = { - "0", "no", "false", "off", NULL - }; - int i; - for (i = 0; yes[i]; i++) { - if (strcasecmp(s, yes[i]) == 0) - return 1; - } - for (i = 0; no[i]; i++) { - if (strcasecmp(s, no[i]) == 0) - return 0; - } - return -1; -} - -static int -bad_argc(const char *file, int line, char const *id) -{ - error("%s:%d: bad number of arguments to '%s'", file, line, id); - return 1; -} - -static int -not_a_bool(const char *file, int line) -{ - error("%s:%d: invalid boolean value", file, line); - return 1; -} - -static int -set_follow(size_t argc, char **argv, DIRCONFIG *conf, - char const *file, int line) -{ - int n; - if (argc != 2) - return bad_argc(file, line, argv[0]); - n = bool_decode(argv[1]); - if (n == -1) - return not_a_bool(file, line); - conf->follow = n; - return 0; -} - -static int -set_listing(size_t argc, char **argv, DIRCONFIG *conf, - char const *file, int line) -{ - int n; - if (argc != 2) - return bad_argc(file, line, argv[0]); - n = bool_decode(argv[1]); - if (n == -1) - return not_a_bool(file, line); - conf->listing = n; - return 0; -} - -static int -set_list_unreadable(size_t argc, char **argv, DIRCONFIG *conf, - char const *file, int line) -{ - int n; - if (argc != 2) - return bad_argc(file, line, argv[0]); - n = bool_decode(argv[1]); - if (n == -1) - return not_a_bool(file, line); - conf->list_unreadable = n; - return 0; -} - -static int -set_index_files(size_t argc, char **argv, DIRCONFIG *conf, - char const *file, int line) -{ - size_t i; - - if (argc < 2) - return bad_argc(file, line, argv[0]); - conf->index_files = xcalloc(argc, sizeof(conf->index_files[0])); - for (i = 1; i < argc; i++) - conf->index_files[i-1] = xstrdup(argv[i]); - conf->index_files[i-1] = NULL; - return 0; -} - -static void -hidden_files_free(DIRCONFIG *conf) -{ - size_t i; - - for (i = 0; i < conf->hidden_files_rxc; i++) - regfree(&conf->hidden_files_rxv[i]); - conf->hidden_files_rxc = 0; - free(conf->hidden_files_rxv); - conf->hidden_files_rxv = NULL; -} - -static int -set_hidden_files(size_t argc, char **argv, DIRCONFIG *conf, - char const *file, int line) -{ - size_t i; - - if (argc < 2) - return bad_argc(file, line, argv[0]); - - i = 1; - if (strcmp(argv[i], "+") == 0) - i++; - else - hidden_files_free(conf); - - conf->hidden_files_rxv = xnrealloc(conf->hidden_files_rxv, - argc + conf->hidden_files_rxc, - sizeof(conf->hidden_files_rxv[0])); - for (; argv[i]; i++) { - int ec; - ec = regcomp(&conf->hidden_files_rxv[conf->hidden_files_rxc], - argv[i], REG_EXTENDED|REG_NOSUB); - if (ec) { - char buf[512]; - regerror(ec, - &conf->hidden_files_rxv[conf->hidden_files_rxc], - buf, sizeof buf); - error("%s: %s", argv[i], buf); - } else - conf->hidden_files_rxc++; - } - return 0; -} - -struct dirconfig_keyword { - char const *ident; - int (*setter)(size_t argc, char **argv, DIRCONFIG *conf, - char const *file, int line); -}; - -static struct dirconfig_keyword keywords[] = { - { "directory-index", set_index_files }, - { "follow", set_follow }, - { "listing", set_listing }, - { "list-unreadable", set_list_unreadable }, - { "hidden-files", set_hidden_files }, - { NULL } -}; - -static int -line_interpret(struct wordsplit const *ws, - DIRCONFIG *conf, char const *file, int line) -{ - struct dirconfig_keyword *kw; - for (kw = keywords; kw->ident; kw++) { - if (strcmp(kw->ident, ws->ws_wordv[0]) == 0) - return kw->setter(ws->ws_wordc, ws->ws_wordv, - conf, file, line); - } - error("%s:%d: unrecognized keyword", file, line); - return 1; -} - -void -dirconfig_parse(const char *file, DIRCONFIG *conf) -{ - FILE *fp; - char buf[1024]; - unsigned ln = 0; - int wsflags = WRDSF_DEFFLAGS; - struct wordsplit ws; - - fp = fopen(file, "r"); - if (!fp) { - if (errno != ENOENT) - error("can't open file %s: %s", file, - strerror(errno)); - return; - } - - ws.ws_comment = "#"; - wsflags |= WRDSF_COMMENT; - - while (fgets(buf, sizeof(buf), fp)) { - int len; - - ln++; - len = strlen(buf); - if (len == 0) - continue; - if (buf[len-1] == '\n') - buf[--len] = 0; - else if (!feof(fp)) { - error("%s:%d: line too long", file, ln); - break; - } - - if (wordsplit(buf, &ws, wsflags)) { - error("%s:%d: %s", file, ln, wordsplit_strerror(&ws)); - break; - } - - wsflags |= WRDSF_REUSE; - if (ws.ws_wordc == 0) - continue; - if (line_interpret(&ws, conf, file, ln)) { - break; - } - } - - if (wsflags & WRDSF_REUSE) - wordsplit_free(&ws); - - if (ferror(fp)) { - error("%s: %s", file, strerror(errno)); - } - fclose(fp); -} - -DIRCONFIG * -dirconfig_init(void) -{ - DIRCONFIG *p = xcalloc(1, sizeof(*p)); - p->list_unreadable = 1; - return p; -} - -void -dirconfig_free(DIRCONFIG *conf) -{ - if (conf) { - if (conf->index_files) { - size_t i; - for (i = 0; conf->index_files[i]; i++) - free(conf->index_files[i]); - free(conf->index_files); - } - hidden_files_free(conf); - free(conf); - } -} - -int -filename_is_valid(char const *name) -{ - enum { a_chr, a_sla, a_dot, a_end, A_MAX }; - enum { s_ini, s_cur, s_sla, s_dot, s_dt2, s_err, s_end, S_MAX = s_end } - state = s_ini; - static char trans[A_MAX][S_MAX] = { - /* ini cur sla dot dt2 err */ - /* chr */ { s_cur, s_cur, s_cur, s_cur, s_cur, s_err }, - /* sla */ { s_sla, s_sla, s_err, s_err, s_err, s_err }, - /* dot */ { s_dot, s_cur, s_dot, s_dt2, s_cur, s_err }, - /* end */ { s_end, s_end, s_end, s_err, s_err, s_err }, - }; - - int c; - while (state != s_err && state != s_end) { - switch (*name++) { - case '/': - c = a_sla; - break; - case '.': - c = a_dot; - break; - case 0: - c = a_end; - break; - default: - c = a_chr; - } - state = trans[c][state]; - } - return state == s_end; -} - -int -filename_is_special(char const *name) -{ - return (name[0] == '.' - && (name[1] == 0 - || (name[1] == '.' && name[2] == 0))); -} - -int -filename_is_hidden(char const *name, DIRCONFIG const *conf) -{ - if (filename_is_special(name) || strcmp(name, dotfile) == 0) - return 1; - - if (conf && conf->hidden_files_rxv) { - size_t i; - for (i = 0; i < conf->hidden_files_rxc; i++) - if (regexec(&conf->hidden_files_rxv[i], name, - 0, NULL, 0) == 0) - return 1; - } - return 0; -} - - - diff --git a/src/fileserv.c b/src/fileserv.c index 9b6aa10..b81c9e7 100644 --- a/src/fileserv.c +++ b/src/fileserv.c @@ -42,6 +42,9 @@ char *address = "0.0.0.0"; char *forwarded_header = "X-Forwarded-For"; char *index_css; char *tmpdir; +char *user; +char *group; +char *mime_types_file; #ifndef DEFAULT_SERVICE # define DEFAULT_SERVICE "8080" @@ -63,15 +66,10 @@ usage(void) { printf("usage: %s [OPTIONS] [HOST:]URL:DIR...\n", progname); printf("OPTIONS are:\n\n"); - printf(" -a ADDRESS:PORT listen on the given address\n"); - printf(" -F FACILITY use this syslog facility\n"); + printf(" -c FILE read configuration from FILE\n"); printf(" -f remain in the foreground\n"); - printf(" -g GROUP run with privileges of GROUP\n"); printf(" -h show this help summary\n"); - printf(" -p FILE store PID in FILE\n"); - printf(" -t IP add trusted IP address\n"); - printf(" -x HEADER use HEADER instead of X-Forwarded-For\n"); - printf(" -u USER run with this USER privileges\n"); + printf(" -v increase verbosity\n"); } int @@ -245,17 +243,17 @@ errno_to_http_code(int ec) } static int -find_index_file(char const *dir, DIRCONFIG *conf, char **index_file, +find_index_file(char const *dir, CONFIG *conf, char **index_file, struct stat *pst) { char *cf; - if (conf->index_files) { + if (conf->index_files_v) { int i; int lasterr = 0; - for (i = 0; conf->index_files[i]; i++) { + for (i = 0; conf->index_files_v[i]; i++) { struct stat st; - cf = catfile(dir, conf->index_files[i]); + cf = catfile(dir, conf->index_files_v[i]); if (access(cf, F_OK) || lstat(cf, &st)) { free(cf); lasterr = errno; @@ -278,7 +276,7 @@ find_index_file(char const *dir, DIRCONFIG *conf, char **index_file, typedef struct file_resp { int fd; char *file_name; - DIRCONFIG *conf; + CONFIG *conf; struct stat st; char const *type; char *location; @@ -297,7 +295,7 @@ file_resp_free(FILE_RESP *resp) if (resp->fd >= 0) close(resp->fd); free(resp->file_name); - dirconfig_free(resp->conf); + config_free(resp->conf); free(resp->location); } @@ -594,7 +592,6 @@ main(int argc, char **argv) int c, i; int fd = -1; struct MHD_Daemon *mhd; - char *user = NULL, *group = NULL; struct sockaddr *server_addr; int foreground = 0; sigset_t sigs; @@ -605,7 +602,6 @@ main(int argc, char **argv) SIGTERM, 0 }; - char *mime_types_file = NULL; progname = basename(argv[0]); @@ -614,41 +610,17 @@ main(int argc, char **argv) if (!tmpdir) tmpdir = "/tmp"; - while ((c = getopt(argc, argv, "a:F:fg:i:hm:p:t:x:u:v")) != EOF) { + while ((c = getopt(argc, argv, "c:fhv")) != EOF) { switch (c) { - case 'a': - address = optarg; - break; - case 'F': - set_log_facility(optarg); + case 'c': + config_file = optarg; break; case 'f': foreground = 1; break; - case 'g': - group = optarg; - break; case 'h': usage(); exit(0); - case 'i': - parse_template_file(optarg); - break; - case 'm': - mime_types_file = optarg; - break; - case 'p': - pidfile = optarg; - break; - case 't': - trusted_ip_add(optarg); - break; - case 'x': - forwarded_header = optarg; - break; - case 'u': - user = optarg; - break; case 'v': verbose++; break; @@ -657,6 +629,8 @@ main(int argc, char **argv) } } + readconfig(); + pidfile_check(); if (mime_types_file) { diff --git a/src/fileserv.h b/src/fileserv.h index e188e28..6c2ea4c 100644 --- a/src/fileserv.h +++ b/src/fileserv.h @@ -1,5 +1,5 @@ /* This file is part of fileserv. - Copyright (C) 2017 Sergey Poznyakoff + Copyright (C) 2017-2018 Sergey Poznyakoff Fileserv is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,15 +24,21 @@ #endif extern char const *progname; +extern char *config_file; extern char *pidfile; extern char *index_css; extern char *tmpdir; extern char *forwarded_header; +extern char *mime_types_file; +extern char *user; +extern char *group; +extern char *address; +extern int verbose; void error(char const *fmt, ...); void info(char const *fmt, ...); void syslog_enable(void); -void set_log_facility(char const *arg); +int set_log_facility(char const *arg); void fileserv_logger(void *arg, const char *fmt, va_list ap); @@ -55,22 +61,36 @@ void pidfile_remove(void); void pidfile_create(void); void pidfile_check(void); -typedef struct fileserv_dirconfig { +void urimap_add(char *arg); + +typedef struct fileserv_config { int follow:1; /* Follow symbolic links */ int listing:1; /* Show directory listing, if a directory is requested and no index file exists */ int list_unreadable:1; /* List unreadable files */ - char **index_files; /* Names of index files */ + + char **index_files_v; /* Names of index files */ + size_t index_files_c; /* Number of index files */ + size_t index_files_n; /* Index of the first allocated index file */ + regex_t *hidden_files_rxv; size_t hidden_files_rxc; -} DIRCONFIG; + size_t hidden_files_rxn; +} CONFIG; + +enum { + CTXNONE = 0, + CTXGLOB = 0x01, + CTXDIR = 0x02 +}; -#define DIRCONFIG_INITIALIZER { 0, 0, 1, NULL } +void readconfig(void); -DIRCONFIG *dirconfig(char const *path, size_t prefix_len); -void dirconfig_parse(const char *file, DIRCONFIG *conf); -DIRCONFIG *dirconfig_init(void); -void dirconfig_free(DIRCONFIG *conf); +CONFIG *dirconfig(char const *path, size_t prefix_len); +int config_parse(const char *file, CONFIG *conf, int ctx); +CONFIG *config_init(void); +CONFIG *config_clone(CONFIG const *source); +void config_free(CONFIG *conf); char *catfile_n(char const *dir, size_t len, char const *file); char *catfile(char const *dir, char const *file); @@ -88,7 +108,7 @@ typedef enum { } INDEX_SORT_ORD; int directory_index(int fd, - DIRCONFIG const *conf, char const *uri, char const *path, + CONFIG const *conf, char const *uri, char const *path, INDEX_SORT_COL col, INDEX_SORT_ORD ord); INDEX_SORT_COL index_sort_col_from_arg(int c); @@ -97,7 +117,7 @@ INDEX_SORT_ORD index_sort_ord_from_arg(int c); int index_sort_ord_to_arg(INDEX_SORT_ORD c); int filename_is_special(char const *name); -int filename_is_hidden(char const *name, DIRCONFIG const *conf); +int filename_is_hidden(char const *name, CONFIG const *conf); int filename_is_valid(char const *name); void parse_template_string(char const *str); @@ -287,7 +287,7 @@ idxlist_append(IDXLIST *lst, IDXENT *ent) } static int -idxlist_scan(IDXLIST *idx_list, char const *path, DIRCONFIG const *conf) +idxlist_scan(IDXLIST *idx_list, char const *path, CONFIG const *conf) { DIR *dir; struct dirent *ent; @@ -429,7 +429,7 @@ index_sort_ord_to_arg(INDEX_SORT_ORD c) typedef struct { int fd; - DIRCONFIG const *conf; + CONFIG const *conf; IDXENT *ent; int n; struct wordsplit ws; @@ -766,7 +766,7 @@ static int (*comp[])(IDXENT const *, IDXENT c |