etcp: переупорядочить close + защита state==2 во всех API-точках; topo: правильная очистка group nodes и registry; chat/member: новый модуль member_sync; chatgui: обновления транспорта и UI
This commit is contained in:
1 parent
b2e4aadbf0
commit
7f901c01e1
38 files changed
+1275
-540
No files matched your search
+142
-115
@@ -4,6 +4,7 @@
|
||||
#include "memberpropsdialog.h"
|
||||
#include "renamedialog.h"
|
||||
#include "../db/db_manager.h"
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QSplitter>
|
||||
#include <QLabel>
|
||||
@@ -11,15 +12,23 @@
|
||||
#include <QFont>
|
||||
#include <QSet>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "../../../lib/platform_compat.h"
|
||||
|
||||
extern "C" {
|
||||
#include "utun_instance.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "stcp_link.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "chat/chat_core.h"
|
||||
#include "../../lib/mem.h"
|
||||
}
|
||||
|
||||
static AccountList* g_detailTarget = nullptr;
|
||||
|
||||
static void memberDetailCb(const uint8_t* data, int len) {
|
||||
if (!g_detailTarget) return;
|
||||
if (len < 13) return;
|
||||
uint64_t nodeId; memcpy(&nodeId, data, 8);
|
||||
g_detailTarget->onMemberDetailData(nodeId, data, len);
|
||||
}
|
||||
|
||||
QString AccountList::addrStr(const void* addr, int family) {
|
||||
@@ -51,7 +60,7 @@ QString AccountList::relativeTimeStr(qint64 timestamp) {
|
||||
AccountList::AccountList(DbManager* db, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_listView(new QListView(this))
|
||||
, m_model(new MemberListModel(db, this))
|
||||
, m_model(new MemberListModel(this))
|
||||
, m_db(db)
|
||||
, m_detailText(nullptr)
|
||||
{
|
||||
@@ -93,10 +102,6 @@ AccountList::AccountList(DbManager* db, QWidget *parent)
|
||||
connect(m_listView, &QListView::clicked, this, &AccountList::onMemberClicked);
|
||||
connect(m_listView, &QListView::doubleClicked, this, &AccountList::onMemberDoubleClicked);
|
||||
|
||||
m_refreshTimer = new QTimer(this);
|
||||
m_refreshTimer->start(2000);
|
||||
connect(m_refreshTimer, &QTimer::timeout, m_model, &MemberListModel::refreshRtt);
|
||||
|
||||
connect(m_listView, &QWidget::customContextMenuRequested,
|
||||
this, &AccountList::onMemberContextMenu);
|
||||
|
||||
@@ -107,8 +112,15 @@ AccountList::AccountList(DbManager* db, QWidget *parent)
|
||||
m_linkTimer = new QTimer(this);
|
||||
m_linkTimer->setInterval(1000);
|
||||
connect(m_linkTimer, &QTimer::timeout, this, [this]{
|
||||
if (m_detailNodeId != 0) updateDetailDisplay();
|
||||
if (m_detailNodeId != 0) requestMemberDetail();
|
||||
});
|
||||
|
||||
g_detailTarget = this;
|
||||
gui_bridge_set_member_detail_cb(memberDetailCb);
|
||||
}
|
||||
|
||||
AccountList::~AccountList() {
|
||||
if (g_detailTarget == this) g_detailTarget = nullptr;
|
||||
}
|
||||
|
||||
void AccountList::setChannel(const QString& channelId) {
|
||||
@@ -139,6 +151,12 @@ void AccountList::showMemberDetail(quint64 nodeId) {
|
||||
m_detailLastSeen = 0;
|
||||
m_detailCreated = 0;
|
||||
m_detailAddrs.clear();
|
||||
m_snapConnPresent = false;
|
||||
m_snapConnUp = false;
|
||||
m_snapConnInit = false;
|
||||
m_snapOwnTcpActive = 0;
|
||||
m_snapLinks.clear();
|
||||
m_snapSocks.clear();
|
||||
|
||||
sqlite3* d = m_db->m_db;
|
||||
sqlite3_stmt* st = nullptr;
|
||||
@@ -193,31 +211,6 @@ void AccountList::showMemberDetail(quint64 nodeId) {
|
||||
sqlite3_finalize(na);
|
||||
}
|
||||
|
||||
/* resolve interface names from live sockets and sort */
|
||||
if (m_inst) {
|
||||
for (auto& a : m_detailAddrs) {
|
||||
if (a.protocol == 1) {
|
||||
for (struct ETCP_SOCKET* s = m_inst->etcp_sockets; s; s = s->next) {
|
||||
if ((int)s->sock_id == a.socket_id) {
|
||||
QString name = QString::fromLatin1(s->name);
|
||||
int idx = name.lastIndexOf("_v");
|
||||
if (idx > 3) a.ifname = name.mid(3, idx - 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (a.protocol == 2) {
|
||||
for (struct TCP_SOCKET* ts = m_inst->tcp_sockets; ts; ts = ts->next) {
|
||||
if ((int)ts->sock_id == a.socket_id) {
|
||||
QString name = QString::fromLatin1(ts->name);
|
||||
int idx = name.lastIndexOf("_v");
|
||||
if (idx > 3) a.ifname = name.mid(3, idx - 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(m_detailAddrs.begin(), m_detailAddrs.end(),
|
||||
[](const DetailAddress& a, const DetailAddress& b) {
|
||||
if (a.ifname != b.ifname) return a.ifname < b.ifname;
|
||||
@@ -229,6 +222,78 @@ void AccountList::showMemberDetail(quint64 nodeId) {
|
||||
m_detailTimer->start();
|
||||
m_linkTimer->start();
|
||||
updateDetailDisplay();
|
||||
requestMemberDetail();
|
||||
}
|
||||
|
||||
void AccountList::requestMemberDetail() {
|
||||
if (m_detailNodeId == 0) return;
|
||||
void* arg = u_malloc(sizeof(uint64_t));
|
||||
if (!arg) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "AccountList: req alloc failed"); return; }
|
||||
uint64_t nid = m_detailNodeId;
|
||||
memcpy(arg, &nid, sizeof(nid));
|
||||
gui_bridge_post_uasync_fn(chat_core_collect_member_detail_trampoline, arg);
|
||||
}
|
||||
|
||||
void AccountList::onMemberDetailData(uint64_t nodeId, const uint8_t* data, int len) {
|
||||
if (nodeId != m_detailNodeId) return; /* stale response from earlier selection */
|
||||
if (len < 13) return;
|
||||
const uint8_t* p = data;
|
||||
/* skip node_id(8) */
|
||||
p += 8;
|
||||
uint8_t flags = *p++;
|
||||
m_snapConnPresent = (flags & 1) != 0;
|
||||
m_snapConnUp = (flags & 2) != 0;
|
||||
m_snapConnInit = (flags & 4) != 0;
|
||||
uint16_t ownTcpActive;
|
||||
memcpy(&ownTcpActive, p, 2); p += 2;
|
||||
m_snapOwnTcpActive = ownTcpActive;
|
||||
|
||||
uint8_t linkCount = *p++;
|
||||
m_snapLinks.clear();
|
||||
for (int i = 0; i < linkCount; i++) {
|
||||
if (p + MEMBER_DETAIL_LINK_SIZE > data + len) break;
|
||||
MemberLinkSnap lk;
|
||||
lk.isTcp = p[0] != 0;
|
||||
lk.linkState = p[1];
|
||||
lk.linkStatus = p[2];
|
||||
lk.tcpReady = p[3] != 0;
|
||||
lk.family = p[4];
|
||||
int addrLen = p[5];
|
||||
lk.addr = QByteArray((const char*)p + 6, qMin(addrLen, 16));
|
||||
memcpy(&lk.port, p + 22, 2);
|
||||
p += MEMBER_DETAIL_LINK_SIZE;
|
||||
m_snapLinks.append(lk);
|
||||
}
|
||||
|
||||
if (p >= data + len) { updateDetailDisplay(); return; }
|
||||
uint8_t sockCount = *p++;
|
||||
m_snapSocks.clear();
|
||||
for (int i = 0; i < sockCount; i++) {
|
||||
if (p + MEMBER_DETAIL_SOCK_SIZE > data + len) break;
|
||||
MemberSockSnap s;
|
||||
memcpy(&s.sockId, p, 4);
|
||||
memcpy(&s.port, p + 4, 2);
|
||||
memcpy(&s.linkCount, p + 6, 4);
|
||||
s.ifname = QString::fromUtf8((const char*)p + 10).trimmed();
|
||||
p += MEMBER_DETAIL_SOCK_SIZE;
|
||||
m_snapSocks.append(s);
|
||||
}
|
||||
|
||||
/* resolve interface names from snapshot sockets (by socket_id) */
|
||||
for (auto& a : m_detailAddrs) {
|
||||
for (const auto& s : m_snapSocks) {
|
||||
if (s.sockId == a.socket_id) { a.ifname = s.ifname; break; }
|
||||
}
|
||||
}
|
||||
std::sort(m_detailAddrs.begin(), m_detailAddrs.end(),
|
||||
[](const DetailAddress& a, const DetailAddress& b) {
|
||||
if (a.ifname != b.ifname) return a.ifname < b.ifname;
|
||||
if (a.addr != b.addr) return a.addr < b.addr;
|
||||
if (a.protocol != b.protocol) return a.protocol < b.protocol;
|
||||
return a.port < b.port;
|
||||
});
|
||||
|
||||
updateDetailDisplay();
|
||||
}
|
||||
|
||||
void AccountList::updateDetailDisplay() {
|
||||
@@ -246,83 +311,36 @@ void AccountList::updateDetailDisplay() {
|
||||
if (m_detailCreated > 0)
|
||||
lines.append(QString("📅 %1").arg(relativeTimeStr(m_detailCreated)));
|
||||
|
||||
struct ETCP_CONN* conn = nullptr;
|
||||
if (m_inst) conn = instance_find_conn(m_inst, m_detailNodeId);
|
||||
|
||||
bool isSelf = m_db ? (m_detailNodeId == m_db->myNodeId()) : false;
|
||||
QSet<struct ETCP_LINK*> matchedLinks;
|
||||
|
||||
/* Count active TCP connections for own node (shared across all TCP sockets) */
|
||||
int ownTcpActiveLinks = 0;
|
||||
if (isSelf && m_inst) {
|
||||
if (m_inst->connections) {
|
||||
for (struct ll_entry* e = m_inst->connections->head; e; e = e->next) {
|
||||
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
|
||||
if (ce && ce->conn && ce->conn->initialized) {
|
||||
for (struct ETCP_LINK* lk = ce->conn->links; lk; lk = lk->next) {
|
||||
if (lk->is_tcp) { ownTcpActiveLinks++; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_inst->tcp_connections) {
|
||||
for (struct ll_entry* e = m_inst->tcp_connections->head; e; e = e->next) {
|
||||
struct tcp_conn_entry* te = (struct tcp_conn_entry*)e->data;
|
||||
if (te && te->etcp_conn && te->etcp_conn->initialized) ownTcpActiveLinks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& a : m_detailAddrs) {
|
||||
QString proto;
|
||||
switch (a.protocol) { case 1: proto = "UDP"; break; case 2: proto = "TCP"; break; default: proto = "UDP/TCP"; break; }
|
||||
QString linkColor = "#9E9E9E";
|
||||
if (conn) {
|
||||
for (struct ETCP_LINK* lk = conn->links; lk; lk = lk->next) {
|
||||
const struct sockaddr_storage* sa = &lk->remote_addr;
|
||||
int lfm = 0; QString lip; uint16_t lpo = 0;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
if (sa->ss_family == AF_INET) {
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
|
||||
inet_ntop(AF_INET, &sin->sin_addr, ipbuf, sizeof(ipbuf));
|
||||
lfm = 4; lpo = ntohs(sin->sin_port); lip = QString::fromLocal8Bit(ipbuf);
|
||||
} else if (sa->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
|
||||
inet_ntop(AF_INET6, &sin6->sin6_addr, ipbuf, sizeof(ipbuf));
|
||||
lfm = 6; lpo = ntohs(sin6->sin6_port); lip = QString::fromLocal8Bit(ipbuf);
|
||||
}
|
||||
if (lfm == a.family && lpo == (uint16_t)a.port && lip == addrStr(a.addr.constData(), a.family)) {
|
||||
linkColor = (lk->link_state == 3 && lk->link_status == 1) ? "#4CAF50" : "#E53935";
|
||||
matchedLinks.insert(lk);
|
||||
if (m_snapConnPresent) {
|
||||
for (const auto& lk : m_snapLinks) {
|
||||
if (lk.family == a.family && lk.port == a.port
|
||||
&& lk.addr == a.addr) {
|
||||
linkColor = (lk.linkState == 3 && lk.linkStatus == 1) ? "#4CAF50" : "#E53935";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (linkColor == "#9E9E9E" && a.protocol == 2) {
|
||||
for (struct ETCP_LINK* tlk = conn->links; tlk; tlk = tlk->next) {
|
||||
if (tlk->is_tcp && tlk->tcp_link) {
|
||||
linkColor = stcp_link_is_ready(tlk->tcp_link) ? "#4CAF50" : "#E53935";
|
||||
break;
|
||||
}
|
||||
for (const auto& lk : m_snapLinks) {
|
||||
if (lk.isTcp && lk.tcpReady) { linkColor = "#4CAF50"; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString prefix;
|
||||
if (isSelf && m_inst) {
|
||||
if (isSelf) {
|
||||
int linkCount = 0;
|
||||
if (a.protocol == 1) {
|
||||
for (struct ETCP_SOCKET* sock = m_inst->etcp_sockets; sock; sock = sock->next) {
|
||||
uint16_t sp = 0;
|
||||
const struct sockaddr_storage* addr = sock->interface_addr.ss_family ? &sock->interface_addr : &sock->local_addr;
|
||||
if (addr->ss_family == AF_INET) sp = ntohs(((const struct sockaddr_in*)addr)->sin_port);
|
||||
else if (addr->ss_family == AF_INET6) sp = ntohs(((const struct sockaddr_in6*)addr)->sin6_port);
|
||||
if (sp == (uint16_t)a.port) {
|
||||
linkCount = sock->links_queue ? queue_entry_count(sock->links_queue) : 0;
|
||||
break;
|
||||
}
|
||||
for (const auto& s : m_snapSocks) {
|
||||
if (s.port == a.port) { linkCount = s.linkCount; break; }
|
||||
}
|
||||
} else if (a.protocol == 2) {
|
||||
linkCount = ownTcpActiveLinks;
|
||||
linkCount = m_snapOwnTcpActive;
|
||||
}
|
||||
if (linkCount > 0) prefix = QString("[%1] ").arg(linkCount);
|
||||
}
|
||||
@@ -338,25 +356,20 @@ void AccountList::updateDetailDisplay() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Show ETCP links that are not in DB (non-self) */
|
||||
if (conn) {
|
||||
for (struct ETCP_LINK* lk = conn->links; lk; lk = lk->next) {
|
||||
if (matchedLinks.contains(lk)) continue;
|
||||
const struct sockaddr_storage* sa = &lk->remote_addr;
|
||||
char ipbuf[INET6_ADDRSTRLEN]; QString lip; uint16_t lpo = 0;
|
||||
if (sa->ss_family == AF_INET) {
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
|
||||
inet_ntop(AF_INET, &sin->sin_addr, ipbuf, sizeof(ipbuf));
|
||||
lpo = ntohs(sin->sin_port); lip = QString::fromLocal8Bit(ipbuf);
|
||||
} else if (sa->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
|
||||
inet_ntop(AF_INET6, &sin6->sin6_addr, ipbuf, sizeof(ipbuf));
|
||||
lpo = ntohs(sin6->sin6_port); lip = QString::fromLocal8Bit(ipbuf);
|
||||
} else { continue; }
|
||||
QString proto = lk->is_tcp ? "TCP" : "UDP";
|
||||
QString lc = (lk->link_state == 3 && lk->link_status == 1) ? "#4CAF50" : "#E53935";
|
||||
/* Show snapshot links that are not in DB (non-self) */
|
||||
if (m_snapConnPresent && m_snapLinks.size() > 0) {
|
||||
for (int li = 0; li < m_snapLinks.size(); li++) {
|
||||
const auto& lk = m_snapLinks[li];
|
||||
bool matched = false;
|
||||
for (const auto& a : m_detailAddrs) {
|
||||
if (lk.family == a.family && lk.port == a.port
|
||||
&& lk.addr == a.addr) { matched = true; break; }
|
||||
}
|
||||
if (matched) continue;
|
||||
QString proto = lk.isTcp ? "TCP" : "UDP";
|
||||
QString lc = (lk.linkState == 3 && lk.linkStatus == 1) ? "#4CAF50" : "#E53935";
|
||||
lines.append(QString("<span style='color:%1;'>●</span> %2:%3:%4 [link]")
|
||||
.arg(lc, proto, lip).arg(lpo));
|
||||
.arg(lc, proto, addrStr(lk.addr.constData(), lk.family)).arg(lk.port));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +377,7 @@ void AccountList::updateDetailDisplay() {
|
||||
}
|
||||
|
||||
void AccountList::onDetailTimerTick() {
|
||||
if (m_detailNodeId != 0) updateDetailDisplay();
|
||||
if (m_detailNodeId != 0) requestMemberDetail();
|
||||
}
|
||||
|
||||
void AccountList::onMemberDoubleClicked(const QModelIndex& index) {
|
||||
@@ -372,7 +385,8 @@ void AccountList::onMemberDoubleClicked(const QModelIndex& index) {
|
||||
quint64 nodeId = m_model->nodeIdAt(index.row());
|
||||
if (!nodeId) return;
|
||||
QString name = m_model->data(index, Qt::DisplayRole).toString();
|
||||
MemberPropsDialog dlg(m_db, m_channelId, nodeId, name, this);
|
||||
uint8_t flags = (uint8_t)(m_model->data(index, Qt::UserRole + 3).toInt() & 0xFF);
|
||||
MemberPropsDialog dlg(m_db, m_channelId, nodeId, name, flags, this);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
m_model->refresh();
|
||||
emit memberRenamed();
|
||||
@@ -404,9 +418,22 @@ void AccountList::onMemberContextMenu(const QPoint& pos) {
|
||||
}
|
||||
if (chosen != editAction) return;
|
||||
|
||||
MemberPropsDialog dlg(m_db, m_channelId, nodeId, name, this);
|
||||
uint8_t flags = (uint8_t)(m_model->data(index, Qt::UserRole + 3).toInt() & 0xFF);
|
||||
MemberPropsDialog dlg(m_db, m_channelId, nodeId, name, flags, this);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
m_model->refresh();
|
||||
emit memberRenamed();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountList::onMemberList(const QString& chId, int count, const uint8_t* data, int dataLen) {
|
||||
m_model->onMemberList(chId, count, data, dataLen);
|
||||
}
|
||||
|
||||
void AccountList::onMemberUpdated(const QString& chId, const uint8_t* data) {
|
||||
m_model->onMemberUpdated(chId, data);
|
||||
}
|
||||
|
||||
void AccountList::onMemberRemoved(const QString& chId, uint64_t nodeId) {
|
||||
m_model->onMemberRemoved(chId, nodeId);
|
||||
}
|
||||
Reference in new issue
Block a user