aboutsummaryrefslogtreecommitdiff
path: root/src/fallback.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fallback.c')
-rw-r--r--src/fallback.c193
1 files changed, 193 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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);
+}