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:
parent
ac6145ed6a
commit
becc4460b6
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
110
src/mime.c
110
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user