From becc4460b6a60fcf193156a3810117a3eaaac182 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Oct 2020 12:38:32 -0400 Subject: Implement MIME database support --- src/main.c | 4 +++ src/mime.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- src/serve.c | 2 +- 3 files changed, 102 insertions(+), 12 deletions(-) (limited to 'src') 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; } +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 * -gmnisrv_mimetype_for_path(const char *path) +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"; - } - if (has_suffix(path, ".xml")) { - return "text/xml"; + + char *ext = strrchr(path, '.'); + if (!ext || !ext[1]) { + return "application/octet-stream"; } - if (has_suffix(path, ".png")) { - return "image/png"; + ++ext; + + struct mime_info *mime = bsearch( + ext, mimedb, mimedb_ln, sizeof(mimedb[0]), path_compar); + if (!mime) { + return "application/octet-stream"; } - 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); } -- cgit v1.2.3