From e4863ffafa168e5848d8ba9709e1e287719c8fb2 Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 14 May 2026 19:37:32 +0300 Subject: [PATCH 01/18] docs: change invalid standard text --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7c57ae..5105517 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Shivers is a Discord bot made for hobby and educational purposes * `include`: Header files of everything ## Code -This project follows GNU99 standard and tries not to use external libraries, so includes some libraries that developed to use in this project. +This project follows C23 standard and tries not to use external libraries, so includes some libraries that developed to use in this project. ## License This project is licensed under [BSD-3-Clause](./LICENSE) license. From 858966d9a0ab243486e08c01045324f477e49326 Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 14 May 2026 21:42:15 +0300 Subject: [PATCH 02/18] feat(utils): add ASSERT() macro --- include/shivers.h | 1 + include/utils.h | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/shivers.h b/include/shivers.h index 53364af..f25a741 100644 --- a/include/shivers.h +++ b/include/shivers.h @@ -9,6 +9,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export diff --git a/include/utils.h b/include/utils.h index 48b441c..8b64786 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,8 +1,11 @@ #pragma once -#include - -#include +// To guarantee code execution +#define ASSERT(actual, op, expected) do { \ + typeof(actual) __actual_val = (actual); \ + typeof(expected) __expected_val = (expected); \ + assert(__actual_val op __expected_val); \ +} while (0) struct SplitData { char *data; From e959878db04c1f48f6b8d834d81824892fbf11ae Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 14 May 2026 21:49:25 +0300 Subject: [PATCH 03/18] refactor: remove redundant headers from `about` command --- include/shivers.h | 1 + src/commands/about.c | 11 ----------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/include/shivers.h b/include/shivers.h index f25a741..aca6cdf 100644 --- a/include/shivers.h +++ b/include/shivers.h @@ -34,6 +34,7 @@ #include // IWYU pragma: export #if defined(__WIN32__) + #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export diff --git a/src/commands/about.c b/src/commands/about.c index 32c5906..adfa2a7 100644 --- a/src/commands/about.c +++ b/src/commands/about.c @@ -1,16 +1,5 @@ #include -#include -#include - -#if defined(_WIN32) - #include - #include - #include -#elif defined(__linux__) - #include -#endif - #define YEAR (60 * 60 * 24 * 30 * 12) #define MONTH (60 * 60 * 24 * 30) #define DAY (60 * 60 * 24) From 9646467cd7b707fd545fdfec1805926545047437 Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 14 May 2026 21:56:53 +0300 Subject: [PATCH 04/18] fix: safe `get_timestamp()` method * Replace deprecated `gettimeofday()` method * Add error return value and handle them --- include/utils.h | 5 ++++- libs/discord/gateway.c | 18 +++++++++++++++--- libs/utils/get_timestamp.c | 16 ++++++++++------ src/commands/about.c | 8 ++++++-- src/utils/cooldown.c | 10 ++++++---- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/include/utils.h b/include/utils.h index 8b64786..22ca4f0 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,5 +1,7 @@ #pragma once +#include + // To guarantee code execution #define ASSERT(actual, op, expected) do { \ typeof(actual) __actual_val = (actual); \ @@ -52,7 +54,8 @@ char *base64_encode(const char *data, const unsigned long data_length); int char_at(char *data, const char ch); -unsigned long long get_timestamp(); +int64_t get_timestamp(); +#define GET_TIMESTAMP_ERROR ("get_timestamp(): Cannot get current time") void throw(const char *format, ...); diff --git a/libs/discord/gateway.c b/libs/discord/gateway.c index 04bb76d..2939e51 100644 --- a/libs/discord/gateway.c +++ b/libs/discord/gateway.c @@ -50,7 +50,11 @@ static void send_heartbeat() { "}", last_sequence); } - heartbeat_sent_at = get_timestamp(); + const int64_t now = get_timestamp(); + if (now == -1) + throw(GET_TIMESTAMP_ERROR); + + heartbeat_sent_at = now; send_websocket_message(&websocket, heartbeat_message); } @@ -212,9 +216,13 @@ static void onmessage(const struct WebsocketFrame frame) { last_sequence = json_get_val(data, "s").value.number; if (streq(event_name, "READY")) { + const int64_t now = get_timestamp(); + if (now == -1) + throw(GET_TIMESTAMP_ERROR); + shivers.client.user = clone_json_element(json_get_val(data, "d.user").element); shivers.client.token = token; - shivers.client.ready_at = get_timestamp(); + shivers.client.ready_at = now; shivers.client.guilds = create_hashmap(16); const jsonresult_t json_resume_gateway_url = json_get_val(data, "d.resume_gateway_url"); @@ -459,8 +467,12 @@ static void onmessage(const struct WebsocketFrame frame) { } case 11: { + const int64_t now = get_timestamp(); + if (now == -1) + throw(GET_TIMESTAMP_ERROR); + previous_heartbeat_sent_at = heartbeat_sent_at; - heartbeat_received_at = get_timestamp(); + heartbeat_received_at = now; break; } } diff --git a/libs/utils/get_timestamp.c b/libs/utils/get_timestamp.c index b9ec3b4..15eb38e 100644 --- a/libs/utils/get_timestamp.c +++ b/libs/utils/get_timestamp.c @@ -1,13 +1,17 @@ #include -unsigned long long get_timestamp() { +int64_t get_timestamp() { #if defined(__linux__) - struct timeval timestamp; - gettimeofday(×tamp, NULL); - return ((timestamp.tv_sec * 1000) + (timestamp.tv_usec / 1000)); - #elif defined(_WIN32) + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) + return -1; + + return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000); + #elif defined(__WIN32__) struct _timeb timestamp; - _ftime_s(×tamp); + if (_ftime_s(×tamp) != 0) + return -1; + return ((timestamp.time * 1000) + timestamp.millitm); #endif } diff --git a/src/commands/about.c b/src/commands/about.c index adfa2a7..758df29 100644 --- a/src/commands/about.c +++ b/src/commands/about.c @@ -7,7 +7,11 @@ #define MINUTE (60) static void set_uptime_text(struct Client client, char uptime_text[]) { - unsigned long long seconds = ((get_timestamp() - client.ready_at) / 1000); + const int64_t now = get_timestamp(); + if (now == -1) + throw(GET_TIMESTAMP_ERROR); + + uint64_t seconds = ((now - client.ready_at) / 1000); const int years = (seconds / YEAR); seconds -= (years * YEAR); const int months = (seconds / MONTH); @@ -63,7 +67,7 @@ static void set_uptime_text(struct Client client, char uptime_text[]) { ++value; uptime[value].data = sseconds; - uptime[value].length = sprintf(uptime[value].data, "%llu secs", seconds); + uptime[value].length = sprintf(uptime[value].data, "%lu secs", seconds); } ++value; diff --git a/src/utils/cooldown.c b/src/utils/cooldown.c index 0bc7f59..e59391f 100644 --- a/src/utils/cooldown.c +++ b/src/utils/cooldown.c @@ -2,8 +2,10 @@ void run_with_cooldown(const char *user_id, void (*execute)(struct Shivers *shivers, const struct InteractionCommand command), struct Shivers *shivers, const struct InteractionCommand command) { const struct Node *cooldown = get_node(shivers->cooldowns, user_id); - unsigned long long target = (cooldown ? (*((unsigned long long *) cooldown->value) + 3000) : 0); - unsigned long long current = get_timestamp(); + const uint64_t target = (cooldown ? (*((uint64_t *) cooldown->value) + 3000) : 0); + int64_t current = get_timestamp(); + if (current == -1) + throw(GET_TIMESTAMP_ERROR); if (target > current) { char warning[51]; @@ -23,9 +25,9 @@ void run_with_cooldown(const char *user_id, void (*execute)(struct Shivers *shiv send_message(shivers->client, message); } else { if (cooldown != NULL) { - memcpy(get_node(shivers->cooldowns, user_id)->value, ¤t, sizeof(unsigned long long)); + memcpy(get_node(shivers->cooldowns, user_id)->value, ¤t, sizeof(int64_t)); } else { - insert_node(shivers->cooldowns, user_id, ¤t, sizeof(unsigned long long)); + insert_node(shivers->cooldowns, user_id, ¤t, sizeof(int64_t)); } execute(shivers, command); From 63a5b7cb95cc000360708080d2bda94a2dbacd3b Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 14 May 2026 22:55:22 +0300 Subject: [PATCH 05/18] fix: safe `base64_encode()` method --- include/utils.h | 3 ++ libs/network/websocket.c | 14 +++++++- libs/utils/types.c | 71 ++++++++++++++++++---------------------- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/include/utils.h b/include/utils.h index 22ca4f0..1715949 100644 --- a/include/utils.h +++ b/include/utils.h @@ -50,7 +50,10 @@ char *ltrim(const char *src); unsigned long ahtoi(const char *data); int atoi_s(const char *str, short length); + + char *base64_encode(const char *data, const unsigned long data_length); +#define BASE64_ENCODE_ERROR ("base64_encode(): cannot decode key") int char_at(char *data, const char ch); diff --git a/libs/network/websocket.c b/libs/network/websocket.c index 8fd8126..2151271 100644 --- a/libs/network/websocket.c +++ b/libs/network/websocket.c @@ -137,11 +137,19 @@ static void check_response(struct Websocket *websocket, const char *response, ch memcpy(final + key_length, websocket_guid, websocket_guid_length); final[final_length] = 0; + free(key); + unsigned char sha1_hash[SHA_DIGEST_LENGTH + 1]; SHA1((unsigned char *) final, final_length, sha1_hash); + char *result = base64_encode((char *) sha1_hash, SHA_DIGEST_LENGTH); + if (result == NULL) { + split_free(line_splitter); + split_free(splitter); + close_websocket(websocket, -1, NULL); - free(key); + throw(BASE64_ENCODE_ERROR); + } if (!streq(result, value)) { free(result); @@ -173,6 +181,10 @@ static void switch_protocols(struct Websocket *websocket) { } websocket->key = base64_encode((char *) key_data, 16); + if (websocket->key == NULL) { + close_websocket(websocket, -1, NULL); + throw(BASE64_ENCODE_ERROR); + } char request_message[512]; diff --git a/libs/utils/types.c b/libs/utils/types.c index 7d1746c..fa2eb14 100644 --- a/libs/utils/types.c +++ b/libs/utils/types.c @@ -1,61 +1,52 @@ #include -char *base64_encode(const char *data, const unsigned long data_length) { +char *base64_encode(const char *data, const uint64_t data_length) { const char base64_alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - const unsigned char m3 = (data_length % 3); - const unsigned long d3 = ((data_length - m3) / 3); - const unsigned long loop_limit = ((m3 == 0) ? d3 : (d3 + 1)); + const uint8_t m3 = (data_length % 3); // surplus length + const uint64_t d3 = ((data_length - m3) / 3); // full parts + const uint64_t loop_limit = ((m3 == 0) ? d3 : (d3 + 1)); - unsigned long response_length; - - switch (m3) { - case 0: - response_length = (d3 * 4); - break; - - case 1: - response_length = ((d3 + 1) * 4); - break; - - case 2: - response_length = ((d3 + 1) * 4); - break; - } + const uint64_t response_length = ((d3 + ((uint64_t) (m3 != 0))) * 4); + uint64_t response_index = 0; char *response = allocate(NULL, 0, response_length + 1, sizeof(char)); + if (response == NULL) + return NULL; - for (unsigned long i = 0; i < loop_limit; ++i) { - unsigned long number = 0; - const int di = i * 3; + for (uint64_t i = 0; i < loop_limit; ++i) { + uint64_t number = 0; + const uint64_t di = i * 3; if (m3 == 0 || (i + 1) != loop_limit) { - number |= (unsigned char) data[di] << 16; - number |= (unsigned char) data[di + 1] << 8; - number |= (unsigned char) data[di + 2]; - - strncat(response, &base64_alphabet[(number >> 18) & 0x3F], 1); - strncat(response, &base64_alphabet[(number >> 12) & 0x3F], 1); - strncat(response, &base64_alphabet[(number >> 6) & 0x3F], 1); - strncat(response, &base64_alphabet[number & 0x3F], 1); + number |= (uint8_t) data[di] << 16; + number |= (uint8_t) data[di + 1] << 8; + number |= (uint8_t) data[di + 2]; + + response[response_index++] = base64_alphabet[(number >> 18) & 0x3F]; + response[response_index++] = base64_alphabet[(number >> 12) & 0x3F]; + response[response_index++] = base64_alphabet[(number >> 6) & 0x3F]; + response[response_index++] = base64_alphabet[number & 0x3F]; } else if (m3 == 2) { - number |= (unsigned char) data[di] << 8; - number |= (unsigned char) data[di + 1]; + number |= (uint8_t) data[di] << 8; + number |= (uint8_t) data[di + 1]; number = number << 2; - strncat(response, &base64_alphabet[(number >> 12) & 0x3F], 1); - strncat(response, &base64_alphabet[(number >> 6) & 0x3F], 1); - strncat(response, &base64_alphabet[number & 0x3F], 1); - strcat(response, "="); + response[response_index++] = base64_alphabet[(number >> 12) & 0x3F]; + response[response_index++] = base64_alphabet[(number >> 6) & 0x3F]; + response[response_index++] = base64_alphabet[number & 0x3F]; + response[response_index++] = '='; } else if (m3 == 1) { - number |= (unsigned char) data[di]; + number |= (uint8_t) data[di]; number = number << 4; - strncat(response, &base64_alphabet[(number >> 6) & 0x3F], 1); - strncat(response, &base64_alphabet[number & 0x3F], 1); - strcat(response, "=="); + response[response_index++] = base64_alphabet[(number >> 6) & 0x3F]; + response[response_index++] = base64_alphabet[number & 0x3F]; + response[response_index++] = '='; + response[response_index++] = '='; } } + ASSERT(response_length, ==, response_index); return response; } From a0e701658a23c7cb02cc277ddabd849c45e6e941 Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 15 May 2026 17:15:36 +0300 Subject: [PATCH 06/18] fix: safe `throw()` method --- libs/utils/errors.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/errors.c b/libs/utils/errors.c index 4f2bb7d..5d502a4 100644 --- a/libs/utils/errors.c +++ b/libs/utils/errors.c @@ -4,9 +4,9 @@ void throw(const char *format, ...) { char message[256]; va_list args; va_start(args, format); - vsnprintf(message, 256, format, args); + ASSERT(vsnprintf(message, 256, format, args), >, 0); va_end(args); - fprintf(stderr, "%s\n", message); + ASSERT(fprintf(stderr, "%s\n", message), >, 0); exit(EXIT_FAILURE); } From 84e96f09fa383f881b1cfb96c31bf3fde435f6d5 Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 15 May 2026 17:16:16 +0300 Subject: [PATCH 07/18] fix: save `ahtoi()` and `atoi_s()` methods * Some `atoi_s()`s have not replaced yet --- include/utils.h | 7 +++++-- libs/network/request.c | 43 +++++++++++++++++++++++++++++++----------- libs/network/utils.c | 13 ++++++++----- libs/utils/types.c | 28 ++++++++++++++------------- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/include/utils.h b/include/utils.h index 1715949..3fd6489 100644 --- a/include/utils.h +++ b/include/utils.h @@ -48,8 +48,11 @@ bool streq(const char *str1, const char *str2); void strreplace(char **source, char *target, char *replacement); char *ltrim(const char *src); -unsigned long ahtoi(const char *data); -int atoi_s(const char *str, short length); +// Converts hex string to integer, -1 on failure +int64_t ahtoi(const char *data); + +// Converts string to integer safely -1 on failure +int64_t atoi_s(const char *str, int16_t length); char *base64_encode(const char *data, const unsigned long data_length); diff --git a/libs/network/request.c b/libs/network/request.c index d386629..40ecbc3 100644 --- a/libs/network/request.c +++ b/libs/network/request.c @@ -219,6 +219,13 @@ struct Response request(struct RequestConfig config) { const unsigned long status_message_length = calculate_join((struct Join *) status_splitter.data + 2, status_splitter.size - 2, " "); response.status.code = atoi_s(status_splitter.data[1].data, status_splitter.data[1].length); + if (response.status.code == -1) { + split_free(line_splitter); + split_free(status_splitter); + close_socket(sockfd, ssl); + throw_network("request(): invalid http status code", false); + } + response.status.message = allocate(NULL, -1, status_message_length + 1, sizeof(char)); join((struct Join *) status_splitter.data + 2, response.status.message, status_splitter.size - 2, " "); @@ -254,19 +261,24 @@ struct Response request(struct RequestConfig config) { ++i; while (i < line_splitter.size) { - const unsigned int hex_length = ahtoi(line_splitter.data[i].data); const unsigned int line_length = line_splitter.data[i + 1].length; + const int64_t hex_length = ahtoi(line_splitter.data[i].data); + if (hex_length == -1) { + split_free(line_splitter); + free(response_message); + close_socket(sockfd, ssl); + throw_network("request(): invalid http chunked message", false); + } - if (line_length != 0) { - if (line_length == hex_length) { - response.data_size += hex_length; - response.data = allocate(response.data, -1, response.data_size + 1, sizeof(char)); - memcpy(response.data + response.data_size - hex_length, line_splitter.data[i + 1].data, line_length); - } else { - throw_network("invalid http message encoding", false); - } - } else { + if (line_length == 0) break; + + if (line_length == hex_length) { + response.data_size += hex_length; + response.data = allocate(response.data, -1, response.data_size + 1, sizeof(char)); + memcpy(response.data + response.data_size - hex_length, line_splitter.data[i + 1].data, line_length); + } else { + throw_network("request(): invalid http message encoding", false); } i += 2; @@ -274,7 +286,16 @@ struct Response request(struct RequestConfig config) { response.data[response.data_size] = 0; } else if (response.status.code != 204) { - response.data_size = atoi_s(get_header(response.headers, response.header_size, "content-length").value, -1); + const int64_t value = atoi_s(get_header(response.headers, response.header_size, "content-length").value, -1); + if (value == -1) { + split_free(line_splitter); + free(response_message); + close_socket(sockfd, ssl); + + throw_network("request(): invalid Content-Length value", false); + } + + response.data_size = value; response.data = allocate(response.data, -1, response.data_size + 1, sizeof(char)); join((struct Join *) line_splitter.data + i + 1, (char *) response.data, line_splitter.size - i - 1, "\r\n"); diff --git a/libs/network/utils.c b/libs/network/utils.c index e266c5f..004a91b 100644 --- a/libs/network/utils.c +++ b/libs/network/utils.c @@ -5,7 +5,7 @@ struct URL parse_url(const char *data) { struct Split splitter = split(data, strlen(data), "/"); if (splitter.size < 3) { - throw("request(): invalid url format"); + throw("parse_url(): invalid url format"); } struct Split hostname_splitter = split(splitter.data[2].data, splitter.data[2].length, ":"); @@ -19,7 +19,11 @@ struct URL parse_url(const char *data) { memcpy(url.hostname, hostname_splitter.data[0].data, hostname_size); if (hostname_splitter.size == 2) { - url.port = atoi_s(hostname_splitter.data[1].data, hostname_splitter.data[1].length); + const int64_t value = atoi_s(hostname_splitter.data[1].data, hostname_splitter.data[1].length); + if (value == -1) + throw("parse_url(): invalid port value"); + + url.port = value; } else { if (streq(url.protocol, "https") || streq(url.protocol, "wss")) { url.port = 443; @@ -67,17 +71,16 @@ void throw_network(const char *value, bool tls) { if (errno != 0) { perror(value); - exit(EXIT_FAILURE); } else if (tls && (tls_error = ERR_get_error()) != 0) { char message[1024]; ERR_error_string(tls_error, message); fprintf(stderr, "%s: %s\n", value, message); - exit(EXIT_FAILURE); } else { fprintf(stderr, "%s\n", value); - exit(EXIT_FAILURE); } + + exit(EXIT_FAILURE); } unsigned long combine_bytes(unsigned char *bytes, unsigned long byte_count) { diff --git a/libs/utils/types.c b/libs/utils/types.c index fa2eb14..ddc3110 100644 --- a/libs/utils/types.c +++ b/libs/utils/types.c @@ -50,34 +50,36 @@ char *base64_encode(const char *data, const uint64_t data_length) { return response; } -unsigned long ahtoi(const char *data) { - char hex_alphabet[17] = "0123456789ABCDEF"; - const unsigned int size = strlen(data); - unsigned long result = 0; +int64_t ahtoi(const char *data) { + static char hex_alphabet[17] = "0123456789ABCDEF"; + const size_t size = strlen(data); + int64_t result = 0; for (unsigned int i = 0; i < size; ++i) { - unsigned long base = pow(16, (size - i - 1)); - result |= (char_at(hex_alphabet, toupper(data[i])) * ((base == 0) ? 1 : base)); + const uint64_t base = pow(16, (size - i - 1)); + const int position = char_at(hex_alphabet, toupper(data[i])); + if (position == -1) + return -1; + + result |= (position * ((base == 0) ? 1 : base)); } return result; } -int atoi_s(const char *str, short length) { - if (length == -1) { +int64_t atoi_s(const char *str, int16_t length) { + if (length == -1) length = strlen(str); - } - int result = 0; + int64_t result = 0; - for (short i = 0; i < length; ++i) { + for (uint16_t i = 0; i < length; ++i) { const char ch = str[i]; if (isdigit(ch)) { result += ((ch - '0') * pow(10.0, (length - i - 1))); } else { - result = -1; - break; + return -1; } } From 497119c705846e6454f85c650cd9e7f641658ff9 Mon Sep 17 00:00:00 2001 From: aloima Date: Sun, 17 May 2026 22:55:50 +0300 Subject: [PATCH 08/18] fix: invalid time calculating on `get_timestamp()` method --- libs/utils/get_timestamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/get_timestamp.c b/libs/utils/get_timestamp.c index 15eb38e..ea622c0 100644 --- a/libs/utils/get_timestamp.c +++ b/libs/utils/get_timestamp.c @@ -6,7 +6,7 @@ int64_t get_timestamp() { if (clock_gettime(CLOCK_REALTIME, &ts) == -1) return -1; - return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000); + return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); #elif defined(__WIN32__) struct _timeb timestamp; if (_ftime_s(×tamp) != 0) From 71482830606fc02d52592aed62b9f2f13efa60a2 Mon Sep 17 00:00:00 2001 From: aloima Date: Sun, 17 May 2026 23:01:48 +0300 Subject: [PATCH 09/18] feat: add assertions to `join()` methods --- include/utils.h | 8 ++++---- libs/utils/join.c | 21 ++++++++++----------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/utils.h b/include/utils.h index 3fd6489..8c07ee7 100644 --- a/include/utils.h +++ b/include/utils.h @@ -29,18 +29,18 @@ struct Split { unsigned int size; }; -struct Join { +typedef struct Join { char *data; unsigned int length; -}; +} Join; struct Split split(const char *text, const unsigned int length, const char *separator); void split_free(struct Split value); void *allocate(void *value, const long old_count, const unsigned long new_count, const unsigned char size); -unsigned long join(const struct Join *value, char *source, unsigned short size, const char *separator); -unsigned long calculate_join(const struct Join *value, unsigned short size, const char *separator); +uint64_t join(const Join *value, char *source, uint16_t size, const char *separator); +uint64_t calculate_join(const Join *value, uint16_t size, const char *separator); void strtolower(char *source, const char *dest); void strtoupper(char *source, const char *dest); diff --git a/libs/utils/join.c b/libs/utils/join.c index 078fceb..7448b8e 100644 --- a/libs/utils/join.c +++ b/libs/utils/join.c @@ -1,10 +1,10 @@ #include -unsigned long calculate_join(const struct Join *value, unsigned short size, const char *separator) { - unsigned long source_length = (strlen(separator) * (size - 1)); +uint64_t calculate_join(const Join *value, uint16_t size, const char *separator) { + uint64_t source_length = (strlen(separator) * (size - 1)); - for (unsigned short i = 0; size != 0; ++i) { - const unsigned long string_length = value[i].length; + for (uint16_t i = 0; size != 0; ++i) { + const uint64_t string_length = value[i].length; if (string_length != 0) { source_length += string_length; @@ -15,19 +15,19 @@ unsigned long calculate_join(const struct Join *value, unsigned short size, cons return source_length; } -unsigned long join(const struct Join *value, char *source, unsigned short size, const char *separator) { - const unsigned long separator_length = strlen(separator); - unsigned long source_length = 0; +uint64_t join(const Join *value, char *source, uint16_t size, const char *separator) { + const uint64_t separator_length = strlen(separator); + uint64_t source_length = 0; for (unsigned short i = 0; size != 0; ++i) { - const struct Join join_element = value[i]; + const Join join_element = value[i]; const bool has_separator = (size != 1); if (join_element.length != 0) { - memcpy(source + source_length, join_element.data, join_element.length); + ASSERT(memcpy(source + source_length, join_element.data, join_element.length), !=, NULL); if (has_separator) { - memcpy(source + source_length + join_element.length, separator, separator_length); + ASSERT(memcpy(source + source_length + join_element.length, separator, separator_length), !=, NULL); } --size; @@ -36,6 +36,5 @@ unsigned long join(const struct Join *value, char *source, unsigned short size, } source[source_length] = 0; - return source_length; } From 7abebf175b748f6e3e29a532c5869a37c867847e Mon Sep 17 00:00:00 2001 From: aloima Date: Mon, 18 May 2026 11:13:44 +0300 Subject: [PATCH 10/18] refactor: debugging and exit on errors for `about` command except memory --- include/json.h | 2 - include/network.h | 6 +- include/utils.h | 13 +++-- src/commands/about.c | 134 +++++++++++++++++++------------------------ 4 files changed, 72 insertions(+), 83 deletions(-) diff --git a/include/json.h b/include/json.h index afc0d11..1c592aa 100644 --- a/include/json.h +++ b/include/json.h @@ -1,7 +1,5 @@ #pragma once -#include - #include enum JSONType { diff --git a/include/network.h b/include/network.h index 1106765..76406e2 100644 --- a/include/network.h +++ b/include/network.h @@ -1,7 +1,5 @@ #pragma once -#include - #if defined(_WIN32) #include #include @@ -21,6 +19,10 @@ struct URL { unsigned short port; }; +typedef enum HTTPStatusCode { + HTTP_NO_CONTENT = 204 +} HTTPStatusCode; + struct Header { char *name, *value; }; diff --git a/include/utils.h b/include/utils.h index 8c07ee7..0eb83f4 100644 --- a/include/utils.h +++ b/include/utils.h @@ -3,11 +3,14 @@ #include // To guarantee code execution -#define ASSERT(actual, op, expected) do { \ - typeof(actual) __actual_val = (actual); \ - typeof(expected) __expected_val = (expected); \ - assert(__actual_val op __expected_val); \ -} while (0) +#define ASSERT(actual, op, expected) ({ \ + typeof(actual) __actual_val = (actual); \ + typeof(expected) __expected_val = (expected); \ + assert(__actual_val op __expected_val); \ + __actual_val; \ +}) + +#define SPRINTF_S(__s, __format, ...) ASSERT(sprintf((__s), (__format), __VA_ARGS__), >, 0) struct SplitData { char *data; diff --git a/src/commands/about.c b/src/commands/about.c index 758df29..22d8449 100644 --- a/src/commands/about.c +++ b/src/commands/about.c @@ -1,5 +1,11 @@ #include +typedef struct TimeUnitFormat { + char src[8]; + int value; + char *format; +} TimeUnitFormat; + #define YEAR (60 * 60 * 24 * 30 * 12) #define MONTH (60 * 60 * 24 * 30) #define DAY (60 * 60 * 24) @@ -11,67 +17,41 @@ static void set_uptime_text(struct Client client, char uptime_text[]) { if (now == -1) throw(GET_TIMESTAMP_ERROR); - uint64_t seconds = ((now - client.ready_at) / 1000); - const int years = (seconds / YEAR); - seconds -= (years * YEAR); - const int months = (seconds / MONTH); - seconds -= (months * MONTH); - const int days = (seconds / DAY); - seconds -= (days * DAY); - const int hours = (seconds / HOUR); - seconds -= (hours * HOUR); - const int minutes = (seconds / MINUTE); - seconds -= (minutes * MINUTE); - - struct Join uptime[6]; - int value = -1; - - char sseconds[8], sminutes[8], shours[7], sdays[8], smonths[8], syears[7]; - - if (years != 0) { - ++value; - - uptime[value].data = syears; - uptime[value].length = sprintf(uptime[value].data, "%hi yrs", years); - } - - if (months != 0) { - ++value; - - uptime[value].data = smonths; - uptime[value].length = sprintf(uptime[value].data, "%hi mths", months); - } - - if (days != 0) { - ++value; - - uptime[value].data = sdays; - uptime[value].length = sprintf(uptime[value].data, "%hi days", days); - } + const uint64_t seconds = (now - client.ready_at) / 1000; - if (hours != 0) { - ++value; + const int years = (seconds / YEAR); + const int months = (seconds % YEAR) / MONTH; + const int days = (seconds % MONTH) / DAY; + const int hours = (seconds % DAY) / HOUR; + const int minutes = (seconds % HOUR) / MINUTE; + const int secs = seconds % MINUTE; - uptime[value].data = shours; - uptime[value].length = sprintf(uptime[value].data, "%hi hrs", hours); - } - - if (minutes != 0) { - ++value; + struct Join uptime[6]; + uint8_t count = 0; + + const TimeUnitFormat pairs[6] = { + {"", years, "%hi yrs"}, + {"", months, "%hi mths"}, + {"", days, "%hi days"}, + {"", hours, "%hi hrs"}, + {"", minutes, "%hi mins"}, + {"", secs, "%hi secs"}, + }; - uptime[value].data = sminutes; - uptime[value].length = sprintf(uptime[value].data, "%hi mins", minutes); - } + for (uint8_t i = 0; i < 6; ++i) { + const TimeUnitFormat *pair = &pairs[i]; - if (seconds != 0) { - ++value; + if (pair->value != 0) { + char *src = (char *) pair->src; + const int length = SPRINTF_S(src, pair->format, pair->value); - uptime[value].data = sseconds; - uptime[value].length = sprintf(uptime[value].data, "%lu secs", seconds); + uptime[count].data = src; + uptime[count].length = length; + count += 1; + } } - ++value; - join(uptime, uptime_text, value, " "); + join(uptime, uptime_text, count, " "); } static void execute(struct Shivers *shivers, const struct InteractionCommand command) { @@ -88,31 +68,38 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com char memory_usage[11]; - #if defined(_WIN32) - PROCESS_MEMORY_COUNTERS memory = {0}; - GetProcessMemoryInfo(GetCurrentProcess(), &memory, sizeof(memory)); +#if defined(_WIN32) + // GetCurrentProcess() is guaranteed method. + PROCESS_MEMORY_COUNTERS memory = {0}; + ASSERT(GetProcessMemoryInfo(GetCurrentProcess(), &memory, sizeof(memory)), ==, true); - sprintf(memory_usage, "%.2f MB", memory.WorkingSetSize / 1024.0 / 1024.0); - #elif defined(__linux__) - FILE *statm = fopen("/proc/self/statm", "r"); - unsigned long rss, vram; + SPRINTF_S(memory_usage, "%.2f MB", memory.WorkingSetSize / 1024.0 / 1024.0); +#elif defined(__linux__) + uint64_t rss, vram; + FILE *statm = fopen("/proc/self/statm", "r"); + if (statm == NULL) + throw("fopen(): cannot open statm file to get memory usage"); - fscanf(statm, "%lu %lu", &vram, &rss); - sprintf(memory_usage, "%.2f MB", (rss * getpagesize()) / 1024.0 / 1024.0); - #endif + ASSERT(fscanf(statm, "%lu %lu", &vram, &rss), >, 0); + SPRINTF_S(memory_usage, "%.2f MB", (rss * getpagesize()) / 1024.0 / 1024.0); +#endif char uptime_text[41]; uptime_text[0] = 0; set_uptime_text(shivers->client, uptime_text); char guilds[4]; - sprintf(guilds, "%u", shivers->client.guilds->length); + SPRINTF_S(guilds, "%u", shivers->client.guilds->length); char latency[7]; - sprintf(latency, "%ums", get_latency()); + SPRINTF_S(latency, "%ums", get_latency()); + + #define ADD_MESSAGE ("[Add me!](https://discord.com/api/v10/oauth2/authorize?client_id=%s&scope=bot&permissions=8)") + const jsonresult_t id_prop = json_get_val(shivers->client.user, "id"); + ASSERT(id_prop.exist, ==, true); char add[110]; - sprintf(add, "[Add me!](https://discord.com/api/v10/oauth2/authorize?client_id=%s&scope=bot&permissions=8)", json_get_val(shivers->client.user, "id").value.string); + SPRINTF_S(add, ADD_MESSAGE, id_prop.value.string); embed.description = add; @@ -125,18 +112,17 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com #if defined(__clang__) char footer[33]; - sprintf(footer, "Compiled using clang %d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); - - set_embed_footer(&embed, footer, NULL); + SPRINTF_S(footer, "Compiled using clang %d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); #elif defined(__GNUC__) char footer[31]; - sprintf(footer, "Compiled using gcc %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); - - set_embed_footer(&embed, footer, NULL); + SPRINTF_S(footer, "Compiled using gcc %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); #endif + set_embed_footer(&embed, footer, NULL); + add_embed_to_message_payload(embed, &(message.payload)); - send_message(shivers->client, message); + ASSERT(send_message(shivers->client, message), ==, HTTP_NO_CONTENT); + free_embed(embed); free_message_payload(message.payload); } From 776fdae2ed1bf314e59f8ecb7b40783c8d7dd9b7 Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 25 Jun 2026 18:01:18 +0300 Subject: [PATCH 11/18] refactor: replace all `sprintf()` methods to `SPRINTF_S()` macros --- include/utils.h | 2 +- libs/database/database.c | 4 ++-- libs/discord/gateway.c | 26 +++++++++++++------------- libs/discord/message.c | 8 ++++---- libs/discord/utils.c | 8 ++++---- libs/json/stringify.c | 6 +++--- libs/network/request.c | 16 ++++++++-------- libs/network/utils.c | 2 +- libs/network/websocket.c | 2 +- src/commands/avatar.c | 4 ++-- src/commands/github.c | 24 ++++++++++++------------ src/commands/level.c | 14 +++++++------- src/commands/level_settings.c | 28 ++++++++++++++-------------- src/commands/vstats.c | 22 +++++++++++----------- src/commands/wikipedia.c | 14 +++++++------- src/events/guild_create.c | 2 +- src/events/guild_delete.c | 2 +- src/events/handle_guilds.c | 2 +- src/events/message_create.c | 16 ++++++++-------- src/utils/commands.c | 12 ++++++------ src/utils/cooldown.c | 2 +- src/utils/voice_stats.c | 12 ++++++------ 22 files changed, 114 insertions(+), 114 deletions(-) diff --git a/include/utils.h b/include/utils.h index 0eb83f4..b4014fe 100644 --- a/include/utils.h +++ b/include/utils.h @@ -10,7 +10,7 @@ __actual_val; \ }) -#define SPRINTF_S(__s, __format, ...) ASSERT(sprintf((__s), (__format), __VA_ARGS__), >, 0) +#define SPRINTF_S(__s, __format, ...) ASSERT(sprintf((__s), (__format), ##__VA_ARGS__), >, 0) struct SplitData { char *data; diff --git a/libs/database/database.c b/libs/database/database.c index 7e9f4c2..c8fa8d1 100644 --- a/libs/database/database.c +++ b/libs/database/database.c @@ -53,12 +53,12 @@ void database_push(char *key, void *value, const enum JSONType type) { if (array.exist) { char json_key[(array.element->size / 10) + 4]; - sprintf(json_key, "[%u]", array.element->size); + SPRINTF_S(json_key, "[%u]", array.element->size); json_set_val(array.element, json_key, value, type); } else { char json_key[5 + strlen(key)]; - sprintf(json_key, "%s.[0]", key); + SPRINTF_S(json_key, "%s.[0]", key); json_set_val(data, json_key, value, type); } diff --git a/libs/discord/gateway.c b/libs/discord/gateway.c index 2939e51..1d36243 100644 --- a/libs/discord/gateway.c +++ b/libs/discord/gateway.c @@ -39,12 +39,12 @@ static void send_heartbeat() { char heartbeat_message[24]; if (last_sequence == -1) { - sprintf(heartbeat_message, "{" + SPRINTF_S(heartbeat_message, "{" "\"op\":1," "\"d\":null" "}"); } else { - sprintf(heartbeat_message, "{" + SPRINTF_S(heartbeat_message, "{" "\"op\":1," "\"d\":%d" "}", last_sequence); @@ -79,7 +79,7 @@ static void *start_heartbeat_thread(void *_) { static void send_identify() { char identify_message[1024]; - sprintf(identify_message, "{" + SPRINTF_S(identify_message, "{" "\"op\":2," "\"d\":{" "\"token\":\"%s\"," @@ -98,7 +98,7 @@ static void send_identify() { static void send_resume() { char resume_message[512]; - sprintf(resume_message, "{" + SPRINTF_S(resume_message, "{" "\"op\":6," "\"d\":{" "\"token\":\"%s\"," @@ -155,8 +155,8 @@ static void parse_interaction_base_arguments(struct InteractionArgument *argumen case USER_ARGUMENT: { char user_search[16 + input.element->size], member_search[18 + input.element->size]; - sprintf(user_search, "resolved.users.%s", input.value.string); - sprintf(member_search, "resolved.members.%s", input.value.string); + SPRINTF_S(user_search, "resolved.users.%s", input.value.string); + SPRINTF_S(member_search, "resolved.members.%s", input.value.string); jsonresult_t member_result = json_get_val(data, member_search); @@ -176,7 +176,7 @@ static void parse_interaction_base_arguments(struct InteractionArgument *argumen case CHANNEL_ARGUMENT: { char search[19 + input.element->size]; - sprintf(search, "resolved.channels.%s", input.value.string); + SPRINTF_S(search, "resolved.channels.%s", input.value.string); *argument = (struct InteractionArgument) { .name = name, @@ -191,7 +191,7 @@ static void parse_interaction_base_arguments(struct InteractionArgument *argumen case ROLE_ARGUMENT: { char search[16 + input.element->size]; - sprintf(search, "resolved.roles.%s", input.value.string); + SPRINTF_S(search, "resolved.roles.%s", input.value.string); *argument = (struct InteractionArgument) { .name = name, @@ -511,7 +511,7 @@ void connect_gateway(const char *bot_token, const char *url, const unsigned int intents = bot_intents; char connection_url[72]; - sprintf(connection_url, "%s/?v=10&encoding=json", url); + SPRINTF_S(connection_url, "%s/?v=10&encoding=json", url); websocket = create_websocket(connection_url, (struct WebsocketMethods) { .onstart = onstart, @@ -534,18 +534,18 @@ void set_presence(const char *name, const char *state, const char *details, cons char activity[128]; if (state == NULL && details == NULL) { - sprintf(activity, "{" + SPRINTF_S(activity, "{" "\"name\":\"%s\"," "\"type\":%d" "}", name, type); } else if (state && details == NULL) { - sprintf(activity, "{" + SPRINTF_S(activity, "{" "\"name\":\"%s\"," "\"state\":\"%s\"," "\"type\":%d" "}", name, state, type); } else { - sprintf(activity, "{" + SPRINTF_S(activity, "{" "\"name\":\"%s\"," "\"state\":\"%s\"," "\"details\":\"%s\"," @@ -554,7 +554,7 @@ void set_presence(const char *name, const char *state, const char *details, cons } char presence[256]; - sprintf(presence, "{" + SPRINTF_S(presence, "{" "\"op\":3," "\"d\":{" "\"since\":null," diff --git a/libs/discord/message.c b/libs/discord/message.c index 8c14c14..b129192 100644 --- a/libs/discord/message.c +++ b/libs/discord/message.c @@ -7,10 +7,10 @@ unsigned short send_message(const struct Client client, const struct Message mes char path[512]; if (message.target_type == TARGET_CHANNEL) { - sprintf(path, "/channels/%s/messages", message.target.channel_id); + SPRINTF_S(path, "/channels/%s/messages", message.target.channel_id); } else { const struct InteractionCommand interaction_command = message.target.interaction_command; - sprintf(path, "/interactions/%s/%s/callback", interaction_command.id, interaction_command.token); + SPRINTF_S(path, "/interactions/%s/%s/callback", interaction_command.id, interaction_command.token); } if (message_payload.content) { @@ -130,7 +130,7 @@ unsigned short send_message(const struct Client client, const struct Message mes const struct File file = message_payload.files[i]; jsonelement_t *attachment = create_empty_json_element(false); char *field_name = allocate(NULL, -1, 12, sizeof(char)); - sprintf(field_name, "files[%u]", i); + SPRINTF_S(field_name, "files[%u]", i); double n = i; json_set_val(attachment, "id", &n, JSON_NUMBER); @@ -173,7 +173,7 @@ unsigned short send_message(const struct Client client, const struct Message mes for (unsigned int i = 0; i < message_payload.file_size; ++i) { const struct File file = message_payload.files[i]; char *field_name = allocate(NULL, -1, 12, sizeof(char)); - sprintf(field_name, "files[%u]", i); + SPRINTF_S(field_name, "files[%u]", i); add_field_to_formdata(&formdata, field_name, file.data, file.size, file.name); add_header_to_formdata_field(&formdata, field_name, "Content-Type", file.type); diff --git a/libs/discord/utils.c b/libs/discord/utils.c index 54b6e11..54025a0 100644 --- a/libs/discord/utils.c +++ b/libs/discord/utils.c @@ -2,10 +2,10 @@ struct Response api_request(const char *token, const char *path, const char *method, const char *body, const struct FormData *formdata) { char url[1024]; - sprintf(url, "https://discord.com/api/v10%s", path); + SPRINTF_S(url, "https://discord.com/api/v10%s", path); char authorization[100]; - sprintf(authorization, "Bot %s", token); + SPRINTF_S(authorization, "Bot %s", token); struct Header headers[2] = { (struct Header) { @@ -69,7 +69,7 @@ bool check_snowflake(const char *snowflake) { void get_avatar_url(char *url, const char *user_id, const char *discriminator, const char *hash, const bool force_png, const short size) { if (hash && hash[0] != 0) { const char *extension = ((!force_png && (strncmp(hash, "a_", 2) == 0)) ? "gif" : "png"); - sprintf(url, AVATAR_URL "?size=%d", user_id, hash, extension, size); + SPRINTF_S(url, AVATAR_URL "?size=%d", user_id, hash, extension, size); } else { unsigned int index; @@ -79,6 +79,6 @@ void get_avatar_url(char *url, const char *user_id, const char *discriminator, c index = ((atoi_s(user_id, -1) >> 22) % 6); } - sprintf(url, DEFAULT_AVATAR_URL "?size=%d", index, size); + SPRINTF_S(url, DEFAULT_AVATAR_URL "?size=%d", index, size); } } diff --git a/libs/json/stringify.c b/libs/json/stringify.c index 604ea50..b4c0ac7 100644 --- a/libs/json/stringify.c +++ b/libs/json/stringify.c @@ -23,11 +23,11 @@ char *json_stringify(const jsonelement_t *element, const unsigned int fractional if (fractional != 0.0) { result = allocate(NULL, -1, digit_count + fractional_limit + 2, sizeof(char)); - sprintf(formatter, "%%.0f.%%%d.0f", fractional_limit); - sprintf(result, formatter, integer, fractional); + SPRINTF_S(formatter, "%%.0f.%%%d.0f", fractional_limit); + SPRINTF_S(result, formatter, integer, fractional); } else { result = allocate(NULL, -1, (digit_count + 1), sizeof(char)); - sprintf(result, "%.0f", integer); + SPRINTF_S(result, "%.0f", integer); } } else if (element->type == JSON_BOOLEAN) { const bool value = *((bool *) element->value); diff --git a/libs/network/request.c b/libs/network/request.c index 40ecbc3..01b4e35 100644 --- a/libs/network/request.c +++ b/libs/network/request.c @@ -85,7 +85,7 @@ struct Response request(struct RequestConfig config) { unsigned long data_length = 0; char separator[32]; - sprintf(separator, "--%s\r\n", boundary); + SPRINTF_S(separator, "--%s\r\n", boundary); const unsigned short field_size = config.body.payload.formdata.field_size; @@ -101,9 +101,9 @@ struct Response request(struct RequestConfig config) { memcpy(data + data_length, separator, 4 + boundary_length); if (field.filename) { - sprintf(line, "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", field.name, field.filename); + SPRINTF_S(line, "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", field.name, field.filename); } else { - sprintf(line, "Content-Disposition: form-data; name=\"%s\"\r\n", field.name); + SPRINTF_S(line, "Content-Disposition: form-data; name=\"%s\"\r\n", field.name); } memcpy(data + data_length + 4 + boundary_length, line, disposition_length); @@ -112,7 +112,7 @@ struct Response request(struct RequestConfig config) { for (unsigned char a = 0; a < field_header_size; ++a) { const struct Header header = field.headers[a]; - sprintf(line, "%s: %s\r\n", header.name, header.value); + SPRINTF_S(line, "%s: %s\r\n", header.name, header.value); line_length = strlen(line); data = allocate(data, -1, data_length + field_length + line_length + 1, sizeof(char)); @@ -135,11 +135,11 @@ struct Response request(struct RequestConfig config) { } data = allocate(data, -1, 4 + boundary_length + data_length + 1, sizeof(char)); - sprintf(separator, "--%s--", boundary); + SPRINTF_S(separator, "--%s--", boundary); memcpy(data + data_length, separator, (4 + boundary_length)); data_length += (4 + boundary_length); - sprintf(request_message_information, ( + SPRINTF_S(request_message_information, ( "%s %s HTTP/1.1\r\n" "Host: %s:%d\r\n" "Accept: */*\r\n" @@ -161,7 +161,7 @@ struct Response request(struct RequestConfig config) { const char *body = config.body.payload.data; const unsigned long body_length = strlen(body); - sprintf(request_message_information, ( + SPRINTF_S(request_message_information, ( "%s %s HTTP/1.1\r\n" "Host: %s:%d\r\n" "Accept: */*\r\n" @@ -177,7 +177,7 @@ struct Response request(struct RequestConfig config) { memcpy(request_message + request_message_length, body, body_length); request_message_length += body_length; } else { - sprintf(request_message_information, + SPRINTF_S(request_message_information, "%s %s HTTP/1.1\r\n" "Host: %s:%d\r\n" "Accept: */*\r\n" diff --git a/libs/network/utils.c b/libs/network/utils.c index 004a91b..81e3104 100644 --- a/libs/network/utils.c +++ b/libs/network/utils.c @@ -188,7 +188,7 @@ char *percent_encode(const char *data) { result_length += 2; char hex[4]; - sprintf(hex, "%%%x", ch); + SPRINTF_S(hex, "%%%x", ch); strncat(result, hex, 4); } else { strncat(result, &ch, 1); diff --git a/libs/network/websocket.c b/libs/network/websocket.c index 2151271..fadbf24 100644 --- a/libs/network/websocket.c +++ b/libs/network/websocket.c @@ -188,7 +188,7 @@ static void switch_protocols(struct Websocket *websocket) { char request_message[512]; - sprintf(request_message, + SPRINTF_S(request_message, "GET %s HTTP/1.1\r\n" "Host: %s:%d\r\n" "Upgrade: websocket\r\n" diff --git a/src/commands/avatar.c b/src/commands/avatar.c index 1251292..2813042 100644 --- a/src/commands/avatar.c +++ b/src/commands/avatar.c @@ -116,7 +116,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com } } - sprintf(description, ( + SPRINTF_S(description, ( "PNG\\n" "[0.25x](%s) **|** [0.5x](%s) **|** [1x](%s) **|** [2x](%s) **|** [4x](%s)\\n\\n" "GIF\\n" @@ -137,7 +137,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com } } - sprintf(description, ( + SPRINTF_S(description, ( "PNG\\n" "[0.25x](%s) **|** [0.5x](%s) **|** 1x **|** [2x](%s) **|** [4x](%s)" ), png_avatar_urls[0], png_avatar_urls[1], png_avatar_urls[3], png_avatar_urls[4]); diff --git a/src/commands/github.c b/src/commands/github.c index 66d06bc..bdc3c29 100644 --- a/src/commands/github.c +++ b/src/commands/github.c @@ -33,7 +33,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com struct Response response; if (char_at(query.value, '/') == -1) { - sprintf(url, "https://api.github.com/users/%s", query.value); + SPRINTF_S(url, "https://api.github.com/users/%s", query.value); config.url = url; response = request(config); @@ -52,16 +52,16 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com const jsonresult_t bio = json_get_val(user, "bio"); char following[8]; - sprintf(following, "%u", (unsigned int) json_get_val(user, "following").value.number); + SPRINTF_S(following, "%u", (unsigned int) json_get_val(user, "following").value.number); char followers[8]; - sprintf(followers, "%u", (unsigned int) json_get_val(user, "followers").value.number); + SPRINTF_S(followers, "%u", (unsigned int) json_get_val(user, "followers").value.number); char repositories[6]; - sprintf(repositories, "%u", (unsigned int) json_get_val(user, "public_repos").value.number); + SPRINTF_S(repositories, "%u", (unsigned int) json_get_val(user, "public_repos").value.number); char gists[6]; - sprintf(gists, "%u", (unsigned int) json_get_val(user, "public_gists").value.number); + SPRINTF_S(gists, "%u", (unsigned int) json_get_val(user, "public_gists").value.number); char joined_at[18]; const char *joined_at_string = json_get_val(user, "created_at").value.string; @@ -73,13 +73,13 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com memcpy(month_string, joined_at_string + 5, 2); month_string[2] = 0; - sprintf(joined_at, "%.2s %s %.4s", joined_at_string + 8, months[atoi_s(month_string, -1) - 1], joined_at_string); + SPRINTF_S(joined_at, "%.2s %s %.4s", joined_at_string + 8, months[atoi_s(month_string, -1) - 1], joined_at_string); char name[json_login.element->size + 1]; - sprintf(name, "@%s", json_login.value.string); + SPRINTF_S(name, "@%s", json_login.value.string); if (json_name.element->type != JSON_NULL && !streq(json_login.value.string, json_name.value.string)) { - sprintf(name, "%s (@%s)", json_name.value.string, json_login.value.string); + SPRINTF_S(name, "%s (@%s)", json_name.value.string, json_login.value.string); } add_field_to_embed(&embed, "Repositories", repositories, true); @@ -103,7 +103,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com json_free(user, false); } } else { - sprintf(url, "https://api.github.com/repos/%s", query.value); + SPRINTF_S(url, "https://api.github.com/repos/%s", query.value); config.url = url; response = request(config); @@ -122,13 +122,13 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com jsonresult_t license = json_get_val(repository, "license"); char stars[8]; - sprintf(stars, "%lu", (unsigned long) json_get_val(repository, "stargazers_count").value.number); + SPRINTF_S(stars, "%lu", (unsigned long) json_get_val(repository, "stargazers_count").value.number); char watchers[8]; - sprintf(watchers, "%lu", (unsigned long) json_get_val(repository, "subscribers_count").value.number); + SPRINTF_S(watchers, "%lu", (unsigned long) json_get_val(repository, "subscribers_count").value.number); char forks[8]; - sprintf(forks, "%lu", (unsigned long) json_get_val(repository, "forks_count").value.number); + SPRINTF_S(forks, "%lu", (unsigned long) json_get_val(repository, "forks_count").value.number); add_field_to_embed(&embed, "Stars", stars, true); add_field_to_embed(&embed, "Watchers", watchers, true); diff --git a/src/commands/level.c b/src/commands/level.c index c3a9dfb..c1090b7 100644 --- a/src/commands/level.c +++ b/src/commands/level.c @@ -76,10 +76,10 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com } char xp_key[50], level_key[53], factor_key[42], levels_key[27]; - sprintf(xp_key, "%s.levels.%s.xp", command.guild_id, user_id); - sprintf(level_key, "%s.levels.%s.level", command.guild_id, user_id); - sprintf(factor_key, "%s.settings.level.factor", command.guild_id); - sprintf(levels_key, "%s.levels", command.guild_id); + SPRINTF_S(xp_key, "%s.levels.%s.xp", command.guild_id, user_id); + SPRINTF_S(level_key, "%s.levels.%s.level", command.guild_id, user_id); + SPRINTF_S(factor_key, "%s.settings.level.factor", command.guild_id); + SPRINTF_S(levels_key, "%s.levels", command.guild_id); const jsonresult_t factor_data = database_get(factor_key); const unsigned short factor = (factor_data.exist ? factor_data.value.number : 100); @@ -109,14 +109,14 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com const unsigned int level = (level_data.exist ? level_data.value.number : 1); char xp_text[24], level_text[16], rank[12]; - sprintf(level_text, "Level %d", level); - sprintf(xp_text, "XP %d / %d", xp, (level * factor)); + SPRINTF_S(level_text, "Level %d", level); + SPRINTF_S(xp_text, "XP %d / %d", xp, (level * factor)); rank[0] = 0; for (unsigned int i = 0; i < sorted_length; ++i) { if (streq(user_id, ((jsonelement_t *) sorted[i].value)->key)) { - sprintf(rank, "#%u", (i + 1)); + SPRINTF_S(rank, "#%u", (i + 1)); break; } } diff --git a/src/commands/level_settings.c b/src/commands/level_settings.c index 60e220d..8c9e004 100644 --- a/src/commands/level_settings.c +++ b/src/commands/level_settings.c @@ -23,33 +23,33 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com jsonresult_t data; { - sprintf(key, "%s.settings.level.factor", command.guild_id); + SPRINTF_S(key, "%s.settings.level.factor", command.guild_id); data = database_get(key); if (!data.exist) { - sprintf(factor_text, "Not set (100)"); + SPRINTF_S(factor_text, "Not set (100)"); } else { - sprintf(factor_text, "%d", (int) data.value.number); + SPRINTF_S(factor_text, "%d", (int) data.value.number); } add_field_to_embed(&embed, "Factor", factor_text, true); } { - sprintf(key, "%s.settings.level.channel", command.guild_id); + SPRINTF_S(key, "%s.settings.level.channel", command.guild_id); data = database_get(key); if (!data.exist) { - sprintf(channel_text, "Not set"); + SPRINTF_S(channel_text, "Not set"); } else { - sprintf(channel_text, "<#%s>", data.value.string); + SPRINTF_S(channel_text, "<#%s>", data.value.string); } add_field_to_embed(&embed, "Channel", channel_text, true); } { - sprintf(key, "%s.settings.level.message", command.guild_id); + SPRINTF_S(key, "%s.settings.level.message", command.guild_id); data = database_get(key); if (!data.exist) { @@ -78,26 +78,26 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com const char *option_name = option.name; if (options_size == 1) { - sprintf(response, "`%.8s` setting is set.", option_name); + SPRINTF_S(response, "`%.8s` setting is set.", option_name); } else if (i == last_index) { - sprintf(temp, " and `%.8s` settings are set.", option_name); + SPRINTF_S(temp, " and `%.8s` settings are set.", option_name); strcat(response, temp); } else if (i == 0) { - sprintf(response, "`%.8s`", option_name); + SPRINTF_S(response, "`%.8s`", option_name); } else { - sprintf(temp, ", `%.8s`", option_name); + SPRINTF_S(temp, ", `%.8s`", option_name); strcat(response, temp); } if (streq(option_name, "factor")) { double value = option.value.number; - sprintf(key, "%s.settings.level.factor", command.guild_id); + SPRINTF_S(key, "%s.settings.level.factor", command.guild_id); database_set(key, &value, JSON_NUMBER); } else if (streq(option_name, "channel")) { - sprintf(key, "%s.settings.level.channel", command.guild_id); + SPRINTF_S(key, "%s.settings.level.channel", command.guild_id); database_set(key, json_get_val(option.value.channel, "id").value.string, JSON_STRING); } else if (streq(option_name, "message")) { - sprintf(key, "%s.settings.level.message", command.guild_id); + SPRINTF_S(key, "%s.settings.level.message", command.guild_id); database_set(key, option.value.string.value, JSON_STRING); } } diff --git a/src/commands/vstats.c b/src/commands/vstats.c index 9b5d8b0..7a7d509 100644 --- a/src/commands/vstats.c +++ b/src/commands/vstats.c @@ -36,7 +36,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com description[0] = 0; char database_key[27]; - sprintf(database_key, "%s.vstats", command.guild_id); + SPRINTF_S(database_key, "%s.vstats", command.guild_id); const jsonresult_t vstats_data = database_get(database_key); @@ -46,13 +46,13 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com for (unsigned int i = 0; i < data->size; ++i) { char id_key[6], name_key[8]; - sprintf(id_key, "%d.id", i); - sprintf(name_key, "%d.name", i); + SPRINTF_S(id_key, "%d.id", i); + SPRINTF_S(name_key, "%d.name", i); const char *id = json_get_val(data, id_key).value.string; const char *name = json_get_val(data, name_key).value.string; - sprintf(line, "%s | <#%s> | %s\\n", id, id, name); + SPRINTF_S(line, "%s | <#%s> | %s\\n", id, id, name); strcat(description, line); } @@ -68,7 +68,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com } } else if (streq(operation, "add")) { char path[37]; - sprintf(path, "/guilds/%s/channels", command.guild_id); + SPRINTF_S(path, "/guilds/%s/channels", command.guild_id); const struct String input = command.arguments[0].value.subcommand.arguments[0].value.string; @@ -84,7 +84,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com prepare_voice_stats_channel_name(shivers->client, &channel_name, command.guild_id); char request_payload[256]; - sprintf(request_payload, "{" + SPRINTF_S(request_payload, "{" "\"name\":\"%s\"," "\"type\":2" "}", channel_name); @@ -108,7 +108,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com json_free(response_data, false); char database_key[27]; - sprintf(database_key, "%s.vstats", command.guild_id); + SPRINTF_S(database_key, "%s.vstats", command.guild_id); database_push(database_key, database_data, JSON_OBJECT); json_free(database_data, false); @@ -116,7 +116,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com message.payload.content = "Channel is created."; } else if (streq(operation, "delete")) { char database_key[27]; - sprintf(database_key, "%s.vstats", command.guild_id); + SPRINTF_S(database_key, "%s.vstats", command.guild_id); const struct String input = command.arguments[0].value.subcommand.arguments[0].value.string; bool deleted = false; @@ -128,14 +128,14 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com for (unsigned int i = 0; i < data->size; ++i) { char id_key[6]; - sprintf(id_key, "%d.id", i); + SPRINTF_S(id_key, "%d.id", i); const char *id = json_get_val(data, id_key).value.string; if (streq(id, input.value)) { char database_deletion_key[30], path[30]; - sprintf(database_deletion_key, "%s.vstats.%d", command.guild_id, i); - sprintf(path, "/channels/%s", id); + SPRINTF_S(database_deletion_key, "%s.vstats.%d", command.guild_id, i); + SPRINTF_S(path, "/channels/%s", id); database_delete(database_deletion_key); response_free(api_request(shivers->client.token, path, "DELETE", NULL, NULL)); diff --git a/src/commands/wikipedia.c b/src/commands/wikipedia.c index 779c17e..03be17b 100644 --- a/src/commands/wikipedia.c +++ b/src/commands/wikipedia.c @@ -14,7 +14,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com const struct String query = command.arguments[0].value.string; char search_url[61 + query.length]; - sprintf(search_url, "https://en.wikipedia.org/w/api.php?action=opensearch&search=%s", query.value); + SPRINTF_S(search_url, "https://en.wikipedia.org/w/api.php?action=opensearch&search=%s", query.value); config.url = search_url; struct Response search_response = request(config); @@ -23,7 +23,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com if (page_name.exist) { char url[512]; - sprintf(url, "https://en.wikipedia.org/w/api.php?action=query&prop=pageprops&titles=%s&format=json", page_name.value.string); + SPRINTF_S(url, "https://en.wikipedia.org/w/api.php?action=query&prop=pageprops&titles=%s&format=json", page_name.value.string); config.url = url; struct Response info_response = request(config); @@ -35,7 +35,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com response_free(info_response); const char *page_id = page_info->key; - sprintf(url, "https://en.wikipedia.org/w/api.php?action=query&format=json&pageids=%s&redirects", page_id); + SPRINTF_S(url, "https://en.wikipedia.org/w/api.php?action=query&format=json&pageids=%s&redirects", page_id); config.url = url; json_free(info_data, false); @@ -44,7 +44,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com response_free(redirect_response); page_name = json_get_val(redirect_result, "query.redirects.[0].to"); - sprintf(url, "https://en.wikipedia.org/w/api.php?action=query&prop=pageprops&titles=%s&format=json", page_name.value.string); + SPRINTF_S(url, "https://en.wikipedia.org/w/api.php?action=query&prop=pageprops&titles=%s&format=json", page_name.value.string); json_free(redirect_result, false); config.url = url; @@ -59,7 +59,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com const char *title = json_get_val(page_info, "title").value.string; char page_url[512], *encoded_page_url = NULL; - sprintf(page_url, "https://en.wikipedia.org/wiki/%s", title); + SPRINTF_S(page_url, "https://en.wikipedia.org/wiki/%s", title); encoded_page_url = percent_encode(page_url); embed.color = COLOR; @@ -70,7 +70,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com if (image_name.exist) { char image_url[512]; - sprintf(image_url, "https://en.wikipedia.org/w/api.php?action=query&titles=File:%s&prop=imageinfo&iiprop=url&format=json", image_name.value.string); + SPRINTF_S(image_url, "https://en.wikipedia.org/w/api.php?action=query&titles=File:%s&prop=imageinfo&iiprop=url&format=json", image_name.value.string); config.url = image_url; struct Response image_response = request(config); @@ -91,7 +91,7 @@ static void execute(struct Shivers *shivers, const struct InteractionCommand com join((const struct Join *) svg_splitter.data + 5, image_code, 2, "/"); split_free(svg_splitter); - sprintf(png_url, "https://upload.wikimedia.org/wikipedia/commons/thumb/%s/%s/1024px-%s.png", image_code, image_name.value.string, image_name.value.string); + SPRINTF_S(png_url, "https://upload.wikimedia.org/wikipedia/commons/thumb/%s/%s/1024px-%s.png", image_code, image_name.value.string, image_name.value.string); embed.image_url = png_url; } else { embed.image_url = final_image_url; diff --git a/src/events/guild_create.c b/src/events/guild_create.c index 6d065ae..476feef 100644 --- a/src/events/guild_create.c +++ b/src/events/guild_create.c @@ -2,6 +2,6 @@ void on_guild_create(struct Shivers *shivers) { char custom_status[12]; - sprintf(custom_status, "%u servers", shivers->client.guilds->length); + SPRINTF_S(custom_status, "%u servers", shivers->client.guilds->length); set_presence("custom", custom_status, NULL, 4, "online"); } diff --git a/src/events/guild_delete.c b/src/events/guild_delete.c index 97b2576..5cc58db 100644 --- a/src/events/guild_delete.c +++ b/src/events/guild_delete.c @@ -2,6 +2,6 @@ void on_guild_delete(struct Shivers *shivers) { char custom_status[12]; - sprintf(custom_status, "%u servers", shivers->client.guilds->length); + SPRINTF_S(custom_status, "%u servers", shivers->client.guilds->length); set_presence("custom", custom_status, NULL, 4, "online"); } diff --git a/src/events/handle_guilds.c b/src/events/handle_guilds.c index 264ff95..37d85c9 100644 --- a/src/events/handle_guilds.c +++ b/src/events/handle_guilds.c @@ -15,6 +15,6 @@ void on_handle_guilds(struct Shivers *shivers) { puts("Updated voice stats of all servers."); char custom_status[12]; - sprintf(custom_status, "%u servers", shivers->client.guilds->length); + SPRINTF_S(custom_status, "%u servers", shivers->client.guilds->length); set_presence("custom", custom_status, NULL, 4, "online"); } diff --git a/src/events/message_create.c b/src/events/message_create.c index fb1152f..7f06a72 100644 --- a/src/events/message_create.c +++ b/src/events/message_create.c @@ -12,11 +12,11 @@ void on_message_create(struct Shivers *shivers, jsonelement_t *message) { const char *guild_id = json_guild_id.value.string; char xp_key[50], level_key[53], factor_key[42], channel_key[43], message_key[43]; - sprintf(xp_key, "%s.levels.%s.xp", guild_id, user_id); - sprintf(level_key, "%s.levels.%s.level", guild_id, user_id); - sprintf(factor_key, "%s.settings.level.factor", guild_id); - sprintf(channel_key, "%s.settings.level.channel", guild_id); - sprintf(message_key, "%s.settings.level.message", guild_id); + SPRINTF_S(xp_key, "%s.levels.%s.xp", guild_id, user_id); + SPRINTF_S(level_key, "%s.levels.%s.level", guild_id, user_id); + SPRINTF_S(factor_key, "%s.settings.level.factor", guild_id); + SPRINTF_S(channel_key, "%s.settings.level.channel", guild_id); + SPRINTF_S(message_key, "%s.settings.level.message", guild_id); const jsonresult_t xp_data = database_get(xp_key); const jsonresult_t level_data = database_get(level_key); @@ -45,9 +45,9 @@ void on_message_create(struct Shivers *shivers, jsonelement_t *message) { char *username = json_get_val(message, "author.username").value.string; char mention[23], old_level[4], new_level[4]; - sprintf(mention, "<@%s>", user_id); - sprintf(old_level, "%.0f", level - 1.0); - sprintf(new_level, "%.0f", level); + SPRINTF_S(mention, "<@%s>", user_id); + SPRINTF_S(old_level, "%.0f", level - 1.0); + SPRINTF_S(new_level, "%.0f", level); strreplace(&level_message_dup, "{name}", username); strreplace(&level_message_dup, "{user}", mention); strreplace(&level_message_dup, "{old}", old_level); diff --git a/src/utils/commands.c b/src/utils/commands.c index 209aa2e..35e978e 100644 --- a/src/utils/commands.c +++ b/src/utils/commands.c @@ -21,7 +21,7 @@ void setup_commands(struct Shivers *shivers) { while (command_node) { struct Command *command = command_node->value; jsonelement_t *command_body = create_empty_json_element(false); - sprintf(key, "[%d]", stringified_commands); + SPRINTF_S(key, "[%d]", stringified_commands); ++stringified_commands; json_set_val(command_body, "name", command_node->key, JSON_STRING); @@ -39,7 +39,7 @@ void setup_commands(struct Shivers *shivers) { if (command->permissions != 0) { char permissions[21]; - sprintf(permissions, "%ld", command->permissions); + SPRINTF_S(permissions, "%ld", command->permissions); json_set_val(command_body, "default_member_permissions", permissions, JSON_STRING); } @@ -51,7 +51,7 @@ void setup_commands(struct Shivers *shivers) { bool argument_required = !argument.optional; double argument_type = argument.type; jsonelement_t *argument_body = create_empty_json_element(false); - sprintf(argument_key, "[%d]", a); + SPRINTF_S(argument_key, "[%d]", a); json_set_val(argument_body, "name", argument.name, JSON_STRING); json_set_val(argument_body, "description", argument.description, JSON_STRING); @@ -63,10 +63,10 @@ void setup_commands(struct Shivers *shivers) { for (unsigned int b = 0; b < argument.arg_size; ++b) { const struct CommandArgument subcommand_arg = argument.args[b]; char *subcommand_arg_required = !subcommand_arg.optional ? "true" : "false"; - sprintf(sc_argument_key, "[%d]", b); + SPRINTF_S(sc_argument_key, "[%d]", b); char subcommand_arg_body[187]; - sprintf(subcommand_arg_body, ( + SPRINTF_S(subcommand_arg_body, ( "{" "\"name\":\"%s\"," "\"description\":\"%s\"," @@ -105,7 +105,7 @@ void setup_commands(struct Shivers *shivers) { json_free(commands_body, false); char path[42]; - sprintf(path, "/applications/%s/commands", json_get_val(shivers->client.user, "id").value.string); + SPRINTF_S(path, "/applications/%s/commands", json_get_val(shivers->client.user, "id").value.string); response_free(api_request(shivers->client.token, path, "PUT", body, NULL)); free(body); diff --git a/src/utils/cooldown.c b/src/utils/cooldown.c index e59391f..eb6d7ad 100644 --- a/src/utils/cooldown.c +++ b/src/utils/cooldown.c @@ -9,7 +9,7 @@ void run_with_cooldown(const char *user_id, void (*execute)(struct Shivers *shiv if (target > current) { char warning[51]; - sprintf(warning, "You need to wait `%.2f seconds` to use a command.", (target - current) / 1000.0); + SPRINTF_S(warning, "You need to wait `%.2f seconds` to use a command.", (target - current) / 1000.0); struct Message message = { .target_type = TARGET_INTERACTION_COMMAND, diff --git a/src/utils/voice_stats.c b/src/utils/voice_stats.c index f36ec5b..258d2db 100644 --- a/src/utils/voice_stats.c +++ b/src/utils/voice_stats.c @@ -5,7 +5,7 @@ unsigned int channel_count = 0; void update_voice_stats(const struct Client client, const char *guild_id) { char database_key[27]; - sprintf(database_key, "%s.vstats", guild_id); + SPRINTF_S(database_key, "%s.vstats", guild_id); const jsonresult_t data = database_get(database_key); @@ -68,7 +68,7 @@ void update_voice_stats(const struct Client client, const char *guild_id) { } char body[256]; - sprintf(body, "{\"name\":\"%s\"}", channels[v].name); + SPRINTF_S(body, "{\"name\":\"%s\"}", channels[v].name); response_free(api_request(client.token, path, "PATCH", body, NULL)); } @@ -83,10 +83,10 @@ void prepare_voice_stats_channel_name(const struct Client client, char **channel char bots[(guild->bot_count / 10) + 2]; char at_voice[(guild->member_at_voice_count / 10) + 2]; - sprintf(members, "%u", guild->members->length); - sprintf(online, "%u", guild->non_offline_count); - sprintf(bots, "%u", guild->bot_count); - sprintf(at_voice, "%u", guild->member_at_voice_count); + SPRINTF_S(members, "%u", guild->members->length); + SPRINTF_S(online, "%u", guild->non_offline_count); + SPRINTF_S(bots, "%u", guild->bot_count); + SPRINTF_S(at_voice, "%u", guild->member_at_voice_count); strreplace(channel_name, "{members}", members); strreplace(channel_name, "{online}", online); From f3689284d0511f55db8ce70784e85553edda7696 Mon Sep 17 00:00:00 2001 From: aloima Date: Thu, 25 Jun 2026 18:34:43 +0300 Subject: [PATCH 12/18] refactor: stack overflow potential on freeing json element --- libs/json/free.c | 66 +++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/libs/json/free.c b/libs/json/free.c index 60f6096..b40e7a8 100644 --- a/libs/json/free.c +++ b/libs/json/free.c @@ -1,49 +1,63 @@ #include static void free_element(jsonelement_t *element) { - if (element) { - if (element->key) { - free(element->key); - } + ASSERT(element, !=, NULL); - if (element->value) { - free(element->value); - } + if (element->key) { + free(element->key); + } - free(element); + if (element->value) { + free(element->value); } + + free(element); } static void free_elements(jsonelement_t *parent) { - if (parent) { - if (parent->type == JSON_ARRAY || parent->type == JSON_OBJECT) { - for (unsigned int i = 0; i < parent->size; ++i) { - jsonelement_t *element = ((jsonelement_t **) parent->value)[i]; - - if (element) { - if (element->type == JSON_ARRAY || element->type == JSON_OBJECT) { - free_elements(element); - } else { - free_element(element); - } + ASSERT(parent, !=, NULL); + + uint32_t capacity = 128; + jsonelement_t **stack = allocate(NULL, 0, capacity, sizeof(jsonelement_t *)); + if (stack == NULL) + return; + + uint32_t top = 0; + stack[top++] = parent; + + while (top > 0) { + jsonelement_t *current = stack[--top]; + + if (current->type == JSON_ARRAY || current->type == JSON_OBJECT) { + jsonelement_t **elements = (jsonelement_t **) current->value; + + for (uint32_t i = 0; i < current->size; ++i) { + jsonelement_t *element = elements[i]; + if (top >= capacity) { + stack = allocate(stack, capacity, capacity * 2, sizeof(jsonelement_t *)); + capacity *= 2; + + if (stack == NULL) + return; } + + stack[top++] = element; } } - free_element(parent); + free_element(current); } + + free(stack); } void json_free(jsonelement_t *element, const bool all) { jsonelement_t *top = element; + ASSERT(element, !=, NULL); if (all) { - while (true) { - if (top->parent) { - top = top->parent; - } else { - break; - } + while (top->parent) { + top = top->parent; } } From 1edbc673b5311ba05f087e258161dd88edd4c6c7 Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 26 Jun 2026 00:19:51 +0300 Subject: [PATCH 13/18] refactor: add `SLEEP()` macro --- include/utils.h | 10 ++++++++++ libs/discord/gateway.c | 14 ++------------ libs/network/websocket.c | 6 +----- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/include/utils.h b/include/utils.h index b4014fe..3c86f03 100644 --- a/include/utils.h +++ b/include/utils.h @@ -12,6 +12,16 @@ #define SPRINTF_S(__s, __format, ...) ASSERT(sprintf((__s), (__format), ##__VA_ARGS__), >, 0) +#if defined(__linux__) + #define SLEEP(ms) do { \ + struct timespec req; \ + req.tv_sec = ms / 1000; \ + req.tv_nsec = (ms % 1000) * 1000000L; \ + } while (0) +#elif defined(__WIN32) + #define SLEEP(ms) Sleep(ms) +#endif + struct SplitData { char *data; unsigned int length; diff --git a/libs/discord/gateway.c b/libs/discord/gateway.c index 1d36243..f20158e 100644 --- a/libs/discord/gateway.c +++ b/libs/discord/gateway.c @@ -25,12 +25,7 @@ static void handle_exit(int sig) { } pthread_cancel(heartbeat_thread); - - #if defined(__linux__) - usleep(250000); - #elif defined(_WIN32) - Sleep(250); - #endif + SLEEP(250); exit(EXIT_SUCCESS); } @@ -61,12 +56,7 @@ static void send_heartbeat() { static void *start_heartbeat_thread(void *_) { do { heartbeat_waiting = true; - - #if defined(__linux__) - usleep(heartbeat_interval * 1000); - #elif defined(_WIN32) - Sleep(heartbeat_interval); - #endif + SLEEP(heartbeat_interval); send_heartbeat(); heartbeat_waiting = false; diff --git a/libs/network/websocket.c b/libs/network/websocket.c index fadbf24..86d7f79 100644 --- a/libs/network/websocket.c +++ b/libs/network/websocket.c @@ -325,11 +325,7 @@ void connect_websocket(struct Websocket *websocket) { handle_events(websocket); } - #if defined(__linux__) - usleep(3000); - #elif defined(_WIN32) - Sleep(3); - #endif + SLEEP(3); } while (websocket->connected && !websocket->closed); } else { throw_network("connect()", !!websocket->ssl); From 46826df1717e6487002575151687eecf0bb3e54f Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 26 Jun 2026 00:20:12 +0300 Subject: [PATCH 14/18] refactor: add cleanup OPENSSL library on exit --- libs/discord/gateway.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/discord/gateway.c b/libs/discord/gateway.c index f20158e..5559255 100644 --- a/libs/discord/gateway.c +++ b/libs/discord/gateway.c @@ -25,6 +25,7 @@ static void handle_exit(int sig) { } pthread_cancel(heartbeat_thread); + OPENSSL_cleanup(); SLEEP(250); exit(EXIT_SUCCESS); From 1b9fa164b62e9015a71204308383591ad1ae5546 Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 26 Jun 2026 00:27:32 +0300 Subject: [PATCH 15/18] refactor(network): add socket_t type for compatibility --- include/network.h | 24 ++++++++++-------------- libs/network/request.c | 7 +------ libs/network/utils.c | 28 +++++----------------------- 3 files changed, 16 insertions(+), 43 deletions(-) diff --git a/include/network.h b/include/network.h index 76406e2..3d6acdf 100644 --- a/include/network.h +++ b/include/network.h @@ -80,17 +80,18 @@ void free_url(struct URL url); char *percent_encode(const char *data); #if defined(_WIN32) - unsigned long s_read(SSL *ssl, SOCKET sockfd, char *buffer, unsigned long size); - unsigned long s_write(SSL *ssl, SOCKET sockfd, char *buffer, unsigned long size); - - void close_socket(SOCKET sockfd, SSL *ssl); + typedef SOCKET socket_t; + #define CLOSE_SOCKET(sockfd) closesocket(sockfd) #elif defined(__linux__) - unsigned long s_read(SSL *ssl, int sockfd, char *buffer, unsigned long size); - unsigned long s_write(SSL *ssl, int sockfd, char *buffer, unsigned long size); - - void close_socket(int sockfd, SSL *ssl); + typedef int socket_t; + #define CLOSE_SOCKET(sockfd) close(sockfd) #endif +unsigned long s_read(SSL *ssl, socket_t sockfd, char *buffer, unsigned long size); +unsigned long s_write(SSL *ssl, socket_t sockfd, char *buffer, unsigned long size); + +void close_socket(socket_t sockfd, SSL *ssl); + unsigned long combine_bytes(unsigned char *bytes, unsigned long byte_count); struct Header get_header(struct Header *headers, const unsigned int header_size, const char *name); struct hostent *resolve_hostname(char *hostname); @@ -120,12 +121,7 @@ struct WebsocketMethods { }; struct Websocket { - #if defined(_WIN32) - SOCKET sockfd; - #elif defined(__linux__) - int sockfd; - #endif - + socket_t sockfd; fd_set readfds, writefds; struct timeval tv; SSL *ssl; diff --git a/libs/network/request.c b/libs/network/request.c index 01b4e35..fe96fbf 100644 --- a/libs/network/request.c +++ b/libs/network/request.c @@ -13,12 +13,7 @@ void response_free(struct Response response) { struct Response request(struct RequestConfig config) { struct Response response = {0}; - - #if defined(_WIN32) - SOCKET sockfd; - #elif defined(__linux__) - int sockfd; - #endif + socket_t sockfd; struct sockaddr_in addr; struct hostent *host = NULL; diff --git a/libs/network/utils.c b/libs/network/utils.c index 81e3104..93472fa 100644 --- a/libs/network/utils.c +++ b/libs/network/utils.c @@ -112,12 +112,7 @@ struct Header get_header(struct Header *headers, const unsigned int header_size, return header; } -#if defined(_WIN32) -unsigned long s_read(SSL *ssl, SOCKET sockfd, char *buffer, unsigned long size) { -#elif defined(__linux__) -unsigned long s_read(SSL *ssl, int sockfd, char *buffer, unsigned long size) { -#endif - +unsigned long s_read(SSL *ssl, socket_t sockfd, char *buffer, unsigned long size) { if (ssl != NULL) { return SSL_read(ssl, buffer, size); } else { @@ -125,12 +120,7 @@ unsigned long s_read(SSL *ssl, int sockfd, char *buffer, unsigned long size) { } } -#if defined(_WIN32) -unsigned long s_write(SSL *ssl, SOCKET sockfd, char *buffer, unsigned long size) { -#elif defined(__linux) -unsigned long s_write(SSL *ssl, int sockfd, char *buffer, unsigned long size) { -#endif - +unsigned long s_write(SSL *ssl, socket_t sockfd, char *buffer, unsigned long size) { unsigned long result; bool err = false; @@ -149,23 +139,14 @@ unsigned long s_write(SSL *ssl, int sockfd, char *buffer, unsigned long size) { return result; } -#if defined(_WIN32) -void close_socket(SOCKET sockfd, SSL *ssl) { -#elif defined(__linux) -void close_socket(int sockfd, SSL *ssl) { -#endif - +void close_socket(socket_t sockfd, SSL *ssl) { if (ssl != NULL) { SSL_shutdown(ssl); SSL_CTX_free(SSL_get_SSL_CTX(ssl)); SSL_free(ssl); } - #if defined(_WIN32) - closesocket(sockfd); - #elif defined(__linux__) - close(sockfd); - #endif + CLOSE_SOCKET(sockfd); } char *percent_encode(const char *data) { @@ -175,6 +156,7 @@ char *percent_encode(const char *data) { ')', '*', '+', ',', ';', '\0' }; // Some of reserved characters are not added because of syntax of URL. + const unsigned int length = strlen(data); unsigned int result_length = (length + 1); char *result = allocate(NULL, -1, (length + 1), sizeof(char)); From 8ea144c49b8d3b2bbd7947dbbaa6882f8d05e1a1 Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 26 Jun 2026 00:30:23 +0300 Subject: [PATCH 16/18] refactor(discord): add `create_header` method for `api_request` method --- libs/discord/utils.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libs/discord/utils.c b/libs/discord/utils.c index 54025a0..1af525f 100644 --- a/libs/discord/utils.c +++ b/libs/discord/utils.c @@ -1,5 +1,12 @@ #include +static inline struct Header create_header(const char *name, const char *value) { + return (struct Header) { + .name = (char *) name, + .value = (char *) value + }; +} + struct Response api_request(const char *token, const char *path, const char *method, const char *body, const struct FormData *formdata) { char url[1024]; SPRINTF_S(url, "https://discord.com/api/v10%s", path); @@ -8,10 +15,7 @@ struct Response api_request(const char *token, const char *path, const char *met SPRINTF_S(authorization, "Bot %s", token); struct Header headers[2] = { - (struct Header) { - .name = "Authorization", - .value = authorization - } + create_header("Authorization", authorization) }; struct RequestConfig config = { @@ -27,10 +31,7 @@ struct Response api_request(const char *token, const char *path, const char *met config.body.payload.data = allocate(NULL, -1, body_size, sizeof(char)); memcpy(config.body.payload.data, body, body_size); - headers[1] = (struct Header) { - .name = "Content-Type", - .value = "application/json" - }; + headers[1] = create_header("Content-Type", "application/json"); config.headers = headers; config.header_size = 2; From 240d25ac79c81a8931cae8c83af1b3d7e6baa767 Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 26 Jun 2026 00:50:32 +0300 Subject: [PATCH 17/18] refactor: add assertions into `send_websocket_message` method --- include/network.h | 2 +- libs/discord/gateway.c | 6 +++--- libs/network/websocket.c | 46 ++++++++++++++++++++++++++++------------ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/include/network.h b/include/network.h index 3d6acdf..e5a0f9b 100644 --- a/include/network.h +++ b/include/network.h @@ -137,4 +137,4 @@ struct Websocket { struct Websocket create_websocket(const char *url, const struct WebsocketMethods methods); void connect_websocket(struct Websocket *websocket); void close_websocket(struct Websocket *websocket, const short code, const char *reason); -void send_websocket_message(struct Websocket *websocket, const char *message); +int send_websocket_message(struct Websocket *websocket, const char *message); diff --git a/libs/discord/gateway.c b/libs/discord/gateway.c index 5559255..7eb7be4 100644 --- a/libs/discord/gateway.c +++ b/libs/discord/gateway.c @@ -51,7 +51,7 @@ static void send_heartbeat() { throw(GET_TIMESTAMP_ERROR); heartbeat_sent_at = now; - send_websocket_message(&websocket, heartbeat_message); + ASSERT(send_websocket_message(&websocket, heartbeat_message), ==, 0); } static void *start_heartbeat_thread(void *_) { @@ -83,7 +83,7 @@ static void send_identify() { "}" "}", token, intents); - send_websocket_message(&websocket, identify_message); + ASSERT(send_websocket_message(&websocket, identify_message), ==, 0); } static void send_resume() { @@ -99,7 +99,7 @@ static void send_resume() { "}", token, session_id, last_sequence); puts(resume_message); - send_websocket_message(&websocket, resume_message); + ASSERT(send_websocket_message(&websocket, resume_message), ==, 0); } static void onstart() { diff --git a/libs/network/websocket.c b/libs/network/websocket.c index 86d7f79..18f6527 100644 --- a/libs/network/websocket.c +++ b/libs/network/websocket.c @@ -201,51 +201,70 @@ static void switch_protocols(struct Websocket *websocket) { websocket->connected = true; } -void send_websocket_message(struct Websocket *websocket, const char *message) { - unsigned char *data = NULL; - const unsigned int message_length = strlen(message); - unsigned int data_length = message_length; - unsigned char masking_key[4]; +int send_websocket_message(struct Websocket *websocket, const char *message) { + uint8_t *data = NULL; + const uint64_t message_length = strlen(message); + uint64_t data_length = message_length; + uint8_t masking_key[4]; for (int i = 0; i < 4; ++i) { masking_key[i] = ((rand() % 255) + 1); } - if (message_length > 65535) { + if (message_length > 65535ULL) { data_length += 14; data = allocate(NULL, -1, data_length, sizeof(char)); + if (data == NULL) + goto ON_ERROR; + data[1] = 255; - for (int i = 0; i < 8; ++i) { + for (uint8_t i = 0; i < 8; ++i) { data[2 + i] = (message_length >> ((7 - i) * 8)) & 0xFF; } - } else if (message_length > 125) { + } else if (message_length > 125ULL) { data_length += 8; data = allocate(NULL, -1, data_length, sizeof(char)); + if (data == NULL) + goto ON_ERROR; + data[1] = 254; data[2] = (message_length >> 8) & 0xFF; data[3] = message_length & 0xFF; } else { data_length += 6; data = allocate(NULL, -1, data_length, sizeof(char)); + if (data == NULL) + goto ON_ERROR; + data[1] = 128 + message_length; } data[0] = WEBSOCKET_FRAME_MAGIC; - strncpy(((char *) data) + data_length - message_length - 4, (char *) masking_key, 4); + ASSERT(strncpy(((char *) data) + data_length - message_length - 4, (char *) masking_key, 4), !=, NULL); for (int i = 0; i < message_length; ++i) { const char ch = (message[i] ^ masking_key[i % 4]); - strncpy(((char *) data) + data_length - message_length + i, &ch, 1); + ASSERT(strncpy(((char *) data) + data_length - message_length + i, &ch, 1), !=, NULL); } ++websocket->queue_size; websocket->queue = allocate(websocket->queue, -1, websocket->queue_size, sizeof(struct WebsocketQueueElement)); - websocket->queue[websocket->queue_size - 1].data = allocate(NULL, -1, data_length, sizeof(char)); - websocket->queue[websocket->queue_size - 1].size = data_length; - memcpy(websocket->queue[websocket->queue_size - 1].data, data, data_length); + + struct WebsocketQueueElement *element = &websocket->queue[websocket->queue_size - 1]; + element->data = allocate(NULL, -1, data_length, sizeof(char)); + element->size = data_length; + ASSERT(memcpy(element->data, data, data_length), !=, NULL); free(data); + return 0; + + ON_ERROR: { + if (data != NULL) + free(data); + + return -1; + } } @@ -254,7 +273,6 @@ struct Websocket create_websocket(const char *url, const struct WebsocketMethods websocket.methods = methods; initialize_websocket(&websocket, url); - return websocket; } From 8ce458f438b457774b8e3675c848c6a27c84ed8e Mon Sep 17 00:00:00 2001 From: aloima Date: Fri, 26 Jun 2026 00:57:42 +0300 Subject: [PATCH 18/18] refactor(discord): improve readability of parsing `InteractionArgument` --- libs/discord/gateway.c | 73 ++++++++++++------------------------------ 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/libs/discord/gateway.c b/libs/discord/gateway.c index 7eb7be4..cb66e0d 100644 --- a/libs/discord/gateway.c +++ b/libs/discord/gateway.c @@ -107,41 +107,24 @@ static void onstart() { } static void parse_interaction_base_arguments(struct InteractionArgument *argument, jsonelement_t *data, unsigned char type, char *name, jsonresult_t input) { + // For zero initialization + *argument = (struct InteractionArgument) { + .name = name, + .type = type + }; + switch (type) { case STRING_ARGUMENT: - *argument = (struct InteractionArgument) { - .name = name, - .type = type, - .value = { - .string = { - .value = input.value.string, - .length = input.element->size - } - } - }; - + argument->value.string.value = input.value.string; + argument->value.string.length = input.element->size; break; case INTEGER_ARGUMENT: - *argument = (struct InteractionArgument) { - .name = name, - .type = type, - .value = { - .number = input.value.number - } - }; - + argument->value.number = input.value.number; break; case BOOLEAN_ARGUMENT: - *argument = (struct InteractionArgument) { - .name = name, - .type = type, - .value = { - .boolean = input.value.boolean - } - }; - + argument->value.boolean = input.value.boolean; break; case USER_ARGUMENT: { @@ -149,18 +132,12 @@ static void parse_interaction_base_arguments(struct InteractionArgument *argumen SPRINTF_S(user_search, "resolved.users.%s", input.value.string); SPRINTF_S(member_search, "resolved.members.%s", input.value.string); - jsonresult_t member_result = json_get_val(data, member_search); + const jsonresult_t user_result = json_get_val(data, user_search); + ASSERT(user_result.exist, ==, true); - *argument = (struct InteractionArgument) { - .name = name, - .type = type, - .value = { - .user = { - .user_data = json_get_val(data, user_search).element, - .member_data = member_result.exist ? member_result.element : NULL - } - } - }; + const jsonresult_t member_result = json_get_val(data, member_search); + argument->value.user.user_data = json_get_val(data, user_search).element; + argument->value.user.member_data = member_result.exist ? member_result.element : NULL; break; } @@ -169,14 +146,10 @@ static void parse_interaction_base_arguments(struct InteractionArgument *argumen char search[19 + input.element->size]; SPRINTF_S(search, "resolved.channels.%s", input.value.string); - *argument = (struct InteractionArgument) { - .name = name, - .type = type, - .value = { - .channel = json_get_val(data, search).element - } - }; + const jsonresult_t channel_result = json_get_val(data, search); + ASSERT(channel_result.exist, ==, true); + argument->value.channel = channel_result.element; break; } @@ -184,14 +157,10 @@ static void parse_interaction_base_arguments(struct InteractionArgument *argumen char search[16 + input.element->size]; SPRINTF_S(search, "resolved.roles.%s", input.value.string); - *argument = (struct InteractionArgument) { - .name = name, - .type = type, - .value = { - .channel = json_get_val(data, search).element - } - }; + const jsonresult_t role_result = json_get_val(data, search); + ASSERT(role_result.exist, ==, true); + argument->value.role = role_result.element; break; } }