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