#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;
}