Files
SimpleRemoter/linux/FileManager.h

281 lines
9.3 KiB
C
Raw Normal View History

#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());
}
};