From 93399b848a0539873da45f2d8b10bd00a3d0c8a0 Mon Sep 17 00:00:00 2001 From: Ralf Date: Sun, 17 Nov 2024 10:24:04 +0300 Subject: [PATCH] Client: DownloadsWidget --- AdaptixServer/core/server/ts_downloads.go | 5 +- AdaptixServer/core/server/ts_syncpacket.go | 13 +- Client/CMakeLists.txt | 2 + Client/Headers/UI/Widgets/AdaptixWidget.h | 4 + Client/Headers/UI/Widgets/DownloadsWidget.h | 25 +++ Client/Headers/UI/Widgets/ListenersWidget.h | 6 +- Client/Headers/Utils/Convert.h | 2 + Client/Headers/main.h | 23 +++ Client/Source/Client/ProcessSyncPacket.cpp | 64 +++++++ Client/Source/UI/Widgets/AdaptixWidget.cpp | 11 +- Client/Source/UI/Widgets/DownloadWidget.cpp | 191 ++++++++++++++++++++ Client/Source/Utils/Convert.cpp | 18 +- 12 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 Client/Headers/UI/Widgets/DownloadsWidget.h create mode 100644 Client/Source/UI/Widgets/DownloadWidget.cpp diff --git a/AdaptixServer/core/server/ts_downloads.go b/AdaptixServer/core/server/ts_downloads.go index 9bdf1644..09692fe7 100644 --- a/AdaptixServer/core/server/ts_downloads.go +++ b/AdaptixServer/core/server/ts_downloads.go @@ -80,7 +80,7 @@ func (ts *Teamserver) TsDownloadUpdate(fileId string, state int, data []byte) er downloadData.State = state - if len(data) == 0 { + if len(data) > 0 { _, err := downloadData.File.Write(data) if err != nil { downloadData.File, err = os.Create(downloadData.LocalPath) @@ -126,7 +126,8 @@ func (ts *Teamserver) TsDownloadFinish(fileId string, state int) error { if downloadData.State == DOWNLOAD_STATE_CANCELED { os.Remove(downloadData.LocalPath) ts.downloads.Delete(fileId) - } else if downloadData.State == DOWNLOAD_STATE_FINISHED { + } else { + downloadData.State = DOWNLOAD_STATE_FINISHED ts.downloads.Put(downloadData.FileId, downloadData) } diff --git a/AdaptixServer/core/server/ts_syncpacket.go b/AdaptixServer/core/server/ts_syncpacket.go index 8d2f9fc8..dca6b4d1 100644 --- a/AdaptixServer/core/server/ts_syncpacket.go +++ b/AdaptixServer/core/server/ts_syncpacket.go @@ -219,12 +219,13 @@ func CreateSpDownloadCreate(downloadData DownloadData) SyncPackerDownloadCreate store: STORE_LOG, SpType: TYPE_DOWNLOAD_CREATE, - AgentId: downloadData.AgentId, - FileId: downloadData.FileId, - Computer: downloadData.Computer, - File: downloadData.RemotePath, - Size: downloadData.TotalSize, - Date: downloadData.Date, + AgentId: downloadData.AgentId, + AgentName: downloadData.AgentName, + FileId: downloadData.FileId, + Computer: downloadData.Computer, + File: downloadData.RemotePath, + Size: downloadData.TotalSize, + Date: downloadData.Date, } } diff --git a/Client/CMakeLists.txt b/Client/CMakeLists.txt index 7f3208ba..e36d954f 100644 --- a/Client/CMakeLists.txt +++ b/Client/CMakeLists.txt @@ -46,6 +46,7 @@ SET( HEADERS Headers/Agent/TableWidgetItemAgent.h Headers/Client/LastTickWorker.h Headers/Agent/Commander.h + Headers/UI/Widgets/DownloadsWidget.h ) add_executable(AdaptixClient @@ -79,6 +80,7 @@ add_executable(AdaptixClient Source/Agent/TableWidgetItemAgent.cpp Source/Client/LastTickWorker.cpp Source/Agent/Commander.cpp + Source/UI/Widgets/DownloadWidget.cpp ) target_link_libraries(AdaptixClient diff --git a/Client/Headers/UI/Widgets/AdaptixWidget.h b/Client/Headers/UI/Widgets/AdaptixWidget.h index d39cb3ed..3872e7c6 100644 --- a/Client/Headers/UI/Widgets/AdaptixWidget.h +++ b/Client/Headers/UI/Widgets/AdaptixWidget.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -55,12 +56,14 @@ public: LogsWidget* LogsTab = nullptr; ListenersWidget* ListenersTab = nullptr; SessionsTableWidget* SessionsTablePage = nullptr; + DownloadsWidget* DownloadsTab = nullptr; QMap RegisterAgentsCmd; QMap RegisterAgentsUI; QMap RegisterListenersUI; QMap LinkListenerAgent; QVector Listeners; + QMap Downloads; QMap Agents; explicit AdaptixWidget(AuthProfile authProfile); @@ -76,6 +79,7 @@ public slots: void LoadLogsUI(); void LoadListenersUI(); + void LoadDownloadsUI(); void LoadConsoleUI(QString AgentId); }; diff --git a/Client/Headers/UI/Widgets/DownloadsWidget.h b/Client/Headers/UI/Widgets/DownloadsWidget.h new file mode 100644 index 00000000..9c955eba --- /dev/null +++ b/Client/Headers/UI/Widgets/DownloadsWidget.h @@ -0,0 +1,25 @@ +#ifndef ADAPTIXCLIENT_DOWNLOADSWIDGET_H +#define ADAPTIXCLIENT_DOWNLOADSWIDGET_H + +#include + +class DownloadsWidget : public QWidget +{ + QWidget* mainWidget = nullptr; + QTableWidget* tableWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; + + void createUI(); + +public: + DownloadsWidget(QWidget* w); + ~DownloadsWidget(); + + void AddDownloadItem(DownloadData newDownload); + void EditDownloadItem(QString fileId, int recvSize, int state); + +public slots: + void handleDownloadsMenu(const QPoint &pos ); +}; + +#endif //ADAPTIXCLIENT_DOWNLOADSWIDGET_H \ No newline at end of file diff --git a/Client/Headers/UI/Widgets/ListenersWidget.h b/Client/Headers/UI/Widgets/ListenersWidget.h index 88239c3e..fcc6e4f6 100644 --- a/Client/Headers/UI/Widgets/ListenersWidget.h +++ b/Client/Headers/UI/Widgets/ListenersWidget.h @@ -7,9 +7,9 @@ class ListenersWidget : public QWidget { - QWidget* mainWidget = nullptr; - QGridLayout* mainGridLayout = nullptr; - QTableWidget* tableWidget = nullptr; + QWidget* mainWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; + QTableWidget* tableWidget = nullptr; void createUI(); diff --git a/Client/Headers/Utils/Convert.h b/Client/Headers/Utils/Convert.h index ed1b4507..5d8ea479 100644 --- a/Client/Headers/Utils/Convert.h +++ b/Client/Headers/Utils/Convert.h @@ -19,4 +19,6 @@ QString FormatSecToStr(int seconds); QString TrimmedEnds(QString str); +QString BytesToFormat(int bytes); + #endif //ADAPTIXCLIENT_CONVERT_H \ No newline at end of file diff --git a/Client/Headers/main.h b/Client/Headers/main.h index 2214c878..dc473a1b 100644 --- a/Client/Headers/main.h +++ b/Client/Headers/main.h @@ -67,8 +67,18 @@ #define TYPE_AGENT_TASK_UPDATE 0x45 #define TYPE_AGENT_CONSOLE_OUT 0x46 +#define TYPE_DOWNLOAD_CREATE 0x51 +#define TYPE_DOWNLOAD_UPDATE 0x52 + ////////// +#define DOWNLOAD_STATE_RUNNING 0x1 +#define DOWNLOAD_STATE_STOPPED 0x2 +#define DOWNLOAD_STATE_FINISHED 0x3 +#define DOWNLOAD_STATE_CANCELED 0x4 + +///////// + #define COLOR_NeonGreen "#39FF14" // green #define COLOR_Berry "#A01641" // red #define COLOR_ChiliPepper "#E32227" // red @@ -119,4 +129,17 @@ typedef struct AgentData int LastTick; } AgentData; +typedef struct DownloadData +{ + QString FileId; + QString AgentId; + QString AgentName; + QString Computer; + QString Filename; + int TotalSize; + int RecvSize; + int State; + QString Date; +} DownloadData; + #endif //ADAPTIXCLIENT_MAIN_H diff --git a/Client/Source/Client/ProcessSyncPacket.cpp b/Client/Source/Client/ProcessSyncPacket.cpp index 3172c260..be978a3a 100644 --- a/Client/Source/Client/ProcessSyncPacket.cpp +++ b/Client/Source/Client/ProcessSyncPacket.cpp @@ -251,6 +251,43 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) return true; } + if(spType == TYPE_DOWNLOAD_CREATE ) { + if (!jsonObj.contains("d_agent_id") || !jsonObj["d_agent_id"].isString()) { + return false; + } + if (!jsonObj.contains("d_file_id") || !jsonObj["d_file_id"].isString()) { + return false; + } + if (!jsonObj.contains("d_agent_name") || !jsonObj["d_agent_name"].isString()) { + return false; + } + if (!jsonObj.contains("d_computer") || !jsonObj["d_computer"].isString()) { + return false; + } + if (!jsonObj.contains("d_file") || !jsonObj["d_file"].isString()) { + return false; + } + if (!jsonObj.contains("d_size") || !jsonObj["d_size"].isDouble()) { + return false; + } + if (!jsonObj.contains("d_date") || !jsonObj["d_date"].isDouble()) { + return false; + } + return true; + } + if(spType == TYPE_DOWNLOAD_UPDATE ) { + if (!jsonObj.contains("d_file_id") || !jsonObj["d_file_id"].isString()) { + return false; + } + if (!jsonObj.contains("d_recv_size") || !jsonObj["d_recv_size"].isDouble()) { + return false; + } + if (!jsonObj.contains("d_state") || !jsonObj["d_state"].isDouble()) { + return false; + } + return true; + } + return false; } @@ -450,4 +487,31 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } + + if(spType == TYPE_DOWNLOAD_CREATE ) + { + DownloadData newDownload = {0}; + newDownload.AgentId = jsonObj["d_agent_id"].toString(); + newDownload.FileId = jsonObj["d_file_id"].toString(); + newDownload.AgentName = jsonObj["d_agent_name"].toString(); + newDownload.Computer = jsonObj["d_computer"].toString(); + newDownload.Filename = jsonObj["d_file"].toString(); + newDownload.TotalSize = jsonObj["d_size"].toDouble(); + newDownload.Date = UnixTimestampGlobalToStringLocal(static_cast(jsonObj["d_date"].toDouble())); + newDownload.RecvSize = 0; + newDownload.State = DOWNLOAD_STATE_RUNNING; + + DownloadsTab->AddDownloadItem(newDownload); + + return; + } + if(spType == TYPE_DOWNLOAD_UPDATE ) { + QString fileId = jsonObj["d_file_id"].toString(); + int recvSize = jsonObj["d_recv_size"].toDouble(); + int state = jsonObj["d_state"].toDouble(); + + DownloadsTab->EditDownloadItem(fileId, recvSize, state); + + return; + } } diff --git a/Client/Source/UI/Widgets/AdaptixWidget.cpp b/Client/Source/UI/Widgets/AdaptixWidget.cpp index d591cb14..41e1f4bc 100644 --- a/Client/Source/UI/Widgets/AdaptixWidget.cpp +++ b/Client/Source/UI/Widgets/AdaptixWidget.cpp @@ -7,6 +7,7 @@ AdaptixWidget::AdaptixWidget(AuthProfile authProfile) LogsTab = new LogsWidget(); ListenersTab = new ListenersWidget(this); SessionsTablePage = new SessionsTableWidget(this); + DownloadsTab = new DownloadsWidget(this); mainStackedWidget->addWidget(SessionsTablePage); mainStackedWidget->setCurrentIndex(0); @@ -26,6 +27,7 @@ AdaptixWidget::AdaptixWidget(AuthProfile authProfile) connect( mainTabWidget->tabBar(), &QTabBar::tabCloseRequested, this, &AdaptixWidget::RemoveTab ); connect( logsButton, &QPushButton::clicked, this, &AdaptixWidget::LoadLogsUI); connect( listenersButton, &QPushButton::clicked, this, &AdaptixWidget::LoadListenersUI); + connect( downloadsButton, &QPushButton::clicked, this, &AdaptixWidget::LoadDownloadsUI); connect( TickThread, &QThread::started, TickWorker, &LastTickWorker::run ); @@ -42,8 +44,6 @@ AdaptixWidget::AdaptixWidget(AuthProfile authProfile) line_2->setVisible(false); jobsButton->setVisible(false); proxyButton->setVisible(false); - line_3->setVisible(false); - downloadsButton->setVisible(false); credsButton->setVisible(false); screensButton->setVisible(false); keysButton->setVisible(false); @@ -242,6 +242,12 @@ void AdaptixWidget::LoadListenersUI() this->AddTab(ListenersTab, "Listeners", ":/icons/listeners"); } +void AdaptixWidget::LoadDownloadsUI() +{ + this->AddTab(DownloadsTab, "Downloads", ":/icons/downloads"); +} + + void AdaptixWidget::LoadConsoleUI(QString AgentId) { if( !Agents.contains(AgentId) ) @@ -251,7 +257,6 @@ void AdaptixWidget::LoadConsoleUI(QString AgentId) this->AddTab(Agents[AgentId]->Console, text); } - void AdaptixWidget::ChannelClose() { LogInfo("WebSocker closed"); diff --git a/Client/Source/UI/Widgets/DownloadWidget.cpp b/Client/Source/UI/Widgets/DownloadWidget.cpp new file mode 100644 index 00000000..9df8fd54 --- /dev/null +++ b/Client/Source/UI/Widgets/DownloadWidget.cpp @@ -0,0 +1,191 @@ +#include +#include + +DownloadsWidget::DownloadsWidget(QWidget* w) +{ + this->mainWidget = w; + this->createUI(); + + connect( tableWidget, &QTableWidget::customContextMenuRequested, this, &DownloadsWidget::handleDownloadsMenu ); +} + +void DownloadsWidget::createUI() +{ + tableWidget = new QTableWidget(this ); + tableWidget->setColumnCount(9 ); + tableWidget->setContextMenuPolicy( Qt::CustomContextMenu ); + tableWidget->setAutoFillBackground( false ); + tableWidget->setShowGrid( false ); + tableWidget->setSortingEnabled( true ); + tableWidget->setWordWrap( true ); + tableWidget->setCornerButtonEnabled( true ); // true + tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); + tableWidget->setFocusPolicy( Qt::NoFocus ); + tableWidget->setAlternatingRowColors( true ); + tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); + tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); + tableWidget->horizontalHeader()->setHighlightSections( false ); + tableWidget->verticalHeader()->setVisible( false ); + + tableWidget->setHorizontalHeaderItem( 0, new QTableWidgetItem( "File ID" ) ); + tableWidget->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Agent Type" ) ); + tableWidget->setHorizontalHeaderItem( 2, new QTableWidgetItem( "Agent ID" ) ); + tableWidget->setHorizontalHeaderItem( 3, new QTableWidgetItem( "Computer" ) ); + tableWidget->setHorizontalHeaderItem( 4, new QTableWidgetItem( "File" ) ); + tableWidget->setHorizontalHeaderItem( 5, new QTableWidgetItem( "Date" ) ); + tableWidget->setHorizontalHeaderItem( 6, new QTableWidgetItem( "Size" ) ); + tableWidget->setHorizontalHeaderItem( 7, new QTableWidgetItem( "Received" ) ); + tableWidget->setHorizontalHeaderItem( 8, new QTableWidgetItem( "Progress" ) ); + tableWidget->hideColumn( 0 ); + + mainGridLayout = new QGridLayout(this ); + mainGridLayout->setContentsMargins(0, 0, 0, 0 ); + mainGridLayout->addWidget(tableWidget, 0, 0, 1, 4 ); +} + +DownloadsWidget::~DownloadsWidget() = default; + +void DownloadsWidget::AddDownloadItem(DownloadData newDownload ) +{ + auto adaptixWidget = qobject_cast( mainWidget ); + if(adaptixWidget->Downloads.contains(newDownload.FileId)) + return; + + auto item_FileID = new QTableWidgetItem( newDownload.FileId ); + auto item_AgentName = new QTableWidgetItem( newDownload.AgentName ); + auto item_AgentID = new QTableWidgetItem( newDownload.AgentId ); + auto item_Computer = new QTableWidgetItem( newDownload.Computer ); + auto item_File = new QTableWidgetItem( newDownload.Filename ); + auto item_Date = new QTableWidgetItem( newDownload.Date ); + auto item_Size = new QTableWidgetItem( BytesToFormat(newDownload.TotalSize) ); + auto item_Received = new QTableWidgetItem( BytesToFormat(newDownload.RecvSize) ); + + item_FileID->setTextAlignment( Qt::AlignCenter ); + item_FileID->setFlags( item_FileID->flags() ^ Qt::ItemIsEditable ); + + item_AgentName->setTextAlignment( Qt::AlignCenter ); + item_AgentName->setFlags( item_AgentName->flags() ^ Qt::ItemIsEditable ); + + item_AgentID->setTextAlignment( Qt::AlignCenter ); + item_AgentID->setFlags( item_AgentID->flags() ^ Qt::ItemIsEditable ); + + item_Computer->setTextAlignment( Qt::AlignCenter ); + item_Computer->setFlags( item_Computer->flags() ^ Qt::ItemIsEditable ); + + item_File->setTextAlignment( Qt::AlignCenter ); + item_File->setFlags( item_File->flags() ^ Qt::ItemIsEditable ); + item_File->setToolTip(item_File->text()); + + item_Date->setTextAlignment( Qt::AlignCenter ); + item_Date->setFlags( item_Date->flags() ^ Qt::ItemIsEditable ); + + item_Size->setTextAlignment( Qt::AlignCenter ); + item_Size->setFlags( item_Size->flags() ^ Qt::ItemIsEditable ); + + item_Received->setTextAlignment( Qt::AlignCenter ); + item_Received->setFlags( item_Received->flags() ^ Qt::ItemIsEditable ); + + QProgressBar *pgbar = new QProgressBar(); + pgbar->setMinimum(0); + pgbar->setMaximum(newDownload.TotalSize); + pgbar->setValue(newDownload.RecvSize); + pgbar->setMinimumSize(QSize(200,24)); + pgbar->setMaximumSize(QSize(200,24)); + + if( tableWidget->rowCount() < 1 ) + tableWidget->setRowCount( 1 ); + else + tableWidget->setRowCount( tableWidget->rowCount() + 1 ); + + bool isSortingEnabled = tableWidget->isSortingEnabled(); + tableWidget->setSortingEnabled( false ); + tableWidget->setItem( tableWidget->rowCount() - 1, 0, item_FileID ); + tableWidget->setItem( tableWidget->rowCount() - 1, 1, item_AgentName ); + tableWidget->setItem( tableWidget->rowCount() - 1, 2, item_AgentID ); + tableWidget->setItem( tableWidget->rowCount() - 1, 3, item_Computer ); + tableWidget->setItem( tableWidget->rowCount() - 1, 4, item_File ); + tableWidget->setItem( tableWidget->rowCount() - 1, 5, item_Date ); + tableWidget->setItem( tableWidget->rowCount() - 1, 6, item_Size ); + tableWidget->setItem( tableWidget->rowCount() - 1, 7, item_Received ); + tableWidget->setCellWidget( tableWidget->rowCount() - 1, 8, pgbar ); + tableWidget->setSortingEnabled( isSortingEnabled ); + + if( newDownload.State == DOWNLOAD_STATE_STOPPED ) + pgbar->setEnabled(false); + + tableWidget->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 6, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 7, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 8, QHeaderView::ResizeToContents ); + + adaptixWidget->Downloads[newDownload.FileId] = newDownload; +} + +void DownloadsWidget::EditDownloadItem(QString fileId, int recvSize, int state) +{ + auto adaptixWidget = qobject_cast( mainWidget ); + adaptixWidget->Downloads[fileId].RecvSize = recvSize; + adaptixWidget->Downloads[fileId].State = state; + + for (int row = 0; row < tableWidget->rowCount(); ++row) { + QTableWidgetItem *item = tableWidget->item(row, 0); + if ( item && item->text() == fileId ) { + tableWidget->item(row, 7)->setText(BytesToFormat(recvSize) ); + + QProgressBar *pgbar = (QProgressBar*) tableWidget->cellWidget(row,8); + pgbar->setValue(recvSize); + + if( state == DOWNLOAD_STATE_STOPPED) { + pgbar->setEnabled(false); + } + else if(state == DOWNLOAD_STATE_RUNNING) { + pgbar->setEnabled(true); + } + else if (state == DOWNLOAD_STATE_FINISHED) { + adaptixWidget->Downloads[fileId].RecvSize = adaptixWidget->Downloads[fileId].TotalSize; + + tableWidget->item( row, 7 )->setText(""); + + auto item_Status = new QTableWidgetItem( "Finished" ); + item_Status->setTextAlignment( Qt::AlignCenter ); + tableWidget->removeCellWidget(row,8); + tableWidget->setItem( row, 8, item_Status ); + tableWidget->item( row, 8 )->setForeground(QColor(COLOR_NeonGreen)); + } + else if (state == DOWNLOAD_STATE_CANCELED) { + tableWidget->removeCellWidget(row,8); + tableWidget->removeRow( row ); + adaptixWidget->Downloads.remove(fileId); + } + + break; + } + } +} + +void DownloadsWidget::handleDownloadsMenu(const QPoint &pos ) +{ + if ( ! tableWidget->itemAt( pos ) ) + return; + + auto FileID = tableWidget->item( tableWidget->currentRow(), 0 )->text(); + auto Received = tableWidget->item( tableWidget->currentRow(), 7 )->text(); + + auto MenuFile = QMenu(); + if(Received.compare("") == 0) { + MenuFile.addAction( "Sync file to client" ); + MenuFile.addAction( "Delete file" ); + } + else { + if( tableWidget->cellWidget( tableWidget->currentRow(), 8)->isEnabled() ) + MenuFile.addAction( "Stop" ); + else + MenuFile.addAction( "Start" ); + MenuFile.addAction( "Cancel" ); + } + + auto *action = MenuFile.exec( tableWidget->horizontalHeader()->viewport()->mapToGlobal( pos ) ); +} + + diff --git a/Client/Source/Utils/Convert.cpp b/Client/Source/Utils/Convert.cpp index e3c39da5..d32a6a7a 100644 --- a/Client/Source/Utils/Convert.cpp +++ b/Client/Source/Utils/Convert.cpp @@ -68,4 +68,20 @@ QString FormatSecToStr(int seconds) QString TrimmedEnds(QString str) { return str.remove(QRegularExpression("\\s+$")); -} \ No newline at end of file +} + +QString BytesToFormat(int bytes) +{ + const double KB = 1024.0; + const double MB = KB * 1024; + const double GB = MB * 1024; + + if (bytes >= GB) { + return QString::number(bytes / GB, 'f', 2) + " Gb"; + } else if (bytes >= MB) { + return QString::number(bytes / MB, 'f', 2) + " Mb"; + } else { + return QString::number(bytes / KB, 'f', 2) + " Kb"; + } +} +