Browse Source

Expose polls and reactions through headless control safely

master
evgeny 7 days ago
parent
commit
f99a8c1da2
  1. 46
      src/chat/chat_headless_control.c

46
src/chat/chat_headless_control.c

@ -13,6 +13,7 @@
#include "chat_core.h"
#include "chat_core_priv.h"
#include "chat_event.h"
#include "chat_poll.h"
#include "chat_sync.h"
#include "chat_join.h"
#include "invite_link.h"
@ -30,6 +31,8 @@
#include "../../lib/ll_queue.h"
#include "../../lib/u_async.h"
#include "../../lib/mem.h"
#include "../../lib/strbuf.h"
#include "../../lib/json_flat.h"
#include "../../lib/socket_compat.h"
#include "../../lib/debug_config.h"
#include "../../lib/platform_compat.h"
@ -167,11 +170,20 @@ static void cli_send_str(struct headless_client* cli, const char* s) {
}
static void send_response(struct headless_client* cli, int id, const char* ok_data, const char* error) {
char buf[4096]; int len;
if (error) len = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":false,\"error\":\"%s\"}\n", id, error);
else if (ok_data) len = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":true,\"data\":%s}\n", id, ok_data);
else len = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":true}\n", id);
if (len > 0) cli_send(cli, buf, (size_t)len);
struct strbuf out = strbuf_new(); int rc;
if (error) {
rc = strbuf_addf(&out, "{\"id\":%d,\"ok\":false,\"error\":", id);
if (rc >= 0) rc = strbuf_add_json_string(&out, error);
if (rc >= 0) rc = strbuf_addf(&out, "}\n");
} else rc = strbuf_addf(&out, "{\"id\":%d,\"ok\":true,\"data\":%s}\n", id, ok_data ? ok_data : "null");
if (rc < 0 || out.len + (size_t)cli->send_len > SEND_BUF_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_HEADLESS, "headless: response rejected id=%d bytes=%zu queued=%d rc=%d",
id, out.len, cli->send_len, rc);
char failure[160]; int bytes = snprintf(failure, sizeof(failure),
"{\"id\":%d,\"ok\":false,\"error\":\"Response too large or out of memory; request fewer records\"}\n", id);
cli_send(cli, failure, bytes);
} else cli_send(cli, strbuf_str(&out), out.len);
strbuf_free(&out);
}
/* ── Broadcast event ── */
@ -191,7 +203,12 @@ static void hc_on_chat_event(struct UTUN_INSTANCE* inst, int type, const uint8_t
if (!hc) return;
if (type == 10) return; /* skip status text */
char json[1024]; json[0] = '\0';
if (type == 1 && data && len >= 10) { /* MSG_RECEIVED */
if (type == CHAT_EVT_FEEDBACK_CHANGED && data && len >= 34 && len == data[0] + 34) {
uint8_t cl = data[0]; char message_id[65];
for (unsigned i = 0; i < 32; i++) snprintf(message_id + i * 2, 3, "%02x", data[1 + cl + i]);
snprintf(json, sizeof(json), "{\"event\":\"feedback\",\"ch\":\"%.*s\",\"message_id\":\"%s\",\"kind\":%u}",
(int)cl, data + 1, message_id, data[33 + cl]);
} else if (type == 1 && data && len >= 10) { /* MSG_RECEIVED */
uint8_t cl = data[0];
uint64_t author; memcpy(&author, data + 1 + cl, 8);
snprintf(json, sizeof(json), "{\"event\":\"msg\",\"ch\":\"%.*s\",\"author_id\":\"0x%016llx\"}", (int)cl, data + 1, (unsigned long long)author);
@ -794,6 +811,19 @@ static void hc_handle_radio_subscribers(struct headless_client* cli, int id, con
typedef void (*hc_cmd_fn)(struct headless_client* cli, int id, const char* json);
/* Опросы и реакции используют общий командный API ядра. */
static void hc_handle_feedback(struct headless_client* cli, int id, const char* json) {
char* result = NULL; int rc = chat_feedback_command(cli->hc->inst, json, &result);
if (rc == 0) send_response(cli, id, result, NULL);
else {
char error[256] = "Interaction failed";
if (result && json_flat_get(result, "error", error, sizeof(error)) < 0)
DEBUG_ERROR(DEBUG_CATEGORY_HEADLESS, "headless: invalid interaction error JSON");
send_response(cli, id, NULL, error);
}
u_free(result);
}
static const char* cmd_names[] = { "ping", "status", "channels", "members", "messages", "send", "attach", "invite", "invite_nodes", "connect", "create_channel", "invite_to", "subscribe", "dm_start", "dm_list", "dm_messages", "dm_send", "call_start", "call_accept", "call_decline", "call_hangup", "radio_on", "radio_off", "radio_subscribers", "quit", NULL };
static hc_cmd_fn cmd_handlers[] = { hc_handle_ping, hc_handle_status, hc_handle_channels, hc_handle_members, hc_handle_messages, hc_handle_send, hc_handle_attach, hc_handle_invite, hc_handle_invite_nodes, hc_handle_connect, hc_handle_create_channel, hc_handle_invite_to, hc_handle_subscribe, hc_handle_dm_start, hc_handle_dm_list, hc_handle_dm_messages, hc_handle_dm_send, hc_handle_call_start, hc_handle_call_accept, hc_handle_call_decline, hc_handle_call_hangup, hc_handle_radio_on, hc_handle_radio_off, hc_handle_radio_subscribers, hc_handle_quit };
@ -806,6 +836,10 @@ static void hc_handle_command(struct headless_client* cli, const char* json) {
size_t clen = (size_t)(ce - cs);
if (clen == 0 || clen > 31) { send_response(cli, id, NULL, "invalid cmd"); return; }
char cmd[32]; memcpy(cmd, cs, clen); cmd[clen] = '\0';
if (!strcmp(cmd, "poll_create") || !strcmp(cmd, "vote") || !strcmp(cmd, "react")
|| !strcmp(cmd, "feedback") || !strcmp(cmd, "reaction_catalog")) {
hc_handle_feedback(cli, id, json); return;
}
for (int i = 0; cmd_names[i]; i++) {
if (strcmp(cmd, cmd_names[i]) == 0) { cmd_handlers[i](cli, id, json); return; }
}

Loading…
Cancel
Save