From 94c45e3c4d185b3f0d70f0d2d761b72c6561e1b5 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 14 Jul 2020 06:55:18 +0200 Subject: Implement add_html --- build.sh | 9 +- src/fileutils.c | 3 +- src/html.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/html.h | 6 ++ src/main.c | 18 +++- src/program.c | 175 +++++++++++++++++++++++++-------- src/program.h | 5 +- src/rss.c | 174 +++------------------------------ src/rss_html_common.c | 158 ++++++++++++++++++++++++++++++ src/rss_html_common.h | 6 ++ 10 files changed, 605 insertions(+), 213 deletions(-) create mode 100644 src/html.c create mode 100644 src/html.h create mode 100644 src/rss_html_common.c create mode 100644 src/rss_html_common.h diff --git a/build.sh b/build.sh index 1db2702..9ecbe97 100755 --- a/build.sh +++ b/build.sh @@ -1,8 +1,5 @@ #!/bin/sh -CFLAGS="-O0 -g3 -Wall -Wextra -Werror" -#CFLAGS="-O3 -s -flto" -#LIBS="-lcurl" -LIBS="" -#gcc -musl-gcc -static src/main.c src/program.c src/alloc.c src/buffer.c src/fileutils.c src/transmission.c src/rss.c src/download.c src/stringutils.c -o automedia $CFLAGS $LIBS +CFLAGS="-O3 -s -flto" +[ -z "$RELEASE" ] && CFLAGS="-O0 -g3 -Wall -Wextra -Werror"; +musl-gcc -static src/main.c src/program.c src/alloc.c src/buffer.c src/fileutils.c src/transmission.c src/rss.c src/html.c src/rss_html_common.c src/download.c src/stringutils.c -o automedia $CFLAGS diff --git a/src/fileutils.c b/src/fileutils.c index fe9ab88..64c7a48 100644 --- a/src/fileutils.c +++ b/src/fileutils.c @@ -83,8 +83,7 @@ int create_directory_recursive(char *path) { } int file_exists(const char *path) { - struct stat st; - return stat(path, &st); + return access(path, F_OK); } int create_lock_file(const char *path) { diff --git a/src/html.c b/src/html.c new file mode 100644 index 0000000..de888a0 --- /dev/null +++ b/src/html.c @@ -0,0 +1,264 @@ +#include "html.h" +#include "fileutils.h" +#include "program.h" +#include "buffer.h" +#include "stringutils.h" +#include "rss_html_common.h" +#include "json.h" +#include +#include +#include +#include +#include +#include + +static int str_starts_with(const char *str, int len, const char *substr, int substr_len) { + return len >= substr_len && memcmp(str, substr, substr_len) == 0; +} + +static int url_extract_domain(const char *url, char *domain, int domain_len) { + int url_len = strlen(url); + if(str_starts_with(url, url_len, "http://", 7)) { + url += 7; + url_len -= 7; + } else if(str_starts_with(url, url_len, "https://", 8)) { + url += 8; + url_len -= 8; + } + + if(str_starts_with(url, url_len, "www.", 4)) { + url += 4; + url_len -= 4; + } + + char *end = strchr(url, '.'); + if(end) { + int len = end - url; + if(len >= domain_len) + return -1; + memcpy(domain, url, len); + domain[len] = '\0'; + } else { + if(url_len >= domain_len) + return -1; + memcpy(domain, url, url_len); + domain[url_len] = '\0'; + } + return 0; +} + +static struct json_value_s* json_object_get_field_by_name(struct json_object_s *json_obj, const char *name) { + struct json_object_element_s *obj_element = json_obj->start; + while(obj_element) { + if(strcmp(obj_element->name->string, name) == 0) + return obj_element->value; + obj_element = obj_element->next; + } + return NULL; +} + +typedef int (*PluginListCallback)(const char *name, const char *url, void *userdata); +static struct json_value_s* plugin_list(char *plugin_filepath, const char *url, const char *latest, PluginListCallback callback, void *userdata) { + (void)latest; + int result; + Buffer buffer; + buffer_init(&buffer); + + const char *args[] = { plugin_filepath, "list", url, NULL }; + int process_id = -1; + int stdin_file = -1; + int stdout_file = -1; + result = program_exec_async(args, &process_id, &stdin_file, &stdout_file); + if(result != 0) { + fprintf(stderr, "Failed to launch plugin list for plugin %s\n", basename(plugin_filepath)); + goto err_cleanup; + } + + result = program_wait_until_exit(process_id, stdin_file, stdout_file, program_buffer_write_callback, &buffer); + if(result != 0) { + fprintf(stderr, "Failed to launch plugin list for plugin %s\n", basename(plugin_filepath)); + goto err_cleanup; + } + + /*fprintf(stderr, "plugin list: %s\n", (char*)buffer.data);*/ + + struct json_value_s *json_root = json_parse(buffer.data, buffer.size); + if(!json_root) { + fprintf(stderr, "Failed to load plugin %s list output as json\n", basename(plugin_filepath)); + goto err_cleanup; + } + + struct json_array_s *json_root_array = json_value_as_array(json_root); + if(!json_root_array) { + fprintf(stderr, "Failed to load plugin %s list output as json\n", basename(plugin_filepath)); + free(json_root); + goto err_cleanup; + } + + struct json_array_element_s *json_array_element = json_root_array->start; + for(; json_array_element; json_array_element = json_array_element->next) { + struct json_object_s *array_element_value = json_value_as_object(json_array_element->value); + if(!array_element_value) + continue; + + struct json_value_s *name_json = json_object_get_field_by_name(array_element_value, "name"); + struct json_value_s *url_json = json_object_get_field_by_name(array_element_value, "url"); + struct json_string_s *name_json_str = json_value_as_string(name_json); + struct json_string_s *url_json_str = json_value_as_string(url_json); + if(!name_json_str || !url_json_str) + continue; + + /* Cast from const to mutable variable because I can */ + char *name = (char*)name_json_str->string; + string_replace(name, '/', '_'); + name = strip(name); + if(callback(name, url_json_str->string, userdata) != 0) + break; + } + + buffer_deinit(&buffer); + return json_root; + + err_cleanup: + buffer_deinit(&buffer); + return NULL; +} + +typedef struct { + const char *start_after; + int found_start_after; + const char *start_after_url; +} PluginListUserdata; + +static int plugin_list_callback(const char *name, const char *url, void *userdata) { + /*fprintf(stderr, "name: |%s|, url: |%s|\n", name, url);*/ + PluginListUserdata *plugin_list_userdata = userdata; + if(plugin_list_userdata->start_after && strcmp(plugin_list_userdata->start_after, name) == 0) { + plugin_list_userdata->found_start_after = 1; + plugin_list_userdata->start_after_url = url; + return 1; + } + return 0; +} + +int add_html(const char *name, const char *url, char *html_config_dir, char *program_dir, const char *start_after) { + int result = 0; + + if(!name || name[0] == '\0') { + fprintf(stderr, "Name not provided or empty\n"); + return -1; + } + + char domain[2086]; + if(url_extract_domain(url, domain, sizeof(domain)) != 0) { + fprintf(stderr, "Url %s is too long\n", url); + return -1; + } + + if(domain[0] == '\0') { + fprintf(stderr, "Invalid url: %s\n", url); + return -1; + } + + const char *path_components[] = { program_dir, "plugins" }; + char domain_plugin_path[PATH_MAX]; + path_join(domain_plugin_path, path_components, 2); + if(file_exists(domain_plugin_path) != 0) { + strcpy(domain_plugin_path, "/usr/share/automedia/plugins"); + if(file_exists(domain_plugin_path) != 0) { + fprintf(stderr, "Failed to find plugins directory\n"); + return -1; + } + } + + strcat(domain_plugin_path, "/"); + strcat(domain_plugin_path, domain); + if(file_exists(domain_plugin_path) != 0) { + strcat(domain_plugin_path, ".py"); + if(file_exists(domain_plugin_path) != 0) { + fprintf(stderr, "Plugin doesn't exist: %s\n", domain); + return -1; + } + } + + PluginListUserdata plugin_list_userdata; + plugin_list_userdata.start_after = start_after; + plugin_list_userdata.found_start_after = 0; + plugin_list_userdata.start_after_url = NULL; + + struct json_value_s *json_root = plugin_list(domain_plugin_path, url, NULL, plugin_list_callback, &plugin_list_userdata); + if(!json_root) + return -1; + + if(start_after && !plugin_list_userdata.found_start_after) { + fprintf(stderr, "Failed to find %s in html %s\n", start_after, url); + result = -1; + goto cleanup; + } + + char *html_tracked_dir = html_config_dir; + strcat(html_tracked_dir, "/tracked/"); + strcat(html_tracked_dir, name); + + if(file_exists(html_tracked_dir) == 0) { + fprintf(stderr, "You are already tracking %s\n", url); + result = -1; + goto cleanup; + } + + result = create_directory_recursive(html_tracked_dir); + if(result != 0) { + fprintf(stderr, "Failed to create %s, error: %s\n", html_tracked_dir, strerror(result)); + goto cleanup; + } + + /* + Create an ".in_progress" file to prevent periodic sync from reading rss data + before we have finished adding all the data. + */ + char in_progress_filepath[PATH_MAX]; + strcpy(in_progress_filepath, html_tracked_dir); + strcat(in_progress_filepath, "/.in_progress"); + result = create_lock_file(in_progress_filepath); + if(result != 0) { + fprintf(stderr, "Failed to create %s/.in_progress\n", html_tracked_dir); + goto cleanup; + } + + result = file_overwrite_in_dir(html_tracked_dir, "link", url, strlen(url)); + if(result != 0) { + fprintf(stderr, "Failed to create %s/link\n", html_tracked_dir); + remove(html_tracked_dir); + goto cleanup; + } + + char *plugin_name = basename(domain_plugin_path); + result = file_overwrite_in_dir(html_tracked_dir, "plugin", plugin_name, strlen(plugin_name)); + if(result != 0) { + fprintf(stderr, "Failed to create %s/link\n", html_tracked_dir); + remove(html_tracked_dir); + goto cleanup; + } + + char updated[32]; + sprintf(updated, "%ld", time(NULL)); + result = file_overwrite_in_dir(html_tracked_dir, "updated", updated, strlen(updated)); + if(result != 0) { + fprintf(stderr, "Failed to create %s/updated\n", html_tracked_dir); + remove(html_tracked_dir); + goto cleanup; + } + + result = write_plugin_json_to_file(html_tracked_dir, "data", url, updated, start_after, plugin_list_userdata.start_after_url, plugin_name); + if(result != 0) { + fprintf(stderr, "Failed to create %s/data\n", html_tracked_dir); + remove(html_tracked_dir); + goto cleanup; + } + + remove(in_progress_filepath); + + cleanup: + free(json_root); + return result; +} diff --git a/src/html.h b/src/html.h new file mode 100644 index 0000000..02c0f7b --- /dev/null +++ b/src/html.h @@ -0,0 +1,6 @@ +#ifndef HTML_H +#define HTML_H + +int add_html(const char *name, const char *url, char *html_config_dir, char *program_dir, const char *start_after); + +#endif diff --git a/src/main.c b/src/main.c index 11b2a5d..bc554b5 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include "fileutils.h" #include "stringutils.h" #include "rss.h" +#include "html.h" #include "json.h" #include @@ -12,6 +13,7 @@ #include #include +#include #define NAME_MAX_LEN 250 @@ -136,8 +138,7 @@ static void data_file_get_downloaded(const char *dir_name, const char *data_file } cleanup: - if(json_root) - free(json_root); + free(json_root); free(file_data); } @@ -185,7 +186,7 @@ static int compare_downloaded_item(const void *a, const void *b) { return list_data_a->timestamp - list_data_b->timestamp; } -static void command_add(int argc, char **argv, char *rss_config_dir, char *html_config_dir) { +static void command_add(int argc, char **argv, char *rss_config_dir, char *html_config_dir, char *program_dir) { if(argc < 2) usage_add(); @@ -246,7 +247,14 @@ static void command_add(int argc, char **argv, char *rss_config_dir, char *html_ if(add_rss(media_name, media_url, rss_config_dir, start_after) != 0) exit(1); } else if(strcmp(media_type, "html") == 0) { - (void)html_config_dir; + int res = create_directory_recursive(html_config_dir); + if(res != 0) { + fprintf(stderr, "Failed to create %s, error: %s\n", html_config_dir, strerror(res)); + exit(1); + } + + if(add_html(media_name, media_url, html_config_dir, program_dir, start_after) != 0) + exit(1); } else { fprintf(stderr, "type should be either rss or html\n"); usage_add(); @@ -307,7 +315,7 @@ int main(int argc, char **argv) { const char *command = argv[1]; if(strcmp(command, "add") == 0) { - command_add(argc - 2, argv + 2, rss_config_dir, html_config_dir); + command_add(argc - 2, argv + 2, rss_config_dir, html_config_dir, dirname(argv[0])); } else if(strcmp(command, "sync") == 0) { command_sync(argc - 2, argv + 2); } else if(strcmp(command, "downloaded") == 0) { diff --git a/src/program.c b/src/program.c index c396695..6150616 100644 --- a/src/program.c +++ b/src/program.c @@ -18,8 +18,42 @@ int program_buffer_write_callback(char *data, int size, void *userdata) { return 0; } +static int program_read_output(int process_id, int read_fd, ProgramOutputCallback output_callback, void *userdata) { + int status; + char buffer[4097]; + + for(;;) { + ssize_t bytes_read = read(read_fd, buffer, sizeof(buffer) - 1); + if(bytes_read == 0) { + break; + } else if(bytes_read == -1) { + int err = errno; + fprintf(stderr, "Failed to read from pipe, error: %s\n", strerror(err)); + return -err; + } + + buffer[bytes_read] = '\0'; + if(output_callback && output_callback(buffer, bytes_read, userdata) != 0) + break; + } + + if(waitpid(process_id, &status, 0) == -1) { + perror("waitpid failed"); + return -5; + } + + if(!WIFEXITED(status)) + return -4; + + int exit_status = WEXITSTATUS(status); + if(exit_status != 0) + return -exit_status; + + return 0; +} + int program_exec(const char **args, ProgramOutputCallback output_callback, void *userdata) { - /* 1 arguments */ + /* 1 argument */ if(args[0] == NULL) return -1; @@ -34,6 +68,8 @@ int program_exec(const char **args, ProgramOutputCallback output_callback, void pid_t pid = fork(); if(pid == -1) { perror("Failed to fork"); + close(fd[READ_END]); + close(fd[WRITE_END]); return -3; } else if(pid == 0) { /* child */ if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { @@ -55,42 +91,8 @@ int program_exec(const char **args, ProgramOutputCallback output_callback, void } else { /* parent */ close(fd[WRITE_END]); - int result = 0; - int status; - - char buffer[4097]; - - if(output_callback) { - for(;;) { - ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer) - 1); - if(bytes_read == 0) { - break; - } else if(bytes_read == -1) { - int err = errno; - fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err)); - result = -err; - goto cleanup; - } - - buffer[bytes_read] = '\0'; - if(output_callback(buffer, bytes_read, userdata) != 0) - break; - } - } - - if(waitpid(pid, &status, 0) == -1) { - perror("waitpid failed"); - result = -5; - goto cleanup; - } - - if(!WIFEXITED(status)) { - result = -4; - goto cleanup; - } - - int exit_status = WEXITSTATUS(status); - if(exit_status != 0) { + int result = program_read_output(pid, fd[READ_END], output_callback, userdata); + if(result != 0) { fprintf(stderr, "Failed to execute program ("); const char **arg = args; while(*arg) { @@ -99,13 +101,104 @@ int program_exec(const char **args, ProgramOutputCallback output_callback, void fprintf(stderr, "'%s'", *arg); ++arg; } - fprintf(stderr, "), exit status %d\n", exit_status); - result = -exit_status; - goto cleanup; + fprintf(stderr, ")\n"); } - cleanup: close(fd[READ_END]); return result; } } + +int program_exec_async(const char **args, int *process_id, int *stdin_file, int *stdout_file) { + int result = 0; + + /* 1 argument */ + if(args[0] == NULL) + return -1; + + int stdin_fd[2] = { -1, -1 }; + int stdout_fd[2] = { -1, -1 }; + + if(stdin_file) { + if(pipe(stdin_fd) == -1) { + perror("Failed to open pipe"); + result = -2; + goto cleanup; + } + } + + if(stdout_file) { + if(pipe(stdout_fd) == -1) { + perror("Failed to open pipe"); + result = -2; + goto cleanup; + } + } + + pid_t parent_pid = getpid(); + + pid_t pid = fork(); + if(pid == -1) { + result = -errno; + perror("failed to fork"); + goto cleanup; + } else if(pid == 0) { /* child */ + if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); + exit(127); + } + + /* Test if the parent died before the above call to prctl */ + if(getppid() != parent_pid) + exit(127); + + if(stdin_file) { + dup2(stdin_fd[READ_END], STDIN_FILENO); + close(stdin_fd[READ_END]); + close(stdin_fd[WRITE_END]); + } + + if(stdout_file) { + dup2(stdout_fd[WRITE_END], STDOUT_FILENO); + close(stdout_fd[READ_END]); + close(stdout_fd[WRITE_END]); + } + + execvp(args[0], (char* const*)args); + perror("execvp"); + exit(127); + } else { /* parent */ + if(process_id) + *process_id = pid; + + if(stdin_file) + close(stdin_fd[READ_END]); + + if(stdout_file) + close(stdout_fd[WRITE_END]); + + *stdin_file = stdin_fd[WRITE_END]; + *stdout_file = stdout_fd[READ_END]; + + return 0; + } + + cleanup: + if(stdin_fd[0] != -1) close(stdin_fd[0]); + if(stdin_fd[1] != -1) close(stdin_fd[1]); + if(stdout_fd[0] != -1) close(stdout_fd[0]); + if(stdout_fd[1] != -1) close(stdout_fd[1]); + return result; +} + +int program_wait_until_exit(int process_id, int stdin_file, int stdout_file, ProgramOutputCallback output_callback, void *userdata) { + if(stdin_file != -1) + close(stdin_file); + + int result = program_read_output(process_id, stdout_file, output_callback, userdata); + + if(stdout_file != -1) + close(stdout_file); + + return result; +} diff --git a/src/program.h b/src/program.h index 265dfa5..8bf721c 100644 --- a/src/program.h +++ b/src/program.h @@ -8,8 +8,11 @@ int program_buffer_write_callback(char *data, int size, void *userdata); /* @args need to have at least 2 arguments. The first which is the program name - and the last which is NULL, which indicates end of args + and the last which is NULL, which indicates end of args. */ int program_exec(const char **args, ProgramOutputCallback output_callback, void *userdata); +int program_exec_async(const char **args, int *process_id, int *stdin_file, int *stdout_file); +/* If stdin_file is -1 or stdout_file is -1 then they are not used */ +int program_wait_until_exit(int process_id, int stdin_file, int stdout_file, ProgramOutputCallback output_callback, void *userdata); #endif diff --git a/src/rss.c b/src/rss.c index 23ccf89..84fa345 100644 --- a/src/rss.c +++ b/src/rss.c @@ -3,12 +3,12 @@ #include "stringutils.h" #include "fileutils.h" #include "buffer.h" +#include "rss_html_common.h" #include #include #include #include #include -#include "json.h" static int is_alpha_lowercase(char c) { return c >= 'a' && c <= 'z'; @@ -163,150 +163,14 @@ static int rss_parse_callback(char *title, char *link, void *userdata) { return 0; } -static void create_json_string(struct json_string_s *json_result, const char *str, int len) { - json_result->string = str; - json_result->string_size = len; -} - -static void init_json_value_str(struct json_value_s *json_value, struct json_string_s *json_str) { - json_value->payload = json_str; - json_value->type = json_type_string; -} - -static int write_rss_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url) { - struct json_string_s title_json_key; - create_json_string(&title_json_key, "title", 5); - - struct json_string_s title_json_value_str; - create_json_string(&title_json_value_str, start_after, start_after ? strlen(start_after) : 0); - struct json_value_s title_json_value; - init_json_value_str(&title_json_value, &title_json_value_str); - - struct json_string_s time_json_key; - create_json_string(&time_json_key, "time", 4); - - struct json_string_s time_value_str; - create_json_string(&time_value_str, updated, updated ? strlen(updated) : 0); - struct json_value_s time_json_value; - init_json_value_str(&time_json_value, &time_value_str); - - struct json_string_s url_json_key; - create_json_string(&url_json_key, "url", 3); - - struct json_string_s url_value_str; - create_json_string(&url_value_str, start_after_url, start_after_url ? strlen(start_after_url) : 0); - struct json_value_s url_json_value; - init_json_value_str(&url_json_value, &url_value_str); - - struct json_string_s link_json_key; - create_json_string(&link_json_key, "link", 4); - - struct json_string_s link_json_value_str; - create_json_string(&link_json_value_str, url, strlen(url)); - struct json_value_s link_json_value; - init_json_value_str(&link_json_value, &link_json_value_str); - - struct json_string_s updated_json_key; - create_json_string(&updated_json_key, "updated", 7); - - struct json_string_s updated_json_value_str; - create_json_string(&updated_json_value_str, updated, strlen(updated)); - struct json_value_s updated_json_value; - init_json_value_str(&updated_json_value, &updated_json_value_str); - - struct json_string_s downloaded_json_key; - create_json_string(&downloaded_json_key, "downloaded", 10); - - struct json_object_s downloaded_json; - downloaded_json.start = NULL; - downloaded_json.length = 0; - - struct json_value_s downloaded_json_value; - downloaded_json_value.payload = &downloaded_json; - downloaded_json_value.type = json_type_object; - - struct json_object_element_s downloaded_title_element; - downloaded_title_element.name = &title_json_key; - downloaded_title_element.value = &title_json_value; - - struct json_object_element_s downloaded_time_element; - downloaded_time_element.name = &time_json_key; - downloaded_time_element.value = &time_json_value; - - struct json_object_element_s downloaded_url_element; - downloaded_url_element.name = &url_json_key; - downloaded_url_element.value = &url_json_value; - - downloaded_title_element.next = &downloaded_time_element; - downloaded_time_element.next = &downloaded_url_element; - downloaded_url_element.next = NULL; - - struct json_array_s downloaded_json_array; - downloaded_json_array.start = NULL; - downloaded_json_array.length = 0; - struct json_array_element_s downloaded_json_array_element; - - if(start_after) { - downloaded_json.start = &downloaded_title_element; - downloaded_json.length = 3; - - downloaded_json_array_element.value = &downloaded_json_value; - downloaded_json_array_element.next = NULL; - downloaded_json_array.start = &downloaded_json_array_element; - downloaded_json_array.length = 1; - } - - struct json_value_s downloaded_json_array_value; - downloaded_json_array_value.payload = &downloaded_json_array; - downloaded_json_array_value.type = json_type_array; - - struct json_object_s json_root; - json_root.length = 3; - - struct json_object_element_s link_element; - link_element.name = &link_json_key; - link_element.value = &link_json_value; - - struct json_object_element_s updated_element; - updated_element.name = &updated_json_key; - updated_element.value = &updated_json_value; - - struct json_object_element_s downloaded_element; - downloaded_element.name = &downloaded_json_key; - downloaded_element.value = &downloaded_json_array_value; - - link_element.next = &updated_element; - updated_element.next = &downloaded_element; - downloaded_element.next = NULL; - json_root.start = &link_element; - - struct json_value_s json_root_value; - json_root_value.payload = &json_root; - json_root_value.type = json_type_object; - size_t json_body_size = 0; - char *json_body_str = json_write_pretty(&json_root_value, " ", "\n", &json_body_size); - if(!json_body_str || json_body_size == 0) { - fprintf(stderr, "Failed to write json data to rss file %s/%s\n", dir, filename); - return -1; - } - - int result = file_overwrite_in_dir(dir, filename, json_body_str, json_body_size - 1); - free(json_body_str); - return result; -} - int add_rss(const char *name, const char *url, char *rss_config_dir, const char *start_after) { - (void)name; - (void)rss_config_dir; - (void)start_after; int result = 0; Buffer buffer; buffer_init(&buffer); - int res = download_to_buffer(url, &buffer); - if(res != 0) { + result = download_to_buffer(url, &buffer); + if(result != 0) { fprintf(stderr, "Failed to download rss: %s\n", url); - result = res; goto cleanup; } @@ -317,10 +181,9 @@ int add_rss(const char *name, const char *url, char *rss_config_dir, const char /* TODO: What if rss title is longer than this? */ char rss_title[250]; - res = parse_rss(buffer.data, rss_title, sizeof(rss_title), rss_parse_callback, &rss_parse_userdata); - if(res != 0) { + result = parse_rss(buffer.data, rss_title, sizeof(rss_title), rss_parse_callback, &rss_parse_userdata); + if(result != 0) { fprintf(stderr, "Failed to parse rss for url: %s\n", url); - result = res; goto cleanup; } @@ -354,10 +217,9 @@ int add_rss(const char *name, const char *url, char *rss_config_dir, const char goto cleanup; } - res = create_directory_recursive(rss_tracked_dir); - if(res != 0) { - fprintf(stderr, "Failed to create %s, error: %s\n", rss_tracked_dir, strerror(res)); - result = res; + result = create_directory_recursive(rss_tracked_dir); + if(result != 0) { + fprintf(stderr, "Failed to create %s, error: %s\n", rss_tracked_dir, strerror(result)); goto cleanup; } @@ -368,36 +230,32 @@ int add_rss(const char *name, const char *url, char *rss_config_dir, const char char in_progress_filepath[PATH_MAX]; strcpy(in_progress_filepath, rss_tracked_dir); strcat(in_progress_filepath, "/.in_progress"); - res = create_lock_file(in_progress_filepath); - if(res != 0) { + result = create_lock_file(in_progress_filepath); + if(result != 0) { fprintf(stderr, "Failed to create %s/.in_progress\n", rss_tracked_dir); - result = res; goto cleanup; } - res = file_overwrite_in_dir(rss_tracked_dir, "link", url, strlen(url)); - if(res != 0) { + result = file_overwrite_in_dir(rss_tracked_dir, "link", url, strlen(url)); + if(result != 0) { fprintf(stderr, "Failed to create %s/link\n", rss_tracked_dir); remove(rss_tracked_dir); - result = res; goto cleanup; } char updated[32]; sprintf(updated, "%ld", time(NULL)); - res = file_overwrite_in_dir(rss_tracked_dir, "updated", updated, strlen(updated)); - if(res != 0) { + result = file_overwrite_in_dir(rss_tracked_dir, "updated", updated, strlen(updated)); + if(result != 0) { fprintf(stderr, "Failed to create %s/updated\n", rss_tracked_dir); remove(rss_tracked_dir); - result = res; goto cleanup; } - res = write_rss_json_to_file(rss_tracked_dir, "data", url, updated, start_after, rss_parse_userdata.start_after_url); - if(res != 0) { + result = write_plugin_json_to_file(rss_tracked_dir, "data", url, updated, start_after, rss_parse_userdata.start_after_url, NULL); + if(result != 0) { fprintf(stderr, "Failed to create %s/data\n", rss_tracked_dir); remove(rss_tracked_dir); - result = res; goto cleanup; } diff --git a/src/rss_html_common.c b/src/rss_html_common.c new file mode 100644 index 0000000..56e1ccb --- /dev/null +++ b/src/rss_html_common.c @@ -0,0 +1,158 @@ +#include "rss_html_common.h" +#include "json.h" +#include "fileutils.h" +#include +#include + +static void create_json_string(struct json_string_s *json_result, const char *str, int len) { + json_result->string = str; + json_result->string_size = len; +} + +static void init_json_value_str(struct json_value_s *json_value, struct json_string_s *json_str) { + json_value->payload = json_str; + json_value->type = json_type_string; +} + +int write_plugin_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url, const char *plugin_name) { + struct json_string_s title_json_key; + create_json_string(&title_json_key, "title", 5); + + struct json_string_s title_json_value_str; + create_json_string(&title_json_value_str, start_after, start_after ? strlen(start_after) : 0); + struct json_value_s title_json_value; + init_json_value_str(&title_json_value, &title_json_value_str); + + struct json_string_s time_json_key; + create_json_string(&time_json_key, "time", 4); + + struct json_string_s time_value_str; + create_json_string(&time_value_str, updated, strlen(updated)); + struct json_value_s time_json_value; + init_json_value_str(&time_json_value, &time_value_str); + + struct json_string_s url_json_key; + create_json_string(&url_json_key, "url", 3); + + struct json_string_s url_value_str; + create_json_string(&url_value_str, start_after_url, start_after_url ? strlen(start_after_url) : 0); + struct json_value_s url_json_value; + init_json_value_str(&url_json_value, &url_value_str); + + struct json_string_s plugin_json_key; + create_json_string(&plugin_json_key, "plugin", 6); + + struct json_string_s plugin_json_value_str; + create_json_string(&plugin_json_value_str, plugin_name, plugin_name ? strlen(plugin_name) : 0); + struct json_value_s plugin_json_value; + init_json_value_str(&plugin_json_value, &plugin_json_value_str); + + struct json_string_s link_json_key; + create_json_string(&link_json_key, "link", 4); + + struct json_string_s link_json_value_str; + create_json_string(&link_json_value_str, url, strlen(url)); + struct json_value_s link_json_value; + init_json_value_str(&link_json_value, &link_json_value_str); + + struct json_string_s updated_json_key; + create_json_string(&updated_json_key, "updated", 7); + + struct json_string_s updated_json_value_str; + create_json_string(&updated_json_value_str, updated, strlen(updated)); + struct json_value_s updated_json_value; + init_json_value_str(&updated_json_value, &updated_json_value_str); + + struct json_string_s downloaded_json_key; + create_json_string(&downloaded_json_key, "downloaded", 10); + + struct json_object_s downloaded_json; + downloaded_json.start = NULL; + downloaded_json.length = 0; + + struct json_value_s downloaded_json_value; + downloaded_json_value.payload = &downloaded_json; + downloaded_json_value.type = json_type_object; + + struct json_object_element_s downloaded_title_element; + downloaded_title_element.name = &title_json_key; + downloaded_title_element.value = &title_json_value; + + struct json_object_element_s downloaded_time_element; + downloaded_time_element.name = &time_json_key; + downloaded_time_element.value = &time_json_value; + + struct json_object_element_s downloaded_url_element; + downloaded_url_element.name = &url_json_key; + downloaded_url_element.value = &url_json_value; + + downloaded_title_element.next = &downloaded_time_element; + downloaded_time_element.next = &downloaded_url_element; + downloaded_url_element.next = NULL; + + struct json_array_s downloaded_json_array; + downloaded_json_array.start = NULL; + downloaded_json_array.length = 0; + struct json_array_element_s downloaded_json_array_element; + + if(start_after) { + downloaded_json.start = &downloaded_title_element; + downloaded_json.length = 3; + + downloaded_json_array_element.value = &downloaded_json_value; + downloaded_json_array_element.next = NULL; + downloaded_json_array.start = &downloaded_json_array_element; + downloaded_json_array.length = 1; + } + + struct json_value_s downloaded_json_array_value; + downloaded_json_array_value.payload = &downloaded_json_array; + downloaded_json_array_value.type = json_type_array; + + struct json_object_s json_root; + json_root.length = 3; + + struct json_object_element_s link_element; + link_element.name = &link_json_key; + link_element.value = &link_json_value; + + struct json_object_element_s updated_element; + updated_element.name = &updated_json_key; + updated_element.value = &updated_json_value; + + struct json_object_element_s downloaded_element; + downloaded_element.name = &downloaded_json_key; + downloaded_element.value = &downloaded_json_array_value; + + struct json_object_element_s plugin_element; + plugin_element.name = &plugin_json_key; + plugin_element.value = &plugin_json_value; + + link_element.next = &updated_element; + updated_element.next = &downloaded_element; + downloaded_element.next = NULL; + + if(plugin_name) { + json_root.start = &plugin_element; + plugin_element.next = &link_element; + } else { + json_root.start = &link_element; + } + + struct json_value_s json_root_value; + json_root_value.payload = &json_root; + json_root_value.type = json_type_object; + size_t json_body_size = 0; + char *json_body_str = json_write_pretty(&json_root_value, " ", "\n", &json_body_size); + if(!json_body_str || json_body_size == 0) { + fprintf(stderr, "Failed to write json data to file %s/%s\n", dir, filename); + return -1; + } + + /* Workaround json bug (?) */ + json_body_size = strlen(json_body_str); + + int result = file_overwrite_in_dir(dir, filename, json_body_str, json_body_size); + free(json_body_str); + return result; +} \ No newline at end of file diff --git a/src/rss_html_common.h b/src/rss_html_common.h new file mode 100644 index 0000000..99f7778 --- /dev/null +++ b/src/rss_html_common.h @@ -0,0 +1,6 @@ +#ifndef RSS_HTML_COMMON_H +#define RSS_HTML_COMMON_H + +int write_plugin_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url, const char *plugin_name); + +#endif \ No newline at end of file -- cgit v1.2.3