From cfc9ef1cade470f6d2a7ba67c625eb8c11ff2743 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 16 Jul 2024 20:27:53 +0200 Subject: Add fallback urls for rss (right now, fallback nyaa.si to nyaa.land) --- src/fallback.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/fallback.c (limited to 'src/fallback.c') diff --git a/src/fallback.c b/src/fallback.c new file mode 100644 index 0000000..058b23a --- /dev/null +++ b/src/fallback.c @@ -0,0 +1,193 @@ +#include "fallback.h" +#include "fileutils.h" +#include "../depends/cJSON.h" +#include +#include +#include + +bool fallback_load_from_file(fallback *self, const char *filepath) { + memset(self, 0, sizeof(*self)); + + char *file_data; + long file_size; + if(file_get_content(filepath, &file_data, &file_size) != 0) { + fprintf(stderr, "Error: fallback_load_from_file: failed to read %s\n", filepath); + return false; + } + + cJSON *json_root = cJSON_ParseWithLength(file_data, file_size); + if(!json_root) { + fprintf(stderr, "Error: fallback_load_from_file: failed to parse file %s as json\n", filepath); + goto error; + } + free(file_data); + file_data = NULL; + + if(!cJSON_IsArray(json_root)) { + fprintf(stderr, "File %s contains malformed json. Expected json root element to be an array\n", filepath); + goto error; + } + + buffer_init(&self->fallbacks); + + const cJSON *fallback_item_json = NULL; + cJSON_ArrayForEach(fallback_item_json, json_root) { + if(!cJSON_IsObject(fallback_item_json)) + continue; + + const cJSON *source_json = cJSON_GetObjectItemCaseSensitive(fallback_item_json, "source"); + if(!cJSON_IsString(source_json)) + continue; + + const cJSON *fallbacks_json = cJSON_GetObjectItemCaseSensitive(fallback_item_json, "fallbacks"); + if(!cJSON_IsArray(fallbacks_json)) + continue; + + char *source_str = strdup(source_json->valuestring); + if(!source_str) { + fprintf(stderr, "Error: failed to clone string: %s\n", source_str); + abort(); + } + + fallback_item item; + item.source = source_str; + item.source_to_use = NULL; + buffer_init(&item.fallbacks); + + const cJSON *fallback_str_json = NULL; + cJSON_ArrayForEach(fallback_str_json, fallbacks_json) { + if(!cJSON_IsString(fallback_str_json)) + continue; + + char *fallback = strdup(fallback_str_json->valuestring); + if(!fallback) { + fprintf(stderr, "Error: failed to clone string: %s\n", fallback); + abort(); + } + + buffer_append(&item.fallbacks, &fallback, sizeof(fallback)); + } + + buffer_append(&self->fallbacks, &item, sizeof(item)); + } + + cJSON_Delete(json_root); + free(file_data); + return true; + + error: + cJSON_Delete(json_root); + free(file_data); + return false; +} + +void fallback_deinit(fallback *self) { + fallback_item *items_it = buffer_begin(&self->fallbacks); + fallback_item *items_end = buffer_end(&self->fallbacks); + for(; items_it != items_end; ++items_it) { + if(items_it->source) { + free(items_it->source); + items_it->source = NULL; + } + + char **fallbacks_it = buffer_begin(&items_it->fallbacks); + char **fallbacks_end = buffer_end(&items_it->fallbacks); + for(; fallbacks_it != fallbacks_end; ++fallbacks_it) { + if(*fallbacks_it) { + free(*fallbacks_it); + *fallbacks_it = NULL; + } + } + buffer_deinit(&items_it->fallbacks); + } + buffer_deinit(&self->fallbacks); +} + +static void url_extract_domain(const char **url, int *len) { + if(*len >= 7 && strncmp("http://", *url, 7) == 0) { + *url += 7; + *len -= 7; + } else if(*len >= 8 && strncmp("https://", *url, 8) == 0) { + *url += 8; + *len -= 8; + } + + const char *end = strchr(*url, '/'); + if(end) + *len = end - *url; +} + +fallback_item* fallback_get_from_url(fallback *self, const char *url) { + int url_len = strlen(url); + url_extract_domain(&url, &url_len); + + fallback_item *items_it = buffer_begin(&self->fallbacks); + fallback_item *items_end = buffer_end(&self->fallbacks); + for(; items_it != items_end; ++items_it) { + int source_len = strlen(items_it->source); + const char *source = items_it->source; + url_extract_domain(&source, &source_len); + + if(url_len == source_len && memcmp(url, source, url_len) == 0) + return items_it; + + if(!items_it->source_to_use) + continue; + + int source_to_use_len = strlen(items_it->source_to_use); + const char *source_to_use = items_it->source_to_use; + url_extract_domain(&source_to_use, &source_to_use_len); + + if(url_len == source_to_use_len && memcmp(url, source_to_use, url_len) == 0) + return items_it; + } + + return NULL; +} + +void fallback_clear_sources_to_use(fallback *self) { + fallback_item *items_it = buffer_begin(&self->fallbacks); + fallback_item *items_end = buffer_end(&self->fallbacks); + for(; items_it != items_end; ++items_it) { + items_it->source_to_use = NULL; + } +} + +static const char* get_url_part_after_domain(const char *url) { + int len = strlen(url); + if(len >= 7 && strncmp(url, "http://", 7) == 0) { + url += 7; + len -= 7; + } else if(len >= 8 && strncmp(url, "https://", 8) == 0) { + url += 8; + len -= 8; + } + + const char *after_domain = strchr(url, '/'); + if(after_domain) + return after_domain; + else + return url + len; +} + +void fallback_replace_url_with_fallback_url(const char *url, const char *fallback_url, char *new_url, size_t new_url_len) { + const char *url_part_after_domain = get_url_part_after_domain(url); + snprintf(new_url, new_url_len, "%s%s", fallback_url, url_part_after_domain); +} + +void fallback_replace_active_fallback_url_with_source_url(fallback *self, const char *url, char *new_url, size_t new_url_len) { + fallback_item *fall_item = fallback_get_from_url(self, url); + if(!fall_item || !fall_item->source_to_use) { + snprintf(new_url, new_url_len, "%s", url); + return; + } + fallback_replace_url_with_fallback_url(url, fall_item->source, new_url, new_url_len); +} + +void fallback_item_replace_url_with_fallback_url(fallback_item *self, const char *url, char *new_url, size_t new_url_len) { + if(!self->source_to_use) { + snprintf(new_url, new_url_len, "%s", url); + return; + } + fallback_replace_url_with_fallback_url(url, self->source_to_use, new_url, new_url_len); +} -- cgit v1.2.3