Compare commits
2 Commits
9ae5529458
...
36423b1c7c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36423b1c7c | ||
|
|
a3611d9fc1 |
16
ReadMe.md
16
ReadMe.md
@@ -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 交互式 Shell(zsh/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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
16
ReadMe_EN.md
16
ReadMe_EN.md
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
16
ReadMe_TW.md
16
ReadMe_TW.md
@@ -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 互動式 Shell(zsh/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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -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:
|
||||||
270
common/PTYHandler.h
Normal file
270
common/PTYHandler.h
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/**
|
||||||
|
* PTYHandler.h - Unix Pseudo Terminal Handler
|
||||||
|
*
|
||||||
|
* This file provides pseudo terminal (PTY) functionality for remote shell access.
|
||||||
|
*
|
||||||
|
* PLATFORM SUPPORT:
|
||||||
|
* - Linux: Supported
|
||||||
|
* - macOS: Supported
|
||||||
|
* - Windows: NOT SUPPORTED (Windows uses different terminal APIs)
|
||||||
|
*
|
||||||
|
* USAGE:
|
||||||
|
* #include "common/PTYHandler.h"
|
||||||
|
*
|
||||||
|
* PTYHandler* handler = new PTYHandler(clientObject);
|
||||||
|
* clientObject->setManagerCallBack(handler, ...);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#error "PTYHandler.h is not supported on Windows. Use Windows ConPTY or other APIs instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform-specific includes
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <util.h> // macOS: openpty()
|
||||||
|
#else
|
||||||
|
#include <pty.h> // Linux: openpty()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Common Unix includes
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "commands.h"
|
||||||
|
#include "../client/IOCPClient.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PTYHandler - Pseudo Terminal Handler
|
||||||
|
*
|
||||||
|
* Manages a pseudo terminal for remote shell access.
|
||||||
|
* Inherits from IOCPManager to integrate with the IOCP client framework.
|
||||||
|
*/
|
||||||
|
class PTYHandler : public IOCPManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Non-copyable, non-movable (owns system resources)
|
||||||
|
PTYHandler(const PTYHandler&) = delete;
|
||||||
|
PTYHandler& operator=(const PTYHandler&) = delete;
|
||||||
|
PTYHandler(PTYHandler&&) = delete;
|
||||||
|
PTYHandler& operator=(PTYHandler&&) = delete;
|
||||||
|
|
||||||
|
PTYHandler(IOCPClient* client) : m_client(client), m_running(false), m_master_fd(-1), m_slave_fd(-1), m_child_pid(-1)
|
||||||
|
{
|
||||||
|
if (!client) {
|
||||||
|
throw std::invalid_argument("IOCPClient pointer cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pseudo terminal pair
|
||||||
|
if (openpty(&m_master_fd, &m_slave_fd, nullptr, nullptr, nullptr) == -1) {
|
||||||
|
throw std::runtime_error("Failed to create pseudo terminal");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set master fd to non-blocking mode
|
||||||
|
int flags = fcntl(m_master_fd, F_GETFL, 0);
|
||||||
|
fcntl(m_master_fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
// Start shell process
|
||||||
|
startShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
~PTYHandler()
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
if (m_readThread.joinable()) {
|
||||||
|
m_readThread.join();
|
||||||
|
}
|
||||||
|
if (m_master_fd >= 0) {
|
||||||
|
close(m_master_fd);
|
||||||
|
}
|
||||||
|
if (m_slave_fd >= 0) {
|
||||||
|
close(m_slave_fd);
|
||||||
|
}
|
||||||
|
if (m_child_pid > 0) {
|
||||||
|
// Check if child is still running before killing
|
||||||
|
int status;
|
||||||
|
pid_t result = waitpid(m_child_pid, &status, WNOHANG);
|
||||||
|
if (result == 0) {
|
||||||
|
// Child still running, terminate it
|
||||||
|
kill(m_child_pid, SIGTERM);
|
||||||
|
waitpid(m_child_pid, nullptr, 0);
|
||||||
|
}
|
||||||
|
// If result == m_child_pid, child already exited and was reaped
|
||||||
|
// If result == -1, child was already reaped elsewhere
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the PTY read thread
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
bool expected = false;
|
||||||
|
if (!m_running.compare_exchange_strong(expected, true)) return;
|
||||||
|
m_readThread = std::thread(&PTYHandler::readFromPTY, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle incoming data from server
|
||||||
|
virtual VOID OnReceive(PBYTE data, ULONG size) override
|
||||||
|
{
|
||||||
|
if (size && data[0] == COMMAND_NEXT) {
|
||||||
|
Start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle terminal resize command
|
||||||
|
if (size >= 5 && data[0] == CMD_TERMINAL_RESIZE) {
|
||||||
|
short cols, rows;
|
||||||
|
memcpy(&cols, data + 1, sizeof(short));
|
||||||
|
memcpy(&rows, data + 3, sizeof(short));
|
||||||
|
SetWindowSize(cols, rows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to PTY
|
||||||
|
if (size > 0) {
|
||||||
|
ssize_t total = 0;
|
||||||
|
while (total < (ssize_t)size) {
|
||||||
|
ssize_t written = write(m_master_fd, (char*)data + total, size - total);
|
||||||
|
if (written == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
total += written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set terminal window size
|
||||||
|
void SetWindowSize(int cols, int rows)
|
||||||
|
{
|
||||||
|
struct winsize ws;
|
||||||
|
ws.ws_col = cols;
|
||||||
|
ws.ws_row = rows;
|
||||||
|
ws.ws_xpixel = 0;
|
||||||
|
ws.ws_ypixel = 0;
|
||||||
|
|
||||||
|
if (ioctl(m_master_fd, TIOCSWINSZ, &ws) == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send SIGWINCH to child process to notify window size change
|
||||||
|
if (m_child_pid > 0) {
|
||||||
|
kill(m_child_pid, SIGWINCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_master_fd;
|
||||||
|
int m_slave_fd;
|
||||||
|
IOCPClient* m_client;
|
||||||
|
std::thread m_readThread;
|
||||||
|
std::atomic<bool> m_running;
|
||||||
|
pid_t m_child_pid;
|
||||||
|
|
||||||
|
void startShell()
|
||||||
|
{
|
||||||
|
m_child_pid = fork();
|
||||||
|
if (m_child_pid == -1) {
|
||||||
|
close(m_master_fd);
|
||||||
|
close(m_slave_fd);
|
||||||
|
throw std::runtime_error("Failed to fork shell process");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_child_pid == 0) {
|
||||||
|
// Child process
|
||||||
|
setsid(); // Create new session, become session leader
|
||||||
|
|
||||||
|
// Set slave PTY as controlling terminal (required for Ctrl+C to work)
|
||||||
|
// This must be done after setsid() and before dup2()
|
||||||
|
ioctl(m_slave_fd, TIOCSCTTY, 0);
|
||||||
|
|
||||||
|
// Redirect stdin/stdout/stderr to slave PTY
|
||||||
|
dup2(m_slave_fd, STDIN_FILENO);
|
||||||
|
dup2(m_slave_fd, STDOUT_FILENO);
|
||||||
|
dup2(m_slave_fd, STDERR_FILENO);
|
||||||
|
close(m_master_fd);
|
||||||
|
close(m_slave_fd);
|
||||||
|
|
||||||
|
// Set terminal environment for xterm.js compatibility
|
||||||
|
setenv("TERM", "xterm-256color", 1);
|
||||||
|
setenv("COLORTERM", "truecolor", 1);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// macOS locale settings
|
||||||
|
setenv("LANG", "en_US.UTF-8", 1);
|
||||||
|
setenv("LC_ALL", "en_US.UTF-8", 1);
|
||||||
|
|
||||||
|
// Try zsh first (macOS default), fallback to bash
|
||||||
|
if (access("/bin/zsh", X_OK) == 0) {
|
||||||
|
execl("/bin/zsh", "zsh", "-i", nullptr);
|
||||||
|
}
|
||||||
|
execl("/bin/bash", "bash", "-i", nullptr);
|
||||||
|
#else
|
||||||
|
// Linux locale settings (C.UTF-8 is most portable)
|
||||||
|
setenv("LANG", "C.UTF-8", 1);
|
||||||
|
setenv("LC_ALL", "C.UTF-8", 1);
|
||||||
|
|
||||||
|
// Start interactive bash
|
||||||
|
execl("/bin/bash", "bash", "-i", nullptr);
|
||||||
|
#endif
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readFromPTY()
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
while (m_running) {
|
||||||
|
// Check if child process has exited
|
||||||
|
int status;
|
||||||
|
pid_t result = waitpid(m_child_pid, &status, WNOHANG);
|
||||||
|
if (result == m_child_pid) {
|
||||||
|
// Shell exited, send close notification
|
||||||
|
if (m_client) {
|
||||||
|
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
||||||
|
m_client->Send2Server((char*)&closeToken, 1);
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t bytes_read = read(m_master_fd, buffer, sizeof(buffer) - 1);
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
if (m_client) {
|
||||||
|
m_client->Send2Server(buffer, bytes_read);
|
||||||
|
}
|
||||||
|
} else if (bytes_read == 0) {
|
||||||
|
// EOF - PTY closed
|
||||||
|
if (m_client) {
|
||||||
|
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
||||||
|
m_client->Send2Server((char*)&closeToken, 1);
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
} else if (bytes_read == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
usleep(10000); // 10ms
|
||||||
|
} else if (errno == EIO) {
|
||||||
|
// EIO typically means PTY slave closed (shell exited)
|
||||||
|
if (m_client) {
|
||||||
|
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
||||||
|
m_client->Send2Server((char*)&closeToken, 1);
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
188
linux/main.cpp
188
linux/main.cpp
@@ -14,7 +14,7 @@
|
|||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <pty.h>
|
#include "common/PTYHandler.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -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"
|
||||||
@@ -338,187 +338,7 @@ struct RttEstimator {
|
|||||||
RttEstimator g_rttEstimator;
|
RttEstimator g_rttEstimator;
|
||||||
int g_heartbeatInterval = 5; // 默认心跳间隔(秒),可被服务端 CMD_MASTERSETTING 更新
|
int g_heartbeatInterval = 5; // 默认心跳间隔(秒),可被服务端 CMD_MASTERSETTING 更新
|
||||||
|
|
||||||
// 伪终端处理类:继承自IOCPManager.
|
// PTYHandler moved to common/PTYHandler.h (shared between Linux and macOS)
|
||||||
class PTYHandler : public IOCPManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PTYHandler(IOCPClient* client) : m_client(client), m_running(false)
|
|
||||||
{
|
|
||||||
if (!client) {
|
|
||||||
throw std::invalid_argument("IOCPClient pointer cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建伪终端
|
|
||||||
if (openpty(&m_master_fd, &m_slave_fd, nullptr, nullptr, nullptr) == -1) {
|
|
||||||
throw std::runtime_error("Failed to create pseudo terminal");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置伪终端为非阻塞模式
|
|
||||||
int flags = fcntl(m_master_fd, F_GETFL, 0);
|
|
||||||
fcntl(m_master_fd, F_SETFL, flags | O_NONBLOCK);
|
|
||||||
|
|
||||||
// 启动 Shell 进程
|
|
||||||
startShell();
|
|
||||||
}
|
|
||||||
|
|
||||||
~PTYHandler()
|
|
||||||
{
|
|
||||||
m_running = false;
|
|
||||||
if (m_readThread.joinable()) m_readThread.join();
|
|
||||||
close(m_master_fd);
|
|
||||||
close(m_slave_fd);
|
|
||||||
if (m_child_pid > 0) {
|
|
||||||
kill(m_child_pid, SIGTERM);
|
|
||||||
waitpid(m_child_pid, nullptr, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动读取线程
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
if (!m_running.compare_exchange_strong(expected, true)) return;
|
|
||||||
m_readThread = std::thread(&PTYHandler::readFromPTY, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual VOID OnReceive(PBYTE data, ULONG size)
|
|
||||||
{
|
|
||||||
if (size && data[0] == COMMAND_NEXT) {
|
|
||||||
Start();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 处理终端尺寸调整命令
|
|
||||||
if (size >= 5 && data[0] == CMD_TERMINAL_RESIZE) {
|
|
||||||
int cols = *(short*)(data + 1);
|
|
||||||
int rows = *(short*)(data + 3);
|
|
||||||
SetWindowSize(cols, rows);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string s((char*)data, size);
|
|
||||||
Mprintf("%s", s.c_str());
|
|
||||||
if (size > 0) {
|
|
||||||
ssize_t total = 0;
|
|
||||||
while (total < (ssize_t)size) {
|
|
||||||
ssize_t written = write(m_master_fd, (char*)data + total, size - total);
|
|
||||||
if (written == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EINTR) continue;
|
|
||||||
Mprintf("OnReceive: write error %d\n", errno);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
total += written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置终端窗口尺寸
|
|
||||||
void SetWindowSize(int cols, int rows)
|
|
||||||
{
|
|
||||||
struct winsize ws;
|
|
||||||
ws.ws_col = cols;
|
|
||||||
ws.ws_row = rows;
|
|
||||||
ws.ws_xpixel = 0;
|
|
||||||
ws.ws_ypixel = 0;
|
|
||||||
|
|
||||||
if (ioctl(m_master_fd, TIOCSWINSZ, &ws) == -1) {
|
|
||||||
Mprintf("SetWindowSize: ioctl failed %d\n", errno);
|
|
||||||
} else {
|
|
||||||
// 发送 SIGWINCH 给子进程,通知其窗口大小已改变
|
|
||||||
if (m_child_pid > 0) {
|
|
||||||
kill(m_child_pid, SIGWINCH);
|
|
||||||
}
|
|
||||||
Mprintf("SetWindowSize: %dx%d\n", cols, rows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
int m_master_fd, m_slave_fd;
|
|
||||||
IOCPClient* m_client;
|
|
||||||
std::thread m_readThread;
|
|
||||||
std::atomic<bool> m_running;
|
|
||||||
pid_t m_child_pid;
|
|
||||||
|
|
||||||
void startShell()
|
|
||||||
{
|
|
||||||
m_child_pid = fork();
|
|
||||||
if (m_child_pid == -1) {
|
|
||||||
close(m_master_fd);
|
|
||||||
close(m_slave_fd);
|
|
||||||
throw std::runtime_error("Failed to fork shell process");
|
|
||||||
}
|
|
||||||
if (m_child_pid == 0) { // 子进程
|
|
||||||
setsid(); // 创建新的会话
|
|
||||||
dup2(m_slave_fd, STDIN_FILENO);
|
|
||||||
dup2(m_slave_fd, STDOUT_FILENO);
|
|
||||||
dup2(m_slave_fd, STDERR_FILENO);
|
|
||||||
close(m_master_fd);
|
|
||||||
close(m_slave_fd);
|
|
||||||
|
|
||||||
// 设置完整终端支持(xterm.js 终端仿真)
|
|
||||||
setenv("TERM", "xterm-256color", 1);
|
|
||||||
setenv("COLORTERM", "truecolor", 1);
|
|
||||||
// 使用 C.UTF-8 是最通用的 UTF-8 locale,几乎所有 Linux 都支持
|
|
||||||
setenv("LANG", "C.UTF-8", 1);
|
|
||||||
setenv("LC_ALL", "C.UTF-8", 1);
|
|
||||||
|
|
||||||
// 启动交互式 Bash
|
|
||||||
execl("/bin/bash", "bash", "-i", nullptr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void readFromPTY()
|
|
||||||
{
|
|
||||||
char buffer[4096];
|
|
||||||
while (m_running) {
|
|
||||||
// 检查子进程是否已退出
|
|
||||||
int status;
|
|
||||||
pid_t result = waitpid(m_child_pid, &status, WNOHANG);
|
|
||||||
if (result == m_child_pid) {
|
|
||||||
// Shell 已退出,发送关闭通知
|
|
||||||
Mprintf("readFromPTY: shell exited (status=%d)\n", WEXITSTATUS(status));
|
|
||||||
if (m_client) {
|
|
||||||
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
|
||||||
m_client->Send2Server((char*)&closeToken, 1);
|
|
||||||
}
|
|
||||||
m_running = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t bytes_read = read(m_master_fd, buffer, sizeof(buffer) - 1);
|
|
||||||
if (bytes_read > 0) {
|
|
||||||
if (m_client) {
|
|
||||||
buffer[bytes_read] = '\0';
|
|
||||||
Mprintf("%s", buffer);
|
|
||||||
m_client->Send2Server(buffer, bytes_read);
|
|
||||||
}
|
|
||||||
} else if (bytes_read == 0) {
|
|
||||||
// EOF - PTY 已关闭
|
|
||||||
Mprintf("readFromPTY: EOF (shell closed)\n");
|
|
||||||
if (m_client) {
|
|
||||||
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
|
||||||
m_client->Send2Server((char*)&closeToken, 1);
|
|
||||||
}
|
|
||||||
m_running = false;
|
|
||||||
break;
|
|
||||||
} else if (bytes_read == -1) {
|
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
usleep(10000);
|
|
||||||
} else if (errno == EIO) {
|
|
||||||
// EIO 通常表示 PTY slave 已关闭(shell 退出)
|
|
||||||
Mprintf("readFromPTY: EIO (shell closed)\n");
|
|
||||||
if (m_client) {
|
|
||||||
BYTE closeToken = TOKEN_TERMINAL_CLOSE;
|
|
||||||
m_client->Send2Server((char*)&closeToken, 1);
|
|
||||||
}
|
|
||||||
m_running = false;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
Mprintf("readFromPTY: read error %d\n", errno);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void* ShellworkingThread(void* param)
|
void* ShellworkingThread(void* param)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +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/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);
|
||||||
@@ -615,6 +619,28 @@ struct RttEstimator {
|
|||||||
RttEstimator g_rttEstimator;
|
RttEstimator g_rttEstimator;
|
||||||
int g_heartbeatInterval = 5; // 心跳间隔(秒),默认 5 秒,后续可由服务端动态调整
|
int g_heartbeatInterval = 5; // 心跳间隔(秒),默认 5 秒,后续可由服务端动态调整
|
||||||
|
|
||||||
|
void* ShellworkingThread(void* param)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
|
||||||
|
void* clientAddr = ClientObject.get();
|
||||||
|
NSLog(@">>> Enter ShellworkingThread [%p]", clientAddr);
|
||||||
|
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
|
||||||
|
std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get()));
|
||||||
|
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
|
||||||
|
BYTE bToken = TOKEN_TERMINAL_START;
|
||||||
|
ClientObject->Send2Server((char*)&bToken, 1);
|
||||||
|
NSLog(@">>> ShellworkingThread [%p] Send: TOKEN_TERMINAL_START", clientAddr);
|
||||||
|
while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit)
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
NSLog(@">>> Leave ShellworkingThread [%p]", clientAddr);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
NSLog(@"*** ShellworkingThread exception: %s ***", e.what());
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void* ScreenworkingThread(void* param)
|
void* ScreenworkingThread(void* param)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -641,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)
|
||||||
@@ -651,6 +697,7 @@ int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
|
|||||||
g_bExit = S_CLIENT_EXIT;
|
g_bExit = S_CLIENT_EXIT;
|
||||||
g_running = false; // Stop main loop to prevent reconnection
|
g_running = false; // Stop main loop to prevent reconnection
|
||||||
} else if (szBuffer[0] == COMMAND_SHELL) {
|
} else if (szBuffer[0] == COMMAND_SHELL) {
|
||||||
|
std::thread(ShellworkingThread, nullptr).detach();
|
||||||
Mprintf("** [%p] Received 'SHELL' command ***\n", user);
|
Mprintf("** [%p] Received 'SHELL' command ***\n", user);
|
||||||
} else if (szBuffer[0] == COMMAND_SCREEN_SPY) {
|
} else if (szBuffer[0] == COMMAND_SCREEN_SPY) {
|
||||||
std::thread(ScreenworkingThread, nullptr).detach();
|
std::thread(ScreenworkingThread, nullptr).detach();
|
||||||
@@ -658,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);
|
||||||
|
|||||||
Reference in New Issue
Block a user