From 309f2fbb52036731b6741accdf5e8fafe37ec69d Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sun, 14 Jun 2020 00:14:13 +0200
Subject: [PATCH] add: POST schedules/list fix: CORS

---
 endpoints/api_v1_schedules_list.c    | 155 +++++++++++++++++++++++++++
 handlers/connection.c                |  35 ++++--
 include/endpoints/api_v1_schedules.h |   3 +
 main.c                               |   3 +-
 router.c                             |   1 +
 5 files changed, 190 insertions(+), 7 deletions(-)
 create mode 100644 endpoints/api_v1_schedules_list.c

diff --git a/endpoints/api_v1_schedules_list.c b/endpoints/api_v1_schedules_list.c
new file mode 100644
index 0000000..1cd9430
--- /dev/null
+++ b/endpoints/api_v1_schedules_list.c
@@ -0,0 +1,155 @@
+#include <cJSON.h>
+#include <macros.h>
+#include <constants.h>
+#include <endpoints/api_v1_schedules.h>
+#include <logger.h>
+#include <models/junction_tag.h>
+#include <models/schedule.h>
+#include <models/tag.h>
+
+void
+api_v1_schedules_list_POST(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
+{
+    (void)args;
+    (void)nc;
+    cJSON *json_list = cJSON_ParseWithLength(hm->body.p, hm->body.len);
+
+
+    cJSON *json;
+    cJSON_ArrayForEach(json, json_list)
+    {
+        if(json == NULL)
+        {
+            static const char content[] = "no valid json was supplied";
+            endpoint_response_text(response, 400, content, STRLEN(content));
+            return;
+        }
+
+        cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
+        if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
+        {
+            LOG_DEBUG("no name for schedule provided\n");
+            cJSON_Delete(json_list);
+
+            static const char content[] = "no name for schedule provided";
+            endpoint_response_text(response, 400, content, STRLEN(content));
+            return;
+        }
+        cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
+        if(!cJSON_IsArray(json_periods))
+        {
+            LOG_DEBUG("no periods for schedule provided\n");
+            cJSON_Delete(json_list);
+
+            static const char content[] = "no periods for schedule provided";
+            endpoint_response_text(response, 400, content, STRLEN(content));
+            return;
+        }
+
+        cJSON *json_tag;
+        cJSON *json_tags = cJSON_GetObjectItemCaseSensitive(json, "tags");
+        cJSON_ArrayForEach(json_tag, json_tags)
+        {
+            if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
+            {
+                LOG_DEBUG("invalid tag in tags\n");
+                cJSON_Delete(json_list);
+
+                static const char content[] = "invalid tag in tags";
+                endpoint_response_text(response, 400, content, STRLEN(content));
+                return;
+            }
+        }
+
+        schedule_t *new_schedule = malloc(sizeof(schedule_t));
+
+        new_schedule->id = 0;
+        uuid_generate(new_schedule->uid);
+
+        strncpy(new_schedule->name, json_name->valuestring, MAX_NAME_LENGTH);
+        new_schedule->name[MAX_NAME_LENGTH] = '\0';
+
+        int periods_count = cJSON_GetArraySize(json_periods);
+        new_schedule->periods = malloc(sizeof(period_t) * periods_count);
+
+        int periods_valid = 0;
+
+        cJSON *json_period;
+        cJSON_ArrayForEach(json_period, json_periods)
+        {
+            cJSON *json_period_start = cJSON_GetObjectItemCaseSensitive(json_period, "start");
+            cJSON *json_period_end = cJSON_GetObjectItemCaseSensitive(json_period, "end");
+
+            if(!cJSON_IsString(json_period_start) || (json_period_start->valuestring == NULL))
+            {
+                LOG_DEBUG("period is missing start\n");
+                cJSON_Delete(json_list);
+                schedule_free(new_schedule);
+
+                static const char content[] = "one period is missing a start";
+                endpoint_response_text(response, 400, content, STRLEN(content));
+                return;
+            }
+            if(!cJSON_IsString(json_period_end) || (json_period_end->valuestring == NULL))
+            {
+                LOG_DEBUG("period is missing end\n");
+                cJSON_Delete(json_list);
+                schedule_free(new_schedule);
+
+                static const char content[] = "one period is missing an end";
+                endpoint_response_text(response, 400, content, STRLEN(content));
+                return;
+            }
+
+            uint16_t start;
+            uint16_t end;
+            if(period_helper_parse_hhmm(json_period_start->valuestring, &start))
+            {
+                LOG_DEBUG("couldn't parse start '%s'\n", json_period_start->valuestring);
+                cJSON_Delete(json_list);
+                schedule_free(new_schedule);
+
+                static const char content[] = "the start for one period is invalid";
+                endpoint_response_text(response, 400, content, STRLEN(content));
+                return;
+            }
+            if(period_helper_parse_hhmm(json_period_end->valuestring, &end))
+            {
+                LOG_DEBUG("couldn't parse end '%s'\n", json_period_end->valuestring);
+                cJSON_Delete(json_list);
+                schedule_free(new_schedule);
+
+                static const char content[] = "the end for one period is invalid";
+                endpoint_response_text(response, 400, content, STRLEN(content));
+                return;
+            }
+
+            new_schedule->periods[periods_valid].start = start;
+            new_schedule->periods[periods_valid].end = end;
+            ++periods_valid;
+        }
+
+        new_schedule->periods_count = periods_valid;
+
+        schedule_save(new_schedule);
+
+        junction_tag_remove_for_schedule(new_schedule->id);
+
+        json_tags = cJSON_GetObjectItemCaseSensitive(json, "tags");
+        cJSON_ArrayForEach(json_tag, json_tags)
+        {
+            const char *tag = json_tag->valuestring;
+            int tag_id = tag_get_id(tag);
+            if(tag_id == 0)
+            {
+                tag_save(tag_id, tag);
+                tag_id = tag_get_id(tag);
+            }
+            junction_tag_insert(tag_id, 0, new_schedule->id);
+        }
+        schedule_free(new_schedule);
+    }
+
+    cJSON_Delete(json_list);
+    api_v1_schedules_GET(nc, hm, args, response);;
+}
diff --git a/handlers/connection.c b/handlers/connection.c
index c7e7870..c726f39 100644
--- a/handlers/connection.c
+++ b/handlers/connection.c
@@ -12,6 +12,23 @@
 // -2 for "%s" -1 for \0
 #define HEADERS_FMT_LEN (sizeof(HEADERS_FMT) - 3)
 
+static char*
+add_extra_headers(char *extra_headers)
+{
+    char *result;
+    size_t std_headers_len = strlen(global_config.http_server_opts.extra_headers);
+    if(extra_headers == NULL)
+    {
+        result = malloc(sizeof(char) * (std_headers_len + 1));
+        strcpy(result, global_config.http_server_opts.extra_headers);
+        return result;
+    }
+
+    result = malloc(sizeof(char) * (std_headers_len + strlen(extra_headers) + 3));
+    sprintf(result, "%s\r\n%s", global_config.http_server_opts.extra_headers, extra_headers);
+    return result;
+}
+
 static void
 send_response(struct mg_connection *nc, endpoint_response_t *response)
 {
@@ -20,10 +37,12 @@ send_response(struct mg_connection *nc, endpoint_response_t *response)
         char *response_headers = malloc(sizeof(char) * (HEADERS_FMT_LEN + strlen(response->content_type) + 1));
         sprintf(response_headers, HEADERS_FMT, response->content_type);
 
-        mg_send_head(nc, response->status_code, response->content_length, response_headers);
+        char *extra_headers = add_extra_headers(response_headers);
+        mg_send_head(nc, response->status_code, response->content_length, extra_headers);
         mg_printf(nc, "%s", response->content);
 
         free(response_headers);
+        free(extra_headers);
 
         if(response->alloced_content)
         {
@@ -50,7 +69,7 @@ handler_connection(struct mg_connection *nc, int ev, void *p)
         {
             /* Normalize path - resolve "." and ".." (in-place). */
             if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) {
-                mg_http_send_error(nc, 400, NULL);
+                mg_http_send_error(nc, 400, global_config.http_server_opts.extra_headers);
                 return;
             }
             char *request_file_org = malloc(sizeof(char) * hm->uri.len);
@@ -89,16 +108,20 @@ handler_connection(struct mg_connection *nc, int ev, void *p)
                         endpoint->options & HTTP_METHOD_PUT ? ", PUT" : "",
                         endpoint->options & HTTP_METHOD_DELETE ? ", DELETE" : ""
                        );
-                mg_send_head(nc, 204, 0, options_header);
+                char *extra_headers = add_extra_headers(options_header);
+                mg_send_head(nc, 204, 0, extra_headers);
+                free(extra_headers);
             }
             else
             {
                 mg_send_head(nc, 501, 0, "Content-Type: text/plain");
             }
         }
-
-        endpoint->func(nc, hm, endpoint->args, &response);
-        send_response(nc, &response);
+        else
+        {
+            endpoint->func(nc, hm, endpoint->args, &response);
+            send_response(nc, &response);
+        }
 
         for(int i = 0; i < endpoint->args_count; ++i)
         {
diff --git a/include/endpoints/api_v1_schedules.h b/include/endpoints/api_v1_schedules.h
index 46483c3..69d3b78 100644
--- a/include/endpoints/api_v1_schedules.h
+++ b/include/endpoints/api_v1_schedules.h
@@ -9,6 +9,9 @@ api_v1_schedules_POST(struct mg_connection *nc, struct http_message *hm, endpoin
 void
 api_v1_schedules_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
 
+void
+api_v1_schedules_list_POST(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
+
 void
 api_v1_schedules_STR_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
 
diff --git a/main.c b/main.c
index 303a238..df4fdf0 100644
--- a/main.c
+++ b/main.c
@@ -81,7 +81,8 @@ main(int argc, const char** argv)
 
     int rc = sqlite3_open(global_config.database, &global_database);
 
-    if(rc) {
+    if(rc)
+    {
         LOG_FATAL("can't open database: %s\n", sqlite3_errmsg(global_database));
         return 1;
     }
diff --git a/router.c b/router.c
index 13d7d19..925477b 100644
--- a/router.c
+++ b/router.c
@@ -59,6 +59,7 @@ router_init()
 
     router_register_endpoint("/api/v1/schedules/", HTTP_METHOD_GET, api_v1_schedules_GET);
     router_register_endpoint("/api/v1/schedules/", HTTP_METHOD_POST, api_v1_schedules_POST);
+    router_register_endpoint("/api/v1/schedules/list/", HTTP_METHOD_POST, api_v1_schedules_list_POST);
     router_register_endpoint("/api/v1/schedules/{str}", HTTP_METHOD_GET, api_v1_schedules_STR_GET);
     router_register_endpoint("/api/v1/schedules/{str}", HTTP_METHOD_PUT, api_v1_schedules_STR_PUT);
     router_register_endpoint("/api/v1/schedules/{str}", HTTP_METHOD_DELETE, api_v1_schedules_STR_DELETE);