Refactor: Move FileManager to common, add macOS file management support
This commit is contained in:
16
ReadMe.md
16
ReadMe.md
@@ -494,27 +494,31 @@ make
|
||||
|
||||
**系统要求**:
|
||||
- macOS 10.15 (Catalina) 及以上
|
||||
- 架构支持:Intel (x64) 和 Apple Silicon (arm64) 通用二进制
|
||||
- 需要授予系统权限:屏幕录制、辅助功能、完全磁盘访问
|
||||
|
||||
**功能支持**:
|
||||
|
||||
| 功能 | 状态 | 实现 |
|
||||
|------|------|------|
|
||||
| 远程桌面 | ✅ | CoreGraphics 屏幕捕获,H.264 硬件编码 |
|
||||
| 远程桌面 | ✅ | CoreGraphics 屏幕捕获,VideoToolbox H.264 硬件编码 |
|
||||
| 鼠标控制 | ✅ | CGEvent 模拟,支持双击、拖拽 |
|
||||
| 键盘控制 | ✅ | CGEvent 模拟,完整键码映射 |
|
||||
| 光标同步 | ✅ | 实时同步远程光标样式 |
|
||||
| 远程终端 | ✅ | PTY 交互式 Shell(zsh/bash) |
|
||||
| 文件管理 | ✅ | 双向传输、V2 协议、大文件支持 |
|
||||
| 心跳/RTT | ✅ | RFC 6298 RTT 估算 |
|
||||
| 文件管理 | ⏳ | 开发中 |
|
||||
| 远程终端 | ⏳ | 开发中 |
|
||||
| 分组管理 | ✅ | 持久化配置文件 |
|
||||
| 进程管理 | ⏳ | 开发中 |
|
||||
| 剪贴板 | ⏳ | 开发中 |
|
||||
|
||||
**编译方式**:
|
||||
|
||||
```bash
|
||||
cd macos
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
./build.sh
|
||||
# 或手动编译:
|
||||
# mkdir build && cd build && cmake .. && make
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
16
ReadMe_EN.md
16
ReadMe_EN.md
@@ -479,27 +479,31 @@ make
|
||||
|
||||
**System Requirements**:
|
||||
- macOS 10.15 (Catalina) or later
|
||||
- Architecture: Universal Binary (Intel x64 + Apple Silicon arm64)
|
||||
- Required permissions: Screen Recording, Accessibility, Full Disk Access
|
||||
|
||||
**Feature Support**:
|
||||
|
||||
| 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 |
|
||||
| Keyboard Control | ✅ | CGEvent simulation, full keycode mapping |
|
||||
| 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 |
|
||||
| File Management | ⏳ | In development |
|
||||
| Remote Terminal | ⏳ | In development |
|
||||
| Group Management | ✅ | Persistent configuration file |
|
||||
| Process Management | ⏳ | In development |
|
||||
| Clipboard | ⏳ | In development |
|
||||
|
||||
**Build Instructions**:
|
||||
|
||||
```bash
|
||||
cd macos
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
./build.sh
|
||||
# Or manually:
|
||||
# mkdir build && cd build && cmake .. && make
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
16
ReadMe_TW.md
16
ReadMe_TW.md
@@ -478,27 +478,31 @@ make
|
||||
|
||||
**系統要求**:
|
||||
- macOS 10.15 (Catalina) 及以上
|
||||
- 架構支援:Intel (x64) 和 Apple Silicon (arm64) 通用二進位
|
||||
- 需要授予系統權限:螢幕錄製、輔助使用、完全磁碟存取
|
||||
|
||||
**功能支援**:
|
||||
|
||||
| 功能 | 狀態 | 實作 |
|
||||
|------|------|------|
|
||||
| 遠端桌面 | ✅ | CoreGraphics 螢幕擷取,H.264 硬體編碼 |
|
||||
| 遠端桌面 | ✅ | CoreGraphics 螢幕擷取,VideoToolbox H.264 硬體編碼 |
|
||||
| 滑鼠控制 | ✅ | CGEvent 模擬,支援雙擊、拖曳 |
|
||||
| 鍵盤控制 | ✅ | CGEvent 模擬,完整鍵碼對應 |
|
||||
| 游標同步 | ✅ | 即時同步遠端游標樣式 |
|
||||
| 遠端終端 | ✅ | PTY 互動式 Shell(zsh/bash) |
|
||||
| 檔案管理 | ✅ | 雙向傳輸、V2 協定、大檔案支援 |
|
||||
| 心跳/RTT | ✅ | RFC 6298 RTT 估算 |
|
||||
| 檔案管理 | ⏳ | 開發中 |
|
||||
| 遠端終端 | ⏳ | 開發中 |
|
||||
| 分組管理 | ✅ | 持久化設定檔 |
|
||||
| 程序管理 | ⏳ | 開發中 |
|
||||
| 剪貼簿 | ⏳ | 開發中 |
|
||||
|
||||
**編譯方式**:
|
||||
|
||||
```bash
|
||||
cd macos
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
./build.sh
|
||||
# 或手動編譯:
|
||||
# mkdir build && cd build && cmake .. && make
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#error "FileManager.h is not supported on Windows."
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <iconv.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@@ -11,15 +27,19 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#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"
|
||||
|
||||
// 外部声明 clientID(在 main.cpp 中定义)
|
||||
// External declaration of clientID (defined in main.cpp/main.mm)
|
||||
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
|
||||
|
||||
class FileManager : public IOCPManager
|
||||
@@ -222,6 +242,13 @@ private:
|
||||
// ---- Get root filesystem type ----
|
||||
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::string line;
|
||||
while (std::getline(f, line)) {
|
||||
@@ -232,6 +259,7 @@ private:
|
||||
}
|
||||
}
|
||||
return "ext4";
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---- 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 + 6, &freeMB, sizeof(unsigned long));
|
||||
|
||||
#ifdef __APPLE__
|
||||
const char* typeName = "macOS";
|
||||
#else
|
||||
const char* typeName = "Linux";
|
||||
#endif
|
||||
int typeNameLen = strlen(typeName) + 1;
|
||||
memcpy(buf + offset + 10, typeName, typeNameLen);
|
||||
|
||||
@@ -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
|
||||
#include "common/commands.h"
|
||||
#include "common/file_upload.h"
|
||||
#include "client/IOCPClient.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#error "FileTransferV2.h is not supported on Windows."
|
||||
#endif
|
||||
|
||||
#include "commands.h"
|
||||
#include "file_upload.h"
|
||||
#include "../client/IOCPClient.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -15,10 +32,6 @@
|
||||
#include <fstream>
|
||||
#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
|
||||
{
|
||||
public:
|
||||
@@ -321,6 +321,16 @@ inline const char* getFileName(const char* path)
|
||||
#endif
|
||||
#elif defined(_WIN32)
|
||||
#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
|
||||
// Linux: 覆盖 commands.h 中的 printf 回退定义,改用 Logger 写文件
|
||||
#ifdef Mprintf
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "client/IOCPClient.h"
|
||||
#include "LinuxConfig.h"
|
||||
#include "ClipboardHandler.h"
|
||||
#include "FileTransferV2.h"
|
||||
#include "common/FileTransferV2.h"
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
#include <cmath>
|
||||
#include "ScreenHandler.h"
|
||||
#include "SystemManager.h"
|
||||
#include "FileManager.h"
|
||||
#include "common/FileManager.h"
|
||||
#include "ClipboardHandler.h"
|
||||
#include "FileTransferV2.h"
|
||||
#include "common/FileTransferV2.h"
|
||||
#include "common/logger.h"
|
||||
#define XXH_INLINE_ALL
|
||||
#include "common/xxhash.h"
|
||||
|
||||
@@ -45,6 +45,7 @@ find_library(CARBON_FRAMEWORK Carbon REQUIRED)
|
||||
find_library(VIDEOTOOLBOX_FRAMEWORK VideoToolbox REQUIRED)
|
||||
find_library(COREMEDIA_FRAMEWORK CoreMedia REQUIRED)
|
||||
find_library(COREVIDEO_FRAMEWORK CoreVideo REQUIRED)
|
||||
find_library(ICONV_LIBRARY iconv REQUIRED)
|
||||
|
||||
target_link_libraries(ghost PRIVATE
|
||||
${COCOA_FRAMEWORK}
|
||||
@@ -57,6 +58,7 @@ target_link_libraries(ghost PRIVATE
|
||||
${VIDEOTOOLBOX_FRAMEWORK}
|
||||
${COREMEDIA_FRAMEWORK}
|
||||
${COREVIDEO_FRAMEWORK}
|
||||
${ICONV_LIBRARY}
|
||||
"${CMAKE_SOURCE_DIR}/lib/libzstd.a"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#import "InputHandler.h"
|
||||
#import "../client/IOCPClient.h"
|
||||
#import "../common/commands.h"
|
||||
#import "../common/FileTransferV2.h"
|
||||
#import "../common/logger.h"
|
||||
#import "Permissions.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <chrono>
|
||||
@@ -280,6 +282,72 @@ void ScreenHandler::OnReceive(uint8_t* data, ULONG size)
|
||||
}
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
#import "ScreenHandler.h"
|
||||
#import "InputHandler.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
|
||||
static std::atomic<bool> g_running(true);
|
||||
@@ -664,6 +667,26 @@ void* ScreenworkingThread(void* param)
|
||||
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)
|
||||
{
|
||||
if (szBuffer == nullptr || ulLength == 0)
|
||||
@@ -682,7 +705,18 @@ int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
|
||||
} else if (szBuffer[0] == COMMAND_SYSTEM) {
|
||||
Mprintf("** [%p] Received 'SYSTEM' command ***\n", user);
|
||||
} else if (szBuffer[0] == COMMAND_LIST_DRIVE) {
|
||||
std::thread(FileManagerworkingThread, nullptr).detach();
|
||||
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) {
|
||||
if (ulLength >= 1 + sizeof(HeartbeatACK)) {
|
||||
HeartbeatACK* ack = (HeartbeatACK*)(szBuffer + 1);
|
||||
|
||||
Reference in New Issue
Block a user