Created
August 22, 2022 19:15
-
-
Save epshteinmatthew/216e7eaca31819c107f3a15f19697dc4 to your computer and use it in GitHub Desktop.
e
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @file webserver.c | |
| * @author Matthew Epshtein (epshteinmatthew@gmail.com) | |
| * @brief The server program for SHtack, the russian roulette for nerds | |
| * @version 1.2 | |
| * @date 2022-08-07 | |
| * | |
| * @copyright Copyright (c) 2022 | |
| * | |
| * | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| * "gcc -o webserver webserver.c -lulfius" to run | |
| */ | |
| #include <stdio.h> //inputs and outputs | |
| #include <ulfius.h> //server library | |
| #include <string.h> //string functions like strcat() and strcpy() | |
| #include <malloc.h> //memory allocation | |
| #include <time.h> //self explanatory... | |
| // vars | |
| #define PORT 8000 | |
| #define NAME "Shtack" | |
| #define VERSION "1.0" | |
| struct _u_instance instance; | |
| // instace of ulfius. Needs to be declared in global scope for dynapathing(see route_push()) | |
| // Note: this variable is initalized. Any function that uses this MUST be called AFTER ulfius_init_instance() | |
| struct linked_list_node | |
| { | |
| char *item; | |
| struct linked_list_node *next; | |
| }; | |
| struct linked_list_node *HEAD = NULL; | |
| /** | |
| * @brief Santizes the command, removes sudo and && | |
| * @param item A string that you wish to sanitize | |
| * @return A sanitized string, in const form to please the ulfius gods | |
| */ | |
| char *sanitize(const char *str) | |
| { | |
| char *item = (char *)malloc(strlen(str) + 1 * sizeof(char)); | |
| strcpy(item, str); | |
| char *cleaneditem = (char *)malloc(strlen(item) + 1 * sizeof(char)); | |
| strcpy(cleaneditem, item); | |
| while (strstr(item, "sudo") != NULL || strstr(item, "su") != NULL) | |
| { | |
| strcpy(cleaneditem, (strstr(item, "su") ? (strstr(item, "do") ? strstr(item, "sudo") + 5 : strstr(item, "su") + 3) : item)); | |
| strcpy(item, cleaneditem); | |
| } | |
| free(item); | |
| char *two = malloc(strlen(cleaneditem) + 1 * sizeof(char)); | |
| do | |
| { | |
| strcpy(two, (strstr(cleaneditem, "&&")) ? strstr(cleaneditem, "&&") : ""); | |
| } while (strcmp(cleaneditem, two) == 0 && strcmp(two, "") != 0); | |
| cleaneditem[strlen(cleaneditem) - (strlen(two))] = '\0'; | |
| char *returned = strdup(cleaneditem); | |
| free(cleaneditem); | |
| free(two); | |
| return returned; | |
| } | |
| /** | |
| * @brief Pushes new item to back of linked list | |
| * @param item A string that you wish to insert to start of linked list | |
| * @return 0 | |
| */ | |
| int push(char *item) | |
| { | |
| struct linked_list_node *new_head = (struct linked_list_node *)malloc(sizeof(struct linked_list_node)); | |
| new_head->item = strdup(item); | |
| new_head->next = HEAD; | |
| HEAD = new_head; | |
| return 0; | |
| } | |
| /** | |
| * @brief Pops last item of linked list | |
| * @return A string containing the popped item, or FAILURE if the linked list is empty | |
| */ | |
| char *pop() | |
| { | |
| if (HEAD == NULL) | |
| { | |
| return NULL; | |
| } | |
| if (HEAD->next == NULL) | |
| { | |
| char *rtrnd = strdup(HEAD->item); | |
| HEAD = NULL; | |
| return rtrnd; | |
| } | |
| struct linked_list_node *current = HEAD; | |
| while (current->next->next != NULL) | |
| { | |
| current = current->next; | |
| } | |
| char *rtrnd = strdup(current->next->item); | |
| current->next = NULL; | |
| free(current->next); | |
| return rtrnd; | |
| } | |
| /** | |
| * @brief Concatenates an array of strings. | |
| * @note Allocates memory for a new string. The user is responsible for freeing this string. | |
| * @param strs Strings to concat, must be terminated by an empty string. | |
| * @return A single string, the result of concatenating all of the input strings. | |
| */ | |
| char *strs_cat(const char **strs) | |
| { | |
| char *output = malloc(1 * sizeof(char)); | |
| size_t curr_len = 0; | |
| for (int i = 0; strs[i][0] != '\0'; ++i) | |
| { | |
| curr_len += strlen(strs[i]); | |
| output = realloc(output, curr_len * sizeof(char)); | |
| if (i == 0) | |
| { | |
| strcpy(output, strs[i]); | |
| continue; | |
| } | |
| strcat(output, strs[i]); | |
| } | |
| return output; | |
| } | |
| char *randpath(char *start){ | |
| int num = random(); | |
| char tstr[64]; | |
| sprintf(tstr, "%d", num); | |
| return strs_cat((const char *[]){"/",start,"/", (char *)strdup(tstr), ""}); | |
| } | |
| char *get_time(void) | |
| { | |
| time_t mytime = time(NULL); | |
| char *time_str = ctime(&mytime); | |
| time_str[strlen(time_str) - 1] = '\0'; | |
| return time_str; | |
| } | |
| /** | |
| * Callback function for the web application on / url call, returns api name and version. | |
| */ | |
| int route(const struct _u_request *request, struct _u_response *response, void *user_data) | |
| { | |
| char *responsetext = strs_cat((const char *[]){"You have reached the 😀", NAME, " API, version ", VERSION, ". ", "It is currently ", get_time(), " wherever this server is running.", ""}); | |
| u_map_put(response->map_header, "Content-Type", "text/plain; charset=utf-8"); | |
| ulfius_set_string_body_response(response, 200, responsetext); | |
| free(responsetext); | |
| return U_CALLBACK_CONTINUE; | |
| } | |
| int route_pop(const struct _u_request *request, struct _u_response *response, void *user_data) | |
| { | |
| char *popped = pop(); | |
| if (popped == NULL) | |
| { | |
| ulfius_set_string_body_response(response, 500, "The SHtack is empty. /push to add to the Shtack."); | |
| return U_CALLBACK_CONTINUE; | |
| } | |
| ulfius_remove_endpoint_by_val(&instance, "GET", request->url_path, NULL); | |
| ulfius_set_string_body_response(response, 200, popped); | |
| return U_CALLBACK_CONTINUE; | |
| } | |
| int route_push(const struct _u_request *request, struct _u_response *response, void *user_data) | |
| { | |
| const char *c1 = sanitize(u_map_get(request->map_post_body, "command")); | |
| if (c1 == NULL) | |
| { | |
| ulfius_set_string_body_response(response, 400, "Not enough commands given. Try again."); | |
| return U_CALLBACK_CONTINUE; | |
| } | |
| push((char *)c1); | |
| if (strcmp(request->url_path, "/push") == 0 || strcmp(request->url_path, "/push/") == 0) | |
| { | |
| char *responsestr = randpath("push"); | |
| ulfius_add_endpoint_by_val(&instance, "POST", responsestr, NULL, 0, &route_push, NULL); | |
| ulfius_set_string_body_response(response, 303, responsestr); | |
| return U_CALLBACK_CONTINUE; | |
| free(responsestr); | |
| } | |
| char *responsestr = randpath("pop"); | |
| ulfius_remove_endpoint_by_val(&instance, "POST", request->url_path, NULL); | |
| ulfius_add_endpoint_by_val(&instance, "GET", responsestr, NULL, 0, &route_pop, NULL); | |
| ulfius_set_string_body_response(response, 303, responsestr); | |
| free(responsestr); | |
| return U_CALLBACK_CONTINUE; | |
| } | |
| /** | |
| * main function | |
| */ | |
| int main(void) | |
| { | |
| // intialize randm seed | |
| srand(time(0)); | |
| // Initialize instance with the port number | |
| if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) | |
| { | |
| fprintf(stderr, "Error ulfius_init_instance, abort\n"); | |
| return (1); | |
| } | |
| // Endpoint list declaration | |
| ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, 0, &route, NULL); | |
| ulfius_add_endpoint_by_val(&instance, "POST", "/push", NULL, 0, &route_push, NULL); | |
| // Start the framework | |
| if (ulfius_start_framework(&instance) == U_OK) | |
| { | |
| printf("Start framework on port %d\n", instance.port); | |
| // Wait for the user to press <enter> on the console to quit the application | |
| getchar(); | |
| } | |
| else | |
| { | |
| fprintf(stderr, "Error starting framework\n"); | |
| } | |
| printf("End framework\n"); | |
| ulfius_stop_framework(&instance); | |
| ulfius_clean_instance(&instance); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment