1
0
mirror of https://git.sr.ht/~sircmpwn/gmnisrv synced 2024-12-04 14:46:42 -05:00

Implement MIME database support

This commit is contained in:
Drew DeVault 2020-10-28 12:38:32 -04:00
parent ac6145ed6a
commit becc4460b6
5 changed files with 111 additions and 14 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}
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;
}

View File

@ -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);
}