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=*)
|
||||||
SYSCONFDIR=${arg#*=}
|
SYSCONFDIR=${arg#*=}
|
||||||
;;
|
;;
|
||||||
|
--with-mimedb=*)
|
||||||
|
MIMEDB=${arg#*=}
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -148,6 +151,7 @@ run_configure() {
|
|||||||
LIBDIR?=${LIBDIR:-\$(PREFIX)/lib}
|
LIBDIR?=${LIBDIR:-\$(PREFIX)/lib}
|
||||||
MANDIR?=${MANDIR:-\$(PREFIX)/share/man}
|
MANDIR?=${MANDIR:-\$(PREFIX)/share/man}
|
||||||
VARLIBDIR?=${MANDIR:-\$(PREFIX)/var/lib}
|
VARLIBDIR?=${MANDIR:-\$(PREFIX)/var/lib}
|
||||||
|
MIMEDB?=${MIMEDB:-${SYSCONFDIR:-/etc}/mime.types}
|
||||||
CACHE=\$(OUTDIR)/cache
|
CACHE=\$(OUTDIR)/cache
|
||||||
CFLAGS=${CFLAGS}
|
CFLAGS=${CFLAGS}
|
||||||
CFLAGS+=-Iinclude -I\$(OUTDIR)
|
CFLAGS+=-Iinclude -I\$(OUTDIR)
|
||||||
@ -155,6 +159,7 @@ run_configure() {
|
|||||||
CFLAGS+=-DLIBDIR='"\$(LIBDIR)"'
|
CFLAGS+=-DLIBDIR='"\$(LIBDIR)"'
|
||||||
CFLAGS+=-DVARLIBDIR='"\$(VARLIBDIR)"'
|
CFLAGS+=-DVARLIBDIR='"\$(VARLIBDIR)"'
|
||||||
CFLAGS+=-DSYSCONFDIR='"\$(SYSCONFDIR)"'
|
CFLAGS+=-DSYSCONFDIR='"\$(SYSCONFDIR)"'
|
||||||
|
CFLAGS+=-DMIMEDB='"\$(MIMEDB)"'
|
||||||
|
|
||||||
all: ${all}
|
all: ${all}
|
||||||
EOF
|
EOF
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef GMNISRV_MIME
|
#ifndef GMNISRV_MIME
|
||||||
#define 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
|
#endif
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "mime.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
|
||||||
@ -37,6 +38,8 @@ main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mime_init();
|
||||||
|
|
||||||
int r = load_config(&conf, confpath);
|
int r = load_config(&conf, confpath);
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
server_error("Config load failed");
|
server_error("Config load failed");
|
||||||
@ -62,5 +65,6 @@ exit_tls:
|
|||||||
exit_conf:
|
exit_conf:
|
||||||
config_finish(&conf);
|
config_finish(&conf);
|
||||||
exit:
|
exit:
|
||||||
|
mime_finish();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
112
src/mime.c
112
src/mime.c
@ -1,7 +1,84 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mime.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
|
static bool
|
||||||
has_suffix(const char *str, const char *suff)
|
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;
|
return strncmp(&str[a - b], suff, b) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
static int
|
||||||
gmnisrv_mimetype_for_path(const char *path)
|
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")) {
|
if (has_suffix(path, ".gmi") || has_suffix(path, ".gemini")) {
|
||||||
return "text/gemini";
|
return "text/gemini";
|
||||||
}
|
}
|
||||||
if (has_suffix(path, ".txt")) {
|
|
||||||
return "text/plain";
|
char *ext = strrchr(path, '.');
|
||||||
}
|
if (!ext || !ext[1]) {
|
||||||
if (has_suffix(path, ".xml")) {
|
|
||||||
return "text/xml";
|
|
||||||
}
|
|
||||||
if (has_suffix(path, ".png")) {
|
|
||||||
return "image/png";
|
|
||||||
}
|
|
||||||
return "application/octet-stream";
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
++ext;
|
||||||
|
|
||||||
|
struct mime_info *mime = bsearch(
|
||||||
|
ext, mimedb, mimedb_ln, sizeof(mimedb[0]), path_compar);
|
||||||
|
if (!mime) {
|
||||||
|
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);
|
client_submit_response(client, GEMINI_STATUS_SUCCESS, meta, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user