Browse Source

Carry encrypted group name metadata in PM invitations

master
evgeny 6 days ago
parent
commit
306aaa94ba
  1. 43
      src/dm/dm_core.c
  2. 6
      src/dm/dm_core.h

43
src/dm/dm_core.c

@ -959,6 +959,18 @@ int dm_list_messages_json(struct UTUN_INSTANCE* inst, const char* conv_id, int c
if (enc_len < DM_TAG_SIZE || enc_len > DM_DATA_MAX + DM_TAG_SIZE ||
dm_decrypt(c.content_key, nonce, enc, (size_t)enc_len, (uint8_t*)text, &plen)) goto fail;
text[plen] = '\0';
const char* group_name = "";
int invite = ct && !strcmp(ct, "invite");
if (invite) {
char* separator = memchr(text, '\0', plen);
if (!separator || (size_t)(separator - text) + 8 > plen ||
strncmp(separator + 1, "utun://", 7) || memchr(separator + 1, '\0', plen - (separator + 1 - text))) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "invite PM: malformed payload conv=%s seq=%llu bytes=%zu", conv_id,
(unsigned long long)seq, plen);
goto fail;
}
group_name = text;
}
struct attachment_info attachment = {0};
int file = ct && !strcmp(ct, "file");
if (file) {
@ -970,15 +982,19 @@ int dm_list_messages_json(struct UTUN_INSTANCE* inst, const char* conv_id, int c
const char* path = ready == 1 ? (const char*)sqlite3_column_text(st, 10) : "";
const char* status = delivered && dir ? "delivered" : ready == 1 ? "ready" : downloading ? "downloading" :
ready < 0 ? "expired" : "pending";
char esc_ct[512], esc_data[DM_DATA_MAX * 2 + 8], esc_path[2048];
json_escape(ct ? ct : "", esc_ct, sizeof(esc_ct)); json_escape(text, esc_data, sizeof(esc_data));
char esc_ct[512], esc_data[DM_DATA_MAX * 6 + 1], esc_group[DM_DATA_MAX * 6 + 1], esc_path[2048];
json_escape(group_name, esc_group, sizeof(esc_group));
const char* display_data = invite ? text + strlen(group_name) + 1 : text;
json_escape(ct ? ct : "", esc_ct, sizeof(esc_ct)); json_escape(display_data, esc_data, sizeof(esc_data));
json_escape(path, esc_path, sizeof(esc_path));
if (strbuf_addf(&sb, "%s{\"dir\":%d,\"seq\":%llu,\"ts\":%llu,\"author\":\"%llu\",\"ct\":\"%s\",\"data\":\"%s\","
"\"pending\":%s,\"media_status\":\"%s\",\"media_path\":\"%s\",\"media_size\":%llu,"
"\"tags\":{\"group_name\":\"%s\"},"
"\"content_type\":\"%s\",\"duration_ms\":%u,\"width\":%u,\"height\":%u,\"waveform\":\"%s\"}", first ? "" : ",", dir,
(unsigned long long)seq, (unsigned long long)ts, (unsigned long long)author, esc_ct, esc_data,
sqlite3_column_int(st, 6) ? "true" : "false", status, esc_path,
(unsigned long long)sqlite3_column_int64(st, 11), file ? attachment_content_type(attachment.kind) : "text/plain",
(unsigned long long)sqlite3_column_int64(st, 11), esc_group,
file ? attachment_content_type(attachment.kind) : "text/plain",
attachment.duration_ms, attachment.width, attachment.height, attachment.waveform) < 0) goto fail;
first = 0;
}
@ -1065,10 +1081,25 @@ static void dm_invite_ready(void* arg, int result, const char* link) {
char conv[64];
snprintf(conv, sizeof(conv), "%llu", (unsigned long long)dm_derive_conv_id(req->inst->node_id, req->peer_node_id));
size_t bytes = strlen(link);
DEBUG_INFO(DEBUG_CATEGORY_DM, "invite PM: registered request=%llu peer=%llu ch=%llu bytes=%zu ttl=%ds",
sqlite3_stmt* st = NULL;
char channel[64], payload[DM_DATA_MAX];
snprintf(channel, sizeof(channel), "%llu", (unsigned long long)req->channel_id);
struct dm_state* dm = dm_of(req->inst);
int rc = sqlite3_prepare_v2(dm->db, "SELECT name FROM channels WHERE channel_id=?", -1, &st, NULL);
if (rc == SQLITE_OK) { sqlite3_bind_text(st, 1, channel, -1, SQLITE_STATIC); rc = sqlite3_step(st); }
size_t name_bytes = rc == SQLITE_ROW ? (size_t)sqlite3_column_bytes(st, 0) : 0;
if (rc != SQLITE_ROW || name_bytes + 1 + bytes > sizeof(payload)) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "invite PM: group metadata failed ch=%s rc=%d name_bytes=%zu link_bytes=%zu sql=%s",
channel, rc, name_bytes, bytes, sqlite3_errmsg(dm->db));
sqlite3_finalize(st); dm_invite_done(req, DM_INVITE_SEND_ERROR); return;
}
memcpy(payload, sqlite3_column_text(st, 0), name_bytes); payload[name_bytes] = '\0';
memcpy(payload + name_bytes + 1, link, bytes);
sqlite3_finalize(st);
DEBUG_INFO(DEBUG_CATEGORY_DM, "invite PM: registered request=%llu peer=%llu ch=%llu name_bytes=%zu link_bytes=%zu ttl=%ds",
(unsigned long long)req->request_id, (unsigned long long)req->peer_node_id,
(unsigned long long)req->channel_id, bytes, JOIN_KEY_TTL_SECONDS);
int rc = dm_send(req->inst, conv, "text", (const uint8_t*)link, (uint32_t)bytes);
(unsigned long long)req->channel_id, name_bytes, bytes, JOIN_KEY_TTL_SECONDS);
rc = dm_send(req->inst, conv, "invite", (const uint8_t*)payload, (uint32_t)(name_bytes + 1 + bytes));
if (rc) DEBUG_ERROR(DEBUG_CATEGORY_DM, "invite PM: send failed request=%llu conv=%s rc=%d",
(unsigned long long)req->request_id, conv, rc);
dm_invite_done(req, rc ? DM_INVITE_SEND_ERROR : DM_INVITE_OK);

6
src/dm/dm_core.h

@ -80,7 +80,9 @@ int dm_start(struct UTUN_INSTANCE* inst, uint64_t peer_node_id,
/* Отправить сообщение в беседу. Шифрует, подписывает, сохраняет локально и
* доставляет (прямое соединение при доступности пира, иначе mailbox).
* conv_id — строковое представление (как в dm_conversations.conv_id).
* Для ct="file" data=[descriptor:56][basename:utf8], только после подготовки dm_media. */
* Для ct="file" data=[descriptor:56][basename:utf8], только после подготовки dm_media.
* Для ct="invite" data=[group_name:utf8][NUL][полная ссылка:utf8], без завершающего NUL.
* Имя и ссылка шифруются и подписываются вместе; GUI получает data=ссылка, tags.group_name=имя. */
int dm_send(struct UTUN_INSTANCE* inst, const char* conv_id, const char* content_type,
const uint8_t* data, uint32_t data_len);
@ -115,7 +117,7 @@ struct dm_send_req {
void dm_send_trampoline(void* arg);
/* Приглашение в выбранный канал через PM: свежая ссылка после ACK регистрации,
* затем обычный dm_send("text"). Получатель фиксирован независимо от текущего UI.
* затем dm_send("invite") с названием группы. Получатель фиксирован независимо от текущего UI.
* Один ожидающий запрос на instance; результат CHAT_EVT_DM_INVITE_RESULT.
* cancel=1 отменяет только ожидающий request_id; отправленное PM не отзывается.
* arg=u_calloc(dm_invite_req); обработчик/финальный callback освобождает arg. */

Loading…
Cancel
Save