AdaptixC2-Mod0/Headers/UI/Widgets/TunnelsWidget.h
2026-04-06 00:20:51 -05:00

298 lines
8.3 KiB
C++

#ifndef TUNNELSWIDGET_H
#define TUNNELSWIDGET_H
#include <main.h>
#include <UI/Widgets/AbstractDock.h>
#include <Utils/CustomElements.h>
#include <QSortFilterProxyModel>
class AdaptixWidget;
enum TunnelsColumns {
TUC_TunnelId,
TUC_AgentId,
TUC_Computer,
TUC_User,
TUC_Process,
TUC_Type,
TUC_Info,
TUC_Interface,
TUC_Port,
TUC_Client,
TUC_Fhost,
TUC_Fport,
TUC_ColumnCount
};
class TunnelsFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
QString textFilter;
bool matchesTerm(const QString &term, const QString &rowData) const {
if (term.isEmpty())
return true;
QRegularExpression re(QRegularExpression::escape(term.trimmed()), QRegularExpression::CaseInsensitiveOption);
return rowData.contains(re);
}
bool evaluateExpression(const QString &expr, const QString &rowData) const {
QString e = expr.trimmed();
if (e.isEmpty())
return true;
int depth = 0;
int lastOr = -1;
for (int i = e.length() - 1; i >= 0; --i) {
QChar c = e[i];
if (c == ')') depth++;
else if (c == '(') depth--;
else if (depth == 0 && c == '|') {
lastOr = i;
break;
}
}
if (lastOr != -1) {
QString left = e.left(lastOr).trimmed();
QString right = e.mid(lastOr + 1).trimmed();
return evaluateExpression(left, rowData) || evaluateExpression(right, rowData);
}
depth = 0;
int lastAnd = -1;
for (int i = e.length() - 1; i >= 0; --i) {
QChar c = e[i];
if (c == ')') depth++;
else if (c == '(') depth--;
else if (depth == 0 && c == '&') {
lastAnd = i;
break;
}
}
if (lastAnd != -1) {
QString left = e.left(lastAnd).trimmed();
QString right = e.mid(lastAnd + 1).trimmed();
return evaluateExpression(left, rowData) && evaluateExpression(right, rowData);
}
if (e.startsWith("^(") && e.endsWith(')')) {
return !evaluateExpression(e.mid(2, e.length() - 3), rowData);
}
if (e.startsWith('(') && e.endsWith(')')) {
return evaluateExpression(e.mid(1, e.length() - 2), rowData);
}
return matchesTerm(e, rowData);
}
public:
explicit TunnelsFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {
setDynamicSortFilter(true);
setSortRole(Qt::UserRole);
}
void setTextFilter(const QString &text) {
if (textFilter == text) return;
textFilter = text;
invalidateFilter();
}
protected:
bool filterAcceptsRow(int row, const QModelIndex &parent) const override {
auto model = sourceModel();
if (!model)
return true;
if (!textFilter.isEmpty()) {
QString rowData;
for (int col = 0; col < model->columnCount(); ++col) {
rowData += model->index(row, col, parent).data().toString() + " ";
}
if (!evaluateExpression(textFilter, rowData))
return false;
}
return true;
}
};
class TunnelsTableModel : public QAbstractTableModel
{
Q_OBJECT
QVector<TunnelData> tunnels;
QHash<QString, int> idToRow;
void rebuildIndex() {
idToRow.clear();
for (int i = 0; i < tunnels.size(); ++i)
idToRow[tunnels[i].TunnelId] = i;
}
public:
explicit TunnelsTableModel(QObject* parent = nullptr) : QAbstractTableModel(parent) {}
int rowCount(const QModelIndex&) const override { return tunnels.size(); }
int columnCount(const QModelIndex&) const override { return TUC_ColumnCount; }
QVariant data(const QModelIndex& index, int role) const override {
if (!index.isValid() || index.row() >= tunnels.size())
return {};
const TunnelData& t = tunnels.at(index.row());
if (role == Qt::DisplayRole) {
switch (index.column()) {
case TUC_TunnelId: return t.TunnelId;
case TUC_AgentId: return t.AgentId;
case TUC_Computer: return t.Computer;
case TUC_User: return t.Username;
case TUC_Process: return t.Process;
case TUC_Type: return t.Type;
case TUC_Info: return t.Info;
case TUC_Interface: return t.Interface;
case TUC_Port: return t.Port;
case TUC_Client: return t.Client;
case TUC_Fhost: return t.Fhost;
case TUC_Fport: return t.Fport;
default: ;
}
}
if (role == Qt::UserRole) {
return data(index, Qt::DisplayRole);
}
if (role == Qt::TextAlignmentRole) {
switch (index.column()) {
case TUC_TunnelId:
case TUC_AgentId:
case TUC_Computer:
case TUC_User:
case TUC_Process:
case TUC_Interface:
case TUC_Port:
case TUC_Client:
case TUC_Fhost:
case TUC_Fport:
return Qt::AlignCenter;
case TUC_Type:
case TUC_Info:
return static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter);
default: ;
}
}
return {};
}
QVariant headerData(int section, Qt::Orientation o, int role) const override {
if (role != Qt::DisplayRole || o != Qt::Horizontal)
return {};
static QStringList headers = {
"Tunnel ID", "Agent ID", "Computer", "User", "Process", "Type",
"Info", "Interface", "Listen port", "Client", "Target host", "Target port"
};
return headers.value(section);
}
void add(const TunnelData& item) {
const int row = tunnels.size();
beginInsertRows(QModelIndex(), row, row);
tunnels.append(item);
idToRow[item.TunnelId] = row;
endInsertRows();
}
void updateInfo(const QString& tunnelId, const QString& info) {
auto it = idToRow.find(tunnelId);
if (it == idToRow.end())
return;
int row = it.value();
tunnels[row].Info = info;
Q_EMIT dataChanged(index(row, TUC_Info), index(row, TUC_Info));
}
void remove(const QString& tunnelId) {
auto it = idToRow.find(tunnelId);
if (it == idToRow.end())
return;
int row = it.value();
beginRemoveRows(QModelIndex(), row, row);
idToRow.remove(tunnels[row].TunnelId);
tunnels.removeAt(row);
endRemoveRows();
rebuildIndex();
}
void clear() {
beginResetModel();
tunnels.clear();
idToRow.clear();
endResetModel();
}
bool contains(const QString& tunnelId) const {
return idToRow.contains(tunnelId);
}
TunnelData getById(const QString& tunnelId) const {
auto it = idToRow.find(tunnelId);
if (it != idToRow.end())
return tunnels.at(it.value());
return {};
}
};
class TunnelsWidget : public DockTab
{
Q_OBJECT
AdaptixWidget* adaptixWidget = nullptr;
QGridLayout* mainGridLayout = nullptr;
QTableView* tableView = nullptr;
QShortcut* shortcutSearch = nullptr;
TunnelsTableModel* tunnelsModel = nullptr;
TunnelsFilterProxyModel* proxyModel = nullptr;
QWidget* searchWidget = nullptr;
QHBoxLayout* searchLayout = nullptr;
QLineEdit* inputFilter = nullptr;
QCheckBox* autoSearchCheck = nullptr;
ClickableLabel* hideButton = nullptr;
void createUI();
public:
explicit TunnelsWidget(AdaptixWidget* w);
~TunnelsWidget() override;
void SetUpdatesEnabled(const bool enabled);
void Clear() const;
void AddTunnelItem(const TunnelData &newTunnel) const;
void EditTunnelItem(const QString &tunnelId, const QString &info) const;
void RemoveTunnelItem(const QString &tunnelId) const;
public Q_SLOTS:
void toggleSearchPanel() const;
void onFilterUpdate() const;
void handleTunnelsMenu(const QPoint &pos) const;
void actionSetInfo() const;
void actionStopTunnel() const;
};
#endif