diff --git a/src/chat/chat_core.c b/src/chat/chat_core.c index b17ba814..0acb58c5 100644 --- a/src/chat/chat_core.c +++ b/src/chat/chat_core.c @@ -690,7 +690,8 @@ struct chat_member_tags* chat_member_tags_load(struct UTUN_INSTANCE* inst, const const void *signature = sqlite3_column_blob(st, 1), *x = sqlite3_column_blob(st, 2), *ed = sqlite3_column_blob(st, 3); if ((type != SQLITE_NULL && type != SQLITE_TEXT) || (type == SQLITE_TEXT && (!text || strlen(text) != (size_t)bytes)) || bytes > MS_ADM_TAGS_MAX || sqlite3_column_type(st, 2) != SQLITE_BLOB || sqlite3_column_bytes(st, 2) != 32 || !x || - sqlite3_column_type(st, 3) != SQLITE_BLOB || sqlite3_column_bytes(st, 3) != 32 || !ed || sqlite3_column_int(st, 4)) goto error; + sqlite3_column_type(st, 3) != SQLITE_BLOB || sqlite3_column_bytes(st, 3) != 32 || !ed || + sqlite3_column_type(st, 4) != SQLITE_INTEGER || sqlite3_column_int(st, 4)) goto error; if (bytes) memcpy(t->baseline, text, (size_t)bytes + 1); if (sig_type != SQLITE_NULL) { if (sig_type != SQLITE_BLOB || sqlite3_column_bytes(st, 1) != 64 || !signature) goto error; @@ -767,7 +768,7 @@ int chat_member_tags_replace(struct chat_member_tags* t, const char* json) { if (!requested && parsed.key_count == CHAT_MEMBER_TAGS_MAX_KEYS) rc = -1; if (rc != 0) { for (int i = 0; i < parsed.key_count; i++) { u_free(parsed.keys[i].raw); u_free(parsed.keys[i].val); } - DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_replace — invalid JSON", CC_ID); return -1; + DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "tags: replace rejected JSON/version/key limit"); return -1; } for (int i = 0; i < t->key_count; i++) { u_free(t->keys[i].raw); u_free(t->keys[i].val); } t->key_count = parsed.key_count; memcpy(t->keys, parsed.keys, sizeof(t->keys)); @@ -902,7 +903,21 @@ void chat_member_tags_commit_trampoline(void* arg) { } struct chat_member_tags* t = chat_member_tags_load(req->inst, req->ch_id, req->node_id); if (!t) { u_free(arg); return; } - if (!req->json[0] || !chat_member_tags_replace(t, req->json)) chat_member_tags_commit(req->inst, t); + int rc = 0; + if (req->json[0]) { + struct chat_member_tags* patch = u_calloc(1, sizeof(*patch)); + if (!patch) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "tags: patch allocation failed"); rc = -1; } + else { + patch->db = t->db; patch->version = t->version; + rc = chat_member_tags_replace(patch, req->json); + for (int i = 0; !rc && i < patch->key_count; i++) { + struct chat_member_tags_key* key = &patch->keys[i]; + if (strcmp(key->key, "ver")) rc = chat_member_tags_set(t, key->key, key->val); + } + chat_member_tags_free(patch); + } + } + if (!rc) chat_member_tags_commit(req->inst, t); chat_member_tags_free(t); u_free(arg); } diff --git a/src/chat/chat_member.c b/src/chat/chat_member.c index 6372fbc0..8b316c0d 100644 --- a/src/chat/chat_member.c +++ b/src/chat/chat_member.c @@ -64,7 +64,7 @@ static uint8_t parse_adm_tags_flags(const char* adm_tags) { uint8_t flags = 0; /* supernode */ - char buf[256]; + char buf[MS_ADM_TAGS_MAX + 1]; if (json_flat_get(adm_tags, "supernode", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) flags |= CHAT_MEMBER_FLAG_SUPERNODE; diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 8df2a1d2..27891391 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c @@ -797,7 +797,7 @@ char* utun_bridge_get_channel_members_json(const char* channel_id) { int is_admin = 0, is_super = 0, is_moder = 0, is_verified = 0, is_deleted = sqlite3_column_int(st, 10); int storage_role = 0; if (adm_tags) { - char buf[256]; + char buf[MS_ADM_TAGS_MAX + 1]; if (json_flat_get(adm_tags, "supernode", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) is_super = 1; if (json_flat_get(adm_tags, "admin", buf, sizeof(buf)) == 0) { const char* last = buf; for (const char* p = buf; *p; p++) if (*p == 'e' || *p == 'd') last = p; @@ -1030,13 +1030,11 @@ static void member_tags_edit_trampoline(void* arg) { u_free(arg); return; } - chat_member_tags_set(t, "admin", req->admin ? "yes" : "no"); - chat_member_tags_set(t, "moder", req->moder ? "yes" : "no"); - chat_member_tags_set(t, "supernode", req->supernode ? "yes" : "no"); - chat_member_tags_set(t, "storage", req->storage ? "yes" : "no"); - chat_member_tags_set(t, "verified", req->verified ? "yes" : "no"); - chat_member_tags_set(t, "deleted", req->deleted ? "yes" : "no"); - int rc = chat_member_tags_commit(req->inst, t); + const char* keys[] = {"admin", "moder", "supernode", "storage", "verified", "deleted"}; + int values[] = {req->admin, req->moder, req->supernode, req->storage, req->verified, req->deleted}; + int rc = 0; + for (size_t i = 0; !rc && i < sizeof(values) / sizeof(values[0]); i++) rc = chat_member_tags_set(t, keys[i], values[i] ? "yes" : "no"); + if (!rc) rc = chat_member_tags_commit(req->inst, t); bridge_log(rc == 0 ? BLEV_INFO : BLEV_ERROR, "set_member_flags ch=%s nid=0x%016llx admin=%d super=%d moder=%d storage=%d verified=%d deleted=%d rc=%d", req->ch_id, (unsigned long long)req->node_id, req->admin, req->supernode,