summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.sh5
-rw-r--r--include/mime.h4
-rw-r--r--src/main.c4
-rw-r--r--src/mime.c108
-rw-r--r--src/serve.c2
5 files changed, 110 insertions, 13 deletions
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 <stdio.h>
#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 <assert.h>
#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#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);
}