Browse Source

msg b64: base64-encode display prefix in d-field to avoid pipe/quote breaking parse

b64_encode/decode (URL-safe A-Za-z0-9-_) in chat_core_priv.h
on_media_registered encodes msg_data before full_data
on_msg_inserted/chat_core_attachment_download decode base_filename
android_jni_bridge decodes prefix for UI display
topo_upd
evgeny 2 months ago
parent
commit
ea6eeae210
  1. 54
      src/chat/chat_core_priv.h
  2. 10
      src/chat/chat_msg.c
  3. 13
      tools/chatgui-android/jni_bridge/android_jni_bridge.c

54
src/chat/chat_core_priv.h

@ -16,9 +16,63 @@
#include <sqlite3.h> #include <sqlite3.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#define CC_ID "chat_core" #define CC_ID "chat_core"
/* ── Base64 encode/decode (URL-safe, used for msg "d" field prefix) ── */
static const char B64_ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static inline int b64_decode_char(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '-') return 62;
if (c == '_') return 63;
return -1;
}
static inline size_t b64_encode(const uint8_t* src, size_t src_len, char* dst, size_t dst_cap) {
size_t out = 0;
for (size_t i = 0; i < src_len; i += 3) {
if (out + 4 > dst_cap) break;
uint32_t v = (uint32_t)src[i] << 16;
if (i + 1 < src_len) v |= (uint32_t)src[i + 1] << 8;
if (i + 2 < src_len) v |= (uint32_t)src[i + 2];
dst[out++] = B64_ALPHABET[(v >> 18) & 0x3F];
dst[out++] = B64_ALPHABET[(v >> 12) & 0x3F];
dst[out++] = (i + 1 < src_len) ? B64_ALPHABET[(v >> 6) & 0x3F] : '=';
dst[out++] = (i + 2 < src_len) ? B64_ALPHABET[v & 0x3F] : '=';
}
return out;
}
static inline size_t b64_decode(const char* src, size_t src_len, uint8_t* dst, size_t dst_cap) {
size_t out = 0;
int vals[4]; size_t vi = 0;
for (size_t i = 0; i < src_len && out < dst_cap; i++) {
if (src[i] == '=') break;
int v = b64_decode_char(src[i]);
if (v < 0) continue;
vals[vi++] = v;
if (vi == 4) {
dst[out++] = (uint8_t)((vals[0] << 2) | (vals[1] >> 4));
if (out < dst_cap) dst[out++] = (uint8_t)((vals[1] << 4) | (vals[2] >> 2));
if (out < dst_cap && i + 1 < src_len && src[i + 1] != '=')
dst[out++] = (uint8_t)((vals[2] << 6) | vals[3]);
vi = 0;
}
}
if (vi == 3 && out < dst_cap) {
dst[out++] = (uint8_t)((vals[0] << 2) | (vals[1] >> 4));
if (out < dst_cap) dst[out++] = (uint8_t)((vals[1] << 4) | (vals[2] >> 2));
} else if (vi == 2 && out < dst_cap) {
dst[out++] = (uint8_t)((vals[0] << 2) | (vals[1] >> 4));
}
return out;
}
/* ── Глобальное состояние (определено в chat_core.c) ── */ /* ── Глобальное состояние (определено в chat_core.c) ── */
struct chat_core_ctx { struct chat_core_ctx {

10
src/chat/chat_msg.c

@ -116,7 +116,10 @@ static void on_media_registered(void* arg, int err, const struct media_index_res
if (!full_data) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: data alloc fail", CC_ID); u_free(mctx); return; } if (!full_data) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: data alloc fail", CC_ID); u_free(mctx); return; }
size_t off = 0; size_t off = 0;
if (base_len > 0) { memcpy(full_data + off, mctx->msg_data, base_len); off += base_len; } if (base_len > 0) {
char b64[2048]; size_t b64len = b64_encode(mctx->msg_data, base_len, b64, sizeof(b64));
memcpy(full_data + off, b64, b64len); off += b64len;
}
off += snprintf(full_data + off, fdcap - off, "|%lld|%lld|%d|", off += snprintf(full_data + off, fdcap - off, "|%lld|%lld|%d|",
(long long)result->file_size, (long long)result->block_size, nb); (long long)result->file_size, (long long)result->block_size, nb);
for (int i = 0; i < 16; i++) off += snprintf(full_data + off, fdcap - off, "%02x", result->media_id[i]); for (int i = 0; i < 16; i++) off += snprintf(full_data + off, fdcap - off, "%02x", result->media_id[i]);
@ -549,6 +552,9 @@ void on_msg_inserted(struct DB_SYNC_INSTANCE* si, uint64_t record_ts, const char
while (body[bfi] && body[bfi] != '|' && bfi < sizeof(base_filename) - 1) while (body[bfi] && body[bfi] != '|' && bfi < sizeof(base_filename) - 1)
base_filename[bfi++] = body[bfi]; base_filename[bfi++] = body[bfi];
base_filename[bfi] = '\0'; base_filename[bfi] = '\0';
/* base64-decode display prefix (encoded in on_media_registered) */
uint8_t dec[256]; int dlen = (int)b64_decode(base_filename, bfi, dec, sizeof(dec));
if (dlen > 0 && dlen < (int)sizeof(base_filename)) { memcpy(base_filename, dec, (size_t)dlen); base_filename[dlen] = '\0'; }
if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file"); if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file");
const uint8_t* sig_field = strstr(data, "\"sig\":\""); const uint8_t* sig_field = strstr(data, "\"sig\":\"");
@ -630,6 +636,8 @@ void chat_core_attachment_download(const char* channel_id, int64_t msg_id) {
while (body[bfi] && body[bfi] != '|' && bfi < sizeof(base_filename) - 1) while (body[bfi] && body[bfi] != '|' && bfi < sizeof(base_filename) - 1)
base_filename[bfi++] = body[bfi]; base_filename[bfi++] = body[bfi];
base_filename[bfi] = '\0'; base_filename[bfi] = '\0';
uint8_t dec[256]; int dlen = (int)b64_decode(base_filename, bfi, dec, sizeof(dec));
if (dlen > 0 && dlen < (int)sizeof(base_filename)) { memcpy(base_filename, dec, (size_t)dlen); base_filename[dlen] = '\0'; }
if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file"); if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file");
uint8_t author_sig[64]; memcpy(author_sig, sig_blob, 64); uint8_t author_sig[64]; memcpy(author_sig, sig_blob, 64);

13
tools/chatgui-android/jni_bridge/android_jni_bridge.c

@ -413,6 +413,19 @@ char* utun_bridge_get_messages_json(const char* channel_id, int limit) {
const char* cts = strstr((const char*)dptr, "\"ct\":\""); const char* cts = strstr((const char*)dptr, "\"ct\":\"");
if (cts) { cts += 6; char* cte = strchr((char*)cts, '"'); if (cte) { size_t tlc = (size_t)(cte - cts); if (tlc < sizeof(ct)) { memcpy(ct, cts, tlc); ct[tlc] = '\0'; } } } if (cts) { cts += 6; char* cte = strchr((char*)cts, '"'); if (cte) { size_t tlc = (size_t)(cte - cts); if (tlc < sizeof(ct)) { memcpy(ct, cts, tlc); ct[tlc] = '\0'; } } }
} }
/* base64-decode display prefix for media messages (encoded in on_media_registered) */
if (strcmp(ct, "text/plain") != 0 && txt[0]) {
char* pipe = strchr(txt, '|');
if (pipe) {
size_t prefix_len = (size_t)(pipe - txt);
uint8_t dec[1024]; int declen = (int)b64_decode(txt, prefix_len, dec, sizeof(dec));
if (declen > 0 && declen < 2000) {
size_t suffix_len = strlen(pipe);
memmove(txt + declen, pipe, suffix_len + 1);
memcpy(txt, dec, (size_t)declen);
}
}
}
char* esc_txt = json_escape_alloc(txt); char* esc_txt = json_escape_alloc(txt);
char* esc_ct = json_escape_alloc(ct); char* esc_ct = json_escape_alloc(ct);
int is_out = (node_id == (int64_t)my_id) ? 1 : 0; int is_out = (node_id == (int64_t)my_id) ? 1 : 0;

Loading…
Cancel
Save