diff --git a/config.sh b/config.sh index cd0b505..5ada371 100644 --- a/config.sh +++ b/config.sh @@ -32,6 +32,9 @@ do --sysconfdir=*) SYSCONFDIR=${arg#*=} ;; + --with-mimedb=*) + MIMEDB=${arg#*=} + ;; esac done @@ -148,6 +151,7 @@ run_configure() { LIBDIR?=${LIBDIR:-\$(PREFIX)/lib} MANDIR?=${MANDIR:-\$(PREFIX)/share/man} VARLIBDIR?=${MANDIR:-\$(PREFIX)/var/lib} + MIMEDB?=${MIMEDB:-${SYSCONFDIR:-/etc}/mime.types} CACHE=\$(OUTDIR)/cache CFLAGS=${CFLAGS} CFLAGS+=-Iinclude -I\$(OUTDIR) @@ -155,6 +159,7 @@ run_configure() { CFLAGS+=-DLIBDIR='"\$(LIBDIR)"' CFLAGS+=-DVARLIBDIR='"\$(VARLIBDIR)"' CFLAGS+=-DSYSCONFDIR='"\$(SYSCONFDIR)"' + CFLAGS+=-DMIMEDB='"\$(MIMEDB)"' all: ${all} EOF diff --git a/include/mime.h b/include/mime.h index 33d36cb..39af6c3 100644 --- a/include/mime.h +++ b/include/mime.h @@ -1,6 +1,8 @@ #ifndef GMNISRV_MIME #define GMNISRV_MIME -const char *gmnisrv_mimetype_for_path(const char *path); +void mime_init(); +void mime_finish(); +const char *mimetype_for_path(const char *path); #endif diff --git a/src/main.c b/src/main.c index 0a6336b..abc80ff 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ #include #include "config.h" #include "log.h" +#include "mime.h" #include "server.h" #include "tls.h" @@ -37,6 +38,8 @@ main(int argc, char **argv) return 1; } + mime_init(); + int r = load_config(&conf, confpath); if (r != 0) { server_error("Config load failed"); @@ -62,5 +65,6 @@ exit_tls: exit_conf: config_finish(&conf); exit: + mime_finish(); return 0; } diff --git a/src/mime.c b/src/mime.c index 1a60ae0..0c4cc8b 100644 --- a/src/mime.c +++ b/src/mime.c @@ -1,7 +1,84 @@ +#include #include +#include +#include +#include #include #include "mime.h" +struct mime_info { + char *mimetype; + char *extension; +}; + +size_t mimedb_ln; +struct mime_info *mimedb; + +static int +mimedb_compar(const void *va, const void *vb) +{ + const struct mime_info *a = va; + const struct mime_info *b = vb; + return strcmp(a->extension, b->extension); +} + +void +mime_init() +{ + size_t mimedb_sz = 4096; + mimedb = malloc(mimedb_sz * sizeof(struct mime_info)); + + FILE *f = fopen(MIMEDB, "r"); + assert(f); + + char *line = NULL; + size_t n = 0; + while (getline(&line, &n, f) != -1) { + if (line[0] == '#') { + continue; + } + size_t l = strlen(line); + if (l > 1 && line[l - 1] == '\n') { + line[l - 1] = '\0'; + } + + if (mimedb_ln >= mimedb_sz) { + mimedb_sz *= 2; + struct mime_info *new = realloc(mimedb, + mimedb_sz * sizeof(struct mime_info)); + assert(new); + mimedb = new; + } + + char *mime = strdup(strtok(line, " \t")); + char *ext = strtok(NULL, " \t"); + if (!ext) { + free(mime); + continue; + } + + do { + mimedb[mimedb_ln].mimetype = strdup(mime); + mimedb[mimedb_ln].extension = strdup(ext); + ++mimedb_ln; + } while ((ext = strtok(NULL, " \t"))); + + free(mime); + } + + qsort(mimedb, mimedb_ln, sizeof(mimedb[0]), mimedb_compar); +} + +void +mime_finish() +{ + for (size_t i = 0; i < mimedb_ln; ++i) { + free(mimedb[i].mimetype); + free(mimedb[i].extension); + } + free(mimedb); +} + static bool has_suffix(const char *str, const char *suff) { @@ -12,22 +89,31 @@ has_suffix(const char *str, const char *suff) return strncmp(&str[a - b], suff, b) == 0; } -const char * -gmnisrv_mimetype_for_path(const char *path) +static int +path_compar(const void *va, const void *vb) +{ + char *ext = (char *)va; + struct mime_info *mime = (struct mime_info *)vb; + return strcmp(ext, mime->extension); +} + +const char * +mimetype_for_path(const char *path) { - // TODO: Read /etc/mime.types - // TODO: Consider adding content-disposition fields like filename if (has_suffix(path, ".gmi") || has_suffix(path, ".gemini")) { return "text/gemini"; } - if (has_suffix(path, ".txt")) { - return "text/plain"; + + char *ext = strrchr(path, '.'); + if (!ext || !ext[1]) { + return "application/octet-stream"; } - if (has_suffix(path, ".xml")) { - return "text/xml"; + ++ext; + + struct mime_info *mime = bsearch( + ext, mimedb, mimedb_ln, sizeof(mimedb[0]), path_compar); + if (!mime) { + return "application/octet-stream"; } - if (has_suffix(path, ".png")) { - return "image/png"; - } - return "application/octet-stream"; + return mime->mimetype; } diff --git a/src/serve.c b/src/serve.c index 4a8e778..b877dd4 100644 --- a/src/serve.c +++ b/src/serve.c @@ -352,7 +352,7 @@ serve_request(struct gmnisrv_client *client) } } - const char *meta = gmnisrv_mimetype_for_path(real_path); + const char *meta = mimetype_for_path(real_path); client_submit_response(client, GEMINI_STATUS_SUCCESS, meta, body); }