From b198855ae520dd4e88c5c98207a8fe50ec5fa92c Mon Sep 17 00:00:00 2001 From: Anders Holck Date: Sat, 2 May 2026 00:00:03 +0200 Subject: [PATCH] Add base64 decode for translation and right-align clock in status bar --- main.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 92e7958..722cc27 100644 --- a/main.c +++ b/main.c @@ -218,6 +218,77 @@ static void config_save(void) fclose(f); } +/* Base64 decode: returns decoded length, or -1 if not valid base64 */ +static int base64_decode(const char *in, size_t in_len, char *out, size_t out_size) +{ + static const int T[256] = { + ['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7, + ['I']=8,['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15, + ['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23, + ['Y']=24,['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31, + ['g']=32,['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39, + ['o']=40,['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47, + ['w']=48,['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55, + ['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['+']=62,['/']=63, + }; + /* Strip trailing padding for length calc */ + size_t pad = 0; + while (in_len > 0 && in[in_len - 1] == '=') { pad++; in_len--; } + if (in_len < 4) return -1; + size_t out_len = in_len * 3 / 4; + if (out_len >= out_size) return -1; + + size_t o = 0; + for (size_t i = 0; i < in_len; i += 4) { + int n = (int)(in_len - i); + if (n < 2) break; + unsigned int a = T[(unsigned char)in[i]]; + unsigned int b = T[(unsigned char)in[i+1]]; + out[o++] = (a << 2) | (b >> 4); + if (n > 2 && i + 2 < in_len + pad) { + unsigned int c = (i+2 < in_len) ? T[(unsigned char)in[i+2]] : 0; + out[o++] = (b << 4) | (c >> 2); + if (n > 3 && i + 3 < in_len + pad) { + unsigned int d = (i+3 < in_len) ? T[(unsigned char)in[i+3]] : 0; + out[o++] = (c << 6) | d; + } + } + } + /* Adjust for padding */ + if (pad == 1 && o > 0) o--; + if (pad == 2 && o > 0) o--; + out[o] = '\0'; + return (int)o; +} + +/* Check if text looks like base64 and decode it. Returns 1 if decoded. */ +static int try_base64_decode(const char *text, char *out, size_t out_size) +{ + size_t len = strlen(text); + if (len < 8 || len > 1000) return 0; + /* Must be valid base64 chars only */ + size_t i; + for (i = 0; i < len; i++) { + char c = text[i]; + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '+' || c == '/' || c == '=')) + return 0; + } + /* Length must be multiple of 4 */ + if (len % 4 != 0) return 0; + int n = base64_decode(text, len, out, out_size); + if (n < 4) return 0; + /* Check that result looks like text (mostly printable) */ + int printable = 0; + for (int j = 0; j < n; j++) { + unsigned char c = (unsigned char)out[j]; + if ((c >= 0x20 && c <= 0x7e) || c == '\n' || c == '\r' || c == '\t' || c >= 0x80) + printable++; + } + if (printable * 100 / n < 80) return 0; + return 1; +} + static int needs_translation(const char *text) { if (!ai_cfg.enabled || !translate_enabled) return 0; @@ -461,6 +532,22 @@ static void draw_statusbar(void) size_t blen = strlen(bar); snprintf(bar + blen, sizeof(bar) - blen, "%s", act); + /* Right-align timestamp */ + time_t now = time(NULL); + struct tm *tm = localtime(&now); + char rhs[64]; + snprintf(rhs, sizeof(rhs), "[Holck Mirk - %02d%02d %02d:%02d]", + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + size_t bar_len = strlen(bar); + size_t rhs_len = strlen(rhs); + if (bar_len + rhs_len < (size_t)term_cols) { + size_t pad = term_cols - rhs_len; + while (bar_len < pad) + bar[bar_len++] = ' '; + memcpy(bar + pad, rhs, rhs_len); + bar[term_cols] = '\0'; + } + /* Status bar on second-to-last row */ printf("\033[%d;1H\033[7m%-*.*s\033[0m", term_rows - 1, term_cols, term_cols, bar); @@ -995,12 +1082,24 @@ static void handle_line(char *line) } else if (strcasecmp(target, nick) == 0) { wprintf(WL_MSG, "<%s> %s\n", sender, text); pm_nick_add(sender); - if (needs_translation(text)) + char b64dec[1024]; + if (try_base64_decode(text, b64dec, sizeof(b64dec))) { + if (needs_translation(b64dec)) + translate_async(b64dec, WL_MSG, NULL); + else + wprintf(WL_MSG, " \033[3m[b64: %s]\033[0m\n", b64dec); + } else if (needs_translation(text)) translate_async(text, WL_MSG, NULL); } else { int lvl = chan_to_level(target); wprintf(lvl, "<%s> %s\n", sender, text); - if (needs_translation(text)) + char b64dec2[1024]; + if (try_base64_decode(text, b64dec2, sizeof(b64dec2))) { + if (needs_translation(b64dec2)) + translate_async(b64dec2, lvl, translate_public ? target : NULL); + else + wprintf(lvl, " \033[3m[b64: %s]\033[0m\n", b64dec2); + } else if (needs_translation(text)) translate_async(text, lvl, translate_public ? target : NULL); } }