diff --git a/CMakeLists.txt b/CMakeLists.txt index 0365112..3dd5743 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set(c_simple_http_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/html_cache.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/static.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/generate.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/helpers.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/linked_list.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c" diff --git a/Makefile b/Makefile index f6c80a8..492fd58 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,8 @@ HEADERS = \ src/http_template.h \ src/helpers.h \ src/html_cache.h \ - src/static.h + src/static.h \ + src/generate.h SOURCES = \ src/main.c \ @@ -59,6 +60,7 @@ SOURCES = \ src/helpers.c \ src/html_cache.c \ src/static.c \ + src/generate.c \ third_party/SimpleArchiver/src/helpers.c \ third_party/SimpleArchiver/src/data_structures/linked_list.c \ third_party/SimpleArchiver/src/data_structures/hash_map.c \ diff --git a/src/generate.c b/src/generate.c new file mode 100644 index 0000000..1909071 --- /dev/null +++ b/src/generate.c @@ -0,0 +1,175 @@ +// ISC License +// +// Copyright (c) 2024 Stephen Seo +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "generate.h" + +// Standard library includes. +#include + +// Linux/Unix includes. +#include +#include +#include +#include +#include + +// Local includes. +#include "helpers.h" +#include "http_template.h" + +// Third party includes. +#include +#include +#include + +int c_simple_http_generate_paths_fn(const void *key, + size_t key_size, + __attribute__((unused)) const void *value, + void *ud) { + const char *path = key; + const ConnectionContext *ctx = ud; + const char *generate_dir = ctx->args->generate_dir; + + const unsigned long path_len = key_size - 1; + const unsigned long generate_dir_len = strlen(generate_dir); + + __attribute__((cleanup(simple_archiver_list_free))) + SDArchiverLinkedList *string_parts = simple_archiver_list_init(); + + // Add generate_dir as first path of paths to join. + c_simple_http_add_string_part(string_parts, generate_dir, 0); + + // Ensure next character after generate_dir contains a '/' if generate_dir + // didn't contain one at the end. + if (generate_dir_len > 0 && generate_dir[generate_dir_len - 1] != '/') { + c_simple_http_add_string_part(string_parts, "/", 0); + } + + // Append the path. + if (strcmp(path, "/") != 0) { + // Is not root. + uint32_t idx = 0; + while (idx <= path_len && path[idx] == '/') { + ++idx; + } + c_simple_http_add_string_part(string_parts, path + idx, 0); + } + + // Add the final '/'. + if (path_len > 0 && path[path_len - 1] != '/') { + c_simple_http_add_string_part(string_parts, "/", 0); + } + + // Add the ending "index.html". + c_simple_http_add_string_part(string_parts, "index.html", 0); + + // Get the combined string. + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *generated_path = c_simple_http_combine_string_parts(string_parts); + if (!generated_path) { + fprintf(stderr, "ERROR Failed to get generated path (path: %s)!\n", path); + return 1; + } + + if ((ctx->args->flags & 4) == 0) { + // Overwrite not enabled, check if file already exists. + FILE *fd = fopen(generated_path, "rb"); + if (fd) { + fclose(fd); + fprintf( + stderr, + "WARNING Path \"%s\" exists and \"--generate-enable-overwrite\" not " + "specified, skipping!\n", + generated_path); + return 0; + } + } + + // Ensure the required dirs exist. + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *generated_path_dup = strdup(generated_path); + + uint_fast8_t did_make_generated_path_dir = 0; + char *generated_path_dir = dirname(generated_path_dup); + if (generated_path_dir) { + DIR *fd = opendir(generated_path_dir); + if (!fd) { + if (errno == ENOENT) { + c_simple_http_helper_mkdir_tree(generated_path_dir); + did_make_generated_path_dir = 1; + } else { + fprintf(stderr, + "ERROR opendir on path dirname failed unexpectedly (path: %s)!" + "\n", + path); + return 1; + } + } else { + // Directory already exists. + closedir(fd); + } + } else { + fprintf(stderr, + "ERROR Failed to get dirname of generated path dir (path: %s)" + "!\n", + path); + return 1; + } + + // Generate the html. + size_t html_buf_size = 0; + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *html_buf = c_simple_http_path_to_generated(path, + ctx->parsed, + &html_buf_size, + NULL); + if (!html_buf || html_buf_size == 0) { + fprintf(stderr, + "WARNING Failed to generate html for generate (path: %s), " + "skipping!\n", + path); + if (did_make_generated_path_dir) { + if (rmdir(generated_path_dir) == -1) { + fprintf(stderr, + "WARNING rmdir on generated_path_dir failed, errno: %d\n", + errno); + } + } + return 0; + } + + // Save the html. + FILE *fd = fopen(generated_path, "wb"); + if (!fd) { + fprintf(stderr, + "WARNING Failed to open \"%s\" for writing, skipping!\n", + generated_path); + return 0; + } + unsigned long fwrite_ret = fwrite(html_buf, 1, html_buf_size, fd); + if (fwrite_ret < html_buf_size) { + fclose(fd); + unlink(generated_path); + fprintf(stderr, + "ERROR Unable to write entirely to \"%s\"!\n", + generated_path); + return 1; + } else { + fclose(fd); + } + + return 0; +} diff --git a/src/generate.h b/src/generate.h new file mode 100644 index 0000000..9f5261a --- /dev/null +++ b/src/generate.h @@ -0,0 +1,30 @@ +// ISC License +// +// Copyright (c) 2024 Stephen Seo +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_GENERATE_H_ +#define SEODISPARATE_COM_C_SIMPLE_HTTP_GENERATE_H_ + +#include + +/// See this function's usage in main.c. +int c_simple_http_generate_paths_fn(const void *key, + size_t key_size, + const void *value, + void *ud); + +#endif + +// vim: et ts=2 sts=2 sw=2 diff --git a/src/helpers.h b/src/helpers.h index 5e1bd9f..a428388 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -21,9 +21,23 @@ #include #include +// libc includes. +#include + +// Local includes. +#include "config.h" +#include "arg_parse.h" + // Third-party includes. #include +typedef struct ConnectionContext { + char *buf; + const Args *args; + C_SIMPLE_HTTP_ParsedConfig *parsed; + struct timespec current_time; +} ConnectionContext; + typedef struct C_SIMPLE_HTTP_String_Part { char *buf; size_t size; diff --git a/src/main.c b/src/main.c index 1f8466e..988f67a 100644 --- a/src/main.c +++ b/src/main.c @@ -21,10 +21,7 @@ #include // Linux/Unix includes. -#include #include -#include -#include #include #include #include @@ -46,6 +43,7 @@ #include "http_template.h" #include "tcp_socket.h" #include "signal_handling.h" +#include "generate.h" #include "globals.h" #include "constants.h" #include "http.h" @@ -80,13 +78,6 @@ typedef struct ConnectionItem { struct in6_addr peer_addr; } ConnectionItem; -typedef struct ConnectionContext { - char *buf; - const Args *args; - C_SIMPLE_HTTP_ParsedConfig *parsed; - struct timespec current_time; -} ConnectionContext; - void c_simple_http_cleanup_connection_item(void *data) { ConnectionItem *citem = data; if (citem) { @@ -332,145 +323,6 @@ int c_simple_http_manage_connections(void *data, void *ud) { return 1; } -int generate_paths_fn(const void *key, - size_t key_size, - const void *value, - void *ud) { - const char *path = key; - const ConnectionContext *ctx = ud; - const char *generate_dir = ctx->args->generate_dir; - - const unsigned long path_len = strlen(path); - const unsigned long generate_dir_len = strlen(generate_dir); - - __attribute__((cleanup(simple_archiver_list_free))) - SDArchiverLinkedList *string_parts = simple_archiver_list_init(); - - // Add generate_dir as first path of paths to join. - c_simple_http_add_string_part(string_parts, generate_dir, 0); - - // Ensure next character after generate_dir contains a '/' if generate_dir - // didn't contain one at the end. - if (generate_dir_len > 0 && generate_dir[generate_dir_len - 1] != '/') { - c_simple_http_add_string_part(string_parts, "/", 0); - } - - // Append the path. - if (strcmp(path, "/") != 0) { - // Is not root. - uint32_t idx = 0; - while (idx <= path_len && path[idx] == '/') { - ++idx; - } - c_simple_http_add_string_part(string_parts, path + idx, 0); - } - - // Add the final '/'. - if (path_len > 0 && path[path_len - 1] != '/') { - c_simple_http_add_string_part(string_parts, "/", 0); - } - - // Add the ending "index.html". - c_simple_http_add_string_part(string_parts, "index.html", 0); - - // Get the combined string. - __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) - char *generated_path = c_simple_http_combine_string_parts(string_parts); - if (!generated_path) { - fprintf(stderr, "ERROR Failed to get generated path (path: %s)!\n", path); - return 1; - } - - if ((ctx->args->flags & 4) == 0) { - // Overwrite not enabled, check if file already exists. - FILE *fd = fopen(generated_path, "rb"); - if (fd) { - fclose(fd); - fprintf( - stderr, - "WARNING Path \"%s\" exists and \"--generate-enable-overwrite\" not " - "specified, skipping!\n", - generated_path); - return 0; - } - } - - // Ensure the required dirs exist. - __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) - char *generated_path_dup = strdup(generated_path); - - uint_fast8_t did_make_generated_path_dir = 0; - char *generated_path_dir = dirname(generated_path_dup); - if (generated_path_dir) { - DIR *fd = opendir(generated_path_dir); - if (!fd) { - if (errno == ENOENT) { - c_simple_http_helper_mkdir_tree(generated_path_dir); - did_make_generated_path_dir = 1; - } else { - fprintf(stderr, - "ERROR opendir on path dirname failed unexpectedly (path: %s)!" - "\n", - path); - return 1; - } - } else { - // Directory already exists. - closedir(fd); - } - } else { - fprintf(stderr, - "ERROR Failed to get dirname of generated path dir (path: %s)" - "!\n", - path); - return 1; - } - - // Generate the html. - size_t html_buf_size = 0; - __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) - char *html_buf = c_simple_http_path_to_generated(path, - ctx->parsed, - &html_buf_size, - NULL); - if (!html_buf || html_buf_size == 0) { - fprintf(stderr, - "WARNING Failed to generate html for generate (path: %s), " - "skipping!\n", - path); - if (did_make_generated_path_dir) { - if (rmdir(generated_path_dir) == -1) { - fprintf(stderr, - "WARNING rmdir on generated_path_dir failed, errno: %d\n", - errno); - } - } - return 0; - } - - // Save the html. - FILE *fd = fopen(generated_path, "wb"); - if (!fd) { - fprintf(stderr, - "WARNING Failed to open \"%s\" for writing, skipping!\n", - generated_path); - return 0; - } - unsigned long fwrite_ret = fwrite(html_buf, 1, html_buf_size, fd); - if (fwrite_ret < html_buf_size) { - fclose(fd); - unlink(generated_path); - fprintf(stderr, - "ERROR Unable to write entirely to \"%s\"!\n", - generated_path); - return 1; - } else { - fclose(fd); - } - - return 0; -} - int main(int argc, char **argv) { __attribute__((cleanup(c_simple_http_free_args))) Args args = parse_args(argc, argv); @@ -500,7 +352,7 @@ int main(int argc, char **argv) { ctx.args = &args; ctx.parsed = &parsed_config; if (simple_archiver_hash_map_iter(parsed_config.paths, - generate_paths_fn, + c_simple_http_generate_paths_fn, &ctx)) { fprintf(stderr, "ERROR during generating!\n"); return 1;