radio

radio.ircforever.org
git clone git://git.ircforever.org/radio
Log | Files | Refs | Submodules | README | LICENSE

commit 5080eb62ec43e44094bd57a060126b577eb1ba50
parent 16583646ecfddbe6bef5999075c7f758bbd8abb2
Author: libredev <libredev@ircforever.org>
Date:   Sun,  8 Jan 2023 15:54:38 +0530

add commenting system prototype

Diffstat:
MMakefile | 14+++++++-------
MREADME | 6+++++-
Acomment.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acomment.db | 8++++++++
Acomment.h | 27+++++++++++++++++++++++++++
Acomment.html | 16++++++++++++++++
Mmain.c | 199+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mstyle.css | 9+++++++++
Autils.c | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autils.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 */