diff options
| author | Jesper Jensen <jesper@jnsn.dev> | 2026-02-21 19:55:14 +0100 |
|---|---|---|
| committer | Jesper Jensen <jesper@jnsn.dev> | 2026-02-21 19:55:14 +0100 |
| commit | 0f3cbe7c7a562d65e16b704a038e8e6868436170 (patch) | |
| tree | e551ba9e5a57983a91332f501ebc6f1be087dbbd /src | |
| parent | cd59f14f9de567f2e84f88b3e70d4652ab1e5ea8 (diff) | |
Introduce new chain style handler processing
Diffstat (limited to 'src')
| -rw-r--r-- | src/html.c | 2 | ||||
| -rw-r--r-- | src/web.c | 923 | ||||
| -rw-r--r-- | src/web.h | 15 |
3 files changed, 517 insertions, 423 deletions
@@ -468,7 +468,7 @@ char *html_render_client_detail_page(const struct Request *request, const struct " <details>\n" " <summary>How To</summary>\n" " <div>\n" - " <pre><code>curl -s -X POST %s://%s \\\n" + " <pre><code>curl -s -X POST %s://%s/api/report \\\n" " -H \"Content-Type: application/json\" \\\n" " -d \'{\"client\": \"%s\", \"secret\": \"%s\", \"event\": \"begin_backup\"}\'" "</pre></code>\n" @@ -120,6 +120,491 @@ form_iterator(void *cls, return MHD_YES; } +static enum HandlerStatus handle_logout( + struct MHD_Connection *connection, + struct Request *request +) { + if (strcmp(request->url, "/logout") != 0) + return HANDLER_DECLINE; + + if (strcmp(request->method, "POST") != 0) + return HANDLER_DECLINE; + + struct MHD_Response *response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, "Set-Cookie", "session=; Path=/; HttpOnly; Max-Age=0"); + MHD_add_response_header(response, "Location", "/login"); + enum MHD_Result ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); + MHD_destroy_response(response); + return ret == MHD_YES ? HANDLER_ACCEPTED : HANDLER_ERROR; +} + +static enum HandlerStatus handle_login( + struct MHD_Connection *connection, + struct Request *request +) { + if (strcmp(request->url, "/login") != 0) + return HANDLER_DECLINE; + + struct Server *server = request->server; + enum MHD_Result ret; + + if (strcmp(request->method, "POST") == 0) { + if (request->post_proc == NULL && !request->form.username_set) { + request->post_proc = MHD_create_post_processor( + connection, 1024, &form_iterator, request); + if (request->post_proc == NULL) + return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + MHD_destroy_post_processor(request->post_proc); + request->post_proc = NULL; + + if (!request->form.username_set || !request->form.password_set) { + size_t html_len; + char *html = html_render_login_page("Invalid credentials", &html_len); + if (!html) return HANDLER_ERROR; + struct MHD_Response *response = MHD_create_response_from_buffer( + html_len, html, MHD_RESPMEM_MUST_FREE); + ret = MHD_add_response_header(response, "Content-Type", "text/html"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + if (!validate_credentials(server->app, request->form.username, request->form.password)) { + size_t html_len; + char *html = html_render_login_page("Invalid credentials", &html_len); + if (!html) return HANDLER_ERROR; + struct MHD_Response *response = MHD_create_response_from_buffer( + html_len, html, MHD_RESPMEM_MUST_FREE); + ret = MHD_add_response_header(response, "Content-Type", "text/html"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + char *token = generate_session_token(server->app, request->form.username, request->form.password); + if (token == NULL) return HANDLER_ERROR; + + char cookie_header[128]; + snprintf(cookie_header, sizeof(cookie_header), "session=%s; Path=/; HttpOnly", token); + + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, "Set-Cookie", cookie_header); + MHD_add_response_header(response, "Location", "/clients"); + ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); + MHD_destroy_response(response); + return ret == MHD_YES ? HANDLER_ACCEPTED : HANDLER_ERROR; + } + + if (strcmp(request->method, "GET") == 0) { + size_t html_len; + char *html = html_render_login_page(NULL, &html_len); + if (!html) return HANDLER_ERROR; + struct MHD_Response *response = MHD_create_response_from_buffer( + html_len, html, MHD_RESPMEM_MUST_FREE); + ret = MHD_add_response_header(response, "Content-Type", "text/html"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + return HANDLER_DECLINE; +} + +static enum HandlerStatus handle_clients( + struct MHD_Connection *connection, + struct Request *request +) { + if (strcmp(request->url, "/clients") != 0) + return HANDLER_DECLINE; + + struct Server *server = request->server; + enum MHD_Result ret; + + if (strcmp(request->method, "POST") == 0) { + if (request->post_proc == NULL && !request->form.name_set) { + request->post_proc = MHD_create_post_processor( + connection, 1024, &form_iterator, request); + if (request->post_proc == NULL) + return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + MHD_destroy_post_processor(request->post_proc); + request->post_proc = NULL; + + if (!is_authenticated(server->app, connection)) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, "Location", "/login"); + ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + if (!request->form.name_set || request->form.name[0] == '\0') { + return HANDLER_ERROR; + } + + struct CreateClientResult result; + ret = create_client(server->app, request->form.name, &result); + if(ret != 0) return HANDLER_ERROR; + + // Post-Redirect-Get: redirect back to GET /clients + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + ret = MHD_add_response_header(response, "Location", "/clients"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + if (strcmp(request->method, "GET") == 0) { + if (!is_authenticated(server->app, connection)) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, "Location", "/login"); + ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + struct ListClientResult *clients = malloc(sizeof(struct ListClientResult) + sizeof(struct ListClientResultClient) * 16); + ret = list_clients(server->app, clients); + if(ret != 0) { + free(clients); + return HANDLER_ERROR; + } + + size_t html_len; + char *html = html_render_clients_page(request, clients, &html_len); + free(clients); + + if (!html) { + return HANDLER_ERROR; + } + + struct MHD_Response *response = MHD_create_response_from_buffer( + html_len, + html, + MHD_RESPMEM_MUST_FREE + ); + ret = MHD_add_response_header(response, "Content-Type", "text/html"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response( + connection, + MHD_HTTP_OK, + response + ); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + return HANDLER_DECLINE; +} + +static enum HandlerStatus handle_clients_delete( + struct MHD_Connection *connection, + struct Request *request +) { + if (strcmp(request->url, "/clients/delete") != 0) + return HANDLER_DECLINE; + + if (strcmp(request->method, "POST") != 0) + return HANDLER_DECLINE; + + struct Server *server = request->server; + enum MHD_Result ret; + + if (request->post_proc == NULL && !request->form.client_id_set) { + request->post_proc = MHD_create_post_processor( + connection, 1024, &form_iterator, request); + if (request->post_proc == NULL) + return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + MHD_destroy_post_processor(request->post_proc); + request->post_proc = NULL; + + if (!is_authenticated(server->app, connection)) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, "Location", "/login"); + ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + if (!request->form.client_id_set || request->form.client_id <= 0) { + return HANDLER_ERROR; + } + + ret = delete_client(server->app, request->form.client_id); + if(ret != 0) return HANDLER_ERROR; + + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + ret = MHD_add_response_header(response, "Location", "/clients"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; +} + +static enum HandlerStatus handle_api_report( + struct MHD_Connection *connection, + struct Request *request +) { + if (strcmp(request->url, "/api/report") != 0) + return HANDLER_DECLINE; + + if (strcmp(request->method, "POST") != 0) + return HANDLER_DECLINE; + + struct Server *server = request->server; + enum MHD_Result ret; + + if (request->pp.capacity == 0) { + create_post_collector(&request->pp); + return HANDLER_ACCEPTED; + } + + cJSON *rootJson = cJSON_ParseWithLength(request->pp.data, request->pp.size); + + cJSON *clientJson = cJSON_GetObjectItemCaseSensitive(rootJson, "client"); + if(!cJSON_IsString(clientJson) || clientJson->valuestring == NULL) { + abort(); + } + + cJSON *secretJson = cJSON_GetObjectItemCaseSensitive(rootJson, "secret"); + if(!cJSON_IsString(secretJson) || secretJson->valuestring == NULL) { + abort(); + } + + cJSON *eventJson = cJSON_GetObjectItemCaseSensitive(rootJson, "event"); + if(!cJSON_IsString(eventJson) || eventJson->valuestring == NULL) { + abort(); + } + int is_finish; + if(strcmp(eventJson->valuestring, "begin_backup") == 0) { + is_finish = 0; + } else if(strcmp(eventJson->valuestring, "end_backup") == 0) { + is_finish = 1; + } else { + abort(); + } + + ret = submit_report(server->app, clientJson->valuestring, secretJson->valuestring, is_finish); + if(ret == -2) { + cJSON_Delete(rootJson); + destroy_post_collector(&request->pp); + + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT + ); + ret = MHD_add_response_header(response, "Content-Type", "application/json"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response( + connection, + MHD_HTTP_UNAUTHORIZED, + response + ); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + if(ret != 0) { + return HANDLER_ERROR; + } + + cJSON_Delete(rootJson); + destroy_post_collector(&request->pp); + + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT + ); + ret = MHD_add_response_header(response, "Content-Type", "application/json"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response( + connection, + MHD_HTTP_NO_CONTENT, + response + ); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; +} + +static enum HandlerStatus handle_client_detail( + struct MHD_Connection *connection, + struct Request *request +) { + if (strncmp(request->url, "/client/", 8) != 0) + return HANDLER_DECLINE; + + if (strcmp(request->method, "GET") != 0) + return HANDLER_DECLINE; + + struct Server *server = request->server; + enum MHD_Result ret; + + if (!is_authenticated(server->app, connection)) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + MHD_add_response_header(response, "Location", "/login"); + ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + int64_t client_id; + if (sscanf(request->url + 8, "%ld", &client_id) != 1 || client_id <= 0) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + struct GetClientOnlyResult client_result; + ret = get_client(server->app, client_id, &client_result); + if(ret != 0) { + return HANDLER_ERROR; + } + + if(!client_result.found) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT + ); + ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + struct GetBackupsResult *backups_result = malloc(sizeof(struct GetBackupsResult) + sizeof(struct BackupRecord) * MAX_BACKUPS_DISPLAY); + backups_result->backups_len = MAX_BACKUPS_DISPLAY; + ret = get_client_backups(server->app, client_id, backups_result); + if(ret != 0) { + free(backups_result); + return HANDLER_ERROR; + } + + size_t html_len; + char *html = html_render_client_detail_page(request, &client_result, backups_result, &html_len); + free(backups_result); + + if (!html) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT + ); + ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + struct MHD_Response *response = MHD_create_response_from_buffer( + html_len, + html, + MHD_RESPMEM_MUST_FREE + ); + ret = MHD_add_response_header(response, "Content-Type", "text/html"); + if(ret != MHD_YES) return HANDLER_ERROR; + ret = MHD_queue_response( + connection, + MHD_HTTP_OK, + response + ); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; +} + +static enum HandlerStatus handle_root( + struct MHD_Connection *connection, + struct Request *request +) { + if (strcmp(request->url, "/") != 0) + return HANDLER_DECLINE; + + if (strcmp(request->method, "GET") != 0) + return HANDLER_DECLINE; + + struct Server *server = request->server; + enum MHD_Result ret; + + if (!is_authenticated(server->app, connection)) { + struct MHD_Response *response = MHD_create_response_from_buffer( + 0, "", MHD_RESPMEM_PERSISTENT + ); + MHD_add_response_header(response, "Location", "/login"); + ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; + } + + struct MHD_Response *response = MHD_create_response_from_buffer( + strlen(server->page), + (void *)server->page, + MHD_RESPMEM_PERSISTENT + ); + + ret = MHD_queue_response( + connection, + MHD_HTTP_OK, + response + ); + MHD_destroy_response(response); + if(ret != MHD_YES) return HANDLER_ERROR; + return HANDLER_ACCEPTED; +} + +static enum HandlerStatus handle_not_found( + struct MHD_Connection *connection, + struct Request *request +) { + // This handler MUST accept the request since it will be the last one in + // the chain + struct MHD_Response *response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT); + MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + return HANDLER_ACCEPTED; +} + +static RouteHandler handlers[] = { + handle_logout, + handle_login, + handle_clients, + handle_clients_delete, + handle_api_report, + handle_client_detail, + handle_root, + handle_not_found, // Catch all +}; + +#define HANDLER_COUNT (sizeof(handlers) / sizeof(handlers[0])) + static enum MHD_Result handler( void *cls, struct MHD_Connection *connection, @@ -131,10 +616,7 @@ static enum MHD_Result handler( void **ptr ) { struct Server *server = cls; - int ret; - // For the first call where con_cls points at NULL we have to create the - // connection state struct Request *request = *ptr; if (request == NULL) { request = calloc(1, sizeof(struct Request)); @@ -144,437 +626,34 @@ static enum MHD_Result handler( *ptr = request; request->server = server; + request->url = url; + request->method = method; // @COMPL: The proxy might rewrite this, and we should support that request->host = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Host"); // @COMPL: There are other headers we should look into request->proto = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "X-Forwarded-Proto"); - - if(strcmp(method, "POST") == 0) { - if(strcmp(url, "/clients") == 0 || strcmp(url, "/clients/delete") == 0 || strcmp(url, "/login") == 0) { - request->post_proc = MHD_create_post_processor( - connection, 1024, &form_iterator, request); - if (request->post_proc == NULL) { - free(request); - return MHD_NO; - } - } else { - create_post_collector(&request->pp); - } - } - - return MHD_YES; + // Fall through - handlers will set up POST processing if needed } - if(strcmp(method, "POST") == 0) { + if (*upload_data_size != 0) { if (request->post_proc != NULL) { - if (*upload_data_size != 0) { - MHD_post_process(request->post_proc, upload_data, *upload_data_size); - *upload_data_size = 0; - return MHD_YES; - } - } else { + MHD_post_process(request->post_proc, upload_data, *upload_data_size); + } else if (request->pp.capacity > 0) { collect_post(&request->pp, upload_data, upload_data_size); - if (*upload_data_size != 0) { - *upload_data_size = 0; - return MHD_YES; - } - } - - if(strcmp(url, "/logout") == 0) { - struct MHD_Response *response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Set-Cookie", "session=; Path=/; HttpOnly; Max-Age=0"); - MHD_add_response_header(response, "Location", "/login"); - ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); - MHD_destroy_response(response); - return ret; - } else if(strcmp(url, "/login") == 0) { - MHD_destroy_post_processor(request->post_proc); - request->post_proc = NULL; - - if (!request->form.username_set || !request->form.password_set) { - size_t html_len; - char *html = html_render_login_page("Invalid credentials", &html_len); - if (!html) return MHD_NO; - struct MHD_Response *response = MHD_create_response_from_buffer( - html_len, html, MHD_RESPMEM_MUST_FREE); - ret = MHD_add_response_header(response, "Content-Type", "text/html"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - return ret; - } - - if (!validate_credentials(server->app, request->form.username, request->form.password)) { - size_t html_len; - char *html = html_render_login_page("Invalid credentials", &html_len); - if (!html) return MHD_NO; - struct MHD_Response *response = MHD_create_response_from_buffer( - html_len, html, MHD_RESPMEM_MUST_FREE); - ret = MHD_add_response_header(response, "Content-Type", "text/html"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - return ret; - } - - // Valid credentials - set cookie and redirect - char *token = generate_session_token(server->app, request->form.username, request->form.password); - if (token == NULL) return MHD_NO; - - char cookie_header[128]; - snprintf(cookie_header, sizeof(cookie_header), "session=%s; Path=/; HttpOnly", token); - - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Set-Cookie", cookie_header); - MHD_add_response_header(response, "Location", "/clients"); - ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); - MHD_destroy_response(response); - return ret; - } else if(strcmp(url, "/clients") == 0) { - MHD_destroy_post_processor(request->post_proc); - request->post_proc = NULL; - - // Auth check - if (!is_authenticated(server->app, connection)) { - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Location", "/login"); - ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); - MHD_destroy_response(response); - return ret; - } - - if (!request->form.name_set || request->form.name[0] == '\0') { - return MHD_NO; - } - - struct CreateClientResult result; - ret = create_client(server->app, request->form.name, &result); - if(ret != 0) return MHD_NO; - - // Post-Redirect-Get: redirect back to GET /clients - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - ret = MHD_add_response_header(response, "Location", "/clients"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); - MHD_destroy_response(response); - return ret; - } else if(strcmp(url, "/clients/delete") == 0) { - MHD_destroy_post_processor(request->post_proc); - request->post_proc = NULL; - - // Auth check - if (!is_authenticated(server->app, connection)) { - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Location", "/login"); - ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); - MHD_destroy_response(response); - return ret; - } - - if (!request->form.client_id_set || request->form.client_id <= 0) { - return MHD_NO; - } - - ret = delete_client(server->app, request->form.client_id); - if(ret != 0) return MHD_NO; - - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - ret = MHD_add_response_header(response, "Location", "/clients"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response(connection, MHD_HTTP_SEE_OTHER, response); - MHD_destroy_response(response); - return ret; - } else if(strcmp(url, "/api/report") == 0) { - - cJSON *rootJson = cJSON_ParseWithLength(request->pp.data, request->pp.size); - - cJSON *clientJson = cJSON_GetObjectItemCaseSensitive(rootJson, "client"); - if(!cJSON_IsString(clientJson) || clientJson->valuestring == NULL) { - abort(); - } - - cJSON *secretJson = cJSON_GetObjectItemCaseSensitive(rootJson, "secret"); - if(!cJSON_IsString(secretJson) || secretJson->valuestring == NULL) { - abort(); - } - - cJSON *eventJson = cJSON_GetObjectItemCaseSensitive(rootJson, "event"); - if(!cJSON_IsString(eventJson) || eventJson->valuestring == NULL) { - abort(); - } - int is_finish; - if(strcmp(eventJson->valuestring, "begin_backup") == 0) { - is_finish = 0; - } else if(strcmp(eventJson->valuestring, "end_backup") == 0) { - is_finish = 1; - } else { - abort(); - } - - - ret = submit_report(server->app, clientJson->valuestring, secretJson->valuestring, is_finish); - if(ret == -2) { - cJSON_Delete(rootJson); - destroy_post_collector(&request->pp); - - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, - "", - MHD_RESPMEM_PERSISTENT - ); - ret = MHD_add_response_header(response, "Content-Type", "application/json"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response( - connection, - MHD_HTTP_UNAUTHORIZED, - response - ); - MHD_destroy_response(response); - return ret; - } - if(ret != 0) { - // Should actually return an error page, but this will do for now - return MHD_NO; - } - - cJSON_Delete(rootJson); - // We've taken in all the data, now we can use it - destroy_post_collector(&request->pp); - - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, - "", - MHD_RESPMEM_PERSISTENT - ); - ret = MHD_add_response_header(response, "Content-Type", "application/json"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response( - connection, - MHD_HTTP_NO_CONTENT, - response - ); - MHD_destroy_response(response); - return ret; - } else if(strcmp(url, "/api/client") == 0) { - cJSON *rootJson = cJSON_ParseWithLength(request->pp.data, request->pp.size); - - cJSON *nameJson = cJSON_GetObjectItemCaseSensitive(rootJson, "name"); - if(!cJSON_IsString(nameJson) || nameJson->valuestring == NULL) { - abort(); - } - - struct CreateClientResult result; - ret = create_client(server->app, nameJson->valuestring, &result); - if(ret != 0) { - // Should actually return an error page, but this will do for now - return MHD_NO; - } - - cJSON_Delete(rootJson); - // We've taken in all the data, now we can use it - destroy_post_collector(&request->pp); - - char *buf = malloc(1024); - size_t buf_len = snprintf(buf, 1024, "{ \"id\": \"%ld\", \"secret\": \"%s\" }", result.id, result.secret); - if(buf_len >= 1024) { - abort(); - } - - struct MHD_Response *response = MHD_create_response_from_buffer( - buf_len, - buf, - MHD_RESPMEM_MUST_FREE - ); - ret = MHD_add_response_header(response, "Content-Type", "application/json"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response( - connection, - MHD_HTTP_OK, - response - ); - MHD_destroy_response(response); - return ret; - } else { - // Not found - destroy_post_collector(&request->pp); - - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, - "", - MHD_RESPMEM_PERSISTENT - ); - ret = MHD_add_response_header(response, "Content-Type", "application/json"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response( - connection, - MHD_HTTP_NOT_FOUND, - response - ); - MHD_destroy_response(response); - return ret; } - } else if (strcmp(method, "GET") == 0) { - if (*upload_data_size != 0) return MHD_NO; - - if(strcmp(url, "/login") == 0) { - size_t html_len; - char *html = html_render_login_page(NULL, &html_len); - if (!html) return MHD_NO; - struct MHD_Response *response = MHD_create_response_from_buffer( - html_len, html, MHD_RESPMEM_MUST_FREE); - ret = MHD_add_response_header(response, "Content-Type", "text/html"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - return ret; - } else if(strcmp(url, "/clients") == 0) { - // Auth check - if (!is_authenticated(server->app, connection)) { - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Location", "/login"); - ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); - MHD_destroy_response(response); - return ret; - } - - // Fetch clients from app layer - struct ListClientResult *clients = malloc(sizeof(struct ListClientResult) + sizeof(struct ListClientResultClient) * 16); - ret = list_clients(server->app, clients); - if(ret != 0) { - free(clients); - return MHD_NO; - } - - // Render HTML - size_t html_len; - char *html = html_render_clients_page(request, clients, &html_len); - free(clients); - - if (!html) { - return MHD_NO; - } - - struct MHD_Response *response = MHD_create_response_from_buffer( - html_len, - html, - MHD_RESPMEM_MUST_FREE - ); - ret = MHD_add_response_header(response, "Content-Type", "text/html"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response( - connection, - MHD_HTTP_OK, - response - ); - MHD_destroy_response(response); - return ret; - } else if (strncmp(url, "/client/", 8) == 0) { - // Auth check - if (!is_authenticated(server->app, connection)) { - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Location", "/login"); - ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); - MHD_destroy_response(response); - return ret; - } - - int64_t client_id; - if (sscanf(url + 8, "%ld", &client_id) != 1 || client_id <= 0) { - // Bad Request - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, response); - MHD_destroy_response(response); - return ret; - } - - // Fetch client data from app layer - struct GetClientOnlyResult client_result; - ret = get_client(server->app, client_id, &client_result); - if(ret != 0) { - return MHD_NO; - } - - if(!client_result.found) { - // Not Found - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); - MHD_destroy_response(response); - return ret; - } - - struct GetBackupsResult *backups_result = malloc(sizeof(struct GetBackupsResult) + sizeof(struct BackupRecord) * MAX_BACKUPS_DISPLAY); - backups_result->backups_len = MAX_BACKUPS_DISPLAY; - ret = get_client_backups(server->app, client_id, backups_result); - if(ret != 0) { - free(backups_result); - return MHD_NO; - } - - // Render HTML - size_t html_len; - char *html = html_render_client_detail_page(request, &client_result, backups_result, &html_len); - free(backups_result); - - if (!html) { - // Not Found - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); - MHD_destroy_response(response); - return ret; - } - - struct MHD_Response *response = MHD_create_response_from_buffer( - html_len, - html, - MHD_RESPMEM_MUST_FREE - ); - ret = MHD_add_response_header(response, "Content-Type", "text/html"); - if(ret != MHD_YES) return ret; - ret = MHD_queue_response( - connection, - MHD_HTTP_OK, - response - ); - MHD_destroy_response(response); - return ret; - } else { - // Auth check for default page - if (!is_authenticated(server->app, connection)) { - struct MHD_Response *response = MHD_create_response_from_buffer( - 0, "", MHD_RESPMEM_PERSISTENT); - MHD_add_response_header(response, "Location", "/login"); - ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response); - MHD_destroy_response(response); - return ret; - } + *upload_data_size = 0; + return MHD_YES; + } - struct MHD_Response *response = MHD_create_response_from_buffer( - strlen(server->page), - (void *)server->page, - MHD_RESPMEM_PERSISTENT - ); - - ret = MHD_queue_response( - connection, - MHD_HTTP_OK, - response - ); - MHD_destroy_response(response); - return ret; - } + for (size_t i = 0; i < HANDLER_COUNT; i++) { + enum HandlerStatus status = handlers[i](connection, request); + if (status == HANDLER_ACCEPTED) return MHD_YES; + if (status == HANDLER_ERROR) return MHD_NO; + // HANDLER_DECLINE: try next } - return MHD_NO; + // Should never reach here - handle_not_found always accepts + abort(); } static void request_completed_callback ( @@ -38,6 +38,21 @@ struct Request { struct PostCollector pp; struct MHD_PostProcessor *post_proc; // NULL when using PostCollector struct FormData form; + const char *url; + const char *method; const char *host; const char *proto; }; + +enum HandlerStatus { + HANDLER_DECLINE, + HANDLER_ACCEPTED, + HANDLER_ERROR +}; + +struct MHD_Connection; + +typedef enum HandlerStatus (*RouteHandler)( + struct MHD_Connection *connection, + struct Request *request +); |
