/* This is part of WHY2 Copyright (C) 2022 Václav Šmejkal 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include pthread_t getline_thread; //LINKED LIST STUFF typedef struct _connection_node { int connection; pthread_t thread; char *username; unsigned long id; char *key; //EACH USER WILL USE DIFFERENT KEY FOR COMMUNICATION struct timespec last_message_time; //USED TO PREVENT SPAMMING unsigned char spam_violations; //SPAM MESSAGES IN ROW } connection_node_t; //SINGLE LINKED LIST typedef struct _read_socket_raw_thread_node { int connection; char *key; } read_socket_raw_thread_node_t; enum ENCRYPTION_DECRYPTION { ENCRYPTION, DECRYPTION }; why2_list_t connection_list = WHY2_LIST_EMPTY; why2_list_t waiting_list = WHY2_LIST_EMPTY; why2_node_t *find_connection(int connection); char *get_string_from_json(struct json_object *json, char *string) { struct json_object *object; json_object_object_get_ex(json, string, &object); return (char*) json_object_get_string(object); } char *get_string_from_json_string(char *json, char *string) { struct json_object *json_obj = json_tokener_parse(json); char *returning = why2_strdup(get_string_from_json(json_obj, string)); //DEALLOCATION json_object_put(json_obj); //GET STRINGS return returning; } int get_int_from_json_string(char *json, char *string) { char *value = get_string_from_json_string(json, string); //GET VALUE int returning = atoi(value); //PARSE why2_deallocate(value); return returning; } int server_config_int(char *key) { char *value_str = why2_chat_server_config(key); //GET STRING int returning = atoi(value_str); //PARSE INT why2_toml_read_free(value_str); return returning; } void send_to_all(char *json) { why2_node_t _first_node = (why2_node_t) { NULL, connection_list.head }; why2_node_t *node_buffer = &_first_node; connection_node_t connection_buffer; //PARSE struct json_object *json_obj = json_tokener_parse(json); char *message = get_string_from_json(json_obj, "message"); char *username = get_string_from_json(json_obj, "username"); while (node_buffer -> next != NULL) //SEND TO ALL CONNECTIONS { node_buffer = node_buffer -> next; connection_buffer = *(connection_node_t*) node_buffer -> value; why2_send_socket(message, username, connection_buffer.key, connection_buffer.connection); //SEND TO CLIENT } //DEALLOCATION json_object_put(json_obj); } void append(char **destination, char *key, char *value) { char *output = why2_calloc(strlen(*destination) + strlen(key) + strlen(value) + 7, sizeof(char)); sprintf(output, "%s%s\"%s\":\"%s\"", *destination, strcmp(*destination, "") == 0 ? "" : ",", key, value); why2_deallocate(*destination); *destination = output; } void add_brackets(char **json) { char *output = why2_calloc(strlen(*json) + 4, sizeof(char)); sprintf(output, "{%s}%c", *json, '\0'); why2_deallocate(*json); *json = output; } why2_bool is_ascii(char c) { return 32 <= c && c <= 126; } void remove_non_ascii(char **text) { //REMOVE NON ASCII CHARS int j = 0; for (int i = 0; (*text)[i] != '\0'; i++) { if (is_ascii((*text)[i])) (*text)[i] = (*text)[j++] = (*text)[i]; } (*text)[j] = '\0'; } char *__fake_base64(char *text, WHY2_UNUSED size_t *length) { return why2_strdup(text); } void encrypt_decrypt_message(char **message, char *key, enum ENCRYPTION_DECRYPTION operation) { //NO ENCRYPTION if (key == NULL) return; //CALLBACKS why2_output_flags (*operation_cb)(char*, char*) = NULL; char *(*base64_before_cb)(char*, size_t*) = NULL; char *(*base64_after_cb)(char*, size_t*) = NULL; //SELECT CORRECT CALLBACK switch (operation) { case ENCRYPTION: operation_cb = why2_encrypt_text; base64_before_cb = __fake_base64; base64_after_cb = why2_chat_base64_encode; break; case DECRYPTION: operation_cb = why2_decrypt_text; base64_before_cb = why2_chat_base64_decode; base64_after_cb = __fake_base64; break; default: why2_die("ENCRYPTION_DECRYPTION not implemented!"); break; } //VARIABLES size_t length = strlen(*message); char *message_decoded = base64_before_cb(*message, &length); //SET FLAGS if (why2_get_key_length() < strlen(key)) why2_set_key_length(strlen(key)); why2_set_flags((why2_input_flags) { 1, 1, 0, WHY2_v4, WHY2_OUTPUT_TEXT, 0 }); //TODO: Add padding //ENCRYPT why2_output_flags output = operation_cb(message_decoded, key); //COPY OUTPUT IN BASE64 why2_deallocate(*message); *message = base64_after_cb(output.output_text, &output.output_text_length); //DEALLOCATION why2_deallocate(message_decoded); why2_deallocate_output(output); } char *read_socket_raw(int socket, char *key) { if (socket == -1) { fprintf(stderr, "Reading socket failed.\n"); return NULL; } char *content_buffer = NULL; char *output; size_t content_size = 0; size_t read_size = 0; char *wait_buffer = why2_malloc(2); //TEMP //WAIT TILl RECEIVED MSG (ik it sucks but i can't think of better solution; anyways, this is way more convenient than infinite loop that makes my computer go wroom wroom) recv(socket, wait_buffer, 1, MSG_PEEK); why2_deallocate(wait_buffer); //PREVENT FROM SPAMMING if (__why2_chat_is_server()) { //VARIABLES int minimal_delay_ns = server_config_int("min_message_delay") * 1000000; struct timespec current_time; why2_node_t *node = find_connection(socket); connection_node_t *client_node; if (node != NULL) { client_node = (connection_node_t*) node -> value; //GET TIME clock_gettime(CLOCK_MONOTONIC, ¤t_time); //NOT THE FIRST MESSAGE if (client_node -> last_message_time.tv_nsec != -1) { int64_t difference = (current_time.tv_sec - client_node -> last_message_time.tv_sec) * 1000000000LL + (current_time.tv_nsec - client_node -> last_message_time.tv_nsec); if (difference < minimal_delay_ns) //USER IS SPAMMING { //CHECK IF USER REACHED MAXIMUM VIOLATIONS if (client_node -> spam_violations > server_config_int("max_message_delay_violations")) return NULL; //WAIT UNTIL MINIMAL DELAY IS ACHIEVED struct timespec sleep_time = { .tv_sec = (minimal_delay_ns - difference) / 1000000000, .tv_nsec = (minimal_delay_ns - difference) % 1000000000 }; nanosleep(&sleep_time, NULL); //SLEEP //ADD VIOLATION client_node -> spam_violations++; } else //USER IS NOT SPAMMING { //RESET VIOLATIONS client_node -> spam_violations = 0; } } //WRITE NEW TIME clock_gettime(CLOCK_MONOTONIC, &client_node -> last_message_time); //GET TIME } } //FIND THE RECEIVED SIZE ioctl(socket, FIONREAD, &content_size); if (content_size == 0) return NULL; //CLIENT PROBABLY QUIT //ALLOCATE content_buffer = why2_malloc(content_size + 1); for (; read_size < content_size; read_size++) { //END OF MESSAGE REACHED (COULD BE CAUSED BY FAST SENT MESSAGES) if (key == NULL && read_size >= 2 && strncmp(content_buffer + read_size - 2, "\"}", 2) == 0) break; if (key != NULL && read_size >= 1 && content_buffer[read_size - 1] == WHY2_CHAT_MESSAGE_DELIMITER) break; //READ JSON MESSAGE if (recv(socket, content_buffer + read_size, 1, 0) != 1) //READ THE MESSAGE BY CHARACTERS { fprintf(stderr, "Socket probably read wrongly!\n"); break; } } //TRUNCATE content_buffer (REMOVE DELIMITER IF ENCRYPTED) output = why2_malloc(read_size + (key != NULL ? 1 : 2)); memcpy(output, content_buffer, read_size - (key != NULL ? 1 : 0)); output[read_size - (key != NULL ? 1 : 0)] = '\0'; encrypt_decrypt_message(&output, key, DECRYPTION); //DECRYPT //REMOVE NON-ASCII remove_non_ascii(&output); //VALIDATE JSON FORMAT struct json_object *json = json_tokener_parse(output); if (json == NULL) { //RESET output why2_deallocate(output); output = NULL; } else { //DEALLOCATION json_object_put(json); } //DEALLOCATION why2_deallocate(content_buffer); return output; } void *read_socket_raw_thread(void *socket) { read_socket_raw_thread_node_t node = *(read_socket_raw_thread_node_t*) socket; return read_socket_raw(node.connection, node.key); } void remove_json_syntax_characters(char *text) { for (size_t i = 0; i < strlen(text); i++) //TODO: DO SOMETHING MORE { if (text[i] == '\"') { text[i] = '\''; } } } void lowercase(char *string) { for (unsigned long i = 0; i < strlen(string); i++) { string[i] = tolower(string[i]); } } why2_bool username_equal(char *u1, char *u2) { char *one = why2_strdup(u1); char *two = why2_strdup(u2); //LOWERCASE lowercase(one); lowercase(two); return strcmp(one, two) == 0; } why2_bool username_equal_deallocate(char *u1, char *u2) //SAME THING AS username_equal BUT IT DEALLOCATES u2 { why2_bool returning = username_equal(u1, u2); why2_toml_read_free(u2); return returning; } why2_bool check_username(char *username) { if (username == NULL) return 0; if (username_equal_deallocate(username, why2_chat_server_config("server_username"))) return 0; //DISABLE 'server' USERNAME if (username_equal_deallocate(username, why2_chat_server_config("default_username"))) return 0; //DISABLE 'anon' USERNAME DUE TO ONE USERNAME PER SERVER for (unsigned long i = 0; i < strlen(username); i++) { if (!((username[i] >= 48 && username[i] <= 57) || (username[i] >= 65 && username[i] <= 90) || //CHECK ONLY FOR a-Z & 0-9 (username[i] >= 97 && username[i] <= 122))) { return 0; } } return 1; } void *stop_oldest_thread(void *id) { sleep(server_config_int("communication_time")); //yk wait if (waiting_list.head == NULL) return NULL; if (**(pthread_t**) waiting_list.head -> value == *(pthread_t*) id) //THREAD IS STILL ALIVE, I HOPE { //KILL THE THREAD pthread_cancel(*(pthread_t*) id); why2_list_remove(&waiting_list, why2_list_find(&waiting_list, id)); } return NULL; } why2_node_t *find_request(void *thread) //COPIED FROM why2_list_find; using double pointers { why2_node_t *head = waiting_list.head; if (head == NULL) return NULL; //EMPTY LIST why2_node_t *buffer = head; while (buffer -> next != NULL) { if (*(void**) buffer -> value == thread) return buffer; buffer = buffer -> next; } if (thread != *(void**) buffer -> value) buffer = NULL; //PREVENT FROM RETURNING INVALID NODE return buffer; } why2_node_t *find_connection(int connection) { why2_node_t *head = connection_list.head; if (head == NULL) return NULL; //EMPTY LIST why2_node_t *buffer = head; while (buffer -> next != NULL) { if ((*(connection_node_t*) buffer -> value).connection == connection) return buffer; buffer = buffer -> next; } if (connection != (*(connection_node_t*) buffer -> value).connection) buffer = NULL; //PREVENT FROM RETURNING INVALID NODE return buffer; } why2_node_t *find_connection_by_id(unsigned long id) { why2_node_t *head = connection_list.head; if (head == NULL) return NULL; //EMPTY LIST why2_node_t *buffer = head; while (buffer -> next != NULL) { if ((*(connection_node_t*) buffer -> value).id == id) return buffer; buffer = buffer -> next; } if (id != (*(connection_node_t*) buffer -> value).id) buffer = NULL; //PREVENT FROM RETURNING INVALID NODE return buffer; } char *read_user(int connection, void **raw_ptr, char *key) { //VARIABLES void *buffer; pthread_t thread_buffer; pthread_t thread_deletion_buffer; //PARSE PARAMETERS read_socket_raw_thread_node_t node = { connection, key }; //RESET VARIABLES *raw_ptr = NULL; buffer = &thread_buffer; //READ pthread_create(&thread_buffer, NULL, read_socket_raw_thread, &node); why2_list_push(&waiting_list, &buffer, sizeof(buffer)); //RUN DELETION THREAD pthread_create(&thread_deletion_buffer, NULL, stop_oldest_thread, &thread_buffer); //WAIT FOR MESSAGE pthread_join(thread_buffer, raw_ptr); why2_list_remove(&waiting_list, find_request(&thread_buffer)); pthread_cancel(thread_deletion_buffer); if (*raw_ptr == WHY2_INVALID_POINTER || *raw_ptr == NULL) return NULL; //QUIT COMMUNICATION IF INVALID PACKET WAS RECEIVED return (char*) *raw_ptr; } char *get_username(int connection) { why2_node_t *node = find_connection(connection); connection_node_t c_node = *(connection_node_t*) node -> value; return c_node.username; } void send_socket_deallocate(char *text, char *username, char *key, int socket) //SAME AS why2_send_socket BUT IT DEALLOCATES username { why2_send_socket(text, username, key, socket); why2_toml_read_free(username); } void send_socket_code_deallocate(char *params, char *username, char *key, int socket, char *code) //SAME AS send_socket_deallocate BUT WITH CODE FIELD { why2_send_socket_code(params, username, key, socket, code); why2_toml_read_free(username); } void send_socket(char *text, char *username, char *why2_key, int socket, why2_bool welcome, char *code) { //VARIABLES char *output = why2_strdup(""); char *buffer; struct json_object *json = json_tokener_parse("{}"); if (text != NULL) { size_t length_buffer = strlen(text); char *text_copy = why2_calloc(length_buffer + 2, sizeof(char)); //COPY text INTO text_copy (WITHOUT LAST CHARACTER WHEN NEWLINE IS AT THE END) if (text[length_buffer - 1] == '\n') length_buffer--; strncpy(text_copy, text, length_buffer); //UNFUCK QUOTES FROM text_copy remove_json_syntax_characters(text_copy); //REMOVE NON ASCII CHARS remove_non_ascii(&text_copy); //ADD OBJECTS json_object_object_add(json, "message", json_object_new_string(text_copy)); //DEALLOCATION why2_deallocate(text_copy); } if (username != NULL) json_object_object_add(json, "username", json_object_new_string(username)); //WAS SENT FROM SERVER if (welcome) //SENDING WELCOME MESSAGE TO USER { //GET FROM CONFIG char *max_uname = why2_chat_server_config("max_username_length"); char *min_uname = why2_chat_server_config("min_username_length"); char *max_tries = why2_chat_server_config("max_username_tries"); char *server_name = why2_chat_server_config("server_name"); //ADD THE INFO OBJS json_object_object_add(json, "max_uname", json_object_new_string(max_uname)); json_object_object_add(json, "min_uname", json_object_new_string(min_uname)); json_object_object_add(json, "max_tries", json_object_new_string(max_tries)); json_object_object_add(json, "server_name", json_object_new_string(server_name)); //DEALLOCATION why2_toml_read_free(max_uname); why2_toml_read_free(min_uname); why2_toml_read_free(max_tries); why2_toml_read_free(server_name); } if (code != NULL) json_object_object_add(json, "code", json_object_new_string(code)); //GENERATE JSON STRING json_object_object_foreach(json, key, value) { append(&output, key, (char*) json_object_get_string(value)); } add_brackets(&output); encrypt_decrypt_message(&output, why2_key, ENCRYPTION); //ENCRYPT //APPEND DELIMITER IF ENCRYPTED if (why2_key != NULL) { //COPY output TO buffer buffer = why2_malloc(strlen(output) + 2); sprintf(buffer, "%s%c%c", output, WHY2_CHAT_MESSAGE_DELIMITER, '\0'); //POINT output TO THE NEW buffer why2_deallocate(output); output = buffer; } //SEND send(socket, output, strlen(output), 0); //DEALLOCATION json_object_put(json); why2_deallocate(output); } void send_welcome_socket_deallocate(char *text, char *username, char *key, int socket) //SAME AS why2_send_socket BUT IT DEALLOCATES username { send_socket(NULL, username, key, socket, 1, text); why2_toml_read_free(username); } void send_welcome_packet(int connection, char *key) { send_welcome_socket_deallocate(WHY2_CHAT_CODE_ACCEPT_MESSAGES, why2_chat_server_config("server_username"), key, connection); } unsigned long get_latest_id() { unsigned long returning = 1; why2_node_t *buffer = connection_list.head; connection_node_t value_buffer; while (buffer != NULL) { value_buffer = *(connection_node_t*) buffer -> value; if (value_buffer.id == returning) { returning++; buffer = connection_list.head; } else { buffer = buffer -> next; } } return returning; } why2_bool perform_key_exchange_client(int connection) { //VARIABLES char *client_pubkey = why2_chat_ecc_serialize_public_key(); //GET PUBLIC KEY char *server_pubkey; char *read = NULL; char *read_code = NULL; why2_bool exiting_read = 0; why2_send_socket_code(client_pubkey, NULL, NULL, connection, WHY2_CHAT_CODE_CLIENT_SERVER_KEY_EXCHANGE); //SEND pubkey TO SERVER why2_deallocate(client_pubkey); //DEALLOCATE client_pubkey //GET SERVER PUBKEY do { why2_deallocate(read); why2_deallocate(read_code); read = read_socket_raw(connection, NULL); read_code = get_string_from_json_string(read, "code"); exiting_read = read_code != NULL && strcmp(read_code, WHY2_CHAT_CODE_SERVER_CLIENT_KEY_EXCHANGE) == 0; } while (!exiting_read); why2_deallocate(read_code); server_pubkey = get_string_from_json_string(read, "message"); why2_chat_set_client_server_key( why2_chat_ecc_shared_key(server_pubkey)); //CALCULATE SHARED KEY //DEALLOCATION why2_deallocate(read); why2_deallocate(server_pubkey); return 0; } why2_bool perform_key_exchange_server(int connection, char **key) { //VARIABLES char *server_pubkey; char *client_pubkey; char *read = NULL; char *read_code = NULL; why2_bool exiting_read = 0; //GET CLIENT PUBKEY do { why2_deallocate(read); why2_deallocate(read_code); read = read_socket_raw(connection, NULL); read_code = get_string_from_json_string(read, "code"); exiting_read = read_code != NULL && strcmp(read_code, WHY2_CHAT_CODE_CLIENT_SERVER_KEY_EXCHANGE) == 0; } while (!exiting_read); why2_deallocate(read_code); client_pubkey = get_string_from_json_string(read, "message"); //GET client_pubkey if (client_pubkey == NULL) return 1; //client is funi //SEND CLIENT PUBKEY server_pubkey = why2_chat_ecc_serialize_public_key(); why2_send_socket_code(server_pubkey, NULL, NULL, connection, WHY2_CHAT_CODE_SERVER_CLIENT_KEY_EXCHANGE); *key = why2_chat_ecc_shared_key(client_pubkey); //DEALLOCATION why2_deallocate(server_pubkey); why2_deallocate(client_pubkey); why2_deallocate(read); return 0; } //GLOBAL void why2_send_socket(char *text, char *username, char *key, int socket) { send_socket(text, username, key, socket, 0, NULL); } void why2_send_socket_code(char *params, char *username, char *key, int socket, char *code) { send_socket(params, username, key, socket, 0, code); } void *why2_communicate_thread(void *arg) { int connection = *(int*) arg; char *client_server_key = NULL; //PERFORM KEY EXCHANGE if (perform_key_exchange_server(connection, &client_server_key)) { close(connection); return NULL; } printf("User connected.\t\t%d\n", connection); send_welcome_packet(connection, client_server_key); //TELL USER ALL THE INFO THEY NEED //GET USERNAME char *config_username = why2_chat_server_config("user_pick_username"); char *raw = NULL; void *raw_ptr = NULL; char *raw_output = NULL; why2_bool force_exiting = 0; why2_bool invalid_username = 1; why2_bool exiting = 0; char *decoded_buffer = NULL; char *decoded_code_buffer = NULL; char *username; int usernames_n = 0; struct json_object *json = json_tokener_parse("{}"); //GET DEFAULT USERNAME char *default_username = why2_chat_server_config("default_username"); username = why2_strdup(default_username); why2_toml_read_free(default_username); if (config_username == NULL || strcmp(config_username, "true") == 0) { if (config_username == NULL) fprintf(stderr, "Your config doesn't contain 'user_pick_username'. Please update your configuration.\n"); send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_PICK_USERNAME); //ASK USER FOR USERNAME while (invalid_username) { why2_deallocate(username); if (usernames_n++ == server_config_int("max_username_tries")) //ASKED CLIENT WAY TOO FUCKING MANY TIMES FOR USERNAME, KICK THEM { exiting = 1; goto deallocation; } //KEEP READING UNTIL CODE ARRIVES char *code = NULL; why2_bool exiting_read = 0; do { //DEALLOCATE why2_deallocate(code); why2_deallocate(raw); if ((raw = read_user(connection, &raw_ptr, client_server_key)) == NULL) //READ { force_exiting = 1; //FAILURE goto deallocation; } //COMPARE CODE code = get_string_from_json_string(raw, "code"); exiting_read = code != NULL && strcmp(code, WHY2_CHAT_CODE_USERNAME) == 0; } while (!exiting_read); why2_deallocate(code); decoded_buffer = get_string_from_json_string(raw, "message"); //DECODE invalid_username = decoded_buffer == NULL || (strlen(decoded_buffer) > (unsigned long) server_config_int("max_username_length")) || (strlen(decoded_buffer) < (unsigned long) server_config_int("min_username_length")) || (!check_username(decoded_buffer)); //CHECK FOR USERNAMES LONGER THAN 20 CHARACTERS username = decoded_buffer; if (!invalid_username) { //GO TROUGH LIST AND CHECK FOR DUPLICITY why2_node_t *buffer = connection_list.head; while (buffer != NULL) { //GET USERNAME connection_node_t *co_node = (connection_node_t*) buffer -> value; if (strcmp(co_node -> username, decoded_buffer) == 0) //COMPARE { invalid_username = 1; break; } buffer = buffer -> next; //NEXT } } //DEALLOCATE STUFF HERE why2_deallocate(raw); why2_deallocate(raw_ptr); if (invalid_username) { send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_INVALID_USERNAME); //TELL THE USER THEY ARE DUMB AS FUCK continue; } //USER CONFIG char *password = NULL; char *user_config_path = why2_get_server_users_path(); if (!why2_toml_contains(user_config_path, decoded_buffer)) //REGISTRATION { send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_ENTER_PASSWORD); //KEEP READING UNTIL CODE ARRIVES char *code = NULL; why2_bool exiting_read = 0; raw = NULL; do { //DEALLOCATE why2_deallocate(code); why2_deallocate(raw); if ((raw = read_user(connection, &raw_ptr, client_server_key)) == NULL) //READ { force_exiting = 1; //FAILURE goto deallocation; } //COMPARE CODE code = get_string_from_json_string(raw, "code"); exiting_read = code != NULL && strcmp(code, WHY2_CHAT_CODE_PASSWORD) == 0; } while (!exiting_read); why2_deallocate(code); password = get_string_from_json_string(raw, "message"); //DECODE why2_toml_write(user_config_path, username, password); //SAVE PASSWORD } else //LOGIN { send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_ENTER_PASSWORD); unsigned char max_tries = (unsigned char) server_config_int("max_password_tries"); for (unsigned char i = 0; i < max_tries; i++) { //KEEP READING UNTIL CODE ARRIVES char *code = NULL; why2_bool exiting_read = 0; raw = NULL; do { //DEALLOCATE why2_deallocate(code); why2_deallocate(raw); if ((raw = read_user(connection, &raw_ptr, client_server_key)) == NULL) //READ { force_exiting = 1; //FAILURE goto deallocation; } //COMPARE CODE code = get_string_from_json_string(raw, "code"); exiting_read = code != NULL && strcmp(code, WHY2_CHAT_CODE_PASSWORD) == 0; } while (!exiting_read); why2_deallocate(code); password = get_string_from_json_string(raw, "message"); //DECODE if (why2_toml_equals(user_config_path, decoded_buffer, password)) break; if (i == max_tries - 1) //NOP, TOO MANY INVALID PASSWORDS { why2_deallocate(password); exiting = 1; goto deallocation; } send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_INVALID_PASSWORD); } } //CLEANUP why2_deallocate(user_config_path); why2_deallocate(password); why2_deallocate(raw); //INFO printf("User set username.\t%d\t%s\n", connection, decoded_buffer); } } why2_toml_read_free(config_username); connection_node_t node = (connection_node_t) { connection, pthread_self(), why2_strdup(username), get_latest_id(), client_server_key, { -1, -1 }, 0 }; why2_list_push(&connection_list, &node, sizeof(node)); //ADD TO LIST raw = why2_strdup(""); char *connection_message = why2_malloc(strlen(username) + 11); char *server_username = why2_chat_server_config("server_username"); //BUILD connection_message sprintf(connection_message, "%s connected", username); //SEND CONNECTION MESSAGE json_object_object_add(json, "message", json_object_new_string(connection_message)); json_object_object_add(json, "username", json_object_new_string(server_username)); json_object_object_foreach(json, key, value) //GENERATE JSON STRING { append(&raw, key, (char*) json_object_get_string(value)); } add_brackets(&raw); json_object_put(json); send_to_all(raw); //FUCKING SEND TO ALL YOU TWAT why2_deallocate(raw); why2_deallocate(connection_message); why2_deallocate(username); why2_toml_read_free(server_username); while (!(exiting || force_exiting)) //KEEP COMMUNICATION ALIVE FOR 5 MINUTES [RESET TIMER AT MESSAGE SENT] { if ((raw = read_user(connection, &raw_ptr, client_server_key)) == NULL) break; //READ json = json_tokener_parse("{}"); raw_output = why2_strdup(""); //REMOVE CONTROL CHARACTERS FROM raw for (size_t i = 0; i < strlen(raw); i++) { if (raw[i] == '\\') raw[i] = '/'; } decoded_buffer = get_string_from_json_string(raw, "message"); //DECODE decoded_code_buffer = get_string_from_json_string(raw, "code"); //DECODE //TRIM MESSAGE if (decoded_buffer != NULL) why2_trim_string(&decoded_buffer); if (decoded_code_buffer != NULL) //CODES FROM CLIENT { if (strcmp(decoded_code_buffer, WHY2_CHAT_CODE_EXIT) == 0) //USER REQUESTED EXIT { exiting = 1; } else if (strcmp(decoded_code_buffer, WHY2_CHAT_CODE_LIST) == 0) //USER REQUESTED LIST OF USERS { why2_node_t *head = connection_list.head; if (head == NULL) goto deallocation; //TODO: Send no users code //BUFFERS why2_node_t *buffer = head; connection_node_t value_buffer; unsigned long alloc_size = 0; size_t last_size = 0; //COUNT REQUIRED MESSAGE ALLOCATION SIZE do { value_buffer = *(connection_node_t*) buffer -> value; alloc_size += strlen(value_buffer.username) + why2_count_int_length(value_buffer.id) + 2; buffer = buffer -> next; //ITER } while (buffer != NULL); char *message = why2_calloc(alloc_size + 1, sizeof(char)); buffer = head; //RESET //FILL THE message do { value_buffer = *(connection_node_t*) buffer -> value; sprintf(message + last_size, "%s;%ld;", value_buffer.username, value_buffer.id); //APPEND last_size = strlen(message); buffer = buffer -> next; //ITER } while (buffer != NULL); //SEND send_socket_code_deallocate(message, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_LIST_SERVER); //DEALLOCATION why2_deallocate(message); } else if (strcmp(decoded_code_buffer, WHY2_CHAT_CODE_VERSION) == 0) { //GET VERSION STRING FROM THE VERSIONS JSON char *message = why2_malloc(strlen(WHY2_VERSION) + 1); //ALLOCATE MESSAGE FOR CLIENT sprintf(message, "%s%c", WHY2_VERSION, '\0'); //CREATE THE MESSAGE //SEND send_socket_code_deallocate(message, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_VERSION_SERVER); //DEALLOCATION why2_deallocate(message); } else if (strcmp(decoded_code_buffer, WHY2_CHAT_CODE_DM) == 0 && decoded_buffer != NULL && strlen(decoded_buffer) != 0) //PM { char *id = NULL; //RECEIVER char *msg; //CHECK ARGS VALIDITY why2_bool valid_param = strlen(decoded_buffer) >= 3; if (valid_param) { valid_param = 0; for (unsigned long i = 1; i < strlen(decoded_buffer); i++) { if (decoded_buffer[i] == ';') { valid_param = 1; //EXTRACT FIRST ARG (ID) id = why2_malloc(i + 1); for (unsigned long j = 0; j < i; j++) { id[j] = decoded_buffer[j]; } id[i] = '\0'; //EXTRACT MESSAGE msg = decoded_buffer + i + 1; break; } } } //FIND CONNECTION why2_node_t *pm_connection = id == NULL ? NULL : find_connection_by_id(atoi(id)); if (pm_connection == NULL) valid_param = 0; if (valid_param) //IGNORE INVALID ARGS { connection_node_t pm_connection_node = *(connection_node_t*) pm_connection -> value; //ALLOCATE MESSAGE TO SEND TO RECEIVER char *private_msg = why2_malloc(strlen(node.username) + strlen(pm_connection_node.username) + strlen(msg) + 5); //CONSTRUCT DA MESSAGE sprintf(private_msg, "%s;%s;%s;%c", node.username, pm_connection_node.username, msg, '\0'); //USER IS SENDING THE MESSAGE TO HIMSELF why2_bool self_pm = pm_connection_node.connection == connection; //SEND YOU DUMB FUCK send_socket_code_deallocate(private_msg, why2_chat_server_config("server_username"), pm_connection_node.key, pm_connection_node.connection, WHY2_CHAT_CODE_DM_SERVER); //RECIPIENT if (!self_pm) send_socket_code_deallocate(private_msg, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_DM_SERVER); //AUTHOR why2_deallocate(private_msg); } //DEALLOCATION why2_deallocate(id); } //IGNORE INVALID CODES, THE USER JUST GOT THEIR LOBOTOMY DONE } else if (decoded_buffer != NULL && strlen(decoded_buffer) != 0 && strncmp(decoded_buffer, WHY2_CHAT_COMMAND_PREFIX, strlen(WHY2_CHAT_COMMAND_PREFIX)) != 0) //IGNORE MESSAGES BEGINNING WITH '!' { //REBUILD MESSAGE WITH USERNAME json_object_object_add(json, "message", json_object_new_string(decoded_buffer)); json_object_object_add(json, "username", json_object_new_string(get_username(connection))); json_object_object_foreach(json, key, value) //GENERATE JSON STRING { append(&raw_output, key, (char*) json_object_get_string(value)); } add_brackets(&raw_output); send_to_all(raw_output); //e } deallocation: //DEALLOCATION why2_deallocate(raw); why2_deallocate(raw_ptr); why2_deallocate(raw_output); why2_deallocate(decoded_buffer); why2_deallocate(decoded_code_buffer); json_object_put(json); } if (exiting) send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), client_server_key, connection, WHY2_CHAT_CODE_SSQC); printf("User disconnected.\t%d\n", connection); //DEALLOCATION close(connection); why2_deallocate(node.username); why2_deallocate(node.key); why2_list_remove(&connection_list, find_connection(connection)); return NULL; } void *why2_authority_communicate_thread(void *arg) { //VARIABLES int connection = *(int*) arg; why2_bool exiting = 0; char *raw; void *raw_ptr; char *message; char *username; char *code; printf("User connected.\t\t%d\n", connection); //SEND USER KEY EXCHANGE CODE why2_send_socket_code(NULL, NULL, NULL, connection, WHY2_CHAT_CODE_KEY_EXCHANGE); do { //READ PACKET if ((raw = read_user(connection, &raw_ptr, NULL)) == NULL) break; //READ //GET DATA message = get_string_from_json_string(raw, "message"); username = get_string_from_json_string(raw, "username"); code = get_string_from_json_string(raw, "code"); if (code != NULL && message != NULL && username != NULL) { if (strcmp(code, WHY2_CHAT_CODE_CLIENT_KEY_EXCHANGE) == 0) { //GET PATH OF USER CERT char *path = why2_get_authority_cert_path(username); FILE *cert; if (access(path, F_OK) == 0) //ALREADY EXISTS { //VARIABLES char *buffer; long buffer_size; cert = why2_fopen(path, "r"); //OPEN FILE //COUNT LENGTH OF buffer AND STORE IT IN buffer_size fseek(cert, 0, SEEK_END); buffer_size = ftell(cert); rewind(cert); //REWIND file_buffer (NO SHIT) //ALLOCATE buffer = why2_calloc(buffer_size + 1, sizeof(char)); //READ if (fread(buffer, buffer_size, 1, cert) == 1) { //SEND STATUS why2_send_socket_code(NULL, NULL, NULL, connection, strcmp(buffer, message) == 0 ? WHY2_CHAT_CODE_SUCCESS : WHY2_CHAT_CODE_FAILURE); } //DEALLOCATION why2_deallocate(buffer); } else { //CREATE CERT cert = why2_fopen(path, "w+"); //CREATE FILE fwrite(message, 1, strlen(message), cert); //WRITE PUBKEY //CONFIRM SUCCESS why2_send_socket_code(NULL, NULL, NULL, connection, WHY2_CHAT_CODE_SUCCESS); } //DEALLOCATION why2_deallocate(path); why2_deallocate(cert); exiting = 1; } } else exiting = 1; //DEALLOCATION why2_deallocate(raw); why2_deallocate(raw_ptr); why2_deallocate(message); why2_deallocate(username); why2_deallocate(code); } while (!exiting); printf("User disconnected.\t%d\n", connection); return NULL; } void *why2_accept_thread(void *params) { __why2_accept_thread_params param = *(__why2_accept_thread_params*) params; //GET PARAMETERS //VARIABLES int accepted; pthread_t thread; //LOOP ACCEPT for (;;) { accepted = accept(param.socket, (struct sockaddr *) NULL, NULL); //ACCEPT NEW SOCKET if (accepted == -1) continue; void *(*communication_thread)(void*) = NULL; switch (param.type) //GET communication_thread { case WHY2_CHAT_SERVER: communication_thread = why2_communicate_thread; break; case WHY2_CHAT_AUTHORITY: communication_thread = why2_authority_communicate_thread; break; default: why2_die("WHY2_CHAT_SERVER_TYPE not implemented!"); break; } pthread_create(&thread, NULL, communication_thread, &accepted); } return NULL; } void why2_clean_connections(void) { why2_node_t *head = connection_list.head; if (head == NULL) return; //EMPTY LIST why2_node_t *node_buffer = head; why2_node_t *node_buffer_2; connection_node_t connection_buffer; do //GO TROUGH LIST { node_buffer_2 = node_buffer; node_buffer = node_buffer -> next; connection_buffer = *(connection_node_t*) node_buffer_2 -> value; send_socket_code_deallocate(NULL, why2_chat_server_config("server_username"), connection_buffer.key, connection_buffer.connection, WHY2_CHAT_CODE_SSQC); close(connection_buffer.connection); why2_list_remove(&connection_list, node_buffer_2); //REMOVE } while (node_buffer != NULL); } void why2_clean_threads(void) { why2_node_t *head = waiting_list.head; if (head == NULL) return; //EMPTY LIST why2_node_t *node_buffer = head; why2_node_t *node_buffer_2; do //GO TROUGH LIST { node_buffer_2 = node_buffer; node_buffer = node_buffer -> next; pthread_cancel(**(pthread_t**)(node_buffer_2 -> value)); why2_list_remove(&waiting_list, node_buffer_2); //REMOVE } while (node_buffer != NULL); } void *why2_listen_server(void *socket) { int connection = *(int*) socket; //PERFORM KEY EXCHANGE if (perform_key_exchange_client(connection)) return NULL; //STUFF char *read = NULL; why2_bool exiting = 0; why2_bool continuing; unsigned char asking_username = 0; unsigned char asking_password = 0; char *server_uname = NULL; //CONTENT char *username = NULL; char *message = NULL; char *code = NULL; //SERVER SETTINGS int max_uname = -1; int min_uname = -1; int max_tries = -1; char *server_name = NULL; printf(">>> "); fflush(stdout); while (!exiting) { continuing = 0; read = read_socket_raw(connection, why2_chat_get_client_server_key()); if (read == NULL) continue; //GET CONTENT username = get_string_from_json_string(read, "username"); message = get_string_from_json_string(read, "message"); code = get_string_from_json_string(read, "code"); if (server_uname == NULL) //GET SERVER USERNAME { if (code == NULL || strcmp(code, WHY2_CHAT_CODE_ACCEPT_MESSAGES) != 0) goto deallocation; server_uname = why2_strdup(username); //GET USERNAME //GET INFO max_uname = get_int_from_json_string(read, "max_uname"); min_uname = get_int_from_json_string(read, "min_uname"); max_tries = get_int_from_json_string(read, "max_tries"); server_name = get_string_from_json_string(read, "server_name"); printf(WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP "Successfully connected to %s.\nUse !help for commands.\n\n\n", server_name); //WELCOME why2_deallocate(server_name); continuing = 1; } if ((strcmp(username, server_uname) == 0 && code != NULL) && !continuing) //CODE WAS SENT { if (strcmp(code, WHY2_CHAT_CODE_SSQC) == 0) //SERVER BROKE UP WITH YOU { printf("%s%s%s\nServer closed the connection.\n", asking_username > max_tries ? WHY2_CLEAR_AND_GO_UP : "", WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP, (asking_username == 0 ? "\n": "")); fflush(stdout); pthread_cancel(getline_thread); //CANCEL CLIENT getline exiting = 1; //EXIT THIS THREAD } else if (strcmp(code, WHY2_CHAT_CODE_PICK_USERNAME) == 0 || strcmp(code, WHY2_CHAT_CODE_INVALID_USERNAME) == 0) //PICK USERNAME (COULD BE CAUSE BY INVALID USERNAME) { __why2_set_asking_username(1); if (strcmp(code, WHY2_CHAT_CODE_INVALID_USERNAME) == 0) //INVALID USERNAME { printf(WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP "%s\nInvalid username!\n\n\n", asking_username > 1 ? WHY2_CLEAR_AND_GO_UP : ""); fflush(stdout); } printf("%s%sEnter username (a-Z, 0-9; %d-%d characters):\n", asking_username++ > 0 ? WHY2_CLEAR_AND_GO_UP : "", WHY2_CLEAR_AND_GO_UP, min_uname, max_uname); fflush(stdout); } else if (strcmp(code, WHY2_CHAT_CODE_LIST_SERVER) == 0) //LIST USERS { why2_bool printing_id = 0; printf("\nList:\n-----\n"); //ITER TROUGH LIST OF USERS FROM SERVER for (unsigned long i = 0; i < strlen(message); i++) { if (message[i] == ';') { printf((printing_id = !printing_id) ? " (" : ")\n"); continue; } printf("%c", message[i]); } printf("\n"); fflush(stdout); } else if (strcmp(code, WHY2_CHAT_CODE_VERSION_SERVER) == 0) { //INFO printf("\nServer Version: %s\nClient Version: %s\n\n", message, WHY2_VERSION); //SERVER IS OUTDATED if (atoi(message + 1) < atoi(WHY2_VERSION + 1)) { printf("Server is outdated. Some new features may not work correctly.\n\n"); } } else if (strcmp(code, WHY2_CHAT_CODE_DM_SERVER) == 0) { printf(WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP); //do not fucking ask me how the fucking formatting fucking works, i dont fucking know //DECODED MESSAGE, AUTHOR AND RECIPIENT; 0 = AUTHOR, 1 = RECIPIENT, 2 = MESSAGE char **pm_info = why2_calloc(3, sizeof(char*)); unsigned long i_buffer = 0; unsigned long n_buffer = 0; //DECODE for (unsigned long i = 0; i < strlen(message); i++) { if (message[i] == ';') //FUTURE ME, THIS IS PRETTY MUCH split FUNCTION IMPLEMENTATION { //ALLOCATE INFO pm_info[n_buffer] = why2_malloc((i - i_buffer) + 1); //COPY INFO for (unsigned long j = i_buffer; j < i; j++) { pm_info[n_buffer][j - i_buffer] = message[j]; } pm_info[n_buffer][i - i_buffer] = '\0'; i_buffer = i + 1; n_buffer++; } } //OUTPUT if (strcmp(pm_info[0], pm_info[1]) != 0 || strlen(pm_info[0]) != 13) { printf("\n\n%s(%s -> %s): %s\n\n", WHY2_CLEAR_AND_GO_UP, pm_info[0], pm_info[1], pm_info[2]); } else { printf("\n\n%s(schizophrenia): %s\n\n", WHY2_CLEAR_AND_GO_UP, pm_info[2]); //:)) } //DEALLOCATION for (int i = 0; i < 3; i++) { why2_deallocate(pm_info[i]); } why2_deallocate(pm_info); } else if (strcmp(code, WHY2_CHAT_CODE_ENTER_PASSWORD) == 0 || strcmp(code, WHY2_CHAT_CODE_INVALID_PASSWORD) == 0) //PICK USERNAME (COULD BE CAUSE BY INVALID USERNAME) { __why2_set_asking_password(1); if (strcmp(code, WHY2_CHAT_CODE_INVALID_PASSWORD) == 0) //INVALID USERNAME { printf(WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP "%s\nInvalid password!\n\n\n", asking_password > 1 ? WHY2_CLEAR_AND_GO_UP : ""); fflush(stdout); } printf("%s%s\nEnter password:\n", asking_password++ > 0 ? WHY2_CLEAR_AND_GO_UP : "", WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP); fflush(stdout); //TODO! THIS SOMEHOW BREAKS THE CLIENT INPUT } } else if (!continuing) { printf(WHY2_CLEAR_AND_GO_UP WHY2_CLEAR_AND_GO_UP); //do not fucking ask me how the fucking formatting fucking works, i dont fucking know if (asking_username > 1) { asking_username = 0; printf(WHY2_CLEAR_AND_GO_UP); } printf("\n\n%s%s: %s\n\n", WHY2_CLEAR_AND_GO_UP, username, message); } if (!exiting && !continuing) { printf(">>> "); fflush(stdout); } deallocation: //DEALLOCATION why2_deallocate(read); why2_deallocate(username); why2_deallocate(message); why2_deallocate(code); } why2_deallocate(server_uname); return NULL; } void *why2_listen_authority(void *socket) { //VARIABLES int socket_ptr = *(int*) socket; char *read; why2_bool exiting = 0; why2_bool *success = why2_malloc(sizeof(why2_bool)); char *code; *success = 0; do { //READ PACKET read = read_socket_raw(socket_ptr, NULL); if (read == NULL) continue; //INVALID PACKET RECEIVED //GET DATA code = get_string_from_json_string(read, "code"); if (code != NULL) { if (strcmp(code, WHY2_CHAT_CODE_KEY_EXCHANGE) == 0) { //VARIABLES char *user_config_path = why2_get_client_config_path(); char *username; why2_bool asked_username = 0; //FOR CORRECT DEALLOCATION FN if (!why2_toml_contains(user_config_path, "authority_username")) { //GET USERNAME FOR CA pthread_t thread_getline; printf("Enter username for CA server.\nIn order to be authenticated, you will need to use this username on server too.\n>>> "); //GET INPUT pthread_create(&thread_getline, NULL, why2_getline_thread, NULL); pthread_join(thread_getline, (void**) &username); //REMOVE \n username[strlen(username) - 1] = '\0'; //SAVE why2_toml_write_preserve(user_config_path, "authority_username", username); asked_username = 1; printf("\n"); } else username = why2_chat_client_config("authority_username"); //SEND CA CLIENT'S ENCRYPTED PUBKEY char *key = why2_chat_ecc_serialize_public_key(); why2_send_socket_code(key, username, NULL, socket_ptr, WHY2_CHAT_CODE_CLIENT_KEY_EXCHANGE); //SEND //DEALLOCATION why2_deallocate(user_config_path); why2_deallocate(key); if (asked_username) { free(username); } else { why2_toml_read_free(username); } } else { //EXIT *success = strcmp(code, WHY2_CHAT_CODE_SUCCESS) == 0; exiting = 1; } } //DEALLOCATION why2_deallocate(read); why2_deallocate(code); } while (!exiting); return success; } void *why2_getline_thread(WHY2_UNUSED void* arg) { getline_thread = pthread_self(); char *line = NULL; size_t line_length = 0; if (getline(&line, &line_length, stdin) == -1) why2_die("Reading input failed."); return line; } void why2_trim_string(char **s) { //GET CORRECT ALLOCATION FNS void* (*allocate)(unsigned long); void (*deallocate)(void*); if (why2_allocated(*s)) { allocate = why2_malloc; deallocate = why2_deallocate; } else { allocate = malloc; deallocate = free; } //BUFFERS unsigned long start_spaces = 0; unsigned long end_spaces = 0; unsigned long actual_length; //COUNT start_spaces (HOW MANY SPACES ARE IN THE START) for (unsigned long i = 0; i < strlen(*s); i++) { if ((*s)[i] != ' ') break; start_spaces++; } //COUNT end_spaces for (long long i = (long long) strlen(*s) - 1; i >= 0; i--) { if ((*s)[i] != ' ') break; end_spaces++; } //USER'S HEAD HAS FELL ON THE SPACEBAR if (start_spaces + end_spaces > strlen(*s)) { deallocate(*s); *s = NULL; return; } //COUNT actual_length actual_length = strlen(*s) - (end_spaces + start_spaces); if (actual_length == strlen(*s)) return; //NO SPACES TO REMOVE char *st = allocate(actual_length + 2); //TRIMMED s for (unsigned long i = start_spaces; i < (start_spaces + actual_length); i++) { st[i - start_spaces] = (*s)[i]; } st[actual_length] = '\0'; //DEALLOCATE UNTRIMMED s deallocate(*s); //SET NEW s *s = st; }