Compare commits
8 commits
0d552d2cb0
...
600d558b11
Author | SHA1 | Date | |
---|---|---|---|
600d558b11 | |||
b155934701 | |||
4784f83234 | |||
37e0c3a98b | |||
0bbe280e1f | |||
faa262ba95 | |||
68551de565 | |||
de2d15033e |
15 changed files with 494 additions and 13 deletions
|
@ -13,6 +13,7 @@ set(c_simple_http_SOURCES
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/html_cache.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/html_cache.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/static.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/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/linked_list.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c"
|
||||||
|
|
11
Changelog.md
11
Changelog.md
|
@ -2,6 +2,17 @@
|
||||||
|
|
||||||
## Upcoming Changes
|
## Upcoming Changes
|
||||||
|
|
||||||
|
## Version 1.2
|
||||||
|
|
||||||
|
Add the `--generate-dir=<DIR>` option, which will generate all html into the
|
||||||
|
given directory. This requires `--config=<CONFIG_FILE>`.
|
||||||
|
`--generate-enable-overwrite` is required to overwrite existing files when using
|
||||||
|
`--generate-dir=<DIR>`.
|
||||||
|
|
||||||
|
If `--enable-static-dir=<DIR>` is also specified with generate, then the files
|
||||||
|
in the given directory will be copied into the directory specified with
|
||||||
|
`--generate-dir=<DIR>`.
|
||||||
|
|
||||||
## Version 1.1
|
## Version 1.1
|
||||||
|
|
||||||
Some refactoring of code handling parsing the config file.
|
Some refactoring of code handling parsing the config file.
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -44,7 +44,8 @@ HEADERS = \
|
||||||
src/http_template.h \
|
src/http_template.h \
|
||||||
src/helpers.h \
|
src/helpers.h \
|
||||||
src/html_cache.h \
|
src/html_cache.h \
|
||||||
src/static.h
|
src/static.h \
|
||||||
|
src/generate.h
|
||||||
|
|
||||||
SOURCES = \
|
SOURCES = \
|
||||||
src/main.c \
|
src/main.c \
|
||||||
|
@ -59,6 +60,7 @@ SOURCES = \
|
||||||
src/helpers.c \
|
src/helpers.c \
|
||||||
src/html_cache.c \
|
src/html_cache.c \
|
||||||
src/static.c \
|
src/static.c \
|
||||||
|
src/generate.c \
|
||||||
third_party/SimpleArchiver/src/helpers.c \
|
third_party/SimpleArchiver/src/helpers.c \
|
||||||
third_party/SimpleArchiver/src/data_structures/linked_list.c \
|
third_party/SimpleArchiver/src/data_structures/linked_list.c \
|
||||||
third_party/SimpleArchiver/src/data_structures/hash_map.c \
|
third_party/SimpleArchiver/src/data_structures/hash_map.c \
|
||||||
|
|
|
@ -15,6 +15,8 @@ A simple HTTP/1.1 server written in C.
|
||||||
--enable-cache-dir=<DIR>
|
--enable-cache-dir=<DIR>
|
||||||
--cache-entry-lifetime-seconds=<SECONDS>
|
--cache-entry-lifetime-seconds=<SECONDS>
|
||||||
--enable-static-dir=<DIR>
|
--enable-static-dir=<DIR>
|
||||||
|
--generate-dir=<DIR>
|
||||||
|
--generate-enable-overwrite
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ void print_usage(void) {
|
||||||
puts(" --enable-cache-dir=<DIR>");
|
puts(" --enable-cache-dir=<DIR>");
|
||||||
puts(" --cache-entry-lifetime-seconds=<SECONDS>");
|
puts(" --cache-entry-lifetime-seconds=<SECONDS>");
|
||||||
puts(" --enable-static-dir=<DIR>");
|
puts(" --enable-static-dir=<DIR>");
|
||||||
|
puts(" --generate-dir=<DIR>");
|
||||||
|
puts(" --generate-enable-overwrite");
|
||||||
}
|
}
|
||||||
|
|
||||||
Args parse_args(int32_t argc, char **argv) {
|
Args parse_args(int32_t argc, char **argv) {
|
||||||
|
@ -152,6 +154,39 @@ Args parse_args(int32_t argc, char **argv) {
|
||||||
printf("Directory \"%s\" exists.\n", args.static_dir);
|
printf("Directory \"%s\" exists.\n", args.static_dir);
|
||||||
}
|
}
|
||||||
closedir(d);
|
closedir(d);
|
||||||
|
} else if (strncmp(argv[0], "--generate-dir=", 15) == 0) {
|
||||||
|
args.generate_dir = argv[0] + 15;
|
||||||
|
// Check if it actually is an existing directory.
|
||||||
|
DIR *d = opendir(args.generate_dir);
|
||||||
|
if (d == NULL) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
printf(
|
||||||
|
"Directory \"%s\" doesn't exist, creating it...\n",
|
||||||
|
args.generate_dir);
|
||||||
|
int ret = mkdir(
|
||||||
|
args.generate_dir,
|
||||||
|
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||||
|
if (ret == -1) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR Failed to create new directory (errno %d)\n",
|
||||||
|
errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR Failed to open directory \"%s\" (errno %d)!\n",
|
||||||
|
args.generate_dir,
|
||||||
|
errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Directory \"%s\" exists.\n", args.generate_dir);
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
} else if (strcmp(argv[0], "--generate-enable-overwrite") == 0) {
|
||||||
|
args.flags |= 4;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "ERROR: Invalid args!\n");
|
fprintf(stderr, "ERROR: Invalid args!\n");
|
||||||
print_usage();
|
print_usage();
|
||||||
|
|
|
@ -28,6 +28,7 @@ typedef struct Args {
|
||||||
// xxxx xxx1 - disable peer addr print.
|
// xxxx xxx1 - disable peer addr print.
|
||||||
// xxxx xx0x - disable listen on config file for reloading.
|
// xxxx xx0x - disable listen on config file for reloading.
|
||||||
// xxxx xx1x - enable listen on config file for reloading.
|
// xxxx xx1x - enable listen on config file for reloading.
|
||||||
|
// xxxx x1xx - enable overwrite on generate.
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
// Does not need to be free'd, this should point to a string in argv.
|
// Does not need to be free'd, this should point to a string in argv.
|
||||||
|
@ -41,6 +42,9 @@ typedef struct Args {
|
||||||
// Non-NULL if static-dir is specified and files in the dir are to be served.
|
// Non-NULL if static-dir is specified and files in the dir are to be served.
|
||||||
// Does not need to be free'd since it points to a string in argv.
|
// Does not need to be free'd since it points to a string in argv.
|
||||||
const char *static_dir;
|
const char *static_dir;
|
||||||
|
// Non-NULL if generate-dir is specified.
|
||||||
|
// Does not need to be free'd since it points to a string in argv.
|
||||||
|
const char *generate_dir;
|
||||||
} Args;
|
} Args;
|
||||||
|
|
||||||
void print_usage(void);
|
void print_usage(void);
|
||||||
|
|
|
@ -126,7 +126,6 @@ void internal_double_quote_decrement(uint_fast8_t *double_quote_count,
|
||||||
/// Returns non-zero if config should be returned.
|
/// Returns non-zero if config should be returned.
|
||||||
int internal_check_add_value(uint32_t *state,
|
int internal_check_add_value(uint32_t *state,
|
||||||
char **key_buf,
|
char **key_buf,
|
||||||
uint32_t *key_capacity,
|
|
||||||
uint32_t *key_idx,
|
uint32_t *key_idx,
|
||||||
char **value_buf,
|
char **value_buf,
|
||||||
uint32_t *value_capacity,
|
uint32_t *value_capacity,
|
||||||
|
@ -496,7 +495,6 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
|
||||||
} else {
|
} else {
|
||||||
if (internal_check_add_value(&state,
|
if (internal_check_add_value(&state,
|
||||||
&key_buf,
|
&key_buf,
|
||||||
&key_capacity,
|
|
||||||
&key_idx,
|
&key_idx,
|
||||||
&value_buf,
|
&value_buf,
|
||||||
&value_capacity,
|
&value_capacity,
|
||||||
|
@ -520,7 +518,6 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
|
||||||
|
|
||||||
if (internal_check_add_value(&state,
|
if (internal_check_add_value(&state,
|
||||||
&key_buf,
|
&key_buf,
|
||||||
&key_capacity,
|
|
||||||
&key_idx,
|
&key_idx,
|
||||||
&value_buf,
|
&value_buf,
|
||||||
&value_capacity,
|
&value_capacity,
|
||||||
|
|
|
@ -25,6 +25,11 @@ typedef struct C_SIMPLE_HTTP_ParsedConfig {
|
||||||
/// Each entry in this data structure is a hash map where its value for the
|
/// Each entry in this data structure is a hash map where its value for the
|
||||||
/// key "PATH" is the path it represents. The "key" value should match the
|
/// key "PATH" is the path it represents. The "key" value should match the
|
||||||
/// mentioned value for "PATH".
|
/// mentioned value for "PATH".
|
||||||
|
///
|
||||||
|
/// An example mapping for this structure (based on current example config):
|
||||||
|
/// KEY: "/", VALUE: HashMapWrapper struct
|
||||||
|
/// KEY: "/inner", VALUE: HashMapWrapper struct
|
||||||
|
/// KEY: "/inner/further", VALUE: HashMapWrapper struct
|
||||||
union {
|
union {
|
||||||
SDArchiverHashMap *paths;
|
SDArchiverHashMap *paths;
|
||||||
SDArchiverHashMap *hash_map;
|
SDArchiverHashMap *hash_map;
|
||||||
|
|
175
src/generate.c
Normal file
175
src/generate.c
Normal file
|
@ -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 <string.h>
|
||||||
|
|
||||||
|
// Linux/Unix includes.
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Local includes.
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "http_template.h"
|
||||||
|
|
||||||
|
// Third party includes.
|
||||||
|
#include <SimpleArchiver/src/helpers.h>
|
||||||
|
#include <SimpleArchiver/src/data_structures/hash_map.h>
|
||||||
|
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
30
src/generate.h
Normal file
30
src/generate.h
Normal file
|
@ -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 <stdlib.h>
|
||||||
|
|
||||||
|
/// 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
|
|
@ -24,7 +24,6 @@
|
||||||
// libc includes.
|
// libc includes.
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
|
@ -258,4 +257,11 @@ int c_simple_http_helper_mkdir_tree(const char *path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void c_simple_http_cleanup_DIR(DIR **fd) {
|
||||||
|
if (fd && *fd) {
|
||||||
|
closedir(*fd);
|
||||||
|
*fd = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// vim: et ts=2 sts=2 sw=2
|
// vim: et ts=2 sts=2 sw=2
|
||||||
|
|
|
@ -21,9 +21,24 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// libc includes.
|
||||||
|
#include <time.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
// Local includes.
|
||||||
|
#include "config.h"
|
||||||
|
#include "arg_parse.h"
|
||||||
|
|
||||||
// Third-party includes.
|
// Third-party includes.
|
||||||
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
||||||
|
|
||||||
|
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 {
|
typedef struct C_SIMPLE_HTTP_String_Part {
|
||||||
char *buf;
|
char *buf;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
@ -63,6 +78,8 @@ char *c_simple_http_helper_unescape_uri(const char *uri);
|
||||||
/// Other return values are errors.
|
/// Other return values are errors.
|
||||||
int c_simple_http_helper_mkdir_tree(const char *dirpath);
|
int c_simple_http_helper_mkdir_tree(const char *dirpath);
|
||||||
|
|
||||||
|
void c_simple_http_cleanup_DIR(DIR **fd);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// vim: et ts=2 sts=2 sw=2
|
// vim: et ts=2 sts=2 sw=2
|
||||||
|
|
39
src/main.c
39
src/main.c
|
@ -33,13 +33,17 @@
|
||||||
|
|
||||||
// Third party includes.
|
// Third party includes.
|
||||||
#include <SimpleArchiver/src/helpers.h>
|
#include <SimpleArchiver/src/helpers.h>
|
||||||
|
#include <SimpleArchiver/src/data_structures/hash_map.h>
|
||||||
|
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
||||||
|
|
||||||
// Local includes.
|
// Local includes.
|
||||||
#include "arg_parse.h"
|
#include "arg_parse.h"
|
||||||
#include "big_endian.h"
|
#include "big_endian.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "http_template.h"
|
||||||
#include "tcp_socket.h"
|
#include "tcp_socket.h"
|
||||||
#include "signal_handling.h"
|
#include "signal_handling.h"
|
||||||
|
#include "generate.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
@ -74,13 +78,6 @@ typedef struct ConnectionItem {
|
||||||
struct in6_addr peer_addr;
|
struct in6_addr peer_addr;
|
||||||
} ConnectionItem;
|
} 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) {
|
void c_simple_http_cleanup_connection_item(void *data) {
|
||||||
ConnectionItem *citem = data;
|
ConnectionItem *citem = data;
|
||||||
if (citem) {
|
if (citem) {
|
||||||
|
@ -348,6 +345,34 @@ int main(int argc, char **argv) {
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If generate-dir is specified, the program must stop after generating or
|
||||||
|
// failure.
|
||||||
|
if (args.generate_dir) {
|
||||||
|
ConnectionContext ctx;
|
||||||
|
ctx.args = &args;
|
||||||
|
ctx.parsed = &parsed_config;
|
||||||
|
printf("Generating html files to \"%s\"...\n", args.generate_dir);
|
||||||
|
if (simple_archiver_hash_map_iter(parsed_config.paths,
|
||||||
|
c_simple_http_generate_paths_fn,
|
||||||
|
&ctx)) {
|
||||||
|
fprintf(stderr, "ERROR during generating!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("Finished generating.");
|
||||||
|
if (args.static_dir) {
|
||||||
|
puts("Static dir option specified, copying over static dir entries...");
|
||||||
|
if (c_simple_http_static_copy_over_dir(args.static_dir,
|
||||||
|
args.generate_dir,
|
||||||
|
(args.flags & 4) != 0 ? 1 : 0)
|
||||||
|
!= 0) {
|
||||||
|
fprintf(stderr, "ERROR during static-dir-entires copying!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("Finished copying over static-dir files.");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((cleanup(cleanup_tcp_socket))) int tcp_socket =
|
__attribute__((cleanup(cleanup_tcp_socket))) int tcp_socket =
|
||||||
create_tcp_socket(args.port);
|
create_tcp_socket(args.port);
|
||||||
if (tcp_socket == -1) {
|
if (tcp_socket == -1) {
|
||||||
|
|
168
src/static.c
168
src/static.c
|
@ -31,10 +31,17 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
// Third party includes.
|
// Third party includes.
|
||||||
|
#include "SimpleArchiver/src/data_structures/linked_list.h"
|
||||||
#include "SimpleArchiver/src/helpers.h"
|
#include "SimpleArchiver/src/helpers.h"
|
||||||
|
|
||||||
|
// Local includes.
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
char **environ;
|
char **environ;
|
||||||
|
|
||||||
void internal_fd_cleanup_helper(int *fd) {
|
void internal_fd_cleanup_helper(int *fd) {
|
||||||
|
@ -180,7 +187,9 @@ C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
|
||||||
|
|
||||||
if (fd == NULL) {
|
if (fd == NULL) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr, "WARNING Failed to open path \"%s\" in static dir!\n", path + idx);
|
stderr,
|
||||||
|
"WARNING Failed to open path \"%s\" in static dir!\n",
|
||||||
|
path + idx);
|
||||||
file_info.result = STATIC_FILE_RESULT_404NotFound;
|
file_info.result = STATIC_FILE_RESULT_404NotFound;
|
||||||
return file_info;
|
return file_info;
|
||||||
}
|
}
|
||||||
|
@ -331,4 +340,161 @@ int c_simple_http_static_validate_path(const char *path) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int c_simple_http_static_copy_over_dir(const char *from,
|
||||||
|
const char *to,
|
||||||
|
uint_fast8_t overwrite_enabled) {
|
||||||
|
__attribute__((cleanup(c_simple_http_cleanup_DIR)))
|
||||||
|
DIR *from_fd = opendir(from);
|
||||||
|
if (!from_fd) {
|
||||||
|
fprintf(stderr, "ERROR Failed to open directory \"%s\"!\n", from);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long from_len = strlen(from);
|
||||||
|
const unsigned long to_len = strlen(to);
|
||||||
|
|
||||||
|
struct dirent *dir_entry = NULL;
|
||||||
|
do {
|
||||||
|
dir_entry = readdir(from_fd);
|
||||||
|
if (!dir_entry) {
|
||||||
|
break;
|
||||||
|
} else if (strcmp(dir_entry->d_name, ".") == 0
|
||||||
|
|| strcmp(dir_entry->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (dir_entry->d_type == DT_DIR) {
|
||||||
|
// Dir entry is a directory.
|
||||||
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
|
SDArchiverLinkedList *string_parts = simple_archiver_list_init();
|
||||||
|
c_simple_http_add_string_part(string_parts, from, 0);
|
||||||
|
if (from_len > 0 && from[from_len - 1] != '/') {
|
||||||
|
c_simple_http_add_string_part(string_parts, "/", 0);
|
||||||
|
}
|
||||||
|
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *combined_from = c_simple_http_combine_string_parts(string_parts);
|
||||||
|
|
||||||
|
simple_archiver_list_free(&string_parts);
|
||||||
|
string_parts = simple_archiver_list_init();
|
||||||
|
c_simple_http_add_string_part(string_parts, to, 0);
|
||||||
|
if (to_len > 0 && to[to_len - 1] != '/') {
|
||||||
|
c_simple_http_add_string_part(string_parts, "/", 0);
|
||||||
|
}
|
||||||
|
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *combined_to = c_simple_http_combine_string_parts(string_parts);
|
||||||
|
|
||||||
|
int ret = c_simple_http_static_copy_over_dir(combined_from,
|
||||||
|
combined_to,
|
||||||
|
overwrite_enabled);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else if (dir_entry->d_type == DT_REG) {
|
||||||
|
// Dir entry is a file.
|
||||||
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
|
SDArchiverLinkedList *string_parts = simple_archiver_list_init();
|
||||||
|
c_simple_http_add_string_part(string_parts, from, 0);
|
||||||
|
if (from_len > 0 && from[from_len - 1] != '/') {
|
||||||
|
c_simple_http_add_string_part(string_parts, "/", 0);
|
||||||
|
}
|
||||||
|
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *combined_from = c_simple_http_combine_string_parts(string_parts);
|
||||||
|
|
||||||
|
simple_archiver_list_free(&string_parts);
|
||||||
|
string_parts = simple_archiver_list_init();
|
||||||
|
c_simple_http_add_string_part(string_parts, to, 0);
|
||||||
|
if (to_len > 0 && to[to_len - 1] != '/') {
|
||||||
|
c_simple_http_add_string_part(string_parts, "/", 0);
|
||||||
|
}
|
||||||
|
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *combined_to = c_simple_http_combine_string_parts(string_parts);
|
||||||
|
|
||||||
|
if (!overwrite_enabled) {
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *fd = fopen(combined_to, "rb");
|
||||||
|
if (fd) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING \"%s\" already exists and --generate-enable-overwrite not "
|
||||||
|
"specified, skipping!\n",
|
||||||
|
combined_to);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *combined_to_dup = strdup(combined_to);
|
||||||
|
char *combined_to_dirname = dirname(combined_to_dup);
|
||||||
|
|
||||||
|
int ret = c_simple_http_helper_mkdir_tree(combined_to_dirname);
|
||||||
|
if (ret != 0 && ret != 1) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR Failed to create directory \"%s\"!\n",
|
||||||
|
combined_to_dirname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *from_file_fd = fopen(combined_from, "rb");
|
||||||
|
if (!from_file_fd) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR Failed to open file \"%s\" for reading!\n",
|
||||||
|
combined_from);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *to_file_fd = fopen(combined_to, "wb");
|
||||||
|
if (!to_file_fd) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR Failed to open file \"%s\" for writing!\n",
|
||||||
|
combined_to);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *buf[1024];
|
||||||
|
size_t fread_ret;
|
||||||
|
unsigned long fwrite_ret;
|
||||||
|
while (!feof(from_file_fd)
|
||||||
|
&& !ferror(from_file_fd)
|
||||||
|
&& !ferror(to_file_fd)) {
|
||||||
|
fread_ret = fread(buf, 1, 1024, from_file_fd);
|
||||||
|
if (fread_ret > 0) {
|
||||||
|
fwrite_ret = fwrite(buf, 1, fread_ret, to_file_fd);
|
||||||
|
if (fwrite_ret < fread_ret) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR Writing to file \"%s\" (not all bytes written)!\n",
|
||||||
|
combined_to);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ferror(from_file_fd)) {
|
||||||
|
fprintf(stderr, "ERROR Reading from file \"%s\"!\n", combined_from);
|
||||||
|
return 1;
|
||||||
|
} else if (ferror(to_file_fd)) {
|
||||||
|
fprintf(stderr, "ERROR Writing to file \"%s\"!\n", combined_to);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s -> %s\n", combined_from, combined_to);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING Non-dir and non-file \"%s/%s\", skipping...\n",
|
||||||
|
from,
|
||||||
|
dir_entry->d_name);
|
||||||
|
}
|
||||||
|
} while (dir_entry != NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// vim: et ts=2 sts=2 sw=2
|
// vim: et ts=2 sts=2 sw=2
|
||||||
|
|
|
@ -51,6 +51,11 @@ C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
|
||||||
/// Returns zero if OK.
|
/// Returns zero if OK.
|
||||||
int c_simple_http_static_validate_path(const char *path);
|
int c_simple_http_static_validate_path(const char *path);
|
||||||
|
|
||||||
|
/// Copies all files in "from" into "to". Returns non-zero on failure.
|
||||||
|
int c_simple_http_static_copy_over_dir(const char *from,
|
||||||
|
const char *to,
|
||||||
|
uint_fast8_t overwrite_enabled);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// vim: et ts=2 sts=2 sw=2
|
// vim: et ts=2 sts=2 sw=2
|
||||||
|
|
Loading…
Reference in a new issue