Allow admins to define their own stylesheet

slcl used to provide a hardcoded stylesheet. However, it would be
desirable for some admins to provide a custom stylesheet without having
to rebuild the application.

Now, slcl creates a default stylesheet, namely style.css, into the
target directory, that can be later modified by admins.

While this might contradict the suckless philosophy a bit, hopefully
some admins might find this new feature useful.
This commit is contained in:
Xavier Del Campo Romero 2023-07-11 01:20:39 +02:00
parent 4236c7fc3a
commit e79e955d93
Signed by: xavi
GPG Key ID: 84FF3612A9BF43F2
7 changed files with 199 additions and 57 deletions

View File

@ -12,6 +12,7 @@ add_executable(${PROJECT_NAME}
main.c
page.c
server.c
style.c
wildcard_cmp.c
)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)

View File

@ -21,6 +21,7 @@ OBJECTS = \
main.o \
page.o \
server.o \
style.o \
wildcard_cmp.o \
all: $(PROJECT)

108
main.c
View File

@ -6,6 +6,7 @@
#include "hex.h"
#include "http.h"
#include "page.h"
#include "style.h"
#include "wildcard_cmp.h"
#include <openssl/err.h>
#include <openssl/rand.h>
@ -24,6 +25,8 @@
#include <stdlib.h>
#include <string.h>
#define STYLE_PATH "style.css"
struct form
{
char *key, *value;
@ -59,7 +62,32 @@ static int serve_index(const struct http_payload *const p,
static int serve_style(const struct http_payload *const p,
struct http_response *const r, void *const user)
{
return page_style(r);
int ret = -1;
struct auth *const a = user;
const char *const dir = auth_dir(a);
struct dynstr d;
dynstr_init(&d);
if (!dir)
{
fprintf(stderr, "%s: auth_dir failed\n", __func__);
goto end;
}
else if (dynstr_append(&d, "%s/" STYLE_PATH, dir))
{
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
goto end;
}
else if ((ret = page_style(r, d.str)))
{
fprintf(stderr, "%s: page_style failed\n", __func__);
goto end;
}
end:
dynstr_free(&d);
return ret;
}
static char *alloc_form_data(const char *const s, const char **const end)
@ -1796,6 +1824,81 @@ end:
return ret;
}
static int dump_default_style(const char *const path)
{
int ret = -1;
FILE *const f = fopen(path, "wb");
if (!f)
{
fprintf(stderr, "%s: fopen(3) %s: %s\n",
__func__, path, strerror(errno));
goto end;
}
else if (!fwrite(style_default, style_default_len, 1, f))
{
fprintf(stderr, "%s: fwrite(3): %s\n", __func__, strerror(errno));
goto end;
}
printf("Dumped default stylesheet into %s\n", path);
ret = 0;
end:
if (f && fclose(f))
{
fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno));
ret = -1;
}
return ret;
}
static int ensure_style(const char *const dir)
{
int ret = -1;
struct dynstr d;
struct stat sb;
dynstr_init(&d);
if (dynstr_append(&d, "%s/" STYLE_PATH, dir))
{
fprintf(stderr, "%s: dynstr_append user failed\n", __func__);
goto end;
}
else if (stat(d.str, &sb))
{
switch (errno)
{
case ENOENT:
if (dump_default_style(d.str))
{
fprintf(stderr, "%s: dump_default_style failed\n",
__func__);
goto end;
}
break;
default:
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
goto end;
}
}
else if (!S_ISREG(sb.st_mode))
{
fprintf(stderr, "%s: %s not a regular file\n", __func__, d.str);
return -1;
}
ret = 0;
end:
dynstr_free(&d);
return ret;
}
int main(int argc, char *argv[])
{
int ret = EXIT_FAILURE;
@ -1806,6 +1909,7 @@ int main(int argc, char *argv[])
if (parse_args(argc, argv, &dir, &port, &tmpdir)
|| init_dirs(dir)
|| ensure_style(dir)
|| !(a = auth_alloc(dir)))
goto end;
@ -1819,7 +1923,7 @@ int main(int argc, char *argv[])
if (!(h = handler_alloc(&cfg))
|| handler_add(h, "/", HTTP_OP_GET, serve_index, a)
|| handler_add(h, "/index.html", HTTP_OP_GET, serve_index, a)
|| handler_add(h, "/style.css", HTTP_OP_GET, serve_style, NULL)
|| handler_add(h, "/style.css", HTTP_OP_GET, serve_style, a)
|| handler_add(h, "/user/*", HTTP_OP_GET, getnode, a)
|| handler_add(h, "/login", HTTP_OP_POST, login, a)
|| handler_add(h, "/logout", HTTP_OP_POST, logout, a)

80
page.c
View File

@ -1535,73 +1535,45 @@ int page_bad_request(struct http_response *const r)
return 0;
}
int page_style(struct http_response *const r)
int page_style(struct http_response *const r, const char *const path)
{
static const char body[] =
"body\n"
"{\n"
" font-family: 'Courier New', Courier, monospace;\n"
"}\n"
"td\n"
"{\n"
" font-size: 14px;\n"
"}\n"
"a\n"
"{\n"
" text-decoration: none;\n"
"}\n"
".userform\n"
"{\n"
" padding: 4px;\n"
"}\n"
".loginform\n"
"{\n"
" display: grid;\n"
"}\n"
"form, label, table\n"
"{\n"
" margin: auto;\n"
"}\n"
"div\n"
"{\n"
" align-items: center;\n"
" display: grid;\n"
"}\n"
"input, .abutton\n"
"{\n"
" margin: auto;\n"
" border: 1px solid;\n"
" border-radius: 8px;\n"
"}\n"
"header, footer\n"
"{\n"
" display: flex;\n"
" justify-content: center;\n"
" text-decoration: auto;\n"
"}\n"
"table\n"
"{\n"
" max-width: 50%;\n"
"}\n"
"tr:nth-child(even)\n"
"{\n"
" background-color: lightgray;\n"
"}\n";
FILE *f = NULL;
struct stat sb;
if (stat(path, &sb))
{
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
goto failure;
}
else if (!(f = fopen(path, "rb")))
{
fprintf(stderr, "%s: fopen(3) %s: %s\n",
__func__, path, strerror(errno));
goto failure;
}
*r = (const struct http_response)
{
.status = HTTP_STATUS_OK,
.buf.ro = body,
.n = sizeof body - 1
.f = f,
.n = sb.st_size
};
if (http_response_add_header(r, "Content-Type", "text/css"))
{
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
return -1;
goto failure;
}
return 0;
failure:
if (f && fclose(f))
fprintf(stderr, "%s: fclose(3) %s: %s\n",
__func__, path, strerror(errno));
return -1;
}
int page_share(struct http_response *const r, const char *const path)

2
page.h
View File

@ -36,7 +36,7 @@ struct page_rm
};
int page_login(struct http_response *r);
int page_style(struct http_response *r);
int page_style(struct http_response *r, const char *path);
int page_failed_login(struct http_response *r);
int page_forbidden(struct http_response *r);
int page_bad_request(struct http_response *r);

55
style.c Normal file
View File

@ -0,0 +1,55 @@
#include "style.h"
#include <stddef.h>
const char style_default[] =
"body\n"
"{\n"
" font-family: 'Courier New', Courier, monospace;\n"
"}\n"
"td\n"
"{\n"
" font-size: 14px;\n"
"}\n"
"a\n"
"{\n"
" text-decoration: none;\n"
"}\n"
".userform\n"
"{\n"
" padding: 4px;\n"
"}\n"
".loginform\n"
"{\n"
" display: grid;\n"
"}\n"
"form, label, table\n"
"{\n"
" margin: auto;\n"
"}\n"
"div\n"
"{\n"
" align-items: center;\n"
" display: grid;\n"
"}\n"
"input, .abutton\n"
"{\n"
" margin: auto;\n"
" border: 1px solid;\n"
" border-radius: 8px;\n"
"}\n"
"header, footer\n"
"{\n"
" display: flex;\n"
" justify-content: center;\n"
" text-decoration: auto;\n"
"}\n"
"table\n"
"{\n"
" max-width: 50%;\n"
"}\n"
"tr:nth-child(even)\n"
"{\n"
" background-color: lightgray;\n"
"}\n";
const size_t style_default_len = sizeof style_default - 1;

9
style.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef STYLE_H
#define STYLE_H
#include <stddef.h>
extern const char style_default[];
extern const size_t style_default_len;
#endif