diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2025-01-18 12:04:22 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2025-01-18 12:04:22 +0100 |
commit | e3603be3dce9faa55c3d0785563b22b19adaf651 (patch) | |
tree | 0c2d1b1dc3c8e876fba1d621acebe9f09a282311 | |
parent | bd16206096354a53dbe096fa41483bd325b78703 (diff) | |
download | cgi-dev.zip |
-rw-r--r-- | compile_flags.txt | 4 | ||||
-rwxr-xr-x | main.c | 232 |
2 files changed, 214 insertions, 22 deletions
diff --git a/compile_flags.txt b/compile_flags.txt deleted file mode 100644 index 1a27fb0..0000000 --- a/compile_flags.txt +++ /dev/null @@ -1,4 +0,0 @@ --Wall --Wextra --Werror --pedantic @@ -70,6 +70,10 @@ exit $? # */ #include <stdlib.h> #include <string.h> #include <time.h> +#include <signal.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/wait.h> // ================================================================ // @@ -156,8 +160,17 @@ typedef struct { } Location; typedef struct { - c8 const *alias; - c8 const *url; + b8 END; + c8 const *name; + c8 const *value; +} Replace_Header; + +typedef struct { + c8 const * alias; + c8 const * url; + c8 * cgi; + b8 is_binary; + Replace_Header *replace_headers; } Redirect; // ================================================================ @@ -492,7 +505,36 @@ static Redirect redirects[] = { }, { .alias = "/deezer/2021", .url = "https://deezer.page.link/PXS8q1jEqECa79rEA", - }, + }, { + .alias = "/juliaset", + .url = "/static/plain/juliaset/index.htm", + .cgi = "/usr/lib/cgit/cgit.cgi", + .replace_headers = (Replace_Header[]) { + { .name = "Content-Type", .value = "text/html; charset=UTF-8", }, + { .name = "X-Content-Type-Options", .value = NULL, }, + { .name = "Content-Security-Policy", .value = NULL, }, + { .name = "Content-Disposition", .value = NULL, }, + { .name = "Last-Modified", .value = NULL, }, + { .name = "Expires", .value = NULL, }, + { .name = "ETag", .value = NULL, }, + { .END=1, } + }, + }, { + .alias = "/juliaset/julia_set.wasm", + .url = "/static/plain/juliaset/julia_set.wasm", + .cgi = "/usr/lib/cgit/cgit.cgi", + .is_binary = 1, + .replace_headers = (Replace_Header[]) { + { .name = "Content-Type", .value = "application/wasm", }, + { .name = "X-Content-Type-Options", .value = NULL, }, + { .name = "Content-Security-Policy", .value = NULL, }, + { .name = "Content-Disposition", .value = NULL, }, + { .name = "Last-Modified", .value = NULL, }, + { .name = "Expires", .value = NULL, }, + { .name = "ETag", .value = NULL, }, + { .END=1, } + }, + } }; static Location locs[] = { @@ -2391,12 +2433,67 @@ c8 const *headlines[] = { "A renaissance of the Pythagorean mindset.", }; +c8 cgi_output[1024 * 1024 * 10] = {0}; +i32 cgi_output_size = 0; + // ================================================================ // // Procedures // // ================================================================ +i32 run_cgi(c8 const *cgi, c8 *const *argv, c8 *const *envp) { + signal(SIGCHLD, SIG_IGN); + signal(SIGALRM, SIG_IGN); + + i32 pipe_out[2]; + + if (pipe(pipe_out) == -1) + return 1; + + if (fcntl(pipe_out[1], F_SETFL, O_NONBLOCK) == -1) + return 2; + + pid_t id = fork(); + + switch (id) { + case -1: + return 3; + + case 0: + if (dup2(pipe_out[1], STDOUT_FILENO) == -1) + return 4; + + close(pipe_out[0]); + close(pipe_out[1]); + + execve(cgi, argv, envp); + return 5; + + default:; + } + + for (;;) { + int status; + + if (waitpid(id, &status, 0) == -1) + break; + + if (WIFEXITED(status)) + break; + } + + i64 n = read(pipe_out[0], cgi_output, sizeof cgi_output - 1); + + if (n <= 0) + return 6; + + cgi_output[n] = '\0'; + cgi_output_size = n; + + return 0; +} + c8 const *get_color(i32 index) { static i64 rainbow_index = -1; if (rainbow_index == -1) @@ -3091,7 +3188,7 @@ void setup_theme_cookie(c8 const *cookie, c8 const *query) { COLORS[i][5]); printf("; Path=/; SameSite=Lax; Expires="); print_time_gmt(6 * SECONDS_IN_MONTH); - printf("; HttpOnly\n"); + printf("; HttpOnly\r\n"); } } @@ -3099,6 +3196,11 @@ i32 main(i32 argc, c8 **argv) { (void) argc; (void) argv; + FILE *_in = freopen(NULL, "rb", stdin); + FILE *_out = freopen(NULL, "wb", stdout); + (void) _in; + (void) _out; + struct timespec time_begin; timespec_get(&time_begin, TIME_UTC); @@ -3119,19 +3221,19 @@ i32 main(i32 argc, c8 **argv) { strlen(content_type) > MAX_CONTENT_TYPE_SIZE || (!str_eq(request_method, NULL, "GET", NULL) && !str_eq(request_method, NULL, "POST", NULL))) { - printf("Status: 500 Internal Server Error\n"); + printf("Status: 500 Internal Server Error 1\r\n"); return 0; } // TODO Check blacklist and throttling if (0) { - printf("Status: 400 Forbidden\n"); + printf("Status: 400 Forbidden\r\n"); return 0; } if (0) { - printf("Status: 429 Too Many Requests\n"); + printf("Status: 429 Too Many Requests\r\n"); return 0; } @@ -3139,12 +3241,104 @@ i32 main(i32 argc, c8 **argv) { for (i64 i = 0; i < (i64)(sizeof redirects / sizeof *redirects); ++i) if (str_eq_with_slash(document_uri, redirects[i].alias)) { - printf("Status: 301 Redirect\n"); - printf("Location: %s\n", redirects[i].url); - printf("Content-Type: text/html\n"); - printf("\n"); - printf("Redirect: <a href='%s'>%s</a>\n", redirects[i].url, redirects[i].url); - return 0; + if (redirects[i].url == NULL) { + printf("Status: 500 Internal Server Error 2\r\n"); + return 0; + } + + if (redirects[i].cgi != NULL) { + static c8 buf[4096] = {0}; + snprintf(buf, sizeof buf - 1, "PATH_INFO=%s", redirects[i].url); + + c8 *argv[] = { redirects[i].cgi, NULL }; + c8 *envp[] = { buf, NULL }; + + i32 s = run_cgi(redirects[i].cgi, argv, envp); + if (s != 0) { + printf("Status: 500 Internal Server Error %d\r\n", s); + return 0; + } + + printf("Status: 200 OK\r\n"); + printf("Cross-Origin-Opener-Policy: same-origin\r\n"); + printf("Cross-Origin-Embedder-Policy: require-corp\r\n"); + + c8 *p = cgi_output; + + while (p < cgi_output + cgi_output_size) { + if (*p == '\r' || *p == '\n') { + printf("\r\n"); + if (*p == '\r') ++p; + if (*p == '\n') ++p; + break; + } + + c8 *q = p; + while (q < cgi_output + cgi_output_size && *q != '\r' && *q != '\n') + ++q; + if (q[0] == '\r' && q[1] == '\n') { + ++q; + *q = '\0'; + } + *q = '\0'; + + b8 replaced = 0; + + for (i32 j = 0;; ++j) { + if (redirects[i].replace_headers[j].END) + break; + + c8 const *name = redirects[i].replace_headers[j].name; + c8 const *value = redirects[i].replace_headers[j].value; + + if (name == NULL) + continue; + + i32 len = strlen(name); + + if (q - p > len && p[len] == ':' && str_eq(p, p + len, name, name + len)) { + if (value != NULL) + printf("%s: %s\r\n", name, value); + replaced = 1; + break; + } + } + + if (!replaced) + printf("%s\r\n", p); + + p = q + 1; + } + + if (redirects[i].is_binary) { + fflush(stdout); + i32 written = write(STDOUT_FILENO, p, (cgi_output + cgi_output_size) - p); + (void) written; + } else + while (p < cgi_output + cgi_output_size) { + c8 *q = p; + while (q < cgi_output + cgi_output_size && *q != '\r' && *q != '\n') + ++q; + if (q[0] == '\r' && q[1] == '\n') { + ++q; + *q = '\0'; + } + *q = '\0'; + + printf("%s\r\n", p); + + p = q + 1; + } + + return 0; + } else { + printf("Status: 301 Redirect\r\n"); + printf("Location: %s\r\n", redirects[i].url); + printf("Content-Type: text/html; charset=UTF-8\r\n"); + printf("\r\n"); + printf("Redirect: <a href='%s'>%s</a>\r\n", redirects[i].url, redirects[i].url); + return 0; + } } for (i64 i = 0; i < (i64)(sizeof locs / sizeof *locs); ++i) @@ -3155,14 +3349,16 @@ i32 main(i32 argc, c8 **argv) { } if (doc == DOC_404) - printf("Status: 404 Page Not Found\n"); + printf("Status: 404 Page Not Found\r\n"); else - printf("Status: 200 OK\n"); + printf("Status: 200 OK\r\n"); setup_theme_cookie(http_cookie, query_string); - printf("Content-Type: text/html\n"); - printf("\n"); + printf("Cross-Origin-Opener-Policy: same-origin\r\n"); + printf("Cross-Origin-Embedder-Policy: require-corp\r\n"); + printf("Content-Type: text/html\r\n"); + printf("\r\n"); if (docs[doc].content[0].type == TAG_RAW) { printf("%s", docs[doc].content[0].text); @@ -3330,7 +3526,7 @@ i32 main(i32 argc, c8 **argv) { "</script>", delta_micros / 1000, (delta_micros / 10) % 100); - printf("\n"); + printf("\r\n"); return 0; } |