Refactor: Move FileManager to common, add macOS file management support

This commit is contained in:
yuanyuanxiang
2026-05-03 09:57:46 +02:00
parent a3611d9fc1
commit 36423b1c7c
11 changed files with 206 additions and 35 deletions

View File

@@ -494,27 +494,31 @@ make
**系统要求** **系统要求**
- macOS 10.15 (Catalina) 及以上 - macOS 10.15 (Catalina) 及以上
- 架构支持Intel (x64) 和 Apple Silicon (arm64) 通用二进制
- 需要授予系统权限:屏幕录制、辅助功能、完全磁盘访问 - 需要授予系统权限:屏幕录制、辅助功能、完全磁盘访问
**功能支持** **功能支持**
| 功能 | 状态 | 实现 | | 功能 | 状态 | 实现 |
|------|------|------| |------|------|------|
| 远程桌面 | ✅ | CoreGraphics 屏幕捕获H.264 硬件编码 | | 远程桌面 | ✅ | CoreGraphics 屏幕捕获,VideoToolbox H.264 硬件编码 |
| 鼠标控制 | ✅ | CGEvent 模拟,支持双击、拖拽 | | 鼠标控制 | ✅ | CGEvent 模拟,支持双击、拖拽 |
| 键盘控制 | ✅ | CGEvent 模拟,完整键码映射 | | 键盘控制 | ✅ | CGEvent 模拟,完整键码映射 |
| 光标同步 | ✅ | 实时同步远程光标样式 | | 光标同步 | ✅ | 实时同步远程光标样式 |
| 远程终端 | ✅ | PTY 交互式 Shellzsh/bash |
| 文件管理 | ✅ | 双向传输、V2 协议、大文件支持 |
| 心跳/RTT | ✅ | RFC 6298 RTT 估算 | | 心跳/RTT | ✅ | RFC 6298 RTT 估算 |
| 文件管理 | | 开发中 | | 分组管理 | | 持久化配置文件 |
| 远程终端 | ⏳ | 开发中 | | 进程管理 | ⏳ | 开发中 |
| 剪贴板 | ⏳ | 开发中 |
**编译方式** **编译方式**
```bash ```bash
cd macos cd macos
mkdir build && cd build ./build.sh
cmake .. # 或手动编译:
make # mkdir build && cd build && cmake .. && make
``` ```
--- ---

View File

@@ -479,27 +479,31 @@ make
**System Requirements**: **System Requirements**:
- macOS 10.15 (Catalina) or later - macOS 10.15 (Catalina) or later
- Architecture: Universal Binary (Intel x64 + Apple Silicon arm64)
- Required permissions: Screen Recording, Accessibility, Full Disk Access - Required permissions: Screen Recording, Accessibility, Full Disk Access
**Feature Support**: **Feature Support**:
| Feature | Status | Implementation | | Feature | Status | Implementation |
|---------|--------|----------------| |---------|--------|----------------|
| Remote Desktop | ✅ | CoreGraphics screen capture, H.264 hardware encoding | | Remote Desktop | ✅ | CoreGraphics screen capture, VideoToolbox H.264 hardware encoding |
| Mouse Control | ✅ | CGEvent simulation, supports double-click, drag | | Mouse Control | ✅ | CGEvent simulation, supports double-click, drag |
| Keyboard Control | ✅ | CGEvent simulation, full keycode mapping | | Keyboard Control | ✅ | CGEvent simulation, full keycode mapping |
| Cursor Sync | ✅ | Real-time remote cursor style synchronization | | Cursor Sync | ✅ | Real-time remote cursor style synchronization |
| Remote Terminal | ✅ | PTY interactive shell (zsh/bash) |
| File Management | ✅ | Bidirectional transfer, V2 protocol, large file support |
| Heartbeat/RTT | ✅ | RFC 6298 RTT estimation | | Heartbeat/RTT | ✅ | RFC 6298 RTT estimation |
| File Management | | In development | | Group Management | | Persistent configuration file |
| Remote Terminal | ⏳ | In development | | Process Management | ⏳ | In development |
| Clipboard | ⏳ | In development |
**Build Instructions**: **Build Instructions**:
```bash ```bash
cd macos cd macos
mkdir build && cd build ./build.sh
cmake .. # Or manually:
make # mkdir build && cd build && cmake .. && make
``` ```
--- ---

View File

@@ -478,27 +478,31 @@ make
**系統要求** **系統要求**
- macOS 10.15 (Catalina) 及以上 - macOS 10.15 (Catalina) 及以上
- 架構支援Intel (x64) 和 Apple Silicon (arm64) 通用二進位
- 需要授予系統權限:螢幕錄製、輔助使用、完全磁碟存取 - 需要授予系統權限:螢幕錄製、輔助使用、完全磁碟存取
**功能支援** **功能支援**
| 功能 | 狀態 | 實作 | | 功能 | 狀態 | 實作 |
|------|------|------| |------|------|------|
| 遠端桌面 | ✅ | CoreGraphics 螢幕擷取H.264 硬體編碼 | | 遠端桌面 | ✅ | CoreGraphics 螢幕擷取,VideoToolbox H.264 硬體編碼 |
| 滑鼠控制 | ✅ | CGEvent 模擬,支援雙擊、拖曳 | | 滑鼠控制 | ✅ | CGEvent 模擬,支援雙擊、拖曳 |
| 鍵盤控制 | ✅ | CGEvent 模擬,完整鍵碼對應 | | 鍵盤控制 | ✅ | CGEvent 模擬,完整鍵碼對應 |
| 游標同步 | ✅ | 即時同步遠端游標樣式 | | 游標同步 | ✅ | 即時同步遠端游標樣式 |
| 遠端終端 | ✅ | PTY 互動式 Shellzsh/bash |
| 檔案管理 | ✅ | 雙向傳輸、V2 協定、大檔案支援 |
| 心跳/RTT | ✅ | RFC 6298 RTT 估算 | | 心跳/RTT | ✅ | RFC 6298 RTT 估算 |
| 檔案管理 | | 開發中 | | 分組管理 | | 持久化設定檔 |
| 遠端終端 | ⏳ | 開發中 | | 程序管理 | ⏳ | 開發中 |
| 剪貼簿 | ⏳ | 開發中 |
**編譯方式** **編譯方式**
```bash ```bash
cd macos cd macos
mkdir build && cd build ./build.sh
cmake .. # 或手動編譯:
make # mkdir build && cd build && cmake .. && make
``` ```
--- ---

View File

@@ -1,8 +1,24 @@
/**
* FileManager.h - Unix File Manager
*
* Implements file transfer between Windows server and Unix client.
* Supports: browse, upload, download, delete, rename, create folder
*
* PLATFORM SUPPORT:
* - Linux: Supported
* - macOS: Supported
* - Windows: NOT SUPPORTED (Windows uses different file APIs)
*/
#pragma once #pragma once
#if defined(_WIN32) || defined(_WIN64)
#error "FileManager.h is not supported on Windows."
#endif
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <iconv.h>
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
#include <string> #include <string>
@@ -11,15 +27,19 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <cerrno> #include <cerrno>
#ifdef __APPLE__
#include <sys/mount.h> // macOS: statfs for filesystem type
#endif
#include <iconv.h> // Character encoding conversion (GBK <-> UTF-8)
// FileTransferV2 is in the same directory (common/)
#include "FileTransferV2.h" #include "FileTransferV2.h"
// 外部声明 clientID在 main.cpp 中定义) // External declaration of clientID (defined in main.cpp/main.mm)
extern uint64_t g_myClientID; extern uint64_t g_myClientID;
// ============== Linux File Manager ==============
// Implements file transfer between Windows server and Linux client
// Supports: browse, upload, download, delete, rename, create folder
#define MAX_SEND_BUFFER 65535 #define MAX_SEND_BUFFER 65535
class FileManager : public IOCPManager class FileManager : public IOCPManager
@@ -222,6 +242,13 @@ private:
// ---- Get root filesystem type ---- // ---- Get root filesystem type ----
static std::string getRootFsType() static std::string getRootFsType()
{ {
#ifdef __APPLE__
struct statfs sf;
if (statfs("/", &sf) == 0) {
return std::string(sf.f_fstypename); // "apfs", "hfs", etc.
}
return "apfs";
#else
std::ifstream f("/proc/mounts"); std::ifstream f("/proc/mounts");
std::string line; std::string line;
while (std::getline(f, line)) { while (std::getline(f, line)) {
@@ -232,6 +259,7 @@ private:
} }
} }
return "ext4"; return "ext4";
#endif
} }
// ---- Ensure parent directory exists (mkdir -p for parent of file path) ---- // ---- Ensure parent directory exists (mkdir -p for parent of file path) ----
@@ -307,7 +335,11 @@ private:
memcpy(buf + offset + 2, &totalMB, sizeof(unsigned long)); memcpy(buf + offset + 2, &totalMB, sizeof(unsigned long));
memcpy(buf + offset + 6, &freeMB, sizeof(unsigned long)); memcpy(buf + offset + 6, &freeMB, sizeof(unsigned long));
#ifdef __APPLE__
const char* typeName = "macOS";
#else
const char* typeName = "Linux"; const char* typeName = "Linux";
#endif
int typeNameLen = strlen(typeName) + 1; int typeNameLen = strlen(typeName) + 1;
memcpy(buf + offset + 10, typeName, typeNameLen); memcpy(buf + offset + 10, typeName, typeNameLen);

View File

@@ -1,7 +1,24 @@
/**
* FileTransferV2.h - Unix V2 File Transfer Protocol
*
* Implements V2 file transfer protocol for Unix clients.
* Supports: receive files from server/C2C, send files to server/C2C
*
* PLATFORM SUPPORT:
* - Linux: Supported
* - macOS: Supported
* - Windows: NOT SUPPORTED (Windows uses different file APIs)
*/
#pragma once #pragma once
#include "common/commands.h"
#include "common/file_upload.h" #if defined(_WIN32) || defined(_WIN64)
#include "client/IOCPClient.h" #error "FileTransferV2.h is not supported on Windows."
#endif
#include "commands.h"
#include "file_upload.h"
#include "../client/IOCPClient.h"
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
@@ -15,10 +32,6 @@
#include <fstream> #include <fstream>
#include <thread> #include <thread>
// ============== Linux V2 File Transfer ==============
// Implements V2 file transfer protocol for Linux client
// Supports: receive files from server/C2C, send files to server/C2C
class FileTransferV2 class FileTransferV2
{ {
public: public:

View File

@@ -321,6 +321,16 @@ inline const char* getFileName(const char* path)
#endif #endif
#elif defined(_WIN32) #elif defined(_WIN32)
#define Mprintf(format, ...) Logger::getInstance().log(getFileName((__FILE__)), __LINE__, (format), __VA_ARGS__) #define Mprintf(format, ...) Logger::getInstance().log(getFileName((__FILE__)), __LINE__, (format), __VA_ARGS__)
#elif defined(__APPLE__)
// macOS: 使用 NSLog 输出到系统日志(可通过 Console.app 查看)
#ifdef Mprintf
#undef Mprintf
#endif
#ifdef __OBJC__
#define Mprintf(format, ...) NSLog(@"%@", [NSString stringWithFormat:@(format), ##__VA_ARGS__])
#else
#define Mprintf(format, ...) printf(format, ##__VA_ARGS__)
#endif
#else #else
// Linux: 覆盖 commands.h 中的 printf 回退定义,改用 Logger 写文件 // Linux: 覆盖 commands.h 中的 printf 回退定义,改用 Logger 写文件
#ifdef Mprintf #ifdef Mprintf

View File

@@ -3,7 +3,7 @@
#include "client/IOCPClient.h" #include "client/IOCPClient.h"
#include "LinuxConfig.h" #include "LinuxConfig.h"
#include "ClipboardHandler.h" #include "ClipboardHandler.h"
#include "FileTransferV2.h" #include "common/FileTransferV2.h"
#include <dlfcn.h> #include <dlfcn.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <thread> #include <thread>

View File

@@ -26,9 +26,9 @@
#include <cmath> #include <cmath>
#include "ScreenHandler.h" #include "ScreenHandler.h"
#include "SystemManager.h" #include "SystemManager.h"
#include "FileManager.h" #include "common/FileManager.h"
#include "ClipboardHandler.h" #include "ClipboardHandler.h"
#include "FileTransferV2.h" #include "common/FileTransferV2.h"
#include "common/logger.h" #include "common/logger.h"
#define XXH_INLINE_ALL #define XXH_INLINE_ALL
#include "common/xxhash.h" #include "common/xxhash.h"

View File

@@ -45,6 +45,7 @@ find_library(CARBON_FRAMEWORK Carbon REQUIRED)
find_library(VIDEOTOOLBOX_FRAMEWORK VideoToolbox REQUIRED) find_library(VIDEOTOOLBOX_FRAMEWORK VideoToolbox REQUIRED)
find_library(COREMEDIA_FRAMEWORK CoreMedia REQUIRED) find_library(COREMEDIA_FRAMEWORK CoreMedia REQUIRED)
find_library(COREVIDEO_FRAMEWORK CoreVideo REQUIRED) find_library(COREVIDEO_FRAMEWORK CoreVideo REQUIRED)
find_library(ICONV_LIBRARY iconv REQUIRED)
target_link_libraries(ghost PRIVATE target_link_libraries(ghost PRIVATE
${COCOA_FRAMEWORK} ${COCOA_FRAMEWORK}
@@ -57,6 +58,7 @@ target_link_libraries(ghost PRIVATE
${VIDEOTOOLBOX_FRAMEWORK} ${VIDEOTOOLBOX_FRAMEWORK}
${COREMEDIA_FRAMEWORK} ${COREMEDIA_FRAMEWORK}
${COREVIDEO_FRAMEWORK} ${COREVIDEO_FRAMEWORK}
${ICONV_LIBRARY}
"${CMAKE_SOURCE_DIR}/lib/libzstd.a" "${CMAKE_SOURCE_DIR}/lib/libzstd.a"
) )

View File

@@ -3,6 +3,8 @@
#import "InputHandler.h" #import "InputHandler.h"
#import "../client/IOCPClient.h" #import "../client/IOCPClient.h"
#import "../common/commands.h" #import "../common/commands.h"
#import "../common/FileTransferV2.h"
#import "../common/logger.h"
#import "Permissions.h" #import "Permissions.h"
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <chrono> #import <chrono>
@@ -280,6 +282,72 @@ void ScreenHandler::OnReceive(uint8_t* data, ULONG size)
} }
break; break;
case COMMAND_GET_FILE:
// Server requests file download: [cmd:1][targetDir\0][file1\0file2\0...\0]
// Use V2 protocol to upload files
{
if (size < 3) break;
// Parse target directory (GBK encoding)
const char* ptr = (const char*)(data + 1);
const char* end = (const char*)(data + size);
std::string targetDirGbk = ptr;
std::string targetDir = FileTransferV2::gbkToUtf8(targetDirGbk);
ptr += targetDirGbk.length() + 1;
// Parse file list
std::vector<std::string> files;
while (ptr < end && *ptr != '\0') {
std::string fileGbk = ptr;
files.push_back(FileTransferV2::gbkToUtf8(fileGbk));
ptr += fileGbk.length() + 1;
}
// TODO: If no file list, get from clipboard (ClipboardHandler not implemented yet)
if (!files.empty() && !targetDir.empty()) {
NSLog(@">>> COMMAND_GET_FILE: %zu files -> %s", files.size(), targetDir.c_str());
// Use V2 protocol to send files
IOCPClient* client = m_client;
std::thread([files, targetDir, client]() {
// Collect all files (expand directories)
std::vector<std::string> allFiles;
std::vector<std::string> rootCandidates;
for (const auto& path : files) {
struct stat st;
if (stat(path.c_str(), &st) != 0) continue;
if (S_ISDIR(st.st_mode)) {
std::string dirPath = path;
if (dirPath.back() != '/') dirPath += '/';
size_t pos = dirPath.rfind('/', dirPath.length() - 2);
std::string parentPath = (pos != std::string::npos) ? dirPath.substr(0, pos + 1) : dirPath;
rootCandidates.push_back(parentPath);
FileTransferV2::CollectFiles(dirPath, allFiles);
} else {
rootCandidates.push_back(path);
allFiles.push_back(path);
}
}
if (allFiles.empty()) {
NSLog(@"*** No files to send");
return;
}
std::string commonRoot = FileTransferV2::GetCommonRoot(rootCandidates);
NSLog(@">>> Sending %zu files, root=%s", allFiles.size(), commonRoot.c_str());
FileTransferV2::SendFilesV2(allFiles, targetDir, commonRoot, client, g_myClientID);
}).detach();
} else {
NSLog(@"*** COMMAND_GET_FILE: no files or empty target");
}
}
break;
default: default:
break; break;
} }

View File

@@ -22,7 +22,10 @@
#import "ScreenHandler.h" #import "ScreenHandler.h"
#import "InputHandler.h" #import "InputHandler.h"
#import "SystemManager.h" #import "SystemManager.h"
#import "common/PTYHandler.h" #import "../common/PTYHandler.h"
#import "../common/FileManager.h"
#import "../common/FileTransferV2.h"
#import "../common/logger.h"
// Global state // Global state
static std::atomic<bool> g_running(true); static std::atomic<bool> g_running(true);
@@ -664,6 +667,26 @@ void* ScreenworkingThread(void* param)
return NULL; return NULL;
} }
void* FileManagerworkingThread(void* param)
{
try {
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get();
Mprintf(">>> Enter FileManagerworkingThread [%p]\n", clientAddr);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<FileManager> handler(new FileManager(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
Mprintf(">>> FileManagerworkingThread [%p] initialized\n", clientAddr);
while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit)
Sleep(1000);
}
Mprintf(">>> Leave FileManagerworkingThread [%p]\n", clientAddr);
} catch (const std::exception& e) {
Mprintf("*** FileManagerworkingThread exception: %s ***\n", e.what());
}
return NULL;
}
int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength) int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
{ {
if (szBuffer == nullptr || ulLength == 0) if (szBuffer == nullptr || ulLength == 0)
@@ -682,7 +705,18 @@ int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
} else if (szBuffer[0] == COMMAND_SYSTEM) { } else if (szBuffer[0] == COMMAND_SYSTEM) {
Mprintf("** [%p] Received 'SYSTEM' command ***\n", user); Mprintf("** [%p] Received 'SYSTEM' command ***\n", user);
} else if (szBuffer[0] == COMMAND_LIST_DRIVE) { } else if (szBuffer[0] == COMMAND_LIST_DRIVE) {
std::thread(FileManagerworkingThread, nullptr).detach();
Mprintf("** [%p] Received 'LIST_DRIVE' command ***\n", user); Mprintf("** [%p] Received 'LIST_DRIVE' command ***\n", user);
} else if (szBuffer[0] == COMMAND_C2C_PREPARE) {
// C2C 准备接收通知
FileTransferV2::HandleC2CPrepare(szBuffer, ulLength, nullptr);
Mprintf("** [%p] C2C Prepare received ***\n", user);
} else if (szBuffer[0] == COMMAND_SEND_FILE_V2 || szBuffer[0] == COMMAND_FILE_COMPLETE_V2) {
// V2 文件接收
int result = FileTransferV2::RecvFileChunkV2(szBuffer, ulLength, g_myClientID);
if (result != 0) {
Mprintf("** [%p] V2 File recv error: %d ***\n", user, result);
}
} else if (szBuffer[0] == CMD_HEARTBEAT_ACK) { } else if (szBuffer[0] == CMD_HEARTBEAT_ACK) {
if (ulLength >= 1 + sizeof(HeartbeatACK)) { if (ulLength >= 1 + sizeof(HeartbeatACK)) {
HeartbeatACK* ack = (HeartbeatACK*)(szBuffer + 1); HeartbeatACK* ack = (HeartbeatACK*)(szBuffer + 1);