Files
SimpleRemoter/linux/FileManager.h

281 lines
9.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <dirent.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <iconv.h>
#include <cstring>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
// ============== Linux 文件管理(参考 Windows 端 client/FileManager.cpp==============
// 最小实现:仅支持浏览文件(驱动器列表 + 目录文件列表)
class FileManager : public IOCPManager
{
public:
FileManager(IOCPClient* client)
: m_client(client)
{
// 与 Windows 端一致:构造时立即发送驱动器列表
SendDriveList();
}
~FileManager() {}
virtual VOID OnReceive(PBYTE szBuffer, ULONG ulLength)
{
if (!szBuffer || ulLength == 0) return;
switch (szBuffer[0]) {
case COMMAND_LIST_FILES:
SendFilesList((char*)szBuffer + 1);
break;
case COMMAND_DELETE_FILE:
case COMMAND_DELETE_DIRECTORY:
// TODO: unlink / recursive rmdir
SendToken(TOKEN_DELETE_FINISH);
break;
case COMMAND_DOWN_FILES:
case COMMAND_CONTINUE:
case COMMAND_STOP:
// TODO: 文件上传(客户端→服务端)
SendToken(TOKEN_TRANSFER_FINISH);
break;
case COMMAND_CREATE_FOLDER:
// TODO: mkdir -p
SendToken(TOKEN_CREATEFOLDER_FINISH);
break;
case COMMAND_RENAME_FILE:
// TODO: rename()
SendToken(TOKEN_RENAME_FINISH);
break;
case COMMAND_SET_TRANSFER_MODE:
// 无需回复,仅存储模式
break;
case COMMAND_FILE_SIZE:
// 服务端想上传文件到本地 → 回复跳过
RejectIncomingFile();
break;
case COMMAND_FILE_DATA:
// 已拒绝,正常不会收到;若收到则忽略
break;
default:
Mprintf("[FileManager] Unhandled command: %d\n", (int)szBuffer[0]);
break;
}
}
private:
IOCPClient* m_client;
// ---- 发送单字节 Token 回复 ----
void SendToken(BYTE token)
{
if (!m_client) return;
m_client->Send2Server((char*)&token, 1);
}
// ---- 拒绝服务端上传文件:回复 TOKEN_DATA_CONTINUE偏移 low = -1跳过----
void RejectIncomingFile()
{
if (!m_client) return;
BYTE buf[9] = {};
buf[0] = (BYTE)TOKEN_DATA_CONTINUE;
DWORD skip = (DWORD)-1;
memcpy(buf + 5, &skip, sizeof(DWORD)); // dwSizeLow = 0xFFFFFFFF
m_client->Send2Server((char*)buf, sizeof(buf));
}
// ---- iconv 编码转换 ----
static std::string iconvConvert(const std::string& input, const char* from, const char* to)
{
if (input.empty()) return input;
iconv_t cd = iconv_open(to, from);
if (cd == (iconv_t)-1) return input;
size_t inLeft = input.size();
size_t outLeft = inLeft * 4; // 最大膨胀 4 倍
std::string output(outLeft, '\0');
char* inPtr = const_cast<char*>(input.data());
char* outPtr = &output[0];
size_t ret = iconv(cd, &inPtr, &inLeft, &outPtr, &outLeft);
iconv_close(cd);
if (ret == (size_t)-1) return input; // 转换失败,原样返回
output.resize(output.size() - outLeft);
return output;
}
// 服务端发来的 GBK 路径 → UTF-8用于 opendir 等系统调用)
static std::string gbkToUtf8(const std::string& gbk)
{
return iconvConvert(gbk, "GBK", "UTF-8");
}
// 本地 UTF-8 文件名 → GBK发送给服务端显示
static std::string utf8ToGbk(const std::string& utf8)
{
return iconvConvert(utf8, "UTF-8", "GBK");
}
// ---- Windows 路径 → Linux 路径 ----
// 服务端发送 "/:\home\user" 格式,需转换为 "/home/user"
static std::string winPathToLinux(const char* winPath)
{
std::string p(winPath);
// 1. 反斜杠 → 正斜杠
for (auto& c : p) {
if (c == '\\') c = '/';
}
// 2. 去掉 "X:/" 前缀X 为驱动器字母Linux 下为 '/'
if (p.size() >= 3 && p[1] == ':' && p[2] == '/') {
p = p.substr(3);
} else if (p.size() >= 2 && p[1] == ':') {
p = p.substr(2);
}
// 3. 确保以 / 开头
if (p.empty() || p[0] != '/') {
p = "/" + p;
}
// 4. 合并连续斜杠
std::string result;
result.reserve(p.size());
for (size_t i = 0; i < p.size(); i++) {
if (i > 0 && p[i] == '/' && p[i - 1] == '/')
continue;
result += p[i];
}
return result.empty() ? "/" : result;
}
// ---- Unix time_t → Windows FILETIME100ns since 1601-01-01----
static uint64_t unixToFiletime(time_t t)
{
return (uint64_t)t * 10000000ULL + 116444736000000000ULL;
}
// ---- 读取根分区文件系统类型(从 /proc/mounts----
static std::string getRootFsType()
{
std::ifstream f("/proc/mounts");
std::string line;
while (std::getline(f, line)) {
std::istringstream iss(line);
std::string dev, mp, fs;
if (iss >> dev >> mp >> fs) {
if (mp == "/") return fs;
}
}
return "ext4";
}
// ---- 发送驱动器列表Linux只发送根分区 /----
void SendDriveList()
{
if (!m_client) return;
BYTE buf[256];
buf[0] = (BYTE)TOKEN_DRIVE_LIST;
DWORD offset = 1;
// 驱动器字母: '/'
buf[offset] = '/';
// 驱动器类型: DRIVE_FIXED = 3
buf[offset + 1] = 3;
// 磁盘大小MB
unsigned long totalMB = 0, freeMB = 0;
struct statvfs sv;
if (statvfs("/", &sv) == 0) {
unsigned long long totalBytes = (unsigned long long)sv.f_blocks * sv.f_frsize;
unsigned long long freeBytes = (unsigned long long)sv.f_bfree * sv.f_frsize;
totalMB = (unsigned long)(totalBytes / 1024 / 1024);
freeMB = (unsigned long)(freeBytes / 1024 / 1024);
}
memcpy(buf + offset + 2, &totalMB, sizeof(unsigned long));
memcpy(buf + offset + 6, &freeMB, sizeof(unsigned long));
// 卷标名(类型描述)
const char* typeName = "Linux";
int typeNameLen = strlen(typeName) + 1;
memcpy(buf + offset + 10, typeName, typeNameLen);
// 文件系统名
std::string fsType = getRootFsType();
int fsNameLen = fsType.size() + 1;
memcpy(buf + offset + 10 + typeNameLen, fsType.c_str(), fsNameLen);
offset += 10 + typeNameLen + fsNameLen;
m_client->Send2Server((char*)buf, offset);
Mprintf("[FileManager] SendDriveList: %u bytes (total=%luMB free=%luMB fs=%s)\n",
(unsigned)offset, totalMB, freeMB, fsType.c_str());
}
// ---- 列出目录文件,构造 TOKEN_FILE_LIST 二进制数据 ----
void SendFilesList(const char* path)
{
if (!m_client) return;
std::string linuxPath = winPathToLinux(gbkToUtf8(path).c_str());
Mprintf("[FileManager] SendFilesList: '%s' -> '%s'\n", path, linuxPath.c_str());
std::vector<uint8_t> buf;
buf.reserve(64 * 1024);
buf.push_back((uint8_t)TOKEN_FILE_LIST);
DIR* dir = opendir(linuxPath.c_str());
if (!dir) {
Mprintf("[FileManager] opendir failed: %s (errno=%d)\n", linuxPath.c_str(), errno);
m_client->Send2Server((char*)buf.data(), (ULONG)buf.size());
return;
}
struct dirent* ent;
while ((ent = readdir(dir)) != nullptr) {
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
// 完整路径用于 stat()
std::string fullPath = linuxPath;
if (fullPath.back() != '/') fullPath += '/';
fullPath += ent->d_name;
struct stat st;
if (lstat(fullPath.c_str(), &st) != 0)
continue;
// 文件属性0x10 = 目录0x00 = 文件(对应 FILE_ATTRIBUTE_DIRECTORY
uint8_t isDir = S_ISDIR(st.st_mode) ? 0x10 : 0x00;
buf.push_back(isDir);
// 文件名UTF-8 → GBKnull 结尾)
std::string gbkName = utf8ToGbk(ent->d_name);
buf.insert(buf.end(), gbkName.begin(), gbkName.end());
buf.push_back(0);
// 文件大小:高 4 字节 + 低 4 字节
uint64_t fileSize = (uint64_t)st.st_size;
DWORD sizeHigh = (DWORD)(fileSize >> 32);
DWORD sizeLow = (DWORD)(fileSize & 0xFFFFFFFF);
const uint8_t* pH = (const uint8_t*)&sizeHigh;
const uint8_t* pL = (const uint8_t*)&sizeLow;
buf.insert(buf.end(), pH, pH + sizeof(DWORD));
buf.insert(buf.end(), pL, pL + sizeof(DWORD));
// 最后修改时间Windows FILETIME 格式8 字节)
uint64_t ft = unixToFiletime(st.st_mtime);
const uint8_t* pFT = (const uint8_t*)&ft;
buf.insert(buf.end(), pFT, pFT + 8);
}
closedir(dir);
m_client->Send2Server((char*)buf.data(), (ULONG)buf.size());
Mprintf("[FileManager] SendFilesList: %u bytes\n", (unsigned)buf.size());
}
};