Download raw body.
types {} block for gotwebd
the second step is this, now that we have enough things in parse.y to
have include =)
While writing this mail I noticed that I forgot to write the man bits.
I'm still sending the diff for review, will update it with the manual
soon. The idea however is that it behaves exactly like httpd.conf'
types {} block.
diff refs/heads/main refs/heads/op/mime
commit - b825ad9bba095c5b64eb9f5bf57fb86a1ac0a52d
commit + 3d0e181d106990b83127d16565a9d11e65cb2515
blob - cb1821eb9160b452f0b06c959c08118f05d37707
blob + 2898ed092da39dc54655a6ebd00b5995d6c50130
--- gotwebd/Makefile
+++ gotwebd/Makefile
@@ -6,7 +6,7 @@
PROG = gotwebd
SRCS = config.c sockets.c auth.c login.c gotwebd.c parse.y \
- fcgi.c gotweb.c got_operations.c tmpl.c pages.c
+ fcgi.c gotweb.c got_operations.c tmpl.c pages.c media.c
SRCS += blame.c commit_graph.c delta.c diff.c \
diffreg.c error.c object.c object_cache.c \
object_idset.c object_parse.c opentemp.c path.c pack.c \
blob - e8c48e960e86b8d7fe58b0f342eef37fefe5c533
blob + 7001246b5711f33cb5061cc4ea4e671e180efc9d
--- gotwebd/config.c
+++ gotwebd/config.c
@@ -44,6 +44,7 @@
#include "gotwebd.h"
#include "log.h"
+#include "media.h"
int
config_init(struct gotwebd *env)
@@ -60,6 +61,11 @@ config_init(struct gotwebd *env)
TAILQ_INIT(&env->addresses);
STAILQ_INIT(&env->access_rules);
+ env->mediatypes = calloc(1, sizeof(*env->mediatypes));
+ if (env->mediatypes == NULL)
+ return (-1);
+ RB_INIT(env->mediatypes);
+
for (i = 0; i < PRIV_FDS__MAX; i++)
env->priv_fd[i] = -1;
@@ -79,6 +85,20 @@ config_getcfg(struct gotwebd *env, struct imsg *imsg)
}
int
+config_getmediatype(struct gotwebd *env, struct imsg *imsg)
+{
+ struct media_type mt;
+
+ if (imsg_get_data(imsg, &mt, sizeof(mt)) == -1)
+ fatal("%s: failed to receive media type", __func__);
+
+ if (media_add(env->mediatypes, &mt) == NULL)
+ fatal("%s: failed to insert media type", __func__);
+
+ return (0);
+}
+
+int
config_getserver(struct gotwebd *env, struct imsg *imsg)
{
struct server *srv;
blob - ab011444f5fd3d10a47a59ee42390f24281009f2
blob + 34d29ddc16962f0053f51525f6e2444d04e42fae
--- gotwebd/gotweb.c
+++ gotwebd/gotweb.c
@@ -53,6 +53,7 @@
#include "gotwebd.h"
#include "log.h"
#include "tmpl.h"
+#include "media.h"
static int gotweb_render_index(struct template *);
static const struct got_error *gotweb_load_got_path(struct repo_dir **,
@@ -265,13 +266,13 @@ gotweb_serve_htdocs(struct request *c)
const struct got_error *error = NULL;
struct server *srv = c->srv;;
struct gotwebd_fcgi_params *p = &c->fcgi_params;
+ struct media_type *m;
char *document_uri = p->document_uri;
char *request_path = NULL;
char *child_path = NULL;
char *ondisk_path = NULL;
- int fd = -1;
- const char *mime_type = "application/octet-stream";
- char *ext;
+ int n, fd = -1;
+ char mime_type[MEDIA_STRMAX] = "application/octet-stream";
while (document_uri[0] == '/')
document_uri++;
@@ -307,29 +308,14 @@ gotweb_serve_htdocs(struct request *c)
goto done;
}
- /*
- * TODO: Port generic mime-type handling from httpd.
- *
- * This hack works only because we know what files our static
- * assets contain. But we should account for other files which
- * might be dropped into our htdocs directory.
- */
- ext = strrchr(ondisk_path, '.');
- if (ext) {
- if (strcmp(ext, ".css") == 0)
- mime_type = "text/css";
- if (strcmp(ext, ".ico") == 0)
- mime_type = "image/x-icon";
- if (strcmp(ext, ".png") == 0)
- mime_type = "image/png";
- if (strcmp(ext, ".svg") == 0)
- mime_type = "image/svg+xml";
- if (strcmp(ext, ".txt") == 0)
- mime_type = "text/plain";
- if (strcmp(ext, ".webmanifest") == 0)
- mime_type = "application/manifest+json";
- if (strcmp(ext, ".xml") == 0)
- mime_type = "text/xml";
+ m = media_find(gotwebd_env->mediatypes, ondisk_path);
+ if (m != NULL) {
+ n = snprintf(mime_type, sizeof(mime_type), "%s/%s",
+ m->media_type, m->media_subtype);
+ if (n < 0 || n >= sizeof(mime_type)) {
+ error = got_error(GOT_ERR_RANGE);
+ goto done;
+ }
}
if (gotweb_reply(c, 200, mime_type, NULL) == -1) {
@@ -1411,6 +1397,9 @@ gotweb_shutdown(void)
imsgbuf_clear(&gotwebd_env->iev_auth->ibuf);
free(gotwebd_env->iev_auth);
+ media_purge(gotwebd_env->mediatypes);
+ free(gotwebd_env->mediatypes);
+
config_free_access_rules(&gotwebd_env->access_rules);
while (!TAILQ_EMPTY(&gotwebd_env->servers)) {
@@ -1673,6 +1662,9 @@ gotweb_dispatch_main(int fd, short event, void *arg)
}
}
break;
+ case GOTWEBD_IMSG_CFG_MEDIA_TYPE:
+ config_getmediatype(env, &imsg);
+ break;
case GOTWEBD_IMSG_CFG_SRV:
config_getserver(env, &imsg);
break;
blob - cf2987ed090a63f0c3bfe94f264c4b8c4afa1fe3
blob + a0aed270e07a1c074e2b0659cab7ee3da28aeee3
--- gotwebd/gotwebd.c
+++ gotwebd/gotwebd.c
@@ -17,6 +17,7 @@
#include <sys/param.h>
#include <sys/queue.h>
+#include <sys/tree.h>
#include <sys/socket.h>
#include <sys/wait.h>
@@ -47,6 +48,7 @@
#include "gotwebd.h"
#include "log.h"
+#include "media.h"
__dead void usage(void);
@@ -586,7 +588,8 @@ main(int argc, char **argv)
env = calloc(1, sizeof(*env));
if (env == NULL)
fatal("%s: calloc", __func__);
- config_init(env);
+ if (config_init(env) == -1)
+ fatal("%s: failed to initialize configuration", __func__);
while ((ch = getopt(argc, argv, "A:D:dG:f:F:L:nS:vW:")) != -1) {
switch (ch) {
@@ -917,6 +920,7 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
struct server *srv;
struct socket *sock;
struct gotwebd_repo *repo;
+ struct media_type *mt;
char secret[32];
int i;
@@ -932,6 +936,13 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
&env->access_rules);
}
+ /* send the mime mapping */
+ RB_FOREACH(mt, mediatypes, env->mediatypes) {
+ if (main_compose_gotweb(env, GOTWEBD_IMSG_CFG_MEDIA_TYPE,
+ -1, mt, sizeof(*mt)) == -1)
+ fatal("send_imsg GOTWEBD_IMSG_CFG_MEDIA_TYPE");
+ }
+
/* send our gotweb servers */
TAILQ_FOREACH(srv, &env->servers, entry) {
if (main_compose_sockets(env, GOTWEBD_IMSG_CFG_SRV,
blob - 7e2e0ab861ef74a5a904a144605d36d05a6540ec
blob + 9746835a3e45fbb2feb6caf2beceb76572ef541b
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
@@ -142,6 +142,7 @@ enum imsg_type {
GOTWEBD_IMSG_CFG_SOCK,
GOTWEBD_IMSG_CFG_FD,
GOTWEBD_IMSG_CFG_ACCESS_RULE,
+ GOTWEBD_IMSG_CFG_MEDIA_TYPE,
GOTWEBD_IMSG_CFG_REPO,
GOTWEBD_IMSG_CFG_DONE,
GOTWEBD_IMSG_CTL_PIPE,
@@ -457,6 +458,7 @@ struct socket {
TAILQ_HEAD(socketlist, socket);
struct passwd;
+struct mediatypes;
struct gotwebd {
struct serverlist servers;
struct socketlist sockets;
@@ -468,6 +470,8 @@ struct gotwebd {
enum gotwebd_auth_config auth_config;
struct gotwebd_access_rule_list access_rules;
+ struct mediatypes *mediatypes;
+
int pack_fds[GOTWEB_PACK_NUM_TEMPFILES];
int priv_fd[PRIV_FDS__MAX];
@@ -645,6 +649,7 @@ const struct got_error *got_output_file_blame(struct r
/* config.c */
int config_setserver(struct gotwebd *, struct server *);
+int config_getmediatype(struct gotwebd *, struct imsg *);
int config_getserver(struct gotwebd *, struct imsg *);
int config_setsock(struct gotwebd *, struct socket *, uid_t, gid_t);
int config_getsock(struct gotwebd *, struct imsg *);
blob - /dev/null
blob + 7913016add2d736f9bc5b85b16c244578a458e1c (mode 644)
--- /dev/null
+++ gotwebd/media.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2025 Omar Polo <op@openbsd.org>
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/tree.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "media.h"
+#include "log.h"
+
+struct media_type *
+media_add(struct mediatypes *types, struct media_type *media)
+{
+ struct media_type *entry;
+
+ if ((entry = RB_FIND(mediatypes, types, media)) != NULL) {
+ log_debug("%s: entry overwritten for \"%s\"", __func__,
+ media->media_name);
+ media_delete(types, entry);
+ }
+
+ if ((entry = malloc(sizeof(*media))) == NULL)
+ return (NULL);
+
+ memcpy(entry, media, sizeof(*entry));
+ RB_INSERT(mediatypes, types, entry);
+
+ return (entry);
+}
+
+void
+media_delete(struct mediatypes *types, struct media_type *media)
+{
+ RB_REMOVE(mediatypes, types, media);
+
+ free(media);
+}
+
+void
+media_purge(struct mediatypes *types)
+{
+ struct media_type *media;
+
+ while ((media = RB_MIN(mediatypes, types)) != NULL)
+ media_delete(types, media);
+}
+
+struct media_type *
+media_find(struct mediatypes *types, const char *file)
+{
+ struct media_type *match, media;
+ char *p;
+
+ /* Last component of the file name */
+ p = strchr(file, '\0');
+ while (p > file && p[-1] != '.' && p[-1] != '/')
+ p--;
+ if (*p == '\0')
+ return (NULL);
+
+ if (strlcpy(media.media_name, p,
+ sizeof(media.media_name)) >=
+ sizeof(media.media_name)) {
+ return (NULL);
+ }
+
+ /* Find media type by extension name */
+ match = RB_FIND(mediatypes, types, &media);
+
+ return (match);
+}
+
+static int
+media_cmp(struct media_type *a, struct media_type *b)
+{
+ return (strcasecmp(a->media_name, b->media_name));
+}
+
+RB_GENERATE(mediatypes, media_type, media_entry, media_cmp);
blob - 6c5defc898c84d1d497ce9239c2741a045bea0e0
blob + 35eb991b6b279882f29ff5976d8511ca761a2925
--- gotwebd/parse.y
+++ gotwebd/parse.y
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2016-2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
* Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -55,6 +56,7 @@
#include "gotwebd.h"
#include "log.h"
+#include "media.h"
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
@@ -102,6 +104,7 @@ static struct server *new_srv;
static struct server *conf_new_server(const char *);
int getservice(const char *);
int n;
+struct media_type media;
int get_addrs(const char *, const char *);
struct address *get_unix_addr(const char *);
@@ -132,6 +135,7 @@ typedef struct {
%token SERVER CHROOT CUSTOM_CSS SOCKET HINT HTDOCS GOTWEB_URL_ROOT
%token SUMMARY_COMMITS_DISPLAY SUMMARY_TAGS_DISPLAY USER AUTHENTICATION
%token ENABLE DISABLE INSECURE REPOSITORY REPOSITORIES PERMIT DENY HIDE
+%token TYPES INCLUDE
%token <v.string> STRING
%token <v.number> NUMBER
@@ -146,9 +150,25 @@ grammar : /* empty */
| grammar varset '\n'
| grammar main '\n'
| grammar server '\n'
+ | grammar types '\n'
| grammar error '\n' { file->errors++; }
;
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
varset : STRING '=' STRING {
char *s = $1;
while (*s++) {
@@ -704,9 +724,63 @@ repoopts1 : DISABLE AUTHENTICATION {
}
;
+types : TYPES '{' optnl mediaopts_l '}'
+ ;
+
+mediaopts_l : mediaopts_l mediaoptsl nl
+ | mediaoptsl nl
+ ;
+
+mediaoptsl : mediastring medianames_l optsemicolon
+ | include
+ ;
+
+mediastring : STRING '/' STRING {
+ if (strlcpy(media.media_type, $1,
+ sizeof(media.media_type)) >=
+ sizeof(media.media_type) ||
+ strlcpy(media.media_subtype, $3,
+ sizeof(media.media_subtype)) >=
+ sizeof(media.media_subtype)) {
+ yyerror("media type too long");
+ free($1);
+ free($3);
+ YYERROR;
+ }
+ free($1);
+ free($3);
+ }
+ ;
+
+medianames_l : medianames_l medianamesl
+ | medianamesl
+ ;
+
+medianamesl : numberstring {
+
+ if (strlcpy(media.media_name, $1,
+ sizeof(media.media_name)) >=
+ sizeof(media.media_name)) {
+ yyerror("media name too long");
+ free($1);
+ YYERROR;
+ }
+ free($1);
+
+ if (media_add(gotwebd->mediatypes, &media) == NULL) {
+ yyerror("failed to add media type");
+ YYERROR;
+ }
+ }
+ ;
+
nl : '\n' optnl
;
+optsemicolon : ';'
+ | /* empty */
+ ;
+
optnl : '\n' optnl /* zero or more newlines */
| /* empty */
;
@@ -755,6 +829,7 @@ lookup(char *s)
{ "hide", HIDE },
{ "hint", HINT },
{ "htdocs", HTDOCS },
+ { "include", INCLUDE },
{ "insecure", INSECURE },
{ "listen", LISTEN },
{ "login", GOTWEBD_LOGIN },
@@ -782,6 +857,7 @@ lookup(char *s)
{ "socket", SOCKET },
{ "summary_commits_display", SUMMARY_COMMITS_DISPLAY },
{ "summary_tags_display", SUMMARY_TAGS_DISPLAY },
+ { "types", TYPES },
{ "user", USER },
{ "www", WWW },
};
@@ -1035,7 +1111,7 @@ nodigits:
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
x != '{' && x != '}' && \
x != '!' && x != '=' && x != '#' && \
- x != ','))
+ x != ',' && x != '/'))
if (isalnum(c) || c == ':' || c == '_') {
do {
@@ -1116,7 +1192,7 @@ pushfile(const char *name, int secret)
free(nfile);
return (NULL);
}
- nfile->lineno = 1;
+ nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
nfile->ungetsize = 16;
nfile->ungetbuf = calloc(1, nfile->ungetsize);
if (nfile->ungetbuf == NULL) {
@@ -1193,6 +1269,57 @@ parse_config(const char *filename, struct gotwebd *env
if (TAILQ_EMPTY(&gotwebd->servers))
add_default_server();
+ /* load default mimes */
+ if (RB_EMPTY(gotwebd->mediatypes)) {
+ struct media_type defaults[] = {
+ {
+ .media_name = "css",
+ .media_type = "text",
+ .media_subtype = "css",
+ },
+ {
+ .media_name = "ico",
+ .media_type = "image",
+ .media_subtype = "x-icon",
+ },
+ {
+ .media_name = "png",
+ .media_type = "image",
+ .media_subtype = "png",
+ },
+ {
+ .media_name = "svg",
+ .media_type = "image",
+ .media_subtype = "svg+xml",
+ },
+ {
+ .media_name = "txt",
+ .media_type = "text",
+ .media_subtype = "plain",
+ },
+ {
+ .media_name = "webmanifest",
+ .media_type = "application",
+ .media_subtype = "manifest+json",
+ },
+ {
+ .media_name = "xml",
+ .media_type = "text",
+ .media_subtype = "xml",
+ },
+ };
+ size_t i;
+
+ for (i = 0; i < nitems(defaults); ++i) {
+ if (media_add(gotwebd->mediatypes, &defaults[i]) == NULL) {
+ fprintf(stderr, "failed to load default"
+ " MIME types\n");
+ errors++;
+ break;
+ }
+ }
+ }
+
/* add the implicit listen on socket */
if (TAILQ_EMPTY(&gotwebd->addresses)) {
char path[_POSIX_PATH_MAX];
blob - /dev/null
blob + 6b3ad97ebfba1a2d4986cfd23eb130040921b8a2 (mode 644)
--- /dev/null
+++ gotwebd/media.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/tree.h>
+
+#define MEDIATYPE_NAMEMAX 128 /* file name extension */
+#define MEDIATYPE_TYPEMAX 64 /* length of type/subtype */
+#define MEDIA_STRMAX (MEDIATYPE_NAMEMAX + 1 + MEDIATYPE_TYPEMAX)
+
+struct media_type {
+ char media_name[MEDIATYPE_NAMEMAX];
+ char media_type[MEDIATYPE_TYPEMAX];
+ char media_subtype[MEDIATYPE_TYPEMAX];
+ RB_ENTRY(media_type) media_entry;
+};
+RB_HEAD(mediatypes, media_type);
+
+struct media_type *media_add(struct mediatypes *, struct media_type *);
+void media_delete(struct mediatypes *, struct media_type *);
+void media_purge(struct mediatypes *);
+struct media_type *media_find(struct mediatypes *, const char *);
+
+
+RB_PROTOTYPE(mediatypes, media_type, media_entry, media_cmp);
types {} block for gotwebd