summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-02-22 17:07:12 +0100
committerMitya Selivanov <automainint@guattari.tech>2025-02-22 17:07:12 +0100
commit0242504a0eecb3543f47d6d6c4ef3e38ee322534 (patch)
tree3099a836891396fc5b3e1ed532e86778de2b13c7 /main.c
parente3603be3dce9faa55c3d0785563b22b19adaf651 (diff)
downloadcgi-0242504a0eecb3543f47d6d6c4ef3e38ee322534.zip
Knock-knock cookie (work in progress)HEADdev
Diffstat (limited to 'main.c')
-rwxr-xr-xmain.c171
1 files changed, 139 insertions, 32 deletions
diff --git a/main.c b/main.c
index 9949195..30500e4 100755
--- a/main.c
+++ b/main.c
@@ -13,29 +13,17 @@
#/ - No JavaScript
#/ - No external dependencies
#/ - No dynamic memory management
-#/ - Simple bootstrap script
-#/ - Not a single LLM-generated line of code
#/
-#/ NOTE
+#/ NOTE: At the start of this file you can see a shell script
+#/ that will compile and install the program into `/srv/`
#/
-#/ - At the start of this file you can see a shell script
-#/ that will compile and install the program into `/srv/`
+#/ To-Do list
#/
-#/ TODO
-#/
-#/ - CGI program
-#/ - Hierarchical tags.
-#/ - Blacklist and throttling.
-#/ - Wrap other CGI programs.
-#/ - CI terminal
-#/ - Convay's Game of Life with `<meta http-equiv="refresh" content="1" >`
-#/ - Bootstrap script
-#/ - ssh
-#/ - git config
-#/ - highlight
-#/ - tor, onion service
-#/ - daemons
-#/ - ufw
+#/ - Hierarchical tags.
+#/ - Blacklist and throttling.
+#/ - Redirects for cgit and git-http-backend.
+#/ - CI terminal.
+#/ - Convay's Game of Life with `<meta http-equiv="refresh" content="1" >`
#/
#/ ================================================================
@@ -86,6 +74,8 @@ exit $? # */
#define FORM_NAME "submit"
#define FORM_ITEM_DATA "data"
+#define CGI_CGIT "/usr/lib/cgit/cgit.cgi"
+
enum {
DEFAULT_SCALE = 100,
DEFAULT_WIDTH = 42,
@@ -508,7 +498,7 @@ static Redirect redirects[] = {
}, {
.alias = "/juliaset",
.url = "/static/plain/juliaset/index.htm",
- .cgi = "/usr/lib/cgit/cgit.cgi",
+ .cgi = CGI_CGIT,
.replace_headers = (Replace_Header[]) {
{ .name = "Content-Type", .value = "text/html; charset=UTF-8", },
{ .name = "X-Content-Type-Options", .value = NULL, },
@@ -2446,29 +2436,38 @@ i32 run_cgi(c8 const *cgi, c8 *const *argv, c8 *const *envp) {
signal(SIGCHLD, SIG_IGN);
signal(SIGALRM, SIG_IGN);
+ i32 pipe_in[2];
i32 pipe_out[2];
- if (pipe(pipe_out) == -1)
+ if (pipe(pipe_in) == -1)
return 1;
+ if (pipe(pipe_out) == -1)
+ return 2;
if (fcntl(pipe_out[1], F_SETFL, O_NONBLOCK) == -1)
- return 2;
+ return 3;
pid_t id = fork();
switch (id) {
case -1:
- return 3;
+ return 4;
case 0:
+ if (dup2(pipe_in[0], STDIN_FILENO) == -1)
+ return 5;
if (dup2(pipe_out[1], STDOUT_FILENO) == -1)
- return 4;
+ return 6;
+ close(pipe_in[0]);
+ close(pipe_in[1]);
close(pipe_out[0]);
close(pipe_out[1]);
execve(cgi, argv, envp);
- return 5;
+ // No return.
+
+ return 7;
default:;
}
@@ -2486,7 +2485,7 @@ i32 run_cgi(c8 const *cgi, c8 *const *argv, c8 *const *envp) {
i64 n = read(pipe_out[0], cgi_output, sizeof cgi_output - 1);
if (n <= 0)
- return 6;
+ return 8;
cgi_output[n] = '\0';
cgi_output_size = n;
@@ -2952,6 +2951,21 @@ b8 str_eq_with_slash(c8 const *a, c8 const *b) {
return 0;
}
+b8 str_eq_prefix(c8 const *a, c8 const *b) {
+ for (; *b != '\0'; ++a, ++b)
+ if (*a != *b)
+ return 0;
+ return 1;
+}
+
+b8 str_eq_postfix(c8 const *a, c8 const *b) {
+ i64 a_len = strlen(a);
+ i64 b_len = strlen(b);
+ if (a_len >= b_len)
+ return memcmp(a + (a_len - b_len), b, b_len) == 0;
+ return 0;
+}
+
i32 check_color(i32 c) {
if (c >= 0 && c < COLORS_MAX)
return c;
@@ -3002,6 +3016,44 @@ void skip_spaces(c8 const *s, i32 *n) {
}
}
+b8 knock_knock_cookie(c8 const *cookie) {
+ c8 knock_knock[] = "knock=knock";
+
+ {
+ c8 const *val = cookie;
+ c8 const *val_end = NULL;
+
+ while (*val != '\0') {
+ while (*val == ' ' || *val == ';') ++val;
+ val_end = val;
+ while (*val_end != ';' && *val_end != '\0') ++val_end;
+
+ if (memcmp(val, knock_knock, sizeof knock_knock - 1) == 0)
+ return 1;
+ }
+ }
+
+ printf("Status: 200 OK\r\n");
+ printf("Set-Cookie: %s; Path=/; SameSite=Lax; Expires=", knock_knock);
+ print_time_gmt(6 * SECONDS_IN_MONTH);
+ printf("; HttpOnly\r\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");
+
+ printf("<!DOCTYPE html><html><head>");
+ printf("<meta http-equiv=\"refresh\" content=\"1\" >");
+ printf("<meta charset='utf-8'>");
+ printf("<meta name='viewport' content='width=device-width,initial-scale=1'>");
+ printf("<link rel='icon' type='image/gif' href='/favicon.gif'>");
+ printf("<title> Knock-knock </title>");
+ printf("</head><body></body>");
+ printf("\r\n");
+
+ return 0;
+}
+
void setup_theme_cookie(c8 const *cookie, c8 const *query) {
// Parse cookie values
//
@@ -3225,7 +3277,7 @@ i32 main(i32 argc, c8 **argv) {
return 0;
}
- // TODO Check blacklist and throttling
+ // TODO: Check blacklist and throttling.
if (0) {
printf("Status: 400 Forbidden\r\n");
@@ -3237,6 +3289,8 @@ i32 main(i32 argc, c8 **argv) {
return 0;
}
+ // Redirects.
+
i32 doc = DOC_404;
for (i64 i = 0; i < (i64)(sizeof redirects / sizeof *redirects); ++i)
@@ -3341,6 +3395,61 @@ i32 main(i32 argc, c8 **argv) {
}
}
+ // Knock-knock.
+
+ if (!knock_knock_cookie(http_cookie))
+ return 0;
+
+ // cgit hooks.
+
+ if (str_eq_prefix(document_uri, "/git/")) {
+ static c8 buf_path [4096] = {0};
+ static c8 buf_query [4096] = {0};
+
+ snprintf(buf_path, sizeof buf_path - 1, "PATH_INFO=%s", document_uri + 4);
+ snprintf(buf_query, sizeof buf_query - 1, "QUERY_STRING=%s", query_string);
+
+ c8 *argv[] = { CGI_CGIT, NULL };
+ c8 *envp[] = { buf_path, buf_query, NULL };
+
+ i32 s = run_cgi(CGI_CGIT, argv, envp);
+ if (s != 0) {
+ printf("Status: 500 Internal Server Error %d\r\n", s);
+ return 0;
+ }
+
+ fflush(stdout);
+ i32 written = write(STDOUT_FILENO, cgi_output, cgi_output_size);
+ (void) written;
+
+ return 0;
+ }
+
+ if (str_eq_postfix(document_uri, ".git")) {
+ static c8 buf_path [4096] = {0};
+ static c8 buf_query [4096] = {0};
+
+ snprintf(buf_path, sizeof buf_path - 1, "PATH_INFO=%s", document_uri + 4);
+ snprintf(buf_query, sizeof buf_query - 1, "QUERY_STRING=%s", query_string);
+
+ c8 *argv[] = { CGI_CGIT, NULL };
+ c8 *envp[] = { buf_path, buf_query, NULL };
+
+ i32 s = run_cgi(CGI_CGIT, argv, envp);
+ if (s != 0) {
+ printf("Status: 500 Internal Server Error %d\r\n", s);
+ return 0;
+ }
+
+ fflush(stdout);
+ i32 written = write(STDOUT_FILENO, cgi_output, cgi_output_size);
+ (void) written;
+
+ return 0;
+ }
+
+ // Normal HTML gen.
+
for (i64 i = 0; i < (i64)(sizeof locs / sizeof *locs); ++i)
if (strcmp(request_method, locs[i].method) == 0 &&
str_eq_with_slash(document_uri, locs[i].uri)) {
@@ -3368,10 +3477,8 @@ i32 main(i32 argc, c8 **argv) {
printf("<!DOCTYPE html><html><head>");
{
printf("<meta charset='utf-8'>");
- printf("<meta name='viewport' "
- "content='width=device-width,initial-scale=1'>");
- printf(
- "<link rel='icon' type='image/gif' href='/favicon.gif'>");
+ printf("<meta name='viewport' content='width=device-width,initial-scale=1'>");
+ printf("<link rel='icon' type='image/gif' href='/favicon.gif'>");
printf("<title> %s </title>", docs[doc].title);
printf("<style>");
for (i64 i = 0; i < (i64)(sizeof fonts / sizeof *fonts); ++i)