mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-05-10 19:00:57 +08:00
fix: guard terminal width/height in all TUI renderers and harden edge cases
- Replace all direct client->width/height reads with local variables clamped to minimums (width>=10, height>=4) across tui_render_screen, tui_render_input, tui_render_command_output, and tui_render_help - Fix tui_render_input underflow when width < 3 (avail = max(rw-3, 1)) - Show username in title bar instead of generic label - Add /me action message support in exec_command_post for scripting parity - Reject empty usernames when loading messages from log file
This commit is contained in:
parent
848ad2e2a6
commit
14789cd1c8
3 changed files with 52 additions and 24 deletions
|
|
@ -134,10 +134,13 @@ read_messages:;
|
||||||
char *username = strtok(NULL, "|");
|
char *username = strtok(NULL, "|");
|
||||||
char *content = strtok(NULL, "\n");
|
char *content = strtok(NULL, "\n");
|
||||||
|
|
||||||
/* Validate all fields exist */
|
/* Validate all fields exist and are non-empty */
|
||||||
if (!timestamp_str || !username || !content) {
|
if (!timestamp_str || !username || !content) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (username[0] == '\0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Validate field lengths */
|
/* Validate field lengths */
|
||||||
if (strlen(username) >= MAX_USERNAME_LEN) {
|
if (strlen(username) >= MAX_USERNAME_LEN) {
|
||||||
|
|
|
||||||
|
|
@ -1006,10 +1006,16 @@ static int exec_command_post(client_t *client, const char *args) {
|
||||||
|
|
||||||
resolve_exec_username(client, username, sizeof(username));
|
resolve_exec_username(client, username, sizeof(username));
|
||||||
|
|
||||||
|
if (strncmp(content, "/me ", 4) == 0 && content[4] != '\0') {
|
||||||
|
msg.username[0] = '*';
|
||||||
|
msg.username[1] = '\0';
|
||||||
|
snprintf(msg.content, sizeof(msg.content), "%s %s", username, content + 4);
|
||||||
|
} else {
|
||||||
strncpy(msg.username, username, sizeof(msg.username) - 1);
|
strncpy(msg.username, username, sizeof(msg.username) - 1);
|
||||||
msg.username[sizeof(msg.username) - 1] = '\0';
|
msg.username[sizeof(msg.username) - 1] = '\0';
|
||||||
strncpy(msg.content, content, sizeof(msg.content) - 1);
|
strncpy(msg.content, content, sizeof(msg.content) - 1);
|
||||||
msg.content[sizeof(msg.content) - 1] = '\0';
|
msg.content[sizeof(msg.content) - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
room_broadcast(g_room, &msg);
|
room_broadcast(g_room, &msg);
|
||||||
if (message_save(&msg) < 0) {
|
if (message_save(&msg) < 0) {
|
||||||
|
|
|
||||||
57
src/tui.c
57
src/tui.c
|
|
@ -56,9 +56,12 @@ void tui_clear_screen(client_t *client) {
|
||||||
void tui_render_screen(client_t *client) {
|
void tui_render_screen(client_t *client) {
|
||||||
if (!client || !client->connected) return;
|
if (!client || !client->connected) return;
|
||||||
|
|
||||||
/* Heap-allocated: worst case is ~200 messages * ~1100 bytes + separator + status.
|
int render_width = client->width;
|
||||||
* 64 KiB covers all real terminal sizes without stack risk. */
|
int render_height = client->height;
|
||||||
const size_t buf_size = 65536;
|
if (render_width < 10) render_width = 10;
|
||||||
|
if (render_height < 4) render_height = 4;
|
||||||
|
|
||||||
|
const size_t buf_size = (size_t)(render_height + 10) * (MAX_MESSAGE_LEN + 64) + 2048;
|
||||||
char *buffer = malloc(buf_size);
|
char *buffer = malloc(buf_size);
|
||||||
if (!buffer) return;
|
if (!buffer) return;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|
@ -71,7 +74,7 @@ void tui_render_screen(client_t *client) {
|
||||||
pthread_rwlock_unlock(&g_room->lock);
|
pthread_rwlock_unlock(&g_room->lock);
|
||||||
|
|
||||||
/* Calculate which messages to show */
|
/* Calculate which messages to show */
|
||||||
int msg_height = client->height - 3;
|
int msg_height = render_height - 3;
|
||||||
if (msg_height < 1) msg_height = 1;
|
if (msg_height < 1) msg_height = 1;
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|
@ -129,11 +132,11 @@ void tui_render_screen(client_t *client) {
|
||||||
|
|
||||||
char title[256];
|
char title[256];
|
||||||
snprintf(title, sizeof(title),
|
snprintf(title, sizeof(title),
|
||||||
" 聊天室 | 在线: %d | 模式: %s | Ctrl+C 退出 | ? 帮助 ",
|
" %s | 在线: %d | 模式: %s | ? 帮助 ",
|
||||||
online, mode_str);
|
client->username, online, mode_str);
|
||||||
|
|
||||||
int title_width = utf8_string_width(title);
|
int title_width = utf8_string_width(title);
|
||||||
int padding = client->width - title_width;
|
int padding = render_width - title_width;
|
||||||
if (padding < 0) padding = 0;
|
if (padding < 0) padding = 0;
|
||||||
|
|
||||||
buffer_appendf(buffer, buf_size, &pos, ANSI_REVERSE "%s", title);
|
buffer_appendf(buffer, buf_size, &pos, ANSI_REVERSE "%s", title);
|
||||||
|
|
@ -146,7 +149,7 @@ void tui_render_screen(client_t *client) {
|
||||||
if (msg_snapshot) {
|
if (msg_snapshot) {
|
||||||
for (int i = 0; i < snapshot_count; i++) {
|
for (int i = 0; i < snapshot_count; i++) {
|
||||||
char msg_line[1024];
|
char msg_line[1024];
|
||||||
message_format(&msg_snapshot[i], msg_line, sizeof(msg_line), client->width);
|
message_format(&msg_snapshot[i], msg_line, sizeof(msg_line), render_width);
|
||||||
buffer_appendf(buffer, buf_size, &pos, "%s\033[K\r\n", msg_line);
|
buffer_appendf(buffer, buf_size, &pos, "%s\033[K\r\n", msg_line);
|
||||||
}
|
}
|
||||||
free(msg_snapshot);
|
free(msg_snapshot);
|
||||||
|
|
@ -158,7 +161,7 @@ void tui_render_screen(client_t *client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Separator - use box drawing character */
|
/* Separator - use box drawing character */
|
||||||
for (int i = 0; i < client->width; i++) {
|
for (int i = 0; i < render_width; i++) {
|
||||||
buffer_append_bytes(buffer, buf_size, &pos, "─", strlen("─"));
|
buffer_append_bytes(buffer, buf_size, &pos, "─", strlen("─"));
|
||||||
}
|
}
|
||||||
buffer_appendf(buffer, buf_size, &pos, "\033[K\r\n");
|
buffer_appendf(buffer, buf_size, &pos, "\033[K\r\n");
|
||||||
|
|
@ -184,17 +187,23 @@ void tui_render_screen(client_t *client) {
|
||||||
void tui_render_input(client_t *client, const char *input) {
|
void tui_render_input(client_t *client, const char *input) {
|
||||||
if (!client || !client->connected) return;
|
if (!client || !client->connected) return;
|
||||||
|
|
||||||
|
int rw = client->width;
|
||||||
|
int rh = client->height;
|
||||||
|
if (rw < 10) rw = 10;
|
||||||
|
if (rh < 4) rh = 4;
|
||||||
|
|
||||||
char buffer[2048];
|
char buffer[2048];
|
||||||
int input_width = utf8_string_width(input);
|
int input_width = utf8_string_width(input);
|
||||||
|
int avail = rw - 3;
|
||||||
|
if (avail < 1) avail = 1;
|
||||||
|
|
||||||
/* Truncate from start if too long */
|
/* Truncate from start if too long */
|
||||||
char display[MAX_MESSAGE_LEN];
|
char display[MAX_MESSAGE_LEN];
|
||||||
strncpy(display, input, sizeof(display) - 1);
|
strncpy(display, input, sizeof(display) - 1);
|
||||||
display[sizeof(display) - 1] = '\0';
|
display[sizeof(display) - 1] = '\0';
|
||||||
|
|
||||||
if (input_width > client->width - 3) {
|
if (input_width > avail) {
|
||||||
/* Find where to start displaying */
|
int excess = input_width - avail;
|
||||||
int excess = input_width - (client->width - 3);
|
|
||||||
int skip_width = 0;
|
int skip_width = 0;
|
||||||
const char *p = input;
|
const char *p = input;
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
|
|
@ -210,7 +219,7 @@ void tui_render_input(client_t *client, const char *input) {
|
||||||
|
|
||||||
/* Move to input line and clear it, then write input */
|
/* Move to input line and clear it, then write input */
|
||||||
snprintf(buffer, sizeof(buffer), "\033[%d;1H" ANSI_CLEAR_LINE "> %s",
|
snprintf(buffer, sizeof(buffer), "\033[%d;1H" ANSI_CLEAR_LINE "> %s",
|
||||||
client->height, display);
|
rh, display);
|
||||||
|
|
||||||
client_send(client, buffer, strlen(buffer));
|
client_send(client, buffer, strlen(buffer));
|
||||||
}
|
}
|
||||||
|
|
@ -219,6 +228,11 @@ void tui_render_input(client_t *client, const char *input) {
|
||||||
void tui_render_command_output(client_t *client) {
|
void tui_render_command_output(client_t *client) {
|
||||||
if (!client || !client->connected) return;
|
if (!client || !client->connected) return;
|
||||||
|
|
||||||
|
int rw = client->width;
|
||||||
|
int rh = client->height;
|
||||||
|
if (rw < 10) rw = 10;
|
||||||
|
if (rh < 4) rh = 4;
|
||||||
|
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
|
|
@ -229,7 +243,7 @@ void tui_render_command_output(client_t *client) {
|
||||||
/* Title */
|
/* Title */
|
||||||
const char *title = " COMMAND OUTPUT ";
|
const char *title = " COMMAND OUTPUT ";
|
||||||
int title_width = strlen(title);
|
int title_width = strlen(title);
|
||||||
int padding = client->width - title_width;
|
int padding = rw - title_width;
|
||||||
if (padding < 0) padding = 0;
|
if (padding < 0) padding = 0;
|
||||||
|
|
||||||
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_REVERSE "%s", title);
|
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_REVERSE "%s", title);
|
||||||
|
|
@ -245,15 +259,15 @@ void tui_render_command_output(client_t *client) {
|
||||||
|
|
||||||
char *line = strtok(output_copy, "\n");
|
char *line = strtok(output_copy, "\n");
|
||||||
int line_count = 0;
|
int line_count = 0;
|
||||||
int max_lines = client->height - 2;
|
int max_lines = rh - 2;
|
||||||
|
|
||||||
while (line && line_count < max_lines) {
|
while (line && line_count < max_lines) {
|
||||||
char truncated[1024];
|
char truncated[1024];
|
||||||
strncpy(truncated, line, sizeof(truncated) - 1);
|
strncpy(truncated, line, sizeof(truncated) - 1);
|
||||||
truncated[sizeof(truncated) - 1] = '\0';
|
truncated[sizeof(truncated) - 1] = '\0';
|
||||||
|
|
||||||
if (utf8_string_width(truncated) > client->width) {
|
if (utf8_string_width(truncated) > rw) {
|
||||||
utf8_truncate(truncated, client->width);
|
utf8_truncate(truncated, rw);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_appendf(buffer, sizeof(buffer), &pos, "%s\r\n", truncated);
|
buffer_appendf(buffer, sizeof(buffer), &pos, "%s\r\n", truncated);
|
||||||
|
|
@ -369,6 +383,11 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
void tui_render_help(client_t *client) {
|
void tui_render_help(client_t *client) {
|
||||||
if (!client || !client->connected) return;
|
if (!client || !client->connected) return;
|
||||||
|
|
||||||
|
int rw = client->width;
|
||||||
|
int rh = client->height;
|
||||||
|
if (rw < 10) rw = 10;
|
||||||
|
if (rh < 4) rh = 4;
|
||||||
|
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
|
|
@ -379,7 +398,7 @@ void tui_render_help(client_t *client) {
|
||||||
/* Title */
|
/* Title */
|
||||||
const char *title = " HELP ";
|
const char *title = " HELP ";
|
||||||
int title_width = strlen(title);
|
int title_width = strlen(title);
|
||||||
int padding = client->width - title_width;
|
int padding = rw - title_width;
|
||||||
if (padding < 0) padding = 0;
|
if (padding < 0) padding = 0;
|
||||||
|
|
||||||
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_REVERSE "%s", title);
|
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_REVERSE "%s", title);
|
||||||
|
|
@ -403,7 +422,7 @@ void tui_render_help(client_t *client) {
|
||||||
line = strtok(NULL, "\n");
|
line = strtok(NULL, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int content_height = client->height - 2;
|
int content_height = rh - 2;
|
||||||
if (content_height < 1) content_height = 1;
|
if (content_height < 1) content_height = 1;
|
||||||
int max_scroll = line_count - content_height + 1;
|
int max_scroll = line_count - content_height + 1;
|
||||||
if (max_scroll < 0) max_scroll = 0;
|
if (max_scroll < 0) max_scroll = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue