commit 5080eb62ec43e44094bd57a060126b577eb1ba50
parent 16583646ecfddbe6bef5999075c7f758bbd8abb2
Author: libredev <libredev@ircforever.org>
Date: Sun, 8 Jan 2023 15:54:38 +0530
add commenting system prototype
Diffstat:
M | Makefile | | | 14 | +++++++------- |
M | README | | | 6 | +++++- |
A | comment.c | | | 140 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | comment.db | | | 8 | ++++++++ |
A | comment.h | | | 27 | +++++++++++++++++++++++++++ |
A | comment.html | | | 16 | ++++++++++++++++ |
M | main.c | | | 199 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
M | style.css | | | 9 | +++++++++ |
A | utils.c | | | 183 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | utils.h | | | 35 | +++++++++++++++++++++++++++++++++++ |
10 files changed, 538 insertions(+), 99 deletions(-)
diff --git a/Makefile b/Makefile
@@ -12,18 +12,18 @@ CFLAGS =\
-Wstrict-prototypes -Wold-style-definition\
-D_DEFAULT_SOURCE
-index.cgi: main.c http.h pdjson/pdjson.c
- $(CC) $(CFLAGS) -o $@ main.c pdjson/pdjson.c
+index.cgi: main.c comment.c utils.c comment.h utils.h http.h pdjson/pdjson.c
+ $(CC) $(CFLAGS) -o $@ main.c comment.c utils.c pdjson/pdjson.c
run: index.cgi
- REQUEST_URI="" valgrind --leak-check=full --show-leak-kinds=all ./index.cgi
+ REQUEST_URI='/?name=hey&email=hey%40lol.orgl&body=log' valgrind --leak-check=full --show-leak-kinds=all ./index.cgi
clean:
rm -f index.cgi
-install: index.cgi header.html footer.html style.css music.svg
- rm -R -f $(INSTALL_DIR)
- mkdir -p $(INSTALL_DIR)
- cp index.cgi header.html footer.html style.css music.svg $(INSTALL_DIR)
+install: index.cgi header.html footer.html comment.html style.css music.svg comment.db
+ #rm -R -f $(INSTALL_DIR)
+ #mkdir -p $(INSTALL_DIR)
+ cp index.cgi header.html footer.html comment.html style.css music.svg comment.db $(INSTALL_DIR)
.PHONY: run clean install
diff --git a/README b/README
@@ -1 +1,5 @@
-Everything in this repository is released under CC0 1.0 Universal.
+License
+-------
+Everything in this repository is released under CC0 1.0 except:
+ http.h - Unlicense or MIT
+ pdjson/ - Unlicense
diff --git a/comment.c b/comment.c
@@ -0,0 +1,140 @@
+/*
+ * The author(s) have dedicated this work to the public domain by waiving all
+ * of his or her rights to this work worldwide under copyright and patent law,
+ * including all related and neighboring rights, to the extent allowed by law.
+ * This work is provided 'as-is', without any express or implied warranty.
+ * See COPYING file for details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "comment.h"
+#include "utils.h"
+
+enum {
+ HANDLE_NAME,
+ HANDLE_EMAIL,
+ HANDLE_BODY,
+ HANDLE_DELIMITER,
+};
+
+struct comment_list *
+comment_list_new(char *path)
+{
+ struct comment_list *head, *tail;
+ FILE *file;
+ char line[512];
+ int state, lineno, len;
+
+ /* init struct */
+ head = tail = NULL;
+ state = HANDLE_NAME;
+ lineno = 0;
+
+ if ((file = fopen(path, "rb")) == NULL)
+ fatal("%s:", path);
+
+ while(fgets(line, sizeof(line), file) != NULL) {
+ /* if line doesn't end with newline character */
+ if (line[strlen(line) - 1] != '\n' && state != HANDLE_BODY) {
+ fatal("%s:%d: name or email is too big", path, lineno);
+ } else {
+ lineno++;
+ if (state == HANDLE_BODY) {
+ if (strcmp(line, "---\n") == 0) {
+ tail->body[strlen(tail->body)-1] = '\0';
+ state = HANDLE_NAME;
+ continue;
+ }
+ } else {
+ line[strlen(line) - 1] = '\0';
+ }
+ }
+ switch (state) {
+ case HANDLE_NAME:
+ if (head == NULL) {
+ head = tail = ecalloc(1, sizeof(*tail));
+ } else {
+ tail->next = ecalloc(1, sizeof(*tail));
+ tail = tail->next;
+ }
+ tail->name = strlen(line) == 0 ? NULL : strdup(line);
+ state = HANDLE_EMAIL;
+ break;
+ case HANDLE_EMAIL:
+ tail->email = strlen(line) == 0 ? NULL : strdup(line);
+ state = HANDLE_BODY;
+ break;
+ case HANDLE_BODY:
+ if (tail->body == NULL)
+ len = strlen(line) + 1;
+ else
+ len = strlen(tail->body) + strlen(line) + 1;
+ tail->body = erealloc(tail->body, len * sizeof(char));
+ strcat(tail->body, line);
+ break;
+ }
+ }
+ fclose(file);
+ if (state != HANDLE_NAME)
+ fatal("%s:%d: incomplete comment", path, lineno);
+ return head;
+}
+
+void
+comment_list_free(struct comment_list *clist)
+{
+ struct comment_list *it, *itn;
+
+ for (it = clist; it != NULL;) {
+ free(it->name);
+ free(it->email);
+ free(it->body);
+ itn = it->next;
+ free(it);
+ it = itn;
+ }
+}
+
+void
+comment_list_print(struct comment_list *clist)
+{
+ struct comment_list *it;
+
+ puts("<h2> Comments: </h2>");
+ puts("<hr>");
+ for (it = clist; it != NULL; it = it->next) {
+ printf("<p><b> %s </b> %s </p>\n",
+ it->name ? it->name : "Anonymous",
+ it->email ? it->email : "");
+ printf("<pre> %s </pre>\n", it->body);
+ puts("<hr>");
+ }
+}
+
+void
+comment_write(struct input_list *ilist, char *path)
+{
+ FILE *file;
+ char *name, *email, *body;
+
+ if ((file = fopen(path, "a")) == NULL)
+ fatal("%s:", path);
+
+ /* first get all the values before writing to the file */
+ name = input_list_find(ilist, "name");
+ email = input_list_find(ilist, "email");
+ body = input_list_find(ilist, "body");
+ if (body == NULL)
+ fatal("comment_write: body can't be empty");
+
+ /* now write the values on the file */
+ name == NULL ? fprintf(file, "\n") : fprintf(file, "%s\n", name);
+ name == NULL ? fprintf(file, "\n") : fprintf(file, "%s\n", email);
+ fprintf(file, "%s\n", body);
+ fprintf(file, "---\n");
+
+ fclose(file);
+}
diff --git a/comment.db b/comment.db
@@ -0,0 +1,8 @@
+libredev
+libredev@ircforever.org
+I love FREEDOM.
+---
+
+
+This is anonymous comment.
+---
diff --git a/comment.h b/comment.h
@@ -0,0 +1,27 @@
+/*
+ * The author(s) have dedicated this work to the public domain by waiving all
+ * of his or her rights to this work worldwide under copyright and patent law,
+ * including all related and neighboring rights, to the extent allowed by law.
+ * This work is provided 'as-is', without any express or implied warranty.
+ * See COPYING file for details.
+ */
+
+#ifndef COMMENT_H
+#define COMMENT_H
+
+struct input_list;
+
+struct comment_list {
+ char *name;
+ char *email;
+ char *body;
+ struct comment_list *next;
+};
+
+struct comment_list *
+ comment_list_new(char *);
+void comment_list_free(struct comment_list *);
+void comment_list_print(struct comment_list *);
+void comment_write(struct input_list *, char *);
+
+#endif /* COMMENT_H */
diff --git a/comment.html b/comment.html
@@ -0,0 +1,16 @@
+
+<form>
+ <fieldset>
+ <legend><b>Add Comment:</b></legend>
+ <p><b> NOTE: By commenting, you agree to release the comment under
+ <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a>
+ .</b></p>
+ <label for="name">Name (optional):</label><br>
+ <input type="text" name="name" id="name"><br><br>
+ <label for="email">Email (optional):</label><br>
+ <input type="email" name="email" id="email" size="30"><br><br>
+ <label for="comment">Comment:</label><br>
+ <textarea name="body" id="body" rows="8" required></textarea><br><br>
+ <input type="submit" value="Submit">
+ </fieldset>
+</form>
diff --git a/main.c b/main.c
@@ -6,18 +6,19 @@
* See COPYING file for details.
*/
+#include <ctype.h> /* tolower */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "utils.h"
+#include "comment.h"
+
#define HTTP_IMPLEMENTATION
#include "http.h"
#include "pdjson/pdjson.h"
-#define TRUE 1
-#define FALSE 0
-
struct icestats {
char *admin;
char *host;
@@ -50,39 +51,32 @@ struct source {
char *subtype;
char *title;
char *dummy;
+ char *url;
};
struct icestats icestats;
struct source *sources = NULL;
int sources_length = 0;
int source_active = FALSE;
+int header_sent = FALSE;
-char *
-read_file(const char* fname)
+void
+not_found(void)
{
- char *content;
- long fsize;
- FILE *file;
-
- file = fopen(fname, "rb");
- if (file == NULL)
- goto err;
- if (fseek(file, 0, SEEK_END) != 0)
- goto err;
- if ((fsize = ftell(file)) == -1)
- goto err;
- if (fseek(file, 0, SEEK_SET) != 0)
- goto err;
- if ((content = malloc((fsize + 1) * sizeof(char))) == NULL)
- goto err;
- if (fread(content, sizeof(char), fsize, file) != (size_t)fsize)
- goto err;
- fclose(file);
- (content)[fsize] = '\0';
- return content;
-err:
- printf("ERROR: %s: %s\n", fname, strerror(errno));
- return NULL;
+ puts("<h1>404 Not Found</h1>");
+ puts("<p>This is not a joke.</p>");
+ exit(1);
+}
+
+void
+format_to_url(char *s)
+{
+ for(int i = 0; i < strlen(s); i++) {
+ if (s[i] == ' ')
+ s[i] = '_';
+ else
+ s[i] = tolower(s[i]);
+ }
}
char *
@@ -106,10 +100,10 @@ icestats_set(struct icestats *s, json_stream *json, const char *key)
else if (strcmp(key, "server_id" ) == 0) var = &s->server_id;
else if (strcmp(key, "server_start" ) == 0) var = &s->server_start;
else if (strcmp(key, "server_start_iso8601" ) == 0) var = &s->server_start_iso8601;
+ else if (strcmp(key, "dummy" ) == 0) return;
if (var == NULL) {
- fprintf(stdout, "Failed to handle icestats key \"%s\"\n", key);
- exit(1);
+ fatal("failed to handle icestats key: %s", key);
} else {
*var = json_get_value(json);
}
@@ -169,17 +163,22 @@ source_set(struct source *s, json_stream *json, const char *key)
else if (strcmp(key, "dummy" ) == 0) var = &s->dummy;
if (var == NULL) {
- fprintf(stdout, "Failed to handle icestats key \"%s\"\n", key);
- exit(1);
+ fatal("failed to handle source key: %s", key);
} else {
*var = json_get_value(json);
+ if (var == &s->server_name) {
+ int len = strlen("/stream/") + strlen(s->server_name) + 1;
+ s->url = emalloc(len * sizeof(char));
+ strcpy(s->url, "/stream/");
+ strcat(s->url, s->server_name);
+ format_to_url(s->url);
+ }
}
}
void
source_print(struct source *s)
{
-
puts("<div class='stream'>");
/* thumbnail and player */
@@ -190,9 +189,9 @@ source_print(struct source *s)
puts("</video>");
puts("</div>");
+ printf("<a href='%s'>%s</a>\n", s->url, s->server_name);
/* info */
puts("<table>");
- printf("<tr><th> Stream Name </th><th> %s </th></tr>\n", s->server_name);
printf("<tr><td> Stream Description </td><td> %s </td></tr>\n", s->server_description);
printf("<tr><td> Stream Type </td><td> %s </td></tr>\n", s->server_type);
/* printf("<tr><td> server_url </td><td> %s </td></tr>\n", s->server_url); */
@@ -246,6 +245,7 @@ source_free(struct source *s)
free(s->subtype);
free(s->title);
free(s->dummy);
+ free(s->url);
}
void
@@ -271,11 +271,7 @@ void
source_increment(void)
{
sources_length++;
- sources = realloc(sources, sources_length * sizeof(struct source));
- if (sources == NULL) {
- printf("ERROR: relloc: %s\n", strerror(errno));
- exit(1);
- }
+ sources = erealloc(sources, sources_length * sizeof(struct source));
memset(&sources[sources_length-1], 0, sizeof(struct source));
}
@@ -299,18 +295,16 @@ json_handle_source(json_stream *json, enum json_type jtype)
json_handle_object(json, JSON_OBJECT);
source_active = FALSE;
} else {
- printf("ERROR: json_handle_source: invalid json_type.\n");
- exit(EXIT_FAILURE);
+ fatal("json_handle_source: invalid json_type");
}
}
void
json_handle_object(json_stream *json, enum json_type jtype)
{
- if (jtype != JSON_OBJECT) {
- printf("ERROR: json_handle_object: invalid json_type.\n");
- exit(EXIT_FAILURE);
- }
+ if (jtype != JSON_OBJECT)
+ fatal("json_handle_object: invalid json_type");
+
while (json_peek(json) != JSON_OBJECT_END && !json_get_error(json)) {
json_next(json);
const char *key = json_get_string(json, NULL);
@@ -332,80 +326,103 @@ main(int argc, char *argv[])
{
json_stream json;
char *buffer;
- char *script_name;
+ char *request_url;
+
+ struct input_list *inputs;
+ struct comment_list *comments;
+
+ /* body */
+ request_url = getenv("REQUEST_URI");
+ if (request_url == NULL)
+ fatal("environment variable 'REQUEST_URI' is not set");
+
+ inputs = input_list_new(request_url);
+ comments = comment_list_new("comment.db");
+ if (inputs != NULL) {
+ comment_write(inputs, "comment.db");
+ printf("Content-Type: text/html\r\n");
+ printf("Status: 302 Found\r\n");
+ printf("Location: %s\r\n", request_url);
+ printf("\r\n");
+ printf("<html><body> Redirecting to <a href='%s'>%s</a></body></html>\n", request_url, request_url);
+ exit(0);
+ }
- http_t* request;
- http_status_t status = HTTP_STATUS_PENDING;
+ if (!header_sent) {
+ puts("Content-Type: text/html\r");
+ puts("Status: 200 OK\r");
+ puts("\r");
+ header_sent = TRUE;
+ }
- puts("Status: 200 OK\r");
- puts("Content-Type: text/html\r");
- puts("\r");
+ /* print header */
+ if ((buffer = read_file("header.html")) == NULL)
+ fatal("read_file: header.html:");
+ printf("%s", buffer);
+ free(buffer);
+ /* get json file to the buffer */
+ http_t* request;
+ http_status_t status = HTTP_STATUS_PENDING;
request = http_get("http://theinterlude.live:8000/status-json.xsl", NULL);
- if (!request) {
- printf("Invalid request.\n");
- return 1;
- }
+ if (request == NULL)
+ fatal("invalid request");
while (status == HTTP_STATUS_PENDING)
status = http_process(request);
if(status == HTTP_STATUS_FAILED)
- {
- printf("HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase);
- http_release(request);
- return 1;
- }
+ fatal("HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase);
buffer = (char*)request->response_data;
+ /* parse json buffer */
json_open_string(&json, buffer);
json_set_streaming(&json, false);
json_handle_object(&json, json_next(&json));
- if (json_get_error(&json)) {
- printf("ERROR: %lu: %s\n", json_get_lineno(&json), json_get_error(&json));
- exit(1);
- }
+ if (json_get_error(&json))
+ fatal("%lu: %s", json_get_lineno(&json), json_get_error(&json));
- /*
- * header
- */
- if ((buffer = read_file("header.html")) == NULL)
- exit(1);
- printf("%s", buffer);
- free(buffer);
-
- /*
- * body
- */
- script_name = getenv("REQUEST_URI");
- if (script_name == NULL) {
- printf("ERROR: environment variable 'REQUEST_URI' is not set.\n");
- return 1;
- }
- if (script_name[strlen(script_name)-1] == '/')
- script_name[strlen(script_name)-1] = '\0';
+ if (request_url[strlen(request_url)-1] == '/')
+ request_url[strlen(request_url)-1] = '\0';
- if ((strlen(script_name) == 0) || (strcmp(script_name, "/stream") == 0)) {
+ if ((strlen(request_url) == 0) || (strcmp(request_url, "/stream") == 0)) {
print_navigation_bar(0);
for (int i = 0; i < sources_length; i++)
source_print(&sources[i]);
- } else if (strcmp(script_name, "/about") == 0) {
+
+ if ((buffer = read_file("comment.html")) == NULL)
+ fatal("read_file: comment.html:");
+ printf("%s", buffer);
+ free(buffer);
+
+ comment_list_print(comments);
+ } else if (strcmp(request_url, "/about") == 0) {
print_navigation_bar(3);
icestats_print(&icestats);
} else {
- puts("<h1>404 Not Found</h1>");
- puts("<p>This is not a joke.</p>");
- return 1;
+ for (int i = 0; i < sources_length; i++) {
+ if (sources[i].url == NULL)
+ continue;
+ if (strcmp(request_url, sources[i].url) == 0) {
+ print_navigation_bar(0);
+ source_print(&sources[i]);
+ goto out;
+ }
+ }
+ not_found();
}
- /*
- * footer
- */
+out:
+ /* comment */
+ /* comment_print("comment.db"); */
+ /* footer */
if ((buffer = read_file("footer.html")) == NULL)
- exit(1);
+ fatal("read_file: footer.html:");
printf("%s", buffer);
free(buffer);
/* cleanup */
+ comment_list_free(comments);
+ input_list_free(inputs);
json_close(&json);
http_release(request);
if (sources != NULL) {
diff --git a/style.css b/style.css
@@ -68,6 +68,11 @@ tr:nth-child(odd) {
border: 1px solid darkgrey;
}
+.stream a {
+ font-size: 20px;
+ font-weight: bold;
+}
+
.stream .player video {
width: 256px;
height: 256px;
@@ -96,3 +101,7 @@ p.footer {
text-align: center;
color: dimgrey;
}
+
+form textarea {
+ width: 100%;
+}
diff --git a/utils.c b/utils.c
@@ -0,0 +1,183 @@
+/*
+ * The author(s) have dedicated this work to the public domain by waiving all
+ * of his or her rights to this work worldwide under copyright and patent law,
+ * including all related and neighboring rights, to the extent allowed by law.
+ * This work is provided 'as-is', without any express or implied warranty.
+ * See COPYING file for details.
+ */
+
+extern int header_sent;
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils.h"
+
+void
+fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!header_sent) {
+ puts("Content-Type: text/html\r");
+ puts("Status: 200 OK\r");
+ puts("\r");
+ header_sent = TRUE;
+ }
+
+ fputs("ERROR: ", stdout);
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fprintf(stdout, " %s\n", strerror(errno));
+ } else {
+ fprintf(stdout, "\n");
+ }
+
+ exit(1);
+}
+
+void *
+emalloc(size_t size)
+{
+ void *p;
+ if ((p = malloc(size)) == NULL)
+ fatal("malloc:");
+ return p;
+}
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+ if ((p = calloc(nmemb, size)) == NULL)
+ fatal("calloc:");
+ return p;
+}
+
+void *
+erealloc(void *ptr, size_t size)
+{
+ void *p;
+ if ((p = realloc(ptr, size)) == NULL)
+ fatal("realloc:");
+ return p;
+}
+
+char *
+read_file(const char *name)
+{
+ char *content;
+ long size;
+ FILE *file;
+
+ file = fopen(name, "rb");
+ if (file == NULL)
+ goto err;
+ if (fseek(file, 0, SEEK_END) != 0)
+ goto err;
+ if ((size = ftell(file)) == -1)
+ goto err;
+ if (fseek(file, 0, SEEK_SET) != 0)
+ goto err;
+ if ((content = malloc((size + 1) * sizeof(char))) == NULL)
+ goto err;
+ if (fread(content, sizeof(char), size, file) != (size_t)size)
+ goto err;
+ fclose(file);
+ (content)[size] = '\0';
+ return content;
+err:
+ fatal("%s:", name);
+ return NULL;
+}
+
+void
+parse_html_str(char *str)
+{
+ char *x, *y, tmp;
+
+ x = y = str;
+ while (*y != '\0') {
+ if (*y == '%') {
+ if (*(y+1) == '\0' || *(y+2) == '\0')
+ fatal("parse_html_str: hex char missing");
+ tmp = *(y+3);
+ *(y+3) = '\0';
+ *x = strtol(y+1, NULL, 16);
+ if (*x != 13) /* ignore carriage return */
+ x++;
+ *(y+3) = tmp;
+ y+=3;
+ } else {
+ *x++ = *y++;
+ }
+ }
+ *x = '\0';
+}
+
+struct input_list *
+input_list_new(char *url)
+{
+ struct input_list *head, *tail;
+ char *name, *value;
+ int done;
+
+ head = tail = NULL;
+ done = FALSE;
+
+ if ((url = strchr(url, '?')) == NULL)
+ return NULL;
+ *url++ = '\0';
+
+ while (!done) {
+ name = url;
+ if ((url = strchr(url, '&')) == NULL)
+ done = TRUE;
+ else
+ *url++ = '\0';
+
+ if ((value = strchr(name, '=')) == NULL)
+ fatal("parse_url: %s: no value", name);
+ *value++ = '\0';
+
+ if (head == NULL) {
+ head = tail = ecalloc(1, sizeof(*tail));
+ } else {
+ tail->next = ecalloc(1, sizeof(*tail));
+ tail = tail->next;
+ }
+ parse_html_str(name);
+ parse_html_str(value);
+ tail->name = strlen(name) == 0 ? NULL : name;
+ tail->value = strlen(value) == 0 ? NULL : value;
+ }
+ return head;
+}
+
+void
+input_list_free(struct input_list *list)
+{
+ struct input_list *it, *itn;
+ for (it = list; it != NULL;) {
+ itn = it->next;
+ free(it);
+ it = itn;
+ }
+}
+
+char *
+input_list_find(struct input_list *list, char *name)
+{
+ struct input_list *it;
+ for (it = list; it != NULL; it = it->next) {
+ if (strcmp(name, it->name) == 0)
+ return it->value;
+ }
+ return NULL;
+}
diff --git a/utils.h b/utils.h
@@ -0,0 +1,35 @@
+/*
+ * The author(s) have dedicated this work to the public domain by waiving all
+ * of his or her rights to this work worldwide under copyright and patent law,
+ * including all related and neighboring rights, to the extent allowed by law.
+ * This work is provided 'as-is', without any express or implied warranty.
+ * See COPYING file for details.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#define TRUE 1
+#define FALSE 0
+
+#include <stddef.h>
+
+struct input_list {
+ char *name;
+ char *value;
+ struct input_list *next;
+};
+
+void fatal(const char *, ...);
+void *emalloc(size_t);
+void *ecalloc(size_t, size_t);
+void *erealloc(void *, size_t);
+char *read_file(const char *);
+void parse_html_str(char *);
+
+struct input_list *
+ input_list_new(char *);
+void input_list_free(struct input_list *);
+char *input_list_find(struct input_list *, char *);
+
+#endif /* UTILS_H */