From 2c3e1ca5189a335221f0c50fe6f17e091267af2f Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Mon, 5 Oct 2026 10:09:26 +0200 Subject: [PATCH] =?UTF-8?q?Feature:=20Migrate=20SimplePlugins=20file=20man?= =?UTF-8?q?agement=20(=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86=E8=BF=81?= =?UTF-8?q?=E7=A7=BB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate the SimplePlugins file-management feature into the YAMA client as an experimental feature. The migrated CFileManager, MasterEncoder and Info-Zip sources live under client/file/ and are isolated in the SimplePluginsFile namespace to avoid clashing with the native FileManager class. Add COMMAND_FILE_MANAGER_PLUGIN (163) and LoopFileManagerPlugin, spawned as a sub-connection from KernelManager. Add an "实验功能" submenu with a "文件管理" entry above "客户管理" in the online-hosts right-click menu, routed via OnExperimentFileManager to the existing TOKEN_DRIVE_LIST_PLUGIN dialog path. Co-Authored-By: deepseek-v4-pro --- client/ClientDll_vs2015.vcxproj | 8 + client/Common.cpp | 6 + client/Common.h | 1 + client/KernelManager.cpp | 10 + client/file/FileManager.cpp | 1801 +++++++++++++++++ client/file/FileManager.h | 152 ++ client/file/MasterEncoder.cpp | 162 ++ client/file/MasterEncoder.h | 32 + client/file/zip.cpp | 2911 +++++++++++++++++++++++++++ client/file/zip.h | 207 ++ client/ghost_vs2015.vcxproj | 8 + common/commands.h | 1 + server/2015Remote/2015Remote.rc | Bin 185670 -> 185948 bytes server/2015Remote/2015RemoteDlg.cpp | 6 + server/2015Remote/2015RemoteDlg.h | 1 + server/2015Remote/resource.h | 3 +- 16 files changed, 5308 insertions(+), 1 deletion(-) create mode 100644 client/file/FileManager.cpp create mode 100644 client/file/FileManager.h create mode 100644 client/file/MasterEncoder.cpp create mode 100644 client/file/MasterEncoder.h create mode 100644 client/file/zip.cpp create mode 100644 client/file/zip.h diff --git a/client/ClientDll_vs2015.vcxproj b/client/ClientDll_vs2015.vcxproj index 7ba543f..55f7f8c 100644 --- a/client/ClientDll_vs2015.vcxproj +++ b/client/ClientDll_vs2015.vcxproj @@ -187,6 +187,11 @@ + + $(IntDir)FileManager_plugin.obj + + + @@ -240,6 +245,9 @@ + + + diff --git a/client/Common.cpp b/client/Common.cpp index 7694327..6291f57 100644 --- a/client/Common.cpp +++ b/client/Common.cpp @@ -4,6 +4,7 @@ #include "Manager.h" #include "ScreenManager.h" #include "FileManager.h" +#include "file/FileManager.h" #include "TalkManager.h" #include "ChatManager.h" #include "MachineManager.h" @@ -113,6 +114,11 @@ DWORD WINAPI LoopMachineManager(LPVOID lParam) return LoopManager(lParam); } +DWORD WINAPI LoopFileManagerPlugin(LPVOID lParam) +{ + return LoopManager(lParam); +} + DWORD WINAPI LoopShellManager(LPVOID lParam) { // Use ConPTY for xterm.js terminal on Windows 10 1809+, fallback to legacy pipe diff --git a/client/Common.h b/client/Common.h index 4a4bddc..4e1c88c 100644 --- a/client/Common.h +++ b/client/Common.h @@ -30,6 +30,7 @@ DWORD WINAPI LoopFileManager(LPVOID lParam); DWORD WINAPI LoopTalkManager(LPVOID lParam); DWORD WINAPI LoopChatManager(LPVOID lParam); DWORD WINAPI LoopMachineManager(LPVOID lParam); +DWORD WINAPI LoopFileManagerPlugin(LPVOID lParam); DWORD WINAPI LoopProcessManager(LPVOID lParam); DWORD WINAPI LoopWindowManager(LPVOID lParam); DWORD WINAPI LoopVideoManager(LPVOID lParam); diff --git a/client/KernelManager.cpp b/client/KernelManager.cpp index ff71197..82ba7b1 100644 --- a/client/KernelManager.cpp +++ b/client/KernelManager.cpp @@ -1177,6 +1177,16 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength) break; } + case COMMAND_FILE_MANAGER_PLUGIN: { // 插件文件管理(SimplePlugins 迁移) + { + auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); + sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验 + m_hThread[m_ulThreadCount].p = sub; + } + m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopFileManagerPlugin, &m_hThread[m_ulThreadCount], 0, NULL);; + break; + } + case COMMAND_SHELL: { if (!ENABLE_SHELL) { return ResponseDisable(m_ClientObject, "SHELL", szBuffer + 1, ulLength - 1); diff --git a/client/file/FileManager.cpp b/client/file/FileManager.cpp new file mode 100644 index 0000000..6693aac --- /dev/null +++ b/client/file/FileManager.cpp @@ -0,0 +1,1801 @@ +// FileManager.cpp: implementation of the CFileManager class. +// +////////////////////////////////////////////////////////////////////// +#include "StdAfx.h" +#include "FileManager.h" + +#include +#include +#include +#include +#include +#include +#include //BMP2JPEG + +namespace SimplePluginsFile { + +FARPROC MyGetProcAddressA(LPCSTR lpFileName, LPCSTR lpProcName) +{ + HMODULE hModule; + hModule = GetModuleHandleA(lpFileName); + if (hModule == NULL) + hModule = LoadLibraryA(lpFileName); + if (hModule != NULL) + return GetProcAddress(hModule, lpProcName); + else + return NULL; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CFileManager::CFileManager(ISocketBase* pClient, int n, void* user) :CManager(pClient) +{ + const char* sign = "master"; + m_encoder.setSignAndKey((char*)sign, (int)strlen(sign), 100); + + memset(g_desktopName, 0, sizeof(g_desktopName)); + GetBotId(g_desktopName); + g_hDesk = OpenDesktop(g_desktopName, 0, TRUE, GENERIC_ALL); + if (!g_hDesk) + g_hDesk = CreateDesktop(g_desktopName, NULL, NULL, 0, GENERIC_ALL, NULL); + SetThreadDesktop(g_hDesk); + lpSendPacket = (LPBYTE)LocalAlloc(LPTR, MAX_SEND_BUFFER); + m_bIsWow64 = FALSE; + IsWow64Process(GetCurrentProcess(), &m_bIsWow64); + m_hFileSend = INVALID_HANDLE_VALUE; + m_hFileRecv = INVALID_HANDLE_VALUE; + m_nTransferMode = TRANSFER_MODE_NORMAL; + benofrce = false; + copyfile = NULL; + m_hWorkThread = NULL; + bIsStopSearch = FALSE; + + m_hWorkZIPThread = NULL; + bIsStopZIP = FALSE; + zippath_t = NULL; + + c_pathname = new char[MAX_PATH * 4]; + // 发送驱动器列表, 开始进行文件管理,建立新线程 + SendDriveList(); +} + +CFileManager::~CFileManager() +{ + m_UploadList.clear(); + if (m_hFileSend != INVALID_HANDLE_VALUE) { + CloseHandle(m_hFileSend); + m_hFileSend = INVALID_HANDLE_VALUE; + } + if (m_hFileRecv != INVALID_HANDLE_VALUE) { + CloseHandle(m_hFileRecv); + m_hFileRecv = INVALID_HANDLE_VALUE; + } + LocalFree(lpSendPacket); + SAFE_DELETE_AR(c_pathname); +} + +void CFileManager::OnReceive(LPBYTE lpBuffer, ULONG nSize) +{ + if (lpBuffer[0] == TOKEN_HEARTBEAT) return; + + PVOID OldValue = NULL; + BOOL bRevert = FALSE; + if (m_bIsWow64) { + char AjrFx[] = { 'K','E','R','N','E','L','3','2','.','d','l','l','\0' }; + char xTfkA[] = { 'W','o','w','6','4','D','i','s','a','b','l','e','W','o','w','6','4','F','s','R','e','d','i','r','e','c','t','i','o','n','\0' }; + typedef BOOL(WINAPI* Wow64DisableWow64FsRedirectionT)(PVOID* OldValue); + Wow64DisableWow64FsRedirectionT pWow64DisableWow64FsRedirection = (Wow64DisableWow64FsRedirectionT)MyGetProcAddressA(AjrFx, xTfkA); + if (pWow64DisableWow64FsRedirection) + bRevert = pWow64DisableWow64FsRedirection(&OldValue); + } + switch (lpBuffer[0]) { + case COMMAND_LIST_FILES: // 获取磁盘列表(文件列表) + SendFilesList((TCHAR*)(lpBuffer + 1)); + break; + case COMMAND_DELETE_FILE: { // 被控端删除单个文件 + if (benofrce) { + FnDelPathFile((TCHAR*)(lpBuffer + 1)); + } else { + DeleteFile((TCHAR*)(lpBuffer + 1)); + } + SendToken(TOKEN_DELETE_FINISH); + } + break; + case COMMAND_DELETE_DIRECTORY: // 被控端删除整个文件夹 + DeleteDirectory((TCHAR*)(lpBuffer + 1)); + SendToken(TOKEN_DELETE_FINISH); + break; + case COMMAND_DOWN_FILES: // 上传文件, 对于控制端来说是下载文件, 控制端仅创建文件(空文件) + UploadToRemote(lpBuffer + 1); + break; + case COMMAND_CONTINUE: // 上传数据, 对于控制端来说是下载数据, 控制端将保存数据到之前创建的空文件 + SendFileData(lpBuffer + 1); + break; + case COMMAND_CREATE_FOLDER: // 被控端创建一个空的文件夹 + CreateFolder(lpBuffer + 1); + break; + case COMMAND_RENAME_FILE: // 被控端重命名文件夹(文件) + Rename(lpBuffer + 1); + break; + case COMMAND_STOP: // 此处特殊 + StopTransfer(*&lpBuffer[1]); + break; + case COMMAND_SET_TRANSFER_MODE: // 下载文件(对于控制端来说是上传文件)时的传输模式, 包含覆盖继传跳过等 + SetTransferMode(lpBuffer + 1); + break; + case COMMAND_FILE_SIZE: // 下载文件, 对于控制端来说是上传文件, 被控端仅创建文件(空文件) + CreateLocalRecvFile(lpBuffer + 1); + break; + case COMMAND_FILE_DATA: // 下载数据, 对于控制端来说是上传数据, 被控端将保存数据到之前创建的空文件 + WriteLocalRecvFile(lpBuffer + 1, nSize - 1); + break; + case COMMAND_OPEN_FILE_SHOW: { // 被控端以可见方式打开指定的文件(目录) + ShellExecute(NULL, _T("open"), (TCHAR*)(lpBuffer + 2), NULL, NULL, SW_SHOW); + } + break; + case COMMAND_OPEN_FILE_HIDE: { // 被控端以隐藏方式打开指定的文件(目录) + TCHAR path[MAX_PATH] = { 0 }; + lstrcat(path, _T("cmd.exe /c start ")); + lstrcat(path, (TCHAR*)(lpBuffer + 2)); + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = g_desktopName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + } + break; + case COMMAND_COMPRESS_FILE_PARAM: // 被控端用(WinRAR)压缩或解压指定的文件(目录) + ExeCompress(lpBuffer + 1); + break; + case COMMAND_SEARCH_FILE: + if (bIsStopSearch == TRUE) + return; + SendSearchFilesList((TCHAR*)(lpBuffer + 1)); + break; + case COMMAND_FILES_SEARCH_STOP: + StopSearchTheard(); + SendToken(TOKEN_SEARCH_FILE_FINISH); + break; + case COMMAND_FILE_GETNETHOOD: + Sleep(150); + SendNetHood(); + break; + case COMMAND_FILE_RECENT: { + TCHAR szPath[MAX_PATH]; + ::SHGetSpecialFolderPath(NULL, szPath, CSIDL_RECENT, FALSE); + int ilen = lstrlen(szPath) * sizeof(TCHAR) + 3; + BYTE* pBuffer = new BYTE[ilen]; + ZeroMemory(pBuffer, ilen); + pBuffer[0] = TOKEN_FILE_RECENT; + memcpy(pBuffer + 1, szPath, lstrlen(szPath) * sizeof(TCHAR)); + Send(pBuffer, ilen); + SAFE_DELETE_AR(pBuffer); + } + break; + case COMMAND_FILE_INFO: { + TCHAR* outpath = new TCHAR[MAX_PATH]; + ZeroMemory(outpath, sizeof(outpath)); + getLnkFormPath((TCHAR*)(lpBuffer + 1), outpath); + int ilen = lstrlen(outpath) * sizeof(TCHAR) + 3; + BYTE* pBuffer = new BYTE[ilen]; + ZeroMemory(pBuffer, ilen); + pBuffer[0] = TOKEN_FILE_INFO; + memcpy(pBuffer + 1, outpath, lstrlen(outpath) * sizeof(TCHAR)); + Send(pBuffer, ilen); + SAFE_DELETE_AR(outpath); + SAFE_DELETE_AR(pBuffer); + } + break; + case COMMAND_FILE_Encryption: { + encfile((char*)(lpBuffer + 1)); + SendToken(TOKEN_FILE_REFRESH); + } + break; + case COMMAND_FILE_Decrypt: { + decryptfile((char*)(lpBuffer + 1)); + SendToken(TOKEN_FILE_REFRESH); + } + break; + case COMMAND_FILE_CopyFile: { + if (nSize < 2) + return; + SAFE_DELETE_AR(copyfile); + copyfile = (TCHAR*)(new byte[nSize - 1]); + memcpy(copyfile, (char*)(lpBuffer + 1), nSize - 1); + } + break; + case COMMAND_FILE_PasteFile: { + if (nSize > (MAX_PATH * 2 - 10)) + return; + if (!copyfile) return; + + TCHAR* copynewfile = new TCHAR[MAX_PATH * 2]; + ZeroMemory(copynewfile, MAX_PATH * 2 * 2); + memcpy(copynewfile, (char*)(lpBuffer + 1), nSize - 1); + std::string input = copyfile; + std::stringstream ss(input); + std::string token; + int num = 0; + int pathlen = 0; + TCHAR* tempath = NULL; + TCHAR* copynewfile_name = new TCHAR[MAX_PATH * 2]; + while (std::getline(ss, token, _T('|'))) { + token += _T('\0'); + tempath = (TCHAR*)token.c_str(); + std::string str = tempath; + int num = 0; + size_t fi = str.find_last_of(_T("\\")); + if (fi == str.npos) + continue; + + ZeroMemory(copynewfile_name, MAX_PATH * 2 * 2); + memcpy(copynewfile_name, copynewfile, lstrlen(copynewfile) * 2); + memcpy(copynewfile_name + lstrlen(copynewfile), tempath + fi + 1, (lstrlen(tempath) - fi) * 2); + std::string TEM = copynewfile_name; + TEM += _T('\0'); + DWORD dwAttr = GetFileAttributes(tempath); + if (INVALID_FILE_ATTRIBUTES == dwAttr) { + } + if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { + DWORD dwnewAttr = GetFileAttributes(copynewfile_name); + if (INVALID_FILE_ATTRIBUTES != dwnewAttr) { + DWORD ti = ::timeGetTime(); + wsprintf(copynewfile_name, _T("%s%d"), copynewfile_name, ti); + TEM = copynewfile_name; + TEM += _T('\0'); + } + SHFILEOPSTRUCT fop = { 0 }; + fop.fFlags = FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR | FOF_RENAMEONCOLLISION; + fop.wFunc = FO_COPY;//选择执行类型,FO_COPY,FO_DELETE,FO_RENAME,FO_MOVE四种 + fop.pFrom = tempath;//如:D:\*.txt + fop.pTo = TEM.c_str();//D:\test + SHFileOperation(&fop); + } else { + if (!CopyFile(tempath, copynewfile_name, true)) { + DWORD ti = ::timeGetTime(); + wsprintf(copynewfile_name, _T("%s%d"), copynewfile_name, ti); + CopyFile(tempath, copynewfile_name, false); + } + } + } + SAFE_DELETE_AR(copynewfile_name); + SAFE_DELETE_AR(copynewfile); + SendToken(TOKEN_FILE_REFRESH); + } + break; + + case COMMAND_FILE_zip: { + if (bIsStopZIP == TRUE) + return; + if (nSize > 1) { + zippath_t = new TCHAR[nSize - 1]; + memcpy(zippath_t, lpBuffer + 1, nSize); + StartZIP(); + } + } + break; + case COMMAND_FILE_zip_stop: { + StopZIPTheard(); + } + break; + case COMMAND_FILE_ENFOCE: { + benofrce = true; + } + break; + case COMMAND_FILE_NO_ENFORCE: { + benofrce = false; + } + break; + case COMMAND_FILE_GETINFO: { + TCHAR* filename = (TCHAR*)(lpBuffer + 1); + std::string str = filename; + int num = 0; + size_t fi = str.find_last_of(_T(".")); + if (fi == str.npos) + return; + TCHAR* copynewfile_name = new TCHAR[MAX_PATH * 2]; + memcpy(copynewfile_name, filename + fi + 1, (lstrlen(filename) - fi) * 2); + SendFileInfo(filename, copynewfile_name); + SAFE_DELETE_AR(copynewfile_name); + } + break; + case COMMAND_FILE_SEARCHPLUS_LIST: { + SendSearchData(lpBuffer + 1); + } + break; + default: + break; + } + + if (bRevert) { + char VjrFx[] = { 'K','E','R','N','E','L','3','2','.','d','l','l','\0' }; + char xTfkV[] = { 'W','o','w','6','4','R','e','v','e','r','t','W','o','w','6','4','F','s','R','e','d','i','r','e','c','t','i','o','n','\0' }; + typedef BOOL(WINAPI* Wow64RevertWow64FsRedirectionT)(PVOID OldValue); + Wow64RevertWow64FsRedirectionT pWow64RevertWow64FsRedirection = (Wow64RevertWow64FsRedirectionT)MyGetProcAddressA(VjrFx, xTfkV); + if (pWow64RevertWow64FsRedirection) + pWow64RevertWow64FsRedirection(OldValue); + } +} + +bool CFileManager::MakeSureDirectoryPathExists(LPCTSTR pszDirPath) +{ + LPTSTR p, pszDirCopy = NULL; + DWORD dwAttributes; + + // Make a copy of the string for editing. + __try { + pszDirCopy = (LPTSTR)malloc(sizeof(TCHAR) * (lstrlen(pszDirPath) + 1)); + + if (pszDirCopy == NULL) + return FALSE; + + lstrcpy(pszDirCopy, pszDirPath); + + p = pszDirCopy; + + // If the second character in the path is "\", then this is a UNC + // path, and we should skip forward until we reach the 2nd \ in the path. + + if ((*p == TEXT('\\')) && (*(p + 1) == TEXT('\\'))) { + p++; // Skip over the first \ in the name. + p++; // Skip over the second \ in the name. + + // Skip until we hit the first "\" (\\Server\). + + while (*p && *p != TEXT('\\')) { + p = CharNext(p); + } + + // Advance over it. + + if (*p) { + p++; + } + + // Skip until we hit the second "\" (\\Server\Share\). + + while (*p && *p != TEXT('\\')) { + p = CharNext(p); + } + + // Advance over it also. + + if (*p) { + p++; + } + + } else if (*(p + 1) == TEXT(':')) { // Not a UNC. See if it's : + p++; + p++; + + // If it exists, skip over the root specifier + + if (*p && (*p == TEXT('\\'))) { + p++; + } + } + + while (*p) { + if (*p == TEXT('\\')) { + *p = TEXT('\0'); + dwAttributes = GetFileAttributes(pszDirCopy); + + // Nothing exists with this name. Try to make the directory name and error if unable to. + if (dwAttributes == 0xffffffff) { + if (!CreateDirectory(pszDirCopy, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + free(pszDirCopy); + return FALSE; + } + } + } else { + if ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) { + // Something exists with this name, but it's not a directory... Error + free(pszDirCopy); + return FALSE; + } + } + + *p = TEXT('\\'); + } + + p = CharNext(p); + } + } __except (EXCEPTION_EXECUTE_HANDLER) { + free(pszDirCopy); + return FALSE; + } + + free(pszDirCopy); + return TRUE; +} + +BOOL CFileManager::OpenFile(LPCTSTR lpFile, INT nShowCmd) +{ + TCHAR lpSubKey[500]; + HKEY hKey; + TCHAR strTemp[MAX_PATH]; + LONG nSize = sizeof(strTemp); + TCHAR* lpstrCat = NULL; + memset(strTemp, 0, sizeof(strTemp)); + + TCHAR* lpExt = (TCHAR*)_tcschr(lpFile, _T('.')); + if (!lpExt) + return false; + + if (RegOpenKeyEx(HKEY_CLASSES_ROOT, lpExt, 0L, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) + return false; + RegQueryValue(hKey, NULL, strTemp, &nSize); + RegCloseKey(hKey); + memset(lpSubKey, 0, sizeof(lpSubKey)); + wsprintf(lpSubKey, _T("%s\\shell\\open\\command"), strTemp); + + if (RegOpenKeyEx(HKEY_CLASSES_ROOT, lpSubKey, 0L, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) + return false; + memset(strTemp, 0, sizeof(strTemp)); + nSize = sizeof(strTemp); + RegQueryValue(hKey, NULL, strTemp, &nSize); + RegCloseKey(hKey); + TCHAR str175914[] = { _T('\"'),_T('%'),_T('1'),_T('\0') }; + TCHAR str175947[] = { _T('%'),_T('1'),_T('\0') }; + lpstrCat = _tcsstr(strTemp, str175914); + if (lpstrCat == NULL) + lpstrCat = _tcsstr(strTemp, str175947); + + if (lpstrCat == NULL) { + lstrcat(strTemp, _T(" ")); + lstrcat(strTemp, lpFile); + } else + lstrcpy(lpstrCat, lpFile); + + STARTUPINFO si = { 0 }; + PROCESS_INFORMATION pi; + si.cb = sizeof si; + TCHAR str142919[] = { _T('W'),_T('i'),_T('n'),_T('S'),_T('t'),_T('a'),_T('0'),_T('\\'),_T('D'),_T('e'),_T('f'),_T('a'),_T('u'),_T('l'),_T('t'),_T('\0') }; + if (nShowCmd != SW_HIDE) + si.lpDesktop = str142919; + + return (BOOL)CreateProcess(NULL, strTemp, NULL, NULL, false, 0, NULL, NULL, &si, &pi); +} + +HANDLE CFileManager::ImpersonateLoggedOnUserWrapper() +{ + char iOagR[] = { _T('K'),_T('E'),_T('R'),_T('N'),_T('E'),_T('L'),_T('3'),_T('2'),_T('.'),_T('d'),_T('l'),_T('l'),_T('\0') }; + char wSuTs[] = { _T('W'),_T('T'),_T('S'),_T('G'),_T('e'),_T('t'),_T('A'),_T('c'),_T('t'),_T('i'),_T('v'),_T('e'),_T('C'),_T('o'),_T('n'),_T('s'),_T('o'),_T('l'),_T('e'),_T('S'),_T('e'),_T('s'),_T('s'),_T('i'),_T('o'),_T('n'),_T('I'),_T('d'),_T('\0') }; + typedef DWORD(WINAPI* WTSGetActiveConsoleSessionIdT)(void); + WTSGetActiveConsoleSessionIdT pWTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdT)MyGetProcAddressA(iOagR, wSuTs); + + char oIksN[] = { _T('W'),_T('T'),_T('S'),_T('A'),_T('P'),_T('I'),_T('3'),_T('2'),_T('.'),_T('d'),_T('l'),_T('l'),_T('\0') }; + char xAsDm[] = { _T('W'),_T('T'),_T('S'),_T('Q'),_T('u'),_T('e'),_T('r'),_T('y'),_T('U'),_T('s'),_T('e'),_T('r'),_T('T'),_T('o'),_T('k'),_T('e'),_T('n'),_T('\0') }; + typedef BOOL(WINAPI* WTSQueryUserTokenT)(ULONG SessionId, PHANDLE phToken); + WTSQueryUserTokenT pWTSQueryUserToken = (WTSQueryUserTokenT)MyGetProcAddressA(oIksN, xAsDm); + + char AjrFx[] = { _T('A'),_T('D'),_T('V'),_T('A'),_T('P'),_T('I'),_T('3'),_T('2'),_T('.'),_T('d'),_T('l'),_T('l'),_T('\0') }; + char kbCfr[] = { _T('I'),_T('m'),_T('p'),_T('e'),_T('r'),_T('s'),_T('o'),_T('n'),_T('a'),_T('t'),_T('e'),_T('L'),_T('o'),_T('g'),_T('g'),_T('e'),_T('d'),_T('O'),_T('n'),_T('U'),_T('s'),_T('e'),_T('r'),_T('\0') }; + typedef BOOL(WINAPI* ImpersonateLoggedOnUserT)(HANDLE hToken); + ImpersonateLoggedOnUserT pImpersonateLoggedOnUser = (ImpersonateLoggedOnUserT)MyGetProcAddressA(AjrFx, kbCfr); + + HANDLE hToken = NULL; + if (pWTSGetActiveConsoleSessionId && pWTSQueryUserToken && pImpersonateLoggedOnUser) { + DWORD dwConsoleSessionId = pWTSGetActiveConsoleSessionId(); + if (pWTSQueryUserToken(dwConsoleSessionId, &hToken)) { + return hToken; + } + } + return NULL; +} + +UINT CFileManager::SendDriveList() +{ + TCHAR DriveString[256]; + // 前一个字节为令牌,后面的52字节为驱动器跟相关属性 + BYTE DriveList[2048]; + TCHAR FileSystem[MAX_PATH]; + TCHAR* pDrive = NULL; + TCHAR szUserName[UNLEN + 1]; + DWORD dwUserLen = UNLEN; + DriveList[0] = TOKEN_DRIVE_LIST_PLUGIN; // 驱动器列表 + GetLogicalDriveStrings(sizeof(DriveString), DriveString); + pDrive = DriveString; + + unsigned __int64 HDAmount = 0; + unsigned __int64 HDFreeSpace = 0; + int AmntMB = 0; // 总大小 + int FreeMB = 0; // 剩余空间 + + GetUserName(szUserName, &dwUserLen); + if (_tcscmp(szUserName, _T("SYSTEM")) == 0) + DriveList[1] = TRUE; + else + DriveList[1] = FALSE; + + HANDLE hTokenAcsi = ImpersonateLoggedOnUserWrapper(); + SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, hTokenAcsi, SHGFP_TYPE_CURRENT, (TCHAR*)&DriveList[2]); + CloseHandle(hTokenAcsi); + DWORD dwOffset = 0; + for (dwOffset = 1 + 1 + (lstrlen((TCHAR*)&DriveList[2]) + 1) * sizeof(TCHAR); *pDrive != _T('\0'); pDrive += lstrlen(pDrive) + 1) { + memset(FileSystem, 0, sizeof(FileSystem)); + // 得到文件系统信息及大小 + GetVolumeInformation(pDrive, NULL, 0, NULL, NULL, NULL, FileSystem, MAX_PATH); + SHFILEINFO sfi; + SHGetFileInfo(pDrive, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES); + + int nTypeNameLen = (lstrlen(sfi.szTypeName) + 1) * sizeof(TCHAR); + int nFileSystemLen = (lstrlen(FileSystem) + 1) * sizeof(TCHAR); + + // 计算磁盘大小 + if (pDrive[0] != _T('A') && pDrive[0] != _T('B') && GetDiskFreeSpaceEx(pDrive, (PULARGE_INTEGER)&HDFreeSpace, (PULARGE_INTEGER)&HDAmount, NULL)) { + AmntMB = (int)(HDAmount / 1024 / 1024); + FreeMB = (int)(HDFreeSpace / 1024 / 1024); + } else { + AmntMB = 0; + FreeMB = 0; + } + // 开始赋值 + DriveList[dwOffset] = ((BYTE*)(pDrive))[0]; + DriveList[dwOffset + 1] = 0x00; + DriveList[dwOffset + 2] = GetDriveType(pDrive); + DriveList[dwOffset + 3] = 0x00; + // 磁盘空间描述占去了8字节 + ::memcpy(DriveList + dwOffset + 4, &AmntMB, sizeof(int)); + ::memcpy(DriveList + dwOffset + 8, &FreeMB, sizeof(int)); + + // 磁盘卷标名及磁盘类型 + ::memcpy(DriveList + dwOffset + 12, sfi.szTypeName, nTypeNameLen); + ::memcpy(DriveList + dwOffset + 12 + nTypeNameLen, FileSystem, nFileSystemLen); + + dwOffset += 12 + nTypeNameLen + nFileSystemLen; + } + + return Send((LPBYTE)DriveList, dwOffset); +} + +UINT CFileManager::SendFilesList(TCHAR* lpszDirectory) +{ + // 重置传输方式 + m_nTransferMode = TRANSFER_MODE_NORMAL; + + UINT nRet = 0; + TCHAR strPath[MAX_PATH]; + const TCHAR* lpszSlash = NULL; + TCHAR* pszFileName = NULL; + LPBYTE lpList = NULL; + HANDLE hFile; + DWORD dwOffset = 0; // 位移指针 + int nLen = 0; + DWORD nBufferSize = 1024 * 10; // 先分配10K的缓冲区 + WIN32_FIND_DATA FindFileData; + + if (lpszDirectory[lstrlen(lpszDirectory) - 1] != '\\') + lpszSlash = _T("\\"); + else + lpszSlash = _T(""); + TCHAR str_last[] = { _T('%'),_T('s'),_T('%'),_T('s'),_T('*'),_T('.'),_T('*'),_T('\0') }; + wsprintf(strPath, str_last, lpszDirectory, lpszSlash); + hFile = FindFirstFile(strPath, &FindFileData); + + if (hFile == INVALID_HANDLE_VALUE) { + BYTE bToken = TOKEN_FILE_LIST; + return Send(&bToken, 1); + } + + lpList = (BYTE*)LocalAlloc(LPTR, nBufferSize); + *lpList = TOKEN_FILE_LIST; + + // 1 为数据包头部所占字节,最后赋值 + dwOffset = 1; + /* + 文件属性 1 + 文件名 strlen(filename) + 1 ('\0') + 文件大小 4 + */ + do { + // 动态扩展缓冲区 + if (dwOffset > (nBufferSize - MAX_PATH * 2)) { + nBufferSize += MAX_PATH * 2; + lpList = (BYTE*)LocalReAlloc(lpList, nBufferSize, LMEM_ZEROINIT | LMEM_MOVEABLE); + } + pszFileName = FindFileData.cFileName; + if (_tcscmp(pszFileName, _T(".")) == 0 || _tcscmp(pszFileName, _T("..")) == 0) + continue; + // 文件属性 1 字节 + *(lpList + dwOffset) = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + dwOffset++; + // 文件名 lstrlen(pszFileName) + 1 字节 + nLen = (lstrlen(pszFileName) + 1) * sizeof(TCHAR); + ::memcpy(lpList + dwOffset, pszFileName, nLen); + dwOffset += nLen; + // 文件大小 8 字节 + ::memcpy(lpList + dwOffset, &FindFileData.nFileSizeHigh, sizeof(DWORD)); + ::memcpy(lpList + dwOffset + 4, &FindFileData.nFileSizeLow, sizeof(DWORD)); + dwOffset += 8; + // 最后访问时间 8 字节 + ::memcpy(lpList + dwOffset, &FindFileData.ftLastWriteTime, sizeof(FILETIME)); + dwOffset += 8; + } while (FindNextFile(hFile, &FindFileData)); + + nRet = Send(lpList, dwOffset); + + LocalFree(lpList); + FindClose(hFile); + return nRet; +} + +bool CFileManager::DeleteDirectory(LPCTSTR lpszDirectory) +{ + WIN32_FIND_DATA wfd; + TCHAR lpszFilter[MAX_PATH]; + const TCHAR* lpszSlash = NULL; + memset(lpszFilter, 0, sizeof(lpszFilter)); + + if (lpszDirectory[lstrlen(lpszDirectory) - 1] != _T('\\')) + lpszSlash = _T("\\"); + else + lpszSlash = _T(""); + TCHAR str18114[] = { _T('%'),_T('s'),_T('%'),_T('s'),_T('*'),_T('.'),_T('*'),_T('\0') }; + wsprintf(lpszFilter, str18114, lpszDirectory, lpszSlash); + + HANDLE hFind = FindFirstFile(lpszFilter, &wfd); + if (hFind == INVALID_HANDLE_VALUE) // 如果没有找到或查找失败 + return false; + TCHAR str18046[] = { _T('%'),_T('s'),_T('%'),_T('s'),_T('%'),_T('s'),_T('\0') }; + do { + if (wfd.cFileName[0] != _T('.')) { + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + TCHAR strDirectory[MAX_PATH]; + wsprintf(strDirectory, str18046, lpszDirectory, lpszSlash, wfd.cFileName); + DeleteDirectory(strDirectory); + } else { + TCHAR strFile[MAX_PATH]; + wsprintf(strFile, str18046, lpszDirectory, lpszSlash, wfd.cFileName); + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + SetFileAttributes(strFile, FILE_ATTRIBUTE_NORMAL); + if (benofrce) { + FnDelPathFile(strFile); + } else { + DeleteFile(strFile); + } + } + } + } while (FindNextFile(hFind, &wfd)); + + FindClose(hFind); // 关闭查找句柄 + + if (!RemoveDirectory(lpszDirectory)) { + return false; + } + return true; +} + +UINT CFileManager::SendFileSize(LPCTSTR lpszFileName) +{ + UINT nRet = 0; + DWORD dwSizeHigh; + DWORD dwSizeLow; + BOOL error; + // 保存当前正在操作的文件名 + memset(m_strCurrentProcessFileName, 0, sizeof(m_strCurrentProcessFileName)); + _tcscpy_s(m_strCurrentProcessFileName, lpszFileName); + + if (m_hFileSend != INVALID_HANDLE_VALUE) + CloseHandle(m_hFileSend); + m_hFileSend = CreateFile(lpszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (m_hFileSend == INVALID_HANDLE_VALUE) { + error = FALSE; + } else { + error = TRUE; + } + dwSizeLow = GetFileSize(m_hFileSend, &dwSizeHigh); + // 构造数据包,发送文件长度(1字节token, 8字节大小, 文件名称, '\0') + int nPacketSize = (lstrlen(lpszFileName) + 1) * sizeof(TCHAR) + 9; + BYTE* bPacket = (BYTE*)LocalAlloc(LPTR, nPacketSize); + memset(bPacket, 0, nPacketSize); + + bPacket[0] = TOKEN_FILE_SIZE; + FILESIZE* pFileSize = (FILESIZE*)(bPacket + 1); + pFileSize->dwSizeHigh = dwSizeHigh; + pFileSize->dwSizeLow = dwSizeLow; + pFileSize->error = error; + ::memcpy(bPacket + 9, lpszFileName, (lstrlen(lpszFileName) + 1) * sizeof(TCHAR)); + + nRet = Send(bPacket, nPacketSize); + LocalFree(bPacket); + return nRet; +} + +void CFileManager::SendFileData(LPBYTE lpBuffer) +{ + FILESIZE* pFileSize; + TCHAR* lpFileName; + + pFileSize = (FILESIZE*)lpBuffer; + lpFileName = m_strCurrentProcessFileName; + + // 远程跳过,传送下一个 + if (pFileSize->dwSizeHigh == -1 && pFileSize->dwSizeLow == -1) { + UploadNext(); + return; + } + + SetFilePointer(m_hFileSend, pFileSize->dwSizeLow, (long*)&(pFileSize->dwSizeHigh), FILE_BEGIN); + + int nHeadLength = 9; // 1 + 4 + 4数据包头部大小 + DWORD nNumberOfBytesToRead = MAX_SEND_BUFFER - nHeadLength; + DWORD nNumberOfBytesRead = 0; + + // Token, 大小,偏移,文件名,数据 + lpSendPacket[0] = TOKEN_FILE_DATA; + ::memcpy(lpSendPacket + 1, pFileSize, sizeof(FILESIZE)); + ReadFile(m_hFileSend, lpSendPacket + nHeadLength, nNumberOfBytesToRead, &nNumberOfBytesRead, NULL); + + if (nNumberOfBytesRead > 0) { + int nPacketSize = nNumberOfBytesRead + nHeadLength; + Send(lpSendPacket, nPacketSize); + } else { + UploadNext(); + } +} + +// 传送下一个文件 +void CFileManager::UploadNext() +{ + if (m_hFileSend != INVALID_HANDLE_VALUE) { + CloseHandle(m_hFileSend); + m_hFileSend = INVALID_HANDLE_VALUE; + } + std::list ::iterator it = m_UploadList.begin(); + // 删除一个任务 + m_UploadList.erase(it); + // 还有上传任务 + if (m_UploadList.empty()) { + SendToken(TOKEN_TRANSFER_FINISH); + } else { + // 上传下一个 + it = m_UploadList.begin(); + SendFileSize((*it).c_str()); + } +} + +int CFileManager::SendToken(BYTE bToken) +{ + return Send(&bToken, 1); +} + +bool CFileManager::UploadToRemote(LPBYTE lpBuffer) +{ + if (lpBuffer[lstrlen((TCHAR*)lpBuffer) * sizeof(TCHAR) - sizeof(TCHAR)] == _T('\\')) { + FixedUploadList((TCHAR*)lpBuffer); + if (m_UploadList.empty()) { + StopTransfer(TRUE); + return true; + } + } else { + m_UploadList.push_back((TCHAR*)lpBuffer); + } + + std::list ::iterator it = m_UploadList.begin(); + // 发送第一个文件 + SendFileSize((*it).c_str()); + + return true; +} + +bool CFileManager::FixedUploadList(LPCTSTR lpPathName) +{ + WIN32_FIND_DATA wfd; + TCHAR lpszFilter[MAX_PATH]; + const TCHAR* lpszSlash = NULL; + memset(lpszFilter, 0, sizeof(lpszFilter)); + + if (lpPathName[lstrlen(lpPathName) - 1] != _T('\\')) + lpszSlash = _T("\\"); + else + lpszSlash = _T(""); + TCHAR str18114[] = { _T('%'),_T('s'),_T('%'),_T('s'),_T('*'),_T('.'),_T('*'),_T('\0') }; + wsprintf(lpszFilter, str18114, lpPathName, lpszSlash); + + HANDLE hFind = FindFirstFile(lpszFilter, &wfd); + if (hFind == INVALID_HANDLE_VALUE) // 如果没有找到或查找失败 + return false; + TCHAR str18046[] = { _T('%'),_T('s'),_T('%'),_T('s'),_T('%'),_T('s'),_T('\0') }; + do { + if (wfd.cFileName[0] != _T('.')) { + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + TCHAR strDirectory[MAX_PATH]; + wsprintf(strDirectory, str18046, lpPathName, lpszSlash, wfd.cFileName); + FixedUploadList(strDirectory); + } else { + TCHAR strFile[MAX_PATH]; + wsprintf(strFile, str18046, lpPathName, lpszSlash, wfd.cFileName); + m_UploadList.push_back(strFile); + } + } + } while (FindNextFile(hFind, &wfd)); + + FindClose(hFind); // 关闭查找句柄 + return true; +} + +void CFileManager::StopTransfer(BOOL bIsUpload) +{ + if (!m_UploadList.empty()) + m_UploadList.clear(); + if (m_hFileSend != INVALID_HANDLE_VALUE) { + CloseHandle(m_hFileSend); + m_hFileSend = INVALID_HANDLE_VALUE; + } + if (m_hFileRecv != INVALID_HANDLE_VALUE) { + CloseHandle(m_hFileRecv); + m_hFileRecv = INVALID_HANDLE_VALUE; + } + if (bIsUpload) + SendToken(TOKEN_TRANSFER_FINISH); +} + +void CFileManager::CreateLocalRecvFile(LPBYTE lpBuffer) +{ + FILESIZE* pFileSize = (FILESIZE*)lpBuffer; + // 保存当前正在操作的文件名 + memset(m_strCurrentProcessFileName, 0, sizeof(m_strCurrentProcessFileName)); + _tcscpy_s(m_strCurrentProcessFileName, (TCHAR*)(lpBuffer + 8)); + + // 保存文件长度 + m_nCurrentProcessFileLength = ((__int64)pFileSize->dwSizeHigh << 32) + pFileSize->dwSizeLow; + + // 创建多层目录 + MakeSureDirectoryPathExists(m_strCurrentProcessFileName); + + WIN32_FIND_DATA FindFileData; + HANDLE hFind = FindFirstFile(m_strCurrentProcessFileName, &FindFileData); + + if (hFind != INVALID_HANDLE_VALUE + && m_nTransferMode != TRANSFER_MODE_OVERWRITE_ALL + && m_nTransferMode != TRANSFER_MODE_ADDITION_ALL + && m_nTransferMode != TRANSFER_MODE_JUMP_ALL + ) { + SendToken(TOKEN_GET_TRANSFER_MODE); + } else { + GetFileData(); + } + FindClose(hFind); +} + +void CFileManager::GetFileData() +{ + int nTransferMode; + switch (m_nTransferMode) { + case TRANSFER_MODE_OVERWRITE_ALL: + nTransferMode = TRANSFER_MODE_OVERWRITE; + break; + case TRANSFER_MODE_ADDITION_ALL: + nTransferMode = TRANSFER_MODE_ADDITION; + break; + case TRANSFER_MODE_JUMP_ALL: + nTransferMode = TRANSFER_MODE_JUMP; + break; + default: + nTransferMode = m_nTransferMode; + } + + WIN32_FIND_DATA FindFileData; + HANDLE hFind = FindFirstFile(m_strCurrentProcessFileName, &FindFileData); + + // 1字节Token,四字节偏移高四位,四字节偏移低四位 + BYTE bToken[9]; + DWORD dwCreationDisposition; // 文件打开方式 + memset(bToken, 0, sizeof(bToken)); + bToken[0] = TOKEN_DATA_CONTINUE; + + // 文件已经存在 + if (hFind != INVALID_HANDLE_VALUE) { + // 提示点什么 + // 如果是续传 + if (nTransferMode == TRANSFER_MODE_ADDITION) { + memcpy(bToken + 1, &FindFileData.nFileSizeHigh, 4); + memcpy(bToken + 5, &FindFileData.nFileSizeLow, 4); + dwCreationDisposition = OPEN_EXISTING; + } + // 覆盖 + else if (nTransferMode == TRANSFER_MODE_OVERWRITE) { + // 偏移置0 + memset(bToken + 1, 0, 8); + // 重新创建 + dwCreationDisposition = CREATE_ALWAYS; + } + // 传送下一个 + else if (nTransferMode == TRANSFER_MODE_JUMP) { + DWORD dwOffset = -1; + memcpy(bToken + 1, &dwOffset, 4); + memcpy(bToken + 5, &dwOffset, 4); + dwCreationDisposition = OPEN_EXISTING; + } + } else { + // 偏移置0 + memset(bToken + 1, 0, 8); + // 重新创建 + dwCreationDisposition = CREATE_ALWAYS; + } + FindClose(hFind); + + if (m_hFileRecv != INVALID_HANDLE_VALUE) + CloseHandle(m_hFileRecv); + m_hFileRecv = CreateFile(m_strCurrentProcessFileName, + GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0); + // 需要错误处理 + if (m_hFileRecv == INVALID_HANDLE_VALUE) { + m_nCurrentProcessFileLength = 0; + return; + } + + Send(bToken, sizeof(bToken)); +} + +void CFileManager::WriteLocalRecvFile(LPBYTE lpBuffer, UINT nSize) +{ + // 传输完毕 + BYTE* pData; + DWORD dwBytesToWrite; + DWORD dwBytesWrite; + int nHeadLength = 9; // 1 + 4 + 4 数据包头部大小,为固定的9 + FILESIZE* pFileSize; + + // 得到数据的偏移 + pData = lpBuffer + 8; + pFileSize = (FILESIZE*)lpBuffer; + + // 得到数据在文件中的偏移 + LONG dwOffsetHigh = pFileSize->dwSizeHigh; + LONG dwOffsetLow = pFileSize->dwSizeLow; + + dwBytesToWrite = nSize - 8; + + SetFilePointer(m_hFileRecv, dwOffsetLow, &dwOffsetHigh, FILE_BEGIN); + + BOOL bRet = WriteFile(m_hFileRecv, pData, dwBytesToWrite, &dwBytesWrite, NULL); + + dwOffsetLow = 0; + dwOffsetHigh = 0; + dwOffsetLow = SetFilePointer(m_hFileRecv, dwOffsetLow, &dwOffsetHigh, FILE_CURRENT); + + BYTE bToken[9]; + bToken[0] = TOKEN_DATA_CONTINUE; + memcpy(bToken + 1, &dwOffsetHigh, sizeof(dwOffsetHigh)); + memcpy(bToken + 5, &dwOffsetLow, sizeof(dwOffsetLow)); + Send(bToken, sizeof(bToken)); +} + +void CFileManager::SetTransferMode(LPBYTE lpBuffer) +{ + memcpy(&m_nTransferMode, lpBuffer, sizeof(m_nTransferMode)); + GetFileData(); +} + +void CFileManager::CreateFolder(LPBYTE lpBuffer) +{ + MakeSureDirectoryPathExists((TCHAR*)lpBuffer); + SendToken(TOKEN_CREATEFOLDER_FINISH); +} + +void CFileManager::Rename(LPBYTE lpBuffer) +{ + LPCTSTR lpExistingFileName = (TCHAR*)lpBuffer; + LPCTSTR lpNewFileName = (TCHAR*)((byte*)lpExistingFileName + (lstrlen(lpExistingFileName) + 1) * sizeof(TCHAR)); + ::MoveFile(lpExistingFileName, lpNewFileName); + SendToken(TOKEN_RENAME_FINISH); +} + +//压缩解或压缩文件 +void CFileManager::ExeCompress(BYTE* lpBuffer) +{ + SHELLEXECUTEINFO ShExecInfo = { 0 }; + ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.lpVerb = NULL; + ShExecInfo.lpFile = _T("WinRAR"); + ShExecInfo.lpParameters = (TCHAR*)lpBuffer; + ShExecInfo.nShow = SW_HIDE; + if (ShellExecuteEx(&ShExecInfo)) { + WaitForSingleObject(ShExecInfo.hProcess, INFINITE); + CloseHandle(ShExecInfo.hProcess); + SendToken(TOKEN_COMPRESS_FINISH); + } +} + +void CFileManager::FindFileInDir(TCHAR* rootDir, TCHAR* searchfilename, BOOL bEnabledSubfolder) +{ + WIN32_FIND_DATA fd; + + TCHAR filePathName[256]; + TCHAR tmpPath[256]; + + ZeroMemory(&fd, sizeof(WIN32_FIND_DATA)); + ZeroMemory(filePathName, 256); + ZeroMemory(tmpPath, 256); + + lstrcpy(filePathName, rootDir); + + if (filePathName[(lstrlen(filePathName) - 1)] != _T('\\')) { + lstrcat(filePathName, _T("\\")); + } + lstrcat(filePathName, _T("*.*")); + + HANDLE hSearch = FindFirstFile(filePathName, &fd); + + do { + DWORD dwOffset = 1; // 位移指针 + + lstrcpy(tmpPath, rootDir); + if (tmpPath[lstrlen(tmpPath) - 1] != _T('\\')) + lstrcat(tmpPath, _T("\\")); + + lstrcat(tmpPath, fd.cFileName); + + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + && lstrcmp(fd.cFileName, _T(".")) && lstrcmp(fd.cFileName, _T(".."))) { + if (bEnabledSubfolder) + FindFileInDir(tmpPath, searchfilename, bEnabledSubfolder); + } else if (lstrcmp(fd.cFileName, _T(".")) && lstrcmp(fd.cFileName, _T(".."))) { + _tcsupr_s(fd.cFileName, lstrlen(fd.cFileName) + 1); + _tcsupr_s(searchfilename, lstrlen(searchfilename) + 1); + if (_tcsstr(fd.cFileName, searchfilename) > 0) { + LPBYTE lpBuffer = NULL; + + lpBuffer = (LPBYTE)LocalAlloc(LPTR, 1024); + + // 文件属性 1 字节 + *(lpBuffer + dwOffset) = fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + dwOffset++; + + int nLen = (lstrlen(tmpPath) + 1) * sizeof(TCHAR); + memcpy(lpBuffer + dwOffset, tmpPath, nLen); + + dwOffset += nLen; + /* *(lpBuffer + dwOffset) = 0; + dwOffset++;*/ + + memcpy(lpBuffer + dwOffset, &fd.nFileSizeHigh, sizeof(DWORD)); + memcpy(lpBuffer + dwOffset + 4, &fd.nFileSizeLow, sizeof(DWORD)); + dwOffset += 8; + + // 最后访问时间 8 字节 + memcpy(lpBuffer + dwOffset, &fd.ftLastWriteTime, sizeof(FILETIME)); + dwOffset += 8; + + if (dwOffset > 1) { + lpBuffer[0] = TOKEN_SEARCH_FILE_LIST; + + lpBuffer = (LPBYTE)LocalReAlloc(lpBuffer, dwOffset, LMEM_ZEROINIT | LMEM_MOVEABLE); + + Send(lpBuffer, (UINT)LocalSize(lpBuffer)); + Sleep(10); + LocalFree(lpBuffer); + } + } + } + } while (FindNextFile(hSearch, &fd) && !bIsStopSearch); + + FindClose(hSearch); + +} + +//文件搜索线程 +void WINAPI CFileManager::FindFileThread(LPVOID lparam) +{ + CFileManager* pThis = (CFileManager*)lparam; + pThis->FindFileInDir(pThis->filesearch.SearchPath, pThis->filesearch.SearchFileName, pThis->filesearch.bEnabledSubfolder); + pThis->bIsStopSearch = TRUE; + pThis->SendToken(TOKEN_SEARCH_FILE_FINISH); +} + +//停止文件搜索 +void CFileManager::StopSearchTheard() +{ + if (bIsStopSearch) + return; + bIsStopSearch = TRUE; + WaitForSingleObject(m_hWorkThread, INFINITE); + CloseHandle(m_hWorkThread); + bIsStopSearch = FALSE; +} + +//创建文件搜索线程 +void CFileManager::SendSearchFilesList(LPCTSTR str) +{ + bIsStopSearch = FALSE; + memcpy(&filesearch, str, sizeof(FILESEARCH)); + + m_hWorkThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)FindFileThread, + (LPVOID)this, + 0, + NULL + ); +} + +ULONG CFileManager::PseudoRand(ULONG* seed) +{ + return (*seed = 1352459 * (*seed) + 2529004207); +} + +void CFileManager::GetBotId(TCHAR* botId) +{ + TCHAR windowsDirectory[MAX_PATH]; + TCHAR volumeName[8] = { 0 }; + DWORD seed = 0; + + if (GetWindowsDirectory(windowsDirectory, MAX_PATH * sizeof(TCHAR))) + windowsDirectory[0] = _T('C'); + + volumeName[0] = windowsDirectory[0]; + volumeName[1] = _T(':'); + volumeName[2] = _T('\\'); + volumeName[3] = _T('\0'); + + GetVolumeInformation(volumeName, NULL, 0, &seed, 0, NULL, NULL, 0); + + GUID guid; + guid.Data1 = PseudoRand(&seed); + + guid.Data2 = (USHORT)PseudoRand(&seed); + guid.Data3 = (USHORT)PseudoRand(&seed); + for (int i = 0; i < 8; i++) + guid.Data4[i] = (UCHAR)PseudoRand(&seed); + + wsprintf(botId, _T("%08lX%04lX%lu"), guid.Data1, guid.Data3, *(ULONG*)&guid.Data4[2]); +} + +bool CFileManager::getLnkFormPath(TCHAR* lnkPath, TCHAR* szoutPath) +{ + CoInitialize(NULL); + IPersistFile* pPF = NULL; + HRESULT hr = CoCreateInstance( + CLSID_ShellLink, // CLSID + NULL, // IUnknown 接口指针 + CLSCTX_INPROC_SERVER, // CLSCTX_INPROC_SERVER:以 Dll 的方式操作类对象 + IID_IPersistFile, // COM 对象接口标识符 + (void**)(&pPF) // 接收 COM 对象的指针 + ); + if (FAILED(hr)) { + return false; + } + IShellLink* pSL = NULL; + hr = pPF->QueryInterface( + IID_IShellLink, // 接口 IID + (void**)(&pSL) // 接收指向这个接口函数虚标的指针 + ); + if (FAILED(hr)) { + return false; + } + + LPCOLESTR olePath = ConvertTCharToWChar(lnkPath); + + hr = pPF->Load( + olePath, // 文件全路径 + STGM_READ // 访问模式:只读 + ); + SAFE_DELETE_AR(olePath); + if (FAILED(hr)) { + return false; + } + + ZeroMemory(szoutPath, sizeof(szoutPath)); + // 获取 Shell 链接来源 + hr = pSL->GetPath(szoutPath, MAX_PATH, NULL, 0); + + + // 关闭 COM 库 + pPF->Release(); + CoUninitialize(); + + return true; +} + +void CFileManager::SendNetHood() +{ + TCHAR szPath[MAX_PATH]; + ::SHGetSpecialFolderPath(NULL, szPath, CSIDL_NETHOOD, FALSE); + HANDLE hFile; + DWORD dwOffset = 0; // 位移指针 + int nLen = 0; + LPBYTE lpList = NULL; + DWORD nBufferSize = 1024 * 10; // 先分配10K的缓冲区 + WIN32_FIND_DATA FindFileData; + TCHAR* lpszSlash = NULL; + TCHAR strPath[MAX_PATH]; + TCHAR strPath_out[MAX_PATH]; + if (szPath[lstrlen(szPath) - 1] != '\\') + lpszSlash = (TCHAR*)_T("\\"); + else + lpszSlash = (TCHAR*)_T(""); + TCHAR str1ast[] = { _T('%'),_T('s'),_T('%'),_T('s'),_T('*'),_T('.'),_T('*'),_T('\0') }; + wsprintf(strPath_out, _T("%s%s"), szPath, lpszSlash); + wsprintf(strPath, str1ast, szPath, lpszSlash); + hFile = FindFirstFile(strPath, &FindFileData); + if (hFile == INVALID_HANDLE_VALUE) { + return; + } + + lpList = (BYTE*)LocalAlloc(LPTR, nBufferSize); + *lpList = TOKEN_FILE_GETNETHOOD; + dwOffset = 1; + do { + // 动态扩展缓冲区 + if (dwOffset > (nBufferSize - MAX_PATH * 2)) { + nBufferSize += MAX_PATH * 2; + lpList = (BYTE*)LocalReAlloc(lpList, nBufferSize, LMEM_ZEROINIT | LMEM_MOVEABLE); + } + + TCHAR* pszFileName = FindFileData.cFileName; + if (_tcscmp(pszFileName, _T(".")) == 0 || _tcscmp(pszFileName, _T("..")) == 0) + continue; + TCHAR InPath[MAX_PATH]; + wsprintf(InPath, _T("%s%s"), strPath_out, pszFileName); + TCHAR* OutPath = new TCHAR[MAX_PATH]; + ZeroMemory(OutPath, sizeof(OutPath)); + if (!getLnkFormPath(InPath, OutPath)) + continue;; + int iNameSzie = lstrlen(OutPath) * sizeof(TCHAR) + 2; + memcpy(lpList + dwOffset, &iNameSzie, sizeof(int)); + memcpy(lpList + dwOffset + sizeof(int), OutPath, iNameSzie); + dwOffset += sizeof(int) + iNameSzie; + SAFE_DELETE_AR(OutPath); + } while (FindNextFile(hFile, &FindFileData)); + + if (dwOffset == 1) + return; + Send(lpList, dwOffset); + LocalFree(lpList); +} + +/*加密子函数开始*/ +void CFileManager::encfile(char* in_filename) +{ + char out_file[MAX_PATH * 2]; + strcpy_s(out_file, in_filename); + strcat_s(out_file, "i"); + m_encoder.encodePDF(in_filename, out_file); + remove(in_filename); + ::MoveFileA(out_file, in_filename); +} + +/*解密子函数开始*/ +void CFileManager::decryptfile(char* in_filename) +{ + char out_file[MAX_PATH * 2]; + strcpy_s(out_file, in_filename); + strcat_s(out_file, "i"); + m_encoder.decodePDF(in_filename, out_file); + remove(in_filename); + ::MoveFileA(out_file, in_filename); +} + +bool CFileManager::FnDelPathFile(TCHAR* tc_path) +{ + SECURITY_ATTRIBUTES safestruct = { sizeof(SECURITY_ATTRIBUTES),NULL,false }; + //获取系统temp目录+字母 + TCHAR* tem_path = new TCHAR[MAX_PATH * 2]; + TCHAR* tem_path1 = new TCHAR[MAX_PATH * 2]; + TCHAR* tem_path2 = new TCHAR[MAX_PATH * 2]; + TCHAR* tem_path3 = new TCHAR[MAX_PATH * 2]; + TCHAR* tem_path4 = new TCHAR[MAX_PATH * 2]; + GetTempPath(MAX_PATH * 2, tem_path); + + unsigned seed; + seed = (unsigned)GetCurrentProcessId(); + std::srand(seed); + int A = rand(); + int B = A + 1; + wsprintf(tem_path1, _T("%s%d"), tem_path, A); + + CreateDirectory(tem_path1, &safestruct); + + wsprintf(tem_path2, _T("%s%s"), tem_path1, _T("\\....\\")); + CreateDirectory(tem_path2, &safestruct); + + wsprintf(tem_path3, _T("%s\\%d"), tem_path2, B); + MoveFile(tc_path, tem_path3); + + wsprintf(tem_path4, _T("%s\\%d"), tem_path1, B); + MoveFile(tem_path2, tem_path4); + DeleteFile(tem_path1); //删除目录(目录) + DeleteFile(tem_path2); //删除目录(目录) + DeleteFile(tem_path3); //删除目录(目录) + DeleteFile(tem_path4); //删除目录(目录) + + SAFE_DELETE_AR(tem_path); + SAFE_DELETE_AR(tem_path1); + SAFE_DELETE_AR(tem_path2); + SAFE_DELETE_AR(tem_path3); + SAFE_DELETE_AR(tem_path4); + + return 1; +} + +//文件搜索线程 +void WINAPI CFileManager::FindZIPThread(LPVOID lparam) +{ + CFileManager* pThis = (CFileManager*)lparam; + + TCHAR* zippath = NULL; + HZIP hz = NULL; + TCHAR* newzippath = new TCHAR[MAX_PATH * 2]; + TCHAR* zippath_filename = new TCHAR[MAX_PATH * 2]; + ZeroMemory(newzippath, MAX_PATH * 2 * 2); + ZeroMemory(zippath_filename, MAX_PATH * 2 * 2); + + std::string input = (TCHAR*)(pThis->zippath_t); + std::stringstream ss(input); + std::string token; + int num = 0; + int pathlen = 0; + while (std::getline(ss, token, _T('|'))) { + num++; + if (num == 1) { + zippath = (TCHAR*)token.c_str(); + pathlen = lstrlen(zippath) * 2; + memset(zippath + lstrlen(zippath) - 1, 0, 2); + std::string str = zippath; + int num = 0; + size_t fi = str.find_last_of(_T("\\")); + if (fi == str.npos) + continue; + std::string strSubPath = str.substr(fi + 1, str.length()); + wsprintf(newzippath, _T("%s\\%s.zip"), zippath, strSubPath.c_str()); + DWORD dwAttr = GetFileAttributes(newzippath); + if (INVALID_FILE_ATTRIBUTES != dwAttr) { + DWORD ti = ::timeGetTime(); + wsprintf(newzippath, _T("%s\\%s%d.zip"), zippath, strSubPath.c_str(), ti); + } + hz = CreateZip(newzippath, 0); + if (hz == NULL) break;; + } else { + ZeroMemory(zippath_filename, MAX_PATH * 2 * 2); + zippath = (TCHAR*)token.c_str(); + DWORD dwAttr = GetFileAttributes(zippath); + if (INVALID_FILE_ATTRIBUTES == dwAttr) { + continue; + } + if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { + std::string str = zippath; + int num = 0; + size_t fi = str.find_last_of(_T("\\")); + if (fi == str.npos) + continue; + memcpy(zippath_filename, zippath + pathlen / 2, lstrlen(zippath) * 2 - pathlen + 2); + ZeroMemory(zippath_filename + lstrlen(zippath_filename) - 1, 2); + ZipAddFolder(hz, zippath_filename); + pThis->FindFileInDirZipAdd(zippath, hz, pathlen); + } else { + std::string str = zippath; + int num = 0; + size_t fi = str.find_last_of(_T("\\")); + if (fi == str.npos) + continue; + memcpy(zippath_filename, zippath + fi + 1, (lstrlen(zippath) - fi) * 2 + 2); + ZipAdd(hz, zippath_filename, zippath); + } + } + } + if (hz) + CloseZip(hz); + if (!pThis->bIsStopZIP) pThis->SendToken(TOKEN_FILE_ZIPOK); + pThis->bIsStopZIP = FALSE; + SAFE_DELETE_AR(newzippath); + SAFE_DELETE_AR(zippath_filename) + SAFE_DELETE_AR(pThis->zippath_t); +} + +//停止文件搜索 +void CFileManager::StopZIPTheard() +{ + if (bIsStopZIP) + return; + bIsStopZIP = TRUE; + WaitForSingleObject(m_hWorkZIPThread, INFINITE); + CloseHandle(m_hWorkZIPThread); + bIsStopZIP = FALSE; +} +//创建文件搜索线程 +void CFileManager::StartZIP() +{ + m_hWorkZIPThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)FindZIPThread, + (LPVOID)this, + 0, + NULL + ); +} + +void CFileManager::FindFileInDirZipAdd(TCHAR* rootDir, HZIP hz, int pathlen) +{ + WIN32_FIND_DATA fd; + + TCHAR filePathName[256]; + TCHAR tmpPath[256]; + + ZeroMemory(&fd, sizeof(WIN32_FIND_DATA)); + ZeroMemory(filePathName, 256); + ZeroMemory(tmpPath, 256); + + lstrcpy(filePathName, rootDir); + + + if (filePathName[(lstrlen(filePathName) - 1)] != _T('\\')) { + lstrcat(filePathName, _T("\\")); + } + lstrcat(filePathName, _T("*.*")); + + HANDLE hSearch = FindFirstFile(filePathName, &fd); + + do { + DWORD dwOffset = 1; // 位移指针 + + lstrcpy(tmpPath, rootDir); + if (tmpPath[lstrlen(tmpPath) - 1] != _T('\\')) + lstrcat(tmpPath, _T("\\")); + + lstrcat(tmpPath, fd.cFileName); + + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + && lstrcmp(fd.cFileName, _T(".")) && lstrcmp(fd.cFileName, _T(".."))) { + TCHAR* zippath_filename = new TCHAR[MAX_PATH * 2]; + std::string str = tmpPath; + int num = 0; + size_t fi = str.find_last_of(_T("\\")); + if (fi == str.npos) + continue; + memcpy(zippath_filename, tmpPath + pathlen / 2, lstrlen(tmpPath) * 2 - pathlen + 2); + ZipAddFolder(hz, zippath_filename); + FindFileInDirZipAdd(tmpPath, hz, pathlen); + SAFE_DELETE_AR(zippath_filename); + } else if (lstrcmp(fd.cFileName, _T(".")) && lstrcmp(fd.cFileName, _T(".."))) { + TCHAR* zippath_filename = new TCHAR[MAX_PATH * 2]; + std::string str = tmpPath; + int num = 0; + size_t fi = str.find_last_of(_T("\\")); + if (fi == str.npos) + continue; + memcpy(zippath_filename, tmpPath + pathlen / 2, lstrlen(tmpPath) * 2 - pathlen + 2); + ZipAdd(hz, zippath_filename, tmpPath); + SAFE_DELETE_AR(zippath_filename); + } + } while (FindNextFile(hSearch, &fd) && !bIsStopZIP); + FindClose(hSearch); +} + +void CFileManager::SendFileInfo(TCHAR* filepath, TCHAR* type) // jpg png ico bmp gif +{ + ResizePicture(filepath); +} + +bool CFileManager::ResizePicture(TCHAR* strSource) +{ + CImage image, imDest; + int w = 0, h = 0; + if (SUCCEEDED(image.Load(strSource))) { + if (image.GetWidth() > 1000 || image.GetHeight() > 700) { + w = 1000; + h = 700; + } else { + w = image.GetWidth(); + h = image.GetHeight(); + } + imDest.Create(w, h, 24); + image.StretchBlt(imDest.GetDC(), CRect(0, 0, w, h), CRect(0, 0, image.GetWidth(), image.GetHeight())); + IStream* pOutStream = NULL; + if (CreateStreamOnHGlobal(NULL, TRUE, &pOutStream) == S_OK) { + imDest.Save(pOutStream, Gdiplus::ImageFormatJPEG); + HGLOBAL hOutGlobal = NULL; + GetHGlobalFromStream(pOutStream, &hOutGlobal); + LPBYTE pBits = (LPBYTE)GlobalLock(hOutGlobal); + if (pBits == nullptr) { + return 0; + } + int heath = sizeof(FILEPICINFO); + unsigned long ulBufferLen = (DWORD)GlobalSize(pBits); + char* pBuffer = new char[ulBufferLen + heath]; + ::memcpy(pBuffer + heath, pBits, ulBufferLen); + FILEPICINFO* m_FILEINFO = new FILEPICINFO; + m_FILEINFO->Token = TOKEN_FILE_GETINFO; + m_FILEINFO->w = imDest.GetWidth(); + m_FILEINFO->h = imDest.GetHeight(); + m_FILEINFO->size = ulBufferLen; + memcpy(pBuffer, m_FILEINFO, heath); + Send((LPBYTE)pBuffer, ulBufferLen + heath); + SAFE_DELETE(pBuffer); + + GlobalUnlock(hOutGlobal); + pOutStream->Release(); + } + imDest.ReleaseDC(); + imDest.Destroy(); + image.Destroy(); + } + return true; +} + +BOOL CFileManager::GetVolumeNameByHandle(HANDLE hFile, char* szFullPath) +{ + //得到所有磁盘卷的卷序号 + char* szBuf =NULL; + DWORD szAllDriveStrings = GetLogicalDriveStringsA(0, NULL); //驱动器总长度 + if (szBuf == NULL) { + szBuf = new char[szAllDriveStrings +1]; //建立数组 + } + int i; + DWORD dwVolumeSerialNumber; + memset(szBuf, 0, sizeof(szBuf)); + //通过句柄得到文件的卷序号 + //得到卷序号 lpFileInformation.dwVolumeSerialNumber + BY_HANDLE_FILE_INFORMATION lpFileInformation; + if (!GetFileInformationByHandle(hFile, &lpFileInformation) || (lpFileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + //通过句柄得到文件信息失败 或者 此句柄为文件夹句柄,并非文件句柄 + return FALSE; + } + if (::GetLogicalDriveStringsA(szAllDriveStrings, szBuf)) { + for (i = 0; szBuf[i]; i += 4) { + //得到卷信息->卷序号 + if (!_stricmp(&(szBuf[i]), "A:\\") || !_stricmp(&(szBuf[i]), "B:\\")) { + //忽略软盘 (一般不会使用,并且查询它的速度非常之慢) + continue; + } + if (GetVolumeInformationA(&(szBuf[i]), NULL, NULL, &dwVolumeSerialNumber, NULL, NULL, NULL, NULL)) { + // 与 lpFileInformation.dwVolumeSerialNumber 比较 + // 如果相同,则找到该磁盘 + if (dwVolumeSerialNumber == lpFileInformation.dwVolumeSerialNumber) { + //找到 + char szVolumeName[4]; + memset(szVolumeName, 0, sizeof(szVolumeName)); + strcpy_s(szVolumeName, &(szBuf[i])); + szVolumeName[strlen(szVolumeName) - 1] = '\0'; + //得到路径 + HMODULE hNt = LoadLibraryA("ntdll.dll"); + if (hNt) { + IO_STATUS_BLOCK isb = { 0 };; + FILE_NAME_INFORMATION fni = { 0 }; + ZwQueryInformationFile fnZwQueryInformationFile = NULL; + fnZwQueryInformationFile = (ZwQueryInformationFile) ::GetProcAddress(hNt, "ZwQueryInformationFile"); + if (fnZwQueryInformationFile) { + DWORD dwRet = 0; + dwRet = fnZwQueryInformationFile(hFile, &isb, &fni, sizeof(FILE_NAME_INFORMATION), 9); + if (!dwRet) { + //获取文件路径成功 + fni.FileName[fni.FileNameLength / 2] = 0; + //构造成完整路径名 + char szFilePath[MAX_PATH * 3]; + memset(szFilePath, 0, sizeof(szFilePath)); + WideCharToMultiByte(CP_ACP, 0, fni.FileName, -1, szFilePath, sizeof(szFilePath) - 1, NULL, NULL); + sprintf_s(szFullPath, MAX_PATH * 4, "%s%s", szVolumeName, szFilePath); + return TRUE; + } + } + FreeLibrary(hNt); + } + } + } + } + } + SAFE_DELETE_AR(szBuf); + return FALSE; +} + + +void CFileManager::GetFullPathByFileReferenceNumber(HANDLE hVol, DWORDLONG FileReferenceNumber) +{ + typedef ULONG(__stdcall* PNtCreateFile)( + PHANDLE FileHandle, + ULONG DesiredAccess, + PVOID ObjectAttributes, + PVOID IoStatusBlock, + PLARGE_INTEGER AllocationSize, + ULONG FileAttributes, + ULONG ShareAccess, + ULONG CreateDisposition, + ULONG CreateOptions, + PVOID EaBuffer, + ULONG EaLength); + PNtCreateFile NtCreatefile = (PNtCreateFile)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtCreateFile"); + typedef struct _UNICODE_STRING { + USHORT Length, MaximumLength; + PWCH Buffer; + } UNICODE_STRING, * PUNICODE_STRING; + UNICODE_STRING fidstr = { 8, 8, (PWSTR)&FileReferenceNumber }; + typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; + } OBJECT_ATTRIBUTES; + const ULONG OBJ_CASE_INSENSITIVE = 0x00000040UL; + OBJECT_ATTRIBUTES oa = { sizeof(OBJECT_ATTRIBUTES), hVol, &fidstr, OBJ_CASE_INSENSITIVE, 0, 0 }; + HANDLE hFile; + long long iosb[2]; + const ULONG FILE_OPEN_BY_FILE_ID = 0x00002000UL; + const ULONG FILE_OPEN = 0x00000001UL; + ULONG status = NtCreatefile(&hFile, GENERIC_ALL, &oa, iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_OPEN_BY_FILE_ID, NULL, 0); + ZeroMemory(c_pathname, MAX_PATH * 4); + if (status == 0) { + GetVolumeNameByHandle(hFile, c_pathname); + CloseHandle(hFile); + } +} + +// 将 ANSI TCHAR* 转换为 WCHAR*,由调用者负责释放返回值 +WCHAR* ConvertTCharToWChar(const TCHAR* ansiStr) { + if (!ansiStr) return nullptr; + + int len = MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, NULL, 0); + if (len == 0) return nullptr; + + WCHAR* wideStr = new WCHAR[len]; + if (!wideStr) return nullptr; + + MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, len); + return wideStr; +} + +void CFileManager::SendSearchData(LPBYTE lpBuffer) +{ + SEARCH S_search; + memset(&S_search, 0, sizeof(SEARCH)); + memcpy(&S_search, lpBuffer, sizeof(SEARCH)); + volName = S_search.TC_disk; + tc_search = S_search.TC_search; + + int all = 0; + BOOL status; + bool isNTFS = false; + bool getHandleSuccess = false; + bool initUsnJournalSuccess = false; + + TCHAR sysNameBuf[MAX_PATH] = { 0 }; + status = GetVolumeInformation(volName, + NULL, + 0, + NULL, + NULL, + NULL, + sysNameBuf, + MAX_PATH); + + if (0 != status) { + if (0 == lstrcmp(sysNameBuf, _T("NTFS"))) { + isNTFS = true; + } else { + BYTE lpbuffer = TOKEN_FILE_SEARCHPLUS_NONTFS; + Send((LPBYTE)&lpbuffer, 1); + return; + } + } + + if (isNTFS) { + TCHAR fileName[MAX_PATH]; + fileName[0] = _T('\0'); + + _tcscpy_s(fileName, _T("\\\\.\\")); + _tcscat_s(fileName, volName); + + std::string fileNameStr = (std::string)fileName; + fileNameStr.erase(fileNameStr.find_last_of(_T(":")) + 1); + hVol = CreateFile(fileNameStr.data(), + GENERIC_READ | GENERIC_WRITE, // 可以为0 + FILE_SHARE_READ | FILE_SHARE_WRITE, // 必须包含有FILE_SHARE_WRITE + NULL, // 这里不需要 + OPEN_EXISTING, // 必须包含OPEN_EXISTING, CREATE_ALWAYS可能会导致错误 + FILE_ATTRIBUTE_READONLY, // FILE_ATTRIBUTE_NORMAL可能会导致错误 + NULL); // 这里不需要 + + if (INVALID_HANDLE_VALUE != hVol) { + getHandleSuccess = true; + } else { + BYTE lpbuffer = TOKEN_FILE_SEARCHPLUS_HANDLE; + Send((LPBYTE)&lpbuffer, 1); + return; + } + } + + if (getHandleSuccess) { + DWORD br; + CREATE_USN_JOURNAL_DATA cujd; + cujd.MaximumSize = 0; // 0表示使用默认值 + cujd.AllocationDelta = 0; // 0表示使用默认值 + status = DeviceIoControl(hVol, + FSCTL_CREATE_USN_JOURNAL, + &cujd, + sizeof(cujd), + NULL, + 0, + &br, + NULL); + + if (0 != status) { + initUsnJournalSuccess = true; + } else { + BYTE lpbuffer = TOKEN_FILE_SEARCHPLUS_INITUSN; + Send((LPBYTE)&lpbuffer, 1); + return; + } + } + lpBuffer = (LPBYTE)LocalAlloc(LPTR, 1024 * sizeof(TCHAR)); + lpBuffer[0] = TOKEN_FILE_SEARCHPLUS_LIST; + DWORD dwOffset = 1; + DWORD dwLength = 1024 * 8; + if (initUsnJournalSuccess) { + bool getBasicInfoSuccess = false; + DWORD br; + status = DeviceIoControl(hVol, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &UsnInfo, sizeof(USN_JOURNAL_DATA), &br, NULL); + if (0 != status) { + getBasicInfoSuccess = true; + } else { + + BYTE lpbuffer = TOKEN_FILE_SEARCHPLUS_GETUSN; + Send((LPBYTE)&lpbuffer, 1); + return; + } + if (getBasicInfoSuccess) { + MFT_ENUM_DATA med; + med.StartFileReferenceNumber = 0; + med.LowUsn = 0;//UsnInfo.FirstUsn; 这里经测试发现,如果用FirstUsn有时候不正确,导致获取到不完整的数据,还是直接写0好. + med.HighUsn = UsnInfo.NextUsn; + + CHAR buffer[4096]; // 用于储存记录的缓冲,尽量足够地大 + DWORD usnDataSize; + PUSN_RECORD UsnRecord; + + while (0 != DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, &med, sizeof(med), buffer, 4096, &usnDataSize, NULL)) { + DWORD dwRetBytes = usnDataSize - sizeof(USN); + UsnRecord = (PUSN_RECORD)(((PCHAR)buffer) + sizeof(USN)); + while (dwRetBytes > 0) { + const int strLen = UsnRecord->FileNameLength; + char c_fileName[MAX_PATH * 2] = { 0 }; + WCHAR *search = ConvertTCharToWChar(tc_search); + if (wcsstr(UsnRecord->FileName, search) != NULL) { //在a中查找b,如果不存在, + WideCharToMultiByte(CP_OEMCP, NULL, UsnRecord->FileName, strLen / 2, c_fileName, strLen, NULL, FALSE); + + GetFullPathByFileReferenceNumber(hVol, UsnRecord->FileReferenceNumber); + + if (LocalSize(lpBuffer) < (dwOffset + dwLength)) + lpBuffer = (LPBYTE)LocalReAlloc(lpBuffer, (dwOffset + dwLength), LMEM_ZEROINIT | LMEM_MOVEABLE); + + memcpy(lpBuffer + dwOffset, c_fileName, strlen(c_fileName)); + dwOffset += (DWORD)strlen(c_fileName) + 1; + + memcpy(lpBuffer + dwOffset, c_pathname, strlen(c_pathname)); + dwOffset += (DWORD)strlen(c_pathname) + 1; + } + SAFE_DELETE_AR(search); + + DWORD recordLen = UsnRecord->RecordLength; + dwRetBytes -= recordLen; + UsnRecord = (PUSN_RECORD)(((PCHAR)UsnRecord) + recordLen); + } + + med.StartFileReferenceNumber = *(USN*)&buffer; + } + } + + DELETE_USN_JOURNAL_DATA dujd; + dujd.UsnJournalID = UsnInfo.UsnJournalID; + dujd.DeleteFlags = USN_DELETE_FLAG_DELETE; + status = DeviceIoControl(hVol, FSCTL_DELETE_USN_JOURNAL, &dujd, sizeof(dujd), NULL, 0, &br, NULL); + } + // 最后释放一些资源 + if (getHandleSuccess) { + CloseHandle(hVol); + } + lpBuffer = (LPBYTE)LocalReAlloc(lpBuffer, dwOffset, LMEM_ZEROINIT | LMEM_MOVEABLE); + if (lpBuffer == NULL) + return; + + Send((LPBYTE)lpBuffer, (int)(LocalSize(lpBuffer))); + LocalFree(lpBuffer); + return; +} + +} diff --git a/client/file/FileManager.h b/client/file/FileManager.h new file mode 100644 index 0000000..eb99b81 --- /dev/null +++ b/client/file/FileManager.h @@ -0,0 +1,152 @@ +#include +#include +#include +#include "Manager.h" +#include +#include "zip.h" + +#pragma once +#include +typedef LONG NTSTATUS; // 仅声明 NTSTATUS;不引入 ,避免 STRING/UNICODE_STRING/OBJ_CASE_INSENSITIVE/FILE_OPEN 等宏与本地命名冲突 +#include "MasterEncoder.h" + +namespace SimplePluginsFile { + +typedef struct { + UINT nFileSize; // 文件大小 + UINT nSendSize; // 已发送大小 +} SENDFILEPROGRESS, * PSENDFILEPROGRESS; + +typedef struct { + DWORD dwSizeHigh; + DWORD dwSizeLow; + BOOL error; +} FILESIZE; + + +typedef struct { + TCHAR SearchFileName[MAX_PATH]; + TCHAR SearchPath[MAX_PATH]; + BOOL bEnabledSubfolder; +} FILESEARCH; + + +typedef struct { + BYTE Token; + int w,h,size; +} FILEPICINFO; + + +struct SEARCH { + TCHAR TC_disk[8]; + TCHAR TC_search[MAX_PATH]; +}; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK; + +typedef struct _FILE_NAME_INFORMATION { + ULONG FileNameLength; + WCHAR FileName[MAX_PATH]; +} FILE_NAME_INFORMATION; + +typedef LONG(__stdcall* ZwQueryInformationFile) ( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG FileInformationLength, + IN ULONG FileInformationClass +); + +WCHAR* ConvertTCharToWChar(const TCHAR* ansiStr); + +class CFileManager : public CManager +{ +public: + MasterEncoder m_encoder; + DWORD dwRet; + void OnReceive(LPBYTE lpBuffer, ULONG nSize); + UINT SendDriveList(); + CFileManager(ISocketBase* pClient, int n, void* user); + virtual ~CFileManager(); +private: + LPBYTE lpSendPacket; + std::list m_UploadList; + BOOL m_bIsWow64; + UINT m_nTransferMode; + HANDLE m_hFileSend; + HANDLE m_hFileRecv; + TCHAR m_strCurrentProcessFileName[MAX_PATH]; // 当前正在处理的文件 + __int64 m_nCurrentProcessFileLength; // 当前正在处理的文件的长度 + bool benofrce; + TCHAR* copyfile; + HANDLE ImpersonateLoggedOnUserWrapper(); + bool MakeSureDirectoryPathExists(LPCTSTR pszDirPath); + bool UploadToRemote(LPBYTE lpBuffer); + bool FixedUploadList(LPCTSTR lpszDirectory); + void StopTransfer(BOOL bIsUpload); + UINT SendFilesList(TCHAR* lpszDirectory); + bool DeleteDirectory(LPCTSTR lpszDirectory); + UINT SendFileSize(LPCTSTR lpszFileName); + void SendFileData(LPBYTE lpBuffer); + void CreateFolder(LPBYTE lpBuffer); + void Rename(LPBYTE lpBuffer); + int SendToken(BYTE bToken); + + void CreateLocalRecvFile(LPBYTE lpBuffer); + void SetTransferMode(LPBYTE lpBuffer); + void GetFileData(); + void WriteLocalRecvFile(LPBYTE lpBuffer, UINT nSize); + void UploadNext(); + BOOL OpenFile(LPCTSTR lpFile, INT nShowCmd); + void ExeCompress(BYTE* lpBuffer); + void SendSearchData(LPBYTE lpBuffer); + BOOL GetVolumeNameByHandle(HANDLE hFile, char* szFullPath); + void GetFullPathByFileReferenceNumber(HANDLE hVol, DWORDLONG FileReferenceNumber); + +protected: + HANDLE m_hWorkThread; + BOOL bIsStopSearch; + FILESEARCH filesearch; + + void StopSearchTheard(); + void SendSearchFilesList(LPCTSTR str); + static void WINAPI FindFileThread(LPVOID lparam); + void FindFileInDir(TCHAR* rootDir, TCHAR* searchfilename, BOOL bEnabledSubfolder); + TCHAR g_desktopName[MAX_PATH]; + ULONG PseudoRand(ULONG* seed); + void GetBotId(TCHAR* botId); + HDESK g_hDesk;; + bool getLnkFormPath(TCHAR* lnkPath, TCHAR* szoutPath); + void SendNetHood(); //发送共享文件目录 + void encfile(char* in_filename); + void decryptfile(char* in_filename); + bool FnDelPathFile(TCHAR* tc_path); + + TCHAR* zippath_t; + HANDLE m_hWorkZIPThread; + BOOL bIsStopZIP; + void StopZIPTheard(); + void StartZIP(); + static void WINAPI FindZIPThread(LPVOID lparam); + void FindFileInDirZipAdd(TCHAR* rootDir, HZIP hz,int pathlen); + + //info + void SendFileInfo(TCHAR* filepath,TCHAR * type); + bool ResizePicture(TCHAR* strSource); + + TCHAR* volName; // 驱动盘名称 + TCHAR* tc_search; // 搜索关键词 + HANDLE hVol; // 用于储存驱动盘句柄 + USN_JOURNAL_DATA UsnInfo; // 用于储存USN日志的基本信息 + long counter; + LPBYTE lpBuffer; + char* c_pathname; +}; + +} diff --git a/client/file/MasterEncoder.cpp b/client/file/MasterEncoder.cpp new file mode 100644 index 0000000..bbbf893 --- /dev/null +++ b/client/file/MasterEncoder.cpp @@ -0,0 +1,162 @@ +#include "StdAfx.h" +#include "MasterEncoder.h" +#include +#include +#include + +namespace SimplePluginsFile { + +MasterEncoder::MasterEncoder() +{ + _codeKey = 100; + _sign = nullptr; + _signLen = 0; +} + +MasterEncoder::~MasterEncoder() +{ + SAFE_DELETE_AR(_sign); +} + +void MasterEncoder::setSignAndKey(char* sign, int signLen, unsigned char key) +{ + if (sign && signLen) { + _signLen = signLen; + SAFE_DELETE_AR(_sign); + _sign = new char[signLen]; + if (_sign) { + memcpy(_sign, sign, signLen); + } else { + Mprintf("setSignAndKey Failed!\n"); + } + } + _codeKey = key; +} + +void MasterEncoder::encode(unsigned char* data, long size) +{ + for (long i = 0; i < size; i++) { + data[i] ^= _codeKey; + } +} + +void MasterEncoder::decode(unsigned char* data, long size) +{ + for (long i = 0; i < size; i++) { + data[i] ^= _codeKey; + } +} + +unsigned char* MasterEncoder::readPDF(const std::string& filepath, long& size) +{ + std::ifstream ifs; + ifs.open(filepath, std::ios::in | std::ios::binary); + + if (!ifs.is_open()) { + Mprintf("文件打开失败: %s\n", filepath.c_str()); + return nullptr; + } + + ifs.seekg(0, std::ios_base::end); + size =(long) ifs.tellg(); + ifs.seekg(0, std::ios_base::beg); + unsigned char* data = new unsigned char[size]; + long count = 0; + while (count < size) { + ifs.read((char*)&data[count], sizeof(unsigned char)); + count++; + } + + Mprintf("read PDF size = %d\n", size); + + ifs.close(); + + return data; +} + +void MasterEncoder::writePDF(const std::string& filePath, unsigned char* data, long size) +{ + std::ofstream ofs; + ofs.open(filePath, std::ios::out | std::ios::trunc | std::ios::binary); + + if (!ofs.is_open()) { + Mprintf("文件打开失败: %s\n", filePath.c_str()); + return; + } + + long count = 0; + while (count < size) { + ofs.write((char*)&data[count], sizeof(unsigned char)); + count++; + } + Mprintf("save PDF size = %d\n", size); + ofs.close(); +} + +void MasterEncoder::encodePDF(const std::string& pdfPath, const std::string& savePath) +{ + long size = 0; + unsigned char* data = readPDF(pdfPath, size); + + //判断是否已经加密过 + if (memcmp(data, _sign, _signLen) == 0) { + Mprintf("加密过了, 直接保存: %s\n", pdfPath.c_str()); + writePDF(savePath, data, size); + } else { + encode(data, size); + + if (_signLen > 0) { + unsigned char* p = new unsigned char[size + _signLen]; + memcpy(p, _sign, _signLen); + memcpy(p + _signLen, data, size); + + writePDF(savePath, p, size + _signLen); + + delete[] p; + } else { + writePDF(savePath, data, size); + } + } + + delete[] data; +} + +void MasterEncoder::decodePDF(const std::string& pdfPath, const std::string& savePath) +{ + long size = 0; + unsigned char* data = readPDF(pdfPath, size); + + if (_signLen == 0) { + decode(data, size); + writePDF(savePath, data, size); + } else if (memcmp(data, _sign, _signLen) == 0) { + unsigned char* p = (data + _signLen); + decode(p, size - _signLen); + writePDF(savePath, p, size - _signLen); + } else { + Mprintf("解密失败: %s\n", pdfPath.c_str()); + } + + delete[] data; +} + +unsigned char* MasterEncoder::decode(const std::string& pdfPath, long& size) +{ + unsigned char* data = readPDF(pdfPath, size); + + if (_signLen == 0) { + decode(data, size); + return data; + } else if (memcmp(data, _sign, _signLen) == 0) { + size -= _signLen; + unsigned char* p = new unsigned char[size]; + memcpy(p, data + _signLen, size); + decode(p, size); + delete[] data; + return p; + } else { + return data; + } +} + +} diff --git a/client/file/MasterEncoder.h b/client/file/MasterEncoder.h new file mode 100644 index 0000000..bdc1707 --- /dev/null +++ b/client/file/MasterEncoder.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace SimplePluginsFile { + +class MasterEncoder +{ +public: + MasterEncoder(); + ~MasterEncoder(); + + void setSignAndKey(char* sign, int signLen, unsigned char key); + + void decode(unsigned char* data, long size); + void encode(unsigned char* data, long size); + + void writePDF(const std::string& filePath, unsigned char* data, long size); + unsigned char* readPDF(const std::string& filepath, long& size); + +public: + void encodePDF(const std::string& pdfPath, const std::string& savePath); + void decodePDF(const std::string& pdfPath, const std::string& savePath); + unsigned char* decode(const std::string& pdfPath, long& size); + +private: + unsigned char _codeKey; + char* _sign; + int _signLen; +}; + +} diff --git a/client/file/zip.cpp b/client/file/zip.cpp new file mode 100644 index 0000000..958802b --- /dev/null +++ b/client/file/zip.cpp @@ -0,0 +1,2911 @@ +#include +#include +#include +#include "zip.h" + + +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. The modifications +// were a complete rewrite of the bit of code that generates the +// layout of the zipfile, and support for zipping to/from memory +// or handles or pipes or pagefile or diskfiles, encryption, unicode. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +// +// +// This is version 1999-Oct-05 of the Info-ZIP copyright and license. +// The definitive version of this document should be available at +// ftp://ftp.cdrom.com/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-1999 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum, +// Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller, +// Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel, +// Steve Salisbury, Dave Smith, Christian Spieler, Antoine Verheijen, +// Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is," without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, definition, disclaimer, and this list of conditions in +// documentation and/or other materials provided with the distribution. +// +// 3. Altered versions--including, but not limited to, ports to new operating +// systems, existing ports with new graphical interfaces, and dynamic, +// shared, or static library versions--must be plainly marked as such +// and must not be misrepresented as being the original source. Such +// altered versions also must not be misrepresented as being Info-ZIP +// releases--including, but not limited to, labeling of the altered +// versions with the names "Info-ZIP" (or any variation thereof, including, +// but not limited to, different capitalizations), "Pocket UnZip," "WiZ" +// or "MacZip" without the explicit permission of Info-ZIP. Such altered +// versions are further prohibited from misrepresentative use of the +// Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," +// "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its own source and +// binary releases. +// + + +typedef unsigned char uch; // unsigned 8-bit value +typedef unsigned short ush; // unsigned 16-bit value +typedef unsigned long ulg; // unsigned 32-bit value +typedef size_t extent; // file size +typedef unsigned Pos; // must be at least 32 bits +typedef unsigned IPos; // A Pos is an index in the character window. Pos is used only for parameter passing + +#ifndef EOF +#define EOF (-1) +#endif + + +// Error return values. The values 0..4 and 12..18 follow the conventions +// of PKZIP. The values 4..10 are all assigned to "insufficient memory" +// by PKZIP, so the codes 5..10 are used here for other purposes. +#define ZE_MISS -1 // used by procname(), zipbare() +#define ZE_OK 0 // success +#define ZE_EOF 2 // unexpected end of zip file +#define ZE_FORM 3 // zip file structure error +#define ZE_MEM 4 // out of memory +#define ZE_LOGIC 5 // internal logic error +#define ZE_BIG 6 // entry too large to split +#define ZE_NOTE 7 // invalid comment format +#define ZE_TEST 8 // zip test (-T) failed or out of memory +#define ZE_ABORT 9 // user interrupt or termination +#define ZE_TEMP 10 // error using a temp file +#define ZE_READ 11 // read or seek error +#define ZE_NONE 12 // nothing to do +#define ZE_NAME 13 // missing or empty zip file +#define ZE_WRITE 14 // error writing to a file +#define ZE_CREAT 15 // couldn't open to write +#define ZE_PARMS 16 // bad command line +#define ZE_OPEN 18 // could not open a specified file to read +#define ZE_MAXERR 18 // the highest error number + + +// internal file attribute +#define UNKNOWN (-1) +#define BINARY 0 +#define ASCII 1 + +#define BEST -1 // Use best method (deflation or store) +#define STORE 0 // Store method +#define DEFLATE 8 // Deflation method + +#define CRCVAL_INITIAL 0L + +// MSDOS file or directory attributes +#define MSDOS_HIDDEN_ATTR 0x02 +#define MSDOS_DIR_ATTR 0x10 + +// Lengths of headers after signatures in bytes +#define LOCHEAD 26 +#define CENHEAD 42 +#define ENDHEAD 18 + +// Definitions for extra field handling: +#define EB_HEADSIZE 4 /* length of a extra field block header */ +#define EB_LEN 2 /* offset of data length field in header */ +#define EB_UT_MINLEN 1 /* minimal UT field contains Flags byte */ +#define EB_UT_FLAGS 0 /* byte offset of Flags field */ +#define EB_UT_TIME1 1 /* byte offset of 1st time value */ +#define EB_UT_FL_MTIME (1 << 0) /* mtime present */ +#define EB_UT_FL_ATIME (1 << 1) /* atime present */ +#define EB_UT_FL_CTIME (1 << 2) /* ctime present */ +#define EB_UT_LEN(n) (EB_UT_MINLEN + 4 * (n)) +#define EB_L_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(3)) +#define EB_C_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(1)) + + +// Macros for writing machine integers to little-endian format +#define PUTSH(a,f) {char _putsh_c=(char)((a)&0xff); wfunc(param,&_putsh_c,1); _putsh_c=(char)((a)>>8); wfunc(param,&_putsh_c,1);} +#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))} + + +// -- Structure of a ZIP file -- +// Signatures for zip file information headers +#define LOCSIG 0x04034b50L +#define CENSIG 0x02014b50L +#define ENDSIG 0x06054b50L +#define EXTLOCSIG 0x08074b50L + + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + + +#define WSIZE (0x8000) +// Maximum window size = 32K. If you are really short of memory, compile +// with a smaller WSIZE but this reduces the compression ratio for files +// of size > WSIZE. WSIZE must be a power of two in the current implementation. +// + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +// Minimum amount of lookahead, except at the end of the input file. +// See deflate.c for comments about the MIN_MATCH+1. +// + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +// In order to simplify the code, particularly on 16 bit machines, match +// distances are limited to MAX_DIST instead of WSIZE. +// + + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 +#define ZIP_FOLDER 4 + + + +// =========================================================================== +// Constants +// + +#define MAX_BITS 15 +// All codes must not exceed MAX_BITS bits + +#define MAX_BL_BITS 7 +// Bit length codes must not exceed MAX_BL_BITS bits + +#define LENGTH_CODES 29 +// number of length codes, not counting the special END_BLOCK code + +#define LITERALS 256 +// number of literal bytes 0..255 + +#define END_BLOCK 256 +// end of block literal code + +#define L_CODES (LITERALS+1+LENGTH_CODES) +// number of Literal or Length codes, including the END_BLOCK code + +#define D_CODES 30 +// number of distance codes + +#define BL_CODES 19 +// number of codes used to transfer the bit lengths + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define LIT_BUFSIZE 0x8000 +#define DIST_BUFSIZE LIT_BUFSIZE +// Sizes of match buffers for literals/lengths and distances. There are +// 4 reasons for limiting LIT_BUFSIZE to 64K: +// - frequencies can be kept in 16 bit counters +// - if compression is not successful for the first block, all input data is +// still in the window so we can still emit a stored block even when input +// comes from standard input. (This can also be done for all blocks if +// LIT_BUFSIZE is not greater than 32K.) +// - if compression is not successful for a file smaller than 64K, we can +// even emit a stored file instead of a stored block (saving 5 bytes). +// - creating new Huffman trees less frequently may not provide fast +// adaptation to changes in the input data statistics. (Take for +// example a binary file with poorly compressible code followed by +// a highly compressible string table.) Smaller buffer sizes give +// fast adaptation but have of course the overhead of transmitting trees +// more frequently. +// - I can't count above 4 +// The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save +// memory at the expense of compression). Some optimizations would be possible +// if we rely on DIST_BUFSIZE == LIT_BUFSIZE. +// + +#define REP_3_6 16 +// repeat previous bit length 3-6 times (2 bits of repeat count) + +#define REPZ_3_10 17 +// repeat a zero length 3-10 times (3 bits of repeat count) + +#define REPZ_11_138 18 +// repeat a zero length 11-138 times (7 bits of repeat count) + +#define HEAP_SIZE (2*L_CODES+1) +// maximum heap size + + +// =========================================================================== +// Local data used by the "bit string" routines. +// + +#define Buf_size (8 * 2*sizeof(char)) +// Number of bits used within bi_buf. (bi_buf may be implemented on +// more than 16 bits on some systems.) + +// Output a 16 bit value to the bit stream, lower (oldest) byte first +#define PUTSHORT(state,w) \ +{ if (state.bs.out_offset >= state.bs.out_size-1) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \ +} + +#define PUTBYTE(state,b) \ +{ if (state.bs.out_offset >= state.bs.out_size) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) (b); \ +} + +// DEFLATE.CPP HEADER + +#define HASH_BITS 15 +// For portability to 16 bit machines, do not use values above 15. + +#define HASH_SIZE (unsigned)(1<= HASH_BITS + +#define max_insert_length max_lazy_match +// Insert new strings in the hash table only if the match length +// is not greater than this length. This saves time but degrades compression. +// max_insert_length is used only for compression levels <= 3. + + + +const int extra_lbits[LENGTH_CODES] // extra bits for each length code += { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 }; + +const int extra_dbits[D_CODES] // extra bits for each distance code += { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + +const int extra_blbits[BL_CODES]// extra bits for each bit length code += { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 }; + +const uch bl_order[BL_CODES] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; +// The lengths of the bit length codes are sent in order of decreasing +// probability, to avoid transmitting the lengths for unused bit length codes. + + +typedef struct config { + ush good_length; // reduce lazy search above this match length + ush max_lazy; // do not perform lazy search above this match length + ush nice_length; // quit search above this match length + ush max_chain; +} config; + +// Values for max_lazy_match, good_match, nice_match and max_chain_length, +// depending on the desired pack level (0..9). The values given below have +// been tuned to exclude worst case performance for pathological files. +// Better values may be found for specific files. +// + +const config configuration_table[10] = { + // good lazy nice chain + {0, 0, 0, 0}, // 0 store only + {4, 4, 8, 4}, // 1 maximum speed, no lazy matches + {4, 5, 16, 8}, // 2 + {4, 6, 32, 32}, // 3 + {4, 4, 16, 16}, // 4 lazy matches */ + {8, 16, 32, 32}, // 5 + {8, 16, 128, 128}, // 6 + {8, 32, 128, 256}, // 7 + {32, 128, 258, 1024}, // 8 + {32, 258, 258, 4096} };// 9 maximum compression */ + + // Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + // For deflate_fast() (levels <= 3) good is ignored and lazy has a different meaning. + + + + + + + + // Data structure describing a single value and its code string. +typedef struct ct_data { + union { + ush freq; // frequency count + ush code; // bit string + } fc; + union { + ush dad; // father node in Huffman tree + ush len; // length of bit string + } dl; +} ct_data; + +typedef struct tree_desc { + ct_data* dyn_tree; // the dynamic tree + ct_data* static_tree; // corresponding static tree or NULL + const int* extra_bits; // extra bits for each code or NULL + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + int max_code; // largest code with non zero frequency +} tree_desc; + + + + +class TTreeState +{ +public: + TTreeState(); + + ct_data dyn_ltree[HEAP_SIZE]; // literal and length tree + ct_data dyn_dtree[2 * D_CODES + 1]; // distance tree + ct_data static_ltree[L_CODES + 2]; // the static literal tree... + // ... Since the bit lengths are imposed, there is no need for the L_CODES + // extra codes used during heap construction. However the codes 286 and 287 + // are needed to build a canonical tree (see ct_init below). + ct_data static_dtree[D_CODES]; // the static distance tree... + // ... (Actually a trivial tree since all codes use 5 bits.) + ct_data bl_tree[2 * BL_CODES + 1]; // Huffman tree for the bit lengths + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS + 1]; // number of codes at each bit length for an optimal tree + + int heap[2 * L_CODES + 1]; // heap used to build the Huffman trees + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + uch depth[2 * L_CODES + 1]; + // Depth of each subtree used as tie breaker for trees of equal frequency + + uch length_code[MAX_MATCH - MIN_MATCH + 1]; + // length code for each normalized match length (0 == MIN_MATCH) + + uch dist_code[512]; + // distance codes. The first 256 values correspond to the distances + // 3 .. 258, the last 256 values correspond to the top 8 bits of + // the 15 bit distances. + + int base_length[LENGTH_CODES]; + // First normalized length for each code (0 = MIN_MATCH) + + int base_dist[D_CODES]; + // First normalized distance for each code (0 = distance of 1) + + uch far l_buf[LIT_BUFSIZE]; // buffer for literals/lengths + ush far d_buf[DIST_BUFSIZE]; // buffer for distances + + uch flag_buf[(LIT_BUFSIZE / 8)]; + // flag_buf is a bit array distinguishing literals from lengths in + // l_buf, and thus indicating the presence or absence of a distance. + + unsigned last_lit; // running index in l_buf + unsigned last_dist; // running index in d_buf + unsigned last_flags; // running index in flag_buf + uch flags; // current flags not yet saved in flag_buf + uch flag_bit; // current bit used in flags + // bits are filled in flags starting at bit 0 (least significant). + // Note: these flags are overkill in the current code since we don't + // take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + + ulg opt_len; // bit length of current block with optimal trees + ulg static_len; // bit length of current block with static trees + + ulg cmpr_bytelen; // total byte length of compressed file + ulg cmpr_len_bits; // number of bits past 'cmpr_bytelen' + + ulg input_len; // total byte length of input file + // input_len is for debugging only since we can get it by other means. + + ush* file_type; // pointer to UNKNOWN, BINARY or ASCII + // int *file_method; // pointer to DEFLATE or STORE +}; + +TTreeState::TTreeState() +{ + tree_desc a = { dyn_ltree, static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS, 0 }; l_desc = a; + tree_desc b = { dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0 }; d_desc = b; + tree_desc c = { bl_tree, NULL, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0 }; bl_desc = c; + last_lit = 0; + last_dist = 0; + last_flags = 0; +} + + + +class TBitState +{ +public: + + int flush_flg; + // + unsigned bi_buf; + // Output buffer. bits are inserted starting at the bottom (least significant + // bits). The width of bi_buf must be at least 16 bits. + int bi_valid; + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + char* out_buf; + // Current output buffer. + unsigned out_offset; + // Current offset in output buffer. + // On 16 bit machines, the buffer is limited to 64K. + unsigned out_size; + // Size of current output buffer + ulg bits_sent; // bit length of the compressed data only needed for debugging??? +}; + + + + + + + +class TDeflateState +{ +public: + TDeflateState() { window_size = 0; } + + uch window[2L * WSIZE]; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least WSIZE + // bytes. With this organization, matches are limited to a distance of + // WSIZE-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: limit the window size to WSIZE+CBSZ if SMALL_MEM (the code would + // be less efficient since the data would have to be copied WSIZE/CBSZ times) + Pos prev[WSIZE]; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + Pos head[HASH_SIZE]; + // Heads of the hash chains or NIL. If your compiler thinks that + // HASH_SIZE is a dynamic value, recompile with -DDYN_ALLOC. + + ulg window_size; + // window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the + // input file length plus MIN_LOOKAHEAD. + + long block_start; + // window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int sliding; + // Set to false when the input file is already in memory + + unsigned ins_h; // hash index of string to be inserted + + unsigned int prev_length; + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + + unsigned strstart; // start of string to insert + unsigned match_start; // start of matching string + int eofile; // flag set at end of input file + unsigned lookahead; // number of valid bytes ahead in window + + unsigned max_chain_length; + // To speed up deflation, hash chains are never searched beyond this length. + // A higher limit improves compression ratio but degrades the speed. + + unsigned int max_lazy_match; + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + + unsigned good_match; + // Use a faster search when the previous match is longer than this + + int nice_match; // Stop searching when current match exceeds this +}; + +typedef __int64 lutime_t; // define it ourselves since we don't include time.h + +typedef struct iztimes { + lutime_t atime, mtime, ctime; +} iztimes; // access, modify, create times + +typedef struct zlist { + ush vem, ver, flg, how; // See central header in zipfile.c for what vem..off are + ulg tim, crc, siz, len; + extent nam, ext, cext, com; // offset of ext must be >= LOCHEAD + ush dsk, att, lflg; // offset of lflg must be >= LOCHEAD + ulg atx, off; + char name[MAX_PATH]; // File name in zip file + char* extra; // Extra field (set only if ext != 0) + char* cextra; // Extra in central (set only if cext != 0) + char* comment; // Comment (set only if com != 0) + char iname[MAX_PATH]; // Internal file name after cleanup + char zname[MAX_PATH]; // External version of internal name + int mark; // Marker for files to operate on + int trash; // Marker for files to delete + int dosflag; // Set to force MSDOS file attributes + struct zlist far* nxt; // Pointer to next header in list +} TZipFileInfo; + + +struct TState; +typedef unsigned (*READFUNC)(TState& state, char* buf, unsigned size); +typedef unsigned (*FLUSHFUNC)(void* param, const char* buf, unsigned* size); +typedef unsigned (*WRITEFUNC)(void* param, const char* buf, unsigned size); +struct TState +{ + void* param; + int level; bool seekable; + READFUNC readfunc; FLUSHFUNC flush_outbuf; + TTreeState ts; TBitState bs; TDeflateState ds; + const char* err; +}; + + + + + + + + + +void Assert(TState& state, bool cond, const char* msg) +{ + if (cond) return; + state.err = msg; +} +void __cdecl Trace(const char* x, ...) { va_list paramList; va_start(paramList, x); paramList; va_end(paramList); } +void __cdecl Tracec(bool, const char* x, ...) { va_list paramList; va_start(paramList, x); paramList; va_end(paramList); } + + + +// =========================================================================== +// Local (static) routines in this file. +// + +void init_block(TState&); +void pqdownheap(TState&, ct_data* tree, int k); +void gen_bitlen(TState&, tree_desc* desc); +void gen_codes(TState& state, ct_data* tree, int max_code); +void build_tree(TState&, tree_desc* desc); +void scan_tree(TState&, ct_data* tree, int max_code); +void send_tree(TState& state, ct_data* tree, int max_code); +int build_bl_tree(TState&); +void send_all_trees(TState& state, int lcodes, int dcodes, int blcodes); +void compress_block(TState& state, ct_data* ltree, ct_data* dtree); +void set_file_type(TState&); +void send_bits(TState& state, int value, int length); +unsigned bi_reverse(unsigned code, int len); +void bi_windup(TState& state); +void copy_block(TState& state, char* buf, unsigned len, int header); + + +#define send_code(state, c, tree) send_bits(state, tree[c].fc.code, tree[c].dl.len) +// Send a code of the given tree. c and tree must not have side effects + +// alternatively... +//#define send_code(state, c, tree) +// { if (state.verbose>1) fprintf(stderr,"\ncd %3d ",(c)); +// send_bits(state, tree[c].fc.code, tree[c].dl.len); } + +#define d_code(dist) ((dist) < 256 ? state.ts.dist_code[dist] : state.ts.dist_code[256+((dist)>>7)]) +// Mapping from a distance to a distance code. dist is the distance - 1 and +// must not have side effects. dist_code[256] and dist_code[257] are never used. + +#define Max(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(TState& state, ush* attr) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + state.ts.file_type = attr; + //state.ts.file_method = method; + state.ts.cmpr_bytelen = state.ts.cmpr_len_bits = 0L; + state.ts.input_len = 0L; + + if (state.ts.static_dtree[0].dl.len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES - 1; code++) { + state.ts.base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + state.ts.length_code[length++] = (uch)code; + } + } + Assert(state, length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + state.ts.length_code[length - 1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + state.ts.base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + state.ts.dist_code[dist++] = (uch)code; + } + } + Assert(state, dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES; code++) { + state.ts.base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + state.ts.dist_code[256 + dist++] = (uch)code; + } + } + Assert(state, dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + n = 0; + while (n <= 143) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + while (n <= 255) state.ts.static_ltree[n++].dl.len = 9, state.ts.bl_count[9]++; + while (n <= 279) state.ts.static_ltree[n++].dl.len = 7, state.ts.bl_count[7]++; + while (n <= 287) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + /* fc.codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(state, (ct_data*)state.ts.static_ltree, L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + state.ts.static_dtree[n].dl.len = 5; + state.ts.static_dtree[n].fc.code = (ush)bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(state); +} + +/* =========================================================================== + * Initialize a new block. + */ +void init_block(TState& state) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) state.ts.dyn_ltree[n].fc.freq = 0; + for (n = 0; n < D_CODES; n++) state.ts.dyn_dtree[n].fc.freq = 0; + for (n = 0; n < BL_CODES; n++) state.ts.bl_tree[n].fc.freq = 0; + + state.ts.dyn_ltree[END_BLOCK].fc.freq = 1; + state.ts.opt_len = state.ts.static_len = 0L; + state.ts.last_lit = state.ts.last_dist = state.ts.last_flags = 0; + state.ts.flags = 0; state.ts.flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = state.ts.heap[SMALLEST]; \ + state.ts.heap[SMALLEST] = state.ts.heap[state.ts.heap_len--]; \ + pqdownheap(state,tree, SMALLEST); \ +} + + /* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].fc.freq < tree[m].fc.freq || \ + (tree[n].fc.freq == tree[m].fc.freq && state.ts.depth[n] <= state.ts.depth[m])) + + /* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +void pqdownheap(TState& state, ct_data* tree, int k) +{ + int v = state.ts.heap[k]; + int j = k << 1; /* left son of k */ + int htemp; /* required because of bug in SASC compiler */ + + while (j <= state.ts.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < state.ts.heap_len && smaller(tree, state.ts.heap[j + 1], state.ts.heap[j])) j++; + + /* Exit if v is smaller than both sons */ + htemp = state.ts.heap[j]; + if (smaller(tree, v, htemp)) break; + + /* Exchange v with the smallest son */ + state.ts.heap[k] = htemp; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + state.ts.heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +void gen_bitlen(TState& state, tree_desc* desc) +{ + ct_data* tree = desc->dyn_tree; + const int* extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data* stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[state.ts.heap[state.ts.heap_max]].dl.len = 0; /* root of the heap */ + + for (h = state.ts.heap_max + 1; h < HEAP_SIZE; h++) { + n = state.ts.heap[h]; + bits = tree[tree[n].dl.dad].dl.len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].dl.len = (ush)bits; + /* We overwrite tree[n].dl.dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + state.ts.bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n - base]; + f = tree[n].fc.freq; + state.ts.opt_len += (ulg)f * (bits + xbits); + if (stree) state.ts.static_len += (ulg)f * (stree[n].dl.len + xbits); + } + if (overflow == 0) return; + + Trace("\nbit length overflow\n"); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (state.ts.bl_count[bits] == 0) bits--; + state.ts.bl_count[bits]--; /* move one leaf down the tree */ + state.ts.bl_count[bits + 1] += (ush)2; /* move one overflow item as its brother */ + state.ts.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = state.ts.bl_count[bits]; + while (n != 0) { + m = state.ts.heap[--h]; + if (m > max_code) continue; + if (tree[m].dl.len != (ush)bits) { + Trace("code %d bits %d->%d\n", m, tree[m].dl.len, bits); + state.ts.opt_len += ((long)bits - (long)tree[m].dl.len) * (long)tree[m].fc.freq; + tree[m].dl.len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +void gen_codes(TState& state, ct_data* tree, int max_code) +{ + ush next_code[MAX_BITS + 1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (ush)((code + state.ts.bl_count[bits - 1]) << 1); + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(state, code + state.ts.bl_count[MAX_BITS] - 1 == (1 << ((ush)MAX_BITS)) - 1, + "inconsistent bit counts"); + Trace("\ngen_codes: max_code %d ", max_code); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].dl.len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].fc.code = (ush)bi_reverse(next_code[len]++, len); + + //Tracec(tree != state.ts.static_ltree, "\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].fc.code, next_code[len]-1); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +void build_tree(TState& state, tree_desc* desc) +{ + ct_data* tree = desc->dyn_tree; + ct_data* stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + state.ts.heap_len = 0, state.ts.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc.freq != 0) { + state.ts.heap[++state.ts.heap_len] = max_code = n; + state.ts.depth[n] = 0; + } + else { + tree[n].dl.len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (state.ts.heap_len < 2) { + int newcp = state.ts.heap[++state.ts.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[newcp].fc.freq = 1; + state.ts.depth[newcp] = 0; + state.ts.opt_len--; if (stree) state.ts.static_len -= stree[newcp].dl.len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = state.ts.heap_len / 2; n >= 1; n--) pqdownheap(state, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = state.ts.heap[SMALLEST]; /* m = node of next least frequency */ + + state.ts.heap[--state.ts.heap_max] = n; /* keep the nodes sorted by frequency */ + state.ts.heap[--state.ts.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].fc.freq = (ush)(tree[n].fc.freq + tree[m].fc.freq); + state.ts.depth[node] = (uch)(Max(state.ts.depth[n], state.ts.depth[m]) + 1); + tree[n].dl.dad = tree[m].dl.dad = (ush)node; + /* and insert the new node in the heap */ + state.ts.heap[SMALLEST] = node++; + pqdownheap(state, tree, SMALLEST); + + } while (state.ts.heap_len >= 2); + + state.ts.heap[--state.ts.heap_max] = state.ts.heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(state, (tree_desc*)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes(state, (ct_data*)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +void scan_tree(TState& state, ct_data* tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code + 1].dl.len = (ush)-1; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n + 1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } + else if (count < min_count) { + state.ts.bl_tree[curlen].fc.freq = (ush)(state.ts.bl_tree[curlen].fc.freq + count); + } + else if (curlen != 0) { + if (curlen != prevlen) state.ts.bl_tree[curlen].fc.freq++; + state.ts.bl_tree[REP_3_6].fc.freq++; + } + else if (count <= 10) { + state.ts.bl_tree[REPZ_3_10].fc.freq++; + } + else { + state.ts.bl_tree[REPZ_11_138].fc.freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } + else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } + else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +void send_tree(TState& state, ct_data* tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].dl.len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n + 1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } + else if (count < min_count) { + do { send_code(state, curlen, state.ts.bl_tree); } while (--count != 0); + + } + else if (curlen != 0) { + if (curlen != prevlen) { + send_code(state, curlen, state.ts.bl_tree); count--; + } + Assert(state, count >= 3 && count <= 6, " 3_6?"); + send_code(state, REP_3_6, state.ts.bl_tree); send_bits(state, count - 3, 2); + + } + else if (count <= 10) { + send_code(state, REPZ_3_10, state.ts.bl_tree); send_bits(state, count - 3, 3); + + } + else { + send_code(state, REPZ_11_138, state.ts.bl_tree); send_bits(state, count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } + else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } + else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +int build_bl_tree(TState& state) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(state, (ct_data*)state.ts.dyn_ltree, state.ts.l_desc.max_code); + scan_tree(state, (ct_data*)state.ts.dyn_dtree, state.ts.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(state, (tree_desc*)(&state.ts.bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (state.ts.bl_tree[bl_order[max_blindex]].dl.len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + state.ts.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + Trace("\ndyn trees: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +void send_all_trees(TState& state, int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(state, lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(state, lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Trace("\nbl counts: "); + send_bits(state, lcodes - 257, 5); + /* not +255 as stated in appnote.txt 1.93a or -256 in 2.04c */ + send_bits(state, dcodes - 1, 5); + send_bits(state, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Trace("\nbl code %2d ", bl_order[rank]); + send_bits(state, state.ts.bl_tree[bl_order[rank]].dl.len, 3); + } + Trace("\nbl tree: sent %ld", state.bs.bits_sent); + + send_tree(state, (ct_data*)state.ts.dyn_ltree, lcodes - 1); /* send the literal tree */ + Trace("\nlit tree: sent %ld", state.bs.bits_sent); + + send_tree(state, (ct_data*)state.ts.dyn_dtree, dcodes - 1); /* send the distance tree */ + Trace("\ndist tree: sent %ld", state.bs.bits_sent); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length (in bytes) for the file so far. + */ +ulg flush_block(TState& state, char* buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + state.ts.flag_buf[state.ts.last_flags] = state.ts.flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*state.ts.file_type == (ush)UNKNOWN) set_file_type(state); + + /* Construct the literal and distance trees */ + build_tree(state, (tree_desc*)(&state.ts.l_desc)); + Trace("\nlit data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + build_tree(state, (tree_desc*)(&state.ts.d_desc)); + Trace("\ndist data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(state); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (state.ts.opt_len + 3 + 7) >> 3; + static_lenb = (state.ts.static_len + 3 + 7) >> 3; + state.ts.input_len += stored_len; /* for debugging only */ + + Trace("\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, state.ts.opt_len, static_lenb, state.ts.static_len, stored_len, + state.ts.last_lit, state.ts.last_dist); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + // Originally, zip allowed the file to be transformed from a compressed + // into a stored file in the case where compression failed, there + // was only one block, and it was allowed to change. I've removed this + // possibility since the code's cleaner if no changes are allowed. + //if (stored_len <= opt_lenb && eof && state.ts.cmpr_bytelen == 0L + // && state.ts.cmpr_len_bits == 0L && state.seekable) + //{ // && state.ts.file_method != NULL + // // Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: + // Assert(state,buf!=NULL,"block vanished"); + // copy_block(state,buf, (unsigned)stored_len, 0); // without header + // state.ts.cmpr_bytelen = stored_len; + // Assert(state,false,"unimplemented *state.ts.file_method = STORE;"); + // //*state.ts.file_method = STORE; + //} + //else + if (stored_len + 4 <= opt_lenb && buf != (char*)NULL) { + /* 4: two words for the lengths */ +/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits(state, (STORED_BLOCK << 1) + eof, 3); /* send block type */ + state.ts.cmpr_bytelen += ((state.ts.cmpr_len_bits + 3 + 7) >> 3) + stored_len + 4; + state.ts.cmpr_len_bits = 0L; + + copy_block(state, buf, (unsigned)stored_len, 1); /* with header */ + } + else if (static_lenb == opt_lenb) { + send_bits(state, (STATIC_TREES << 1) + eof, 3); + compress_block(state, (ct_data*)state.ts.static_ltree, (ct_data*)state.ts.static_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.static_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + else { + send_bits(state, (DYN_TREES << 1) + eof, 3); + send_all_trees(state, state.ts.l_desc.max_code + 1, state.ts.d_desc.max_code + 1, max_blindex + 1); + compress_block(state, (ct_data*)state.ts.dyn_ltree, (ct_data*)state.ts.dyn_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.opt_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + Assert(state, ((state.ts.cmpr_bytelen << 3) + state.ts.cmpr_len_bits) == state.bs.bits_sent, "bad compressed size"); + init_block(state); + + if (eof) { + // Assert(state,input_len == isize, "bad input size"); + bi_windup(state); + state.ts.cmpr_len_bits += 7; /* align on byte boundary */ + } + Trace("\n"); + + return state.ts.cmpr_bytelen + (state.ts.cmpr_len_bits >> 3); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally(TState& state, int dist, int lc) +{ + state.ts.l_buf[state.ts.last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + state.ts.dyn_ltree[lc].fc.freq++; + } + else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert(state, (ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH - MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + state.ts.dyn_ltree[state.ts.length_code[lc] + LITERALS + 1].fc.freq++; + state.ts.dyn_dtree[d_code(dist)].fc.freq++; + + state.ts.d_buf[state.ts.last_dist++] = (ush)dist; + state.ts.flags |= state.ts.flag_bit; + } + state.ts.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((state.ts.last_lit & 7) == 0) { + state.ts.flag_buf[state.ts.last_flags++] = state.ts.flags; + state.ts.flags = 0, state.ts.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if (state.level > 2 && (state.ts.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)state.ts.last_lit * 8L; + ulg in_length = (ulg)state.ds.strstart - state.ds.block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)state.ts.dyn_dtree[dcode].fc.freq * (5L + extra_dbits[dcode]); + } + out_length >>= 3; + Trace("\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + state.ts.last_lit, state.ts.last_dist, in_length, out_length, + 100L - out_length * 100L / in_length); + if (state.ts.last_dist < state.ts.last_lit / 2 && out_length < in_length / 2) return 1; + } + return (state.ts.last_lit == LIT_BUFSIZE - 1 || state.ts.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +void compress_block(TState& state, ct_data* ltree, ct_data* dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (state.ts.last_lit != 0) do { + if ((lx & 7) == 0) flag = state.ts.flag_buf[fx++]; + lc = state.ts.l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(state, lc, ltree); /* send a literal byte */ + } + else { + /* Here, lc is the match length - MIN_MATCH */ + code = state.ts.length_code[lc]; + send_code(state, code + LITERALS + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= state.ts.base_length[code]; + send_bits(state, lc, extra); /* send the extra length bits */ + } + dist = state.ts.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert(state, code < D_CODES, "bad d_code"); + + send_code(state, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= state.ts.base_dist[code]; + send_bits(state, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < state.ts.last_lit); + + send_code(state, END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +void set_file_type(TState& state) +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < 128) ascii_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < LITERALS) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + *state.ts.file_type = (ush)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); +} + + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init(TState& state, char* tgt_buf, unsigned tgt_size, int flsh_allowed) +{ + state.bs.out_buf = tgt_buf; + state.bs.out_size = tgt_size; + state.bs.out_offset = 0; + state.bs.flush_flg = flsh_allowed; + + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = 0L; +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(TState& state, int value, int length) +{ + Assert(state, length > 0 && length <= 15, "invalid length"); + state.bs.bits_sent += (ulg)length; + /* If not enough room in bi_buf, use (bi_valid) bits from bi_buf and + * (Buf_size - bi_valid) bits from value to flush the filled bi_buf, + * then fill in the rest of (value), leaving (length - (Buf_size-bi_valid)) + * unused bits in bi_buf. + */ + state.bs.bi_buf |= (value << state.bs.bi_valid); + state.bs.bi_valid += length; + if (state.bs.bi_valid > (int)Buf_size) { + PUTSHORT(state, state.bs.bi_buf); + state.bs.bi_valid -= Buf_size; + state.bs.bi_buf = (unsigned)value >> (length - state.bs.bi_valid); + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup(TState& state) +{ + if (state.bs.bi_valid > 8) { + PUTSHORT(state, state.bs.bi_buf); + } + else if (state.bs.bi_valid > 0) { + PUTBYTE(state, state.bs.bi_buf); + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param, state.bs.out_buf, &state.bs.out_offset); + } + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = (state.bs.bits_sent + 7) & ~7; +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(TState& state, char* block, unsigned len, int header) +{ + bi_windup(state); /* align on byte boundary */ + + if (header) { + PUTSHORT(state, (ush)len); + PUTSHORT(state, (ush)~len); + state.bs.bits_sent += 2 * 16; + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param, state.bs.out_buf, &state.bs.out_offset); + state.bs.out_offset = len; + state.flush_outbuf(state.param, block, &state.bs.out_offset); + } + else if (state.bs.out_offset + len > state.bs.out_size) { + Assert(state, false, "output buffer too small for in-memory compression"); + } + else { + memcpy(state.bs.out_buf + state.bs.out_offset, block, len); + state.bs.out_offset += len; + } + state.bs.bits_sent += (ulg)len << 3; +} + + + + + + + + +/* =========================================================================== + * Prototypes for functions. + */ + +void fill_window(TState& state); +ulg deflate_fast(TState& state); + +int longest_match(TState& state, IPos cur_match); + + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)< 0 if the input file is already read or + * mmap'ed in the window[] array, 0 otherwise. In the first case, + * window_size is sufficient to contain the whole input file plus + * MIN_LOOKAHEAD bytes (to avoid referencing memory beyond the end + * of window[] when looking for matches towards the end). + */ +void lm_init(TState& state, int pack_level, ush* flags) +{ + register unsigned j; + + Assert(state, pack_level >= 1 && pack_level <= 8, "bad pack level"); + + /* Do not slide the window if the whole input is already in memory + * (window_size > 0) + */ + state.ds.sliding = 0; + if (state.ds.window_size == 0L) { + state.ds.sliding = 1; + state.ds.window_size = (ulg)2L * WSIZE; + } + + /* Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ + state.ds.head[HASH_SIZE - 1] = NIL; + memset((char*)state.ds.head, NIL, (unsigned)(HASH_SIZE - 1) * sizeof(*state.ds.head)); + + /* Set the default configuration parameters: + */ + state.ds.max_lazy_match = configuration_table[pack_level].max_lazy; + state.ds.good_match = configuration_table[pack_level].good_length; + state.ds.nice_match = configuration_table[pack_level].nice_length; + state.ds.max_chain_length = configuration_table[pack_level].max_chain; + if (pack_level <= 2) { + *flags |= FAST; + } + else if (pack_level >= 8) { + *flags |= SLOW; + } + /* ??? reduce max_chain_length for binary files */ + + state.ds.strstart = 0; + state.ds.block_start = 0L; + + j = WSIZE; + j <<= 1; // Can read 64K in one step + state.ds.lookahead = state.readfunc(state, (char*)state.ds.window, j); + + if (state.ds.lookahead == 0 || state.ds.lookahead == (unsigned)EOF) { + state.ds.eofile = 1, state.ds.lookahead = 0; + return; + } + state.ds.eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + + state.ds.ins_h = 0; + for (j = 0; j < MIN_MATCH - 1; j++) UPDATE_HASH(state.ds.ins_h, state.ds.window[j]); + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +} + + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ + // For 80x86 and 680x0 and ARM, an optimized version is in match.asm or + // match.S. The code is functionally equivalent, so you can use the C version + // if desired. Which I do so desire! +int longest_match(TState& state, IPos cur_match) +{ + unsigned chain_length = state.ds.max_chain_length; /* max hash chain length */ + register uch far* scan = state.ds.window + state.ds.strstart; /* current string */ + register uch far* match; /* matched string */ + register int len; /* length of current match */ + int best_len = state.ds.prev_length; /* best match length so far */ + IPos limit = state.ds.strstart > (IPos)MAX_DIST ? state.ds.strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + Assert(state, HASH_BITS >= 8 && MAX_MATCH == 258, "Code too clever"); + + + + register uch far* strend = state.ds.window + state.ds.strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len - 1]; + register uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (state.ds.prev_length >= state.ds.good_match) { + chain_length >>= 2; + } + + Assert(state, state.ds.strstart <= state.ds.window_size - MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(state, cur_match < state.ds.strstart, "no future"); + match = state.ds.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end || + match[best_len - 1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(state, scan <= state.ds.window + (unsigned)(state.ds.window_size - 1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + + if (len > best_len) { + state.ds.match_start = cur_match; + best_len = len; + if (len >= state.ds.nice_match) break; + scan_end1 = scan[best_len - 1]; + scan_end = scan[best_len]; + } + } while ((cur_match = state.ds.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + + + +#define check_match(state,start, match, length) +// or alternatively... +//void check_match(TState &state,IPos start, IPos match, int length) +//{ // check that the match is indeed a match +// if (memcmp((char*)state.ds.window + match, +// (char*)state.ds.window + start, length) != EQUAL) { +// fprintf(stderr, +// " start %d, match %d, length %d\n", +// start, match, length); +// error("invalid match"); +// } +// if (state.verbose > 1) { +// fprintf(stderr,"\\[%d,%d]", start-match, length); +// do { fprintf(stdout,"%c",state.ds.window[start++]); } while (--length != 0); +// } +//} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or eofile is set; file reads are + * performed for at least two bytes (required for the translate_eol option). + */ +void fill_window(TState& state) +{ + register unsigned n, m; + unsigned more; /* Amount of free space at the end of the window. */ + + do { + more = (unsigned)(state.ds.window_size - (ulg)state.ds.lookahead - (ulg)state.ds.strstart); + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* For MMAP or BIG_MEM, the whole input file is already in memory so + * we must not perform sliding. We must however call (*read_buf)() in + * order to compute the crc, update lookahead and possibly set eofile. + */ + } + else if (state.ds.strstart >= WSIZE + MAX_DIST && state.ds.sliding) { + + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + memcpy((char*)state.ds.window, (char*)state.ds.window + WSIZE, (unsigned)WSIZE); + state.ds.match_start -= WSIZE; + state.ds.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + state.ds.block_start -= (long)WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = state.ds.head[n]; + state.ds.head[n] = (Pos)(m >= WSIZE ? m - WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = state.ds.prev[n]; + state.ds.prev[n] = (Pos)(m >= WSIZE ? m - WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + if (state.ds.eofile) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the MMAP or BIG_MEM case (not yet supported in gzip), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(state, more >= 2, "more < 2"); + + n = state.readfunc(state, (char*)state.ds.window + state.ds.strstart + state.ds.lookahead, more); + + if (n == 0 || n == (unsigned)EOF) { + state.ds.eofile = 1; + } + else { + state.ds.lookahead += n; + } + } while (state.ds.lookahead < MIN_LOOKAHEAD && !state.ds.eofile); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(state,eof) \ + flush_block(state,state.ds.block_start >= 0L ? (char*)&state.ds.window[(unsigned)state.ds.block_start] : \ + (char*)NULL, (long)state.ds.strstart - state.ds.block_start, (eof)) + + /* =========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +ulg deflate_fast(TState& state) +{ + IPos hash_head = NIL; /* head of the hash chain */ + int flush; /* set if current block must be flushed */ + unsigned match_length = 0; /* length of best match */ + + state.ds.prev_length = MIN_MATCH - 1; + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match(state, hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + } + if (match_length >= MIN_MATCH) { + check_match(state, state.ds.strstart, state.ds.match_start, match_length); + + flush = ct_tally(state, state.ds.strstart - state.ds.match_start, match_length - MIN_MATCH); + + state.ds.lookahead -= match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= state.ds.max_insert_length + && state.ds.lookahead >= MIN_MATCH) { + match_length--; /* string at strstart already in hash table */ + do { + state.ds.strstart++; + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--match_length != 0); + state.ds.strstart++; + } + else { + state.ds.strstart += match_length; + match_length = 0; + state.ds.ins_h = state.ds.window[state.ds.strstart]; + UPDATE_HASH(state.ds.ins_h, state.ds.window[state.ds.strstart + 1]); + Assert(state, MIN_MATCH == 3, "Call UPDATE_HASH() MIN_MATCH-3 more times"); + } + } + else { + /* No match, output a literal byte */ + flush = ct_tally(state, 0, state.ds.window[state.ds.strstart]); + state.ds.lookahead--; + state.ds.strstart++; + } + if (flush) FLUSH_BLOCK(state, 0), state.ds.block_start = state.ds.strstart; + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + return FLUSH_BLOCK(state, 1); /* eof */ +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate(TState& state) +{ + IPos hash_head = NIL; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH - 1; /* length of best match */ + + if (state.level <= 3) return deflate_fast(state); /* optimized for speed */ + + /* Process the input block. */ + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + state.ds.prev_length = match_length, prev_match = state.ds.match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != NIL && state.ds.prev_length < state.ds.max_lazy_match && + state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match(state, hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && state.ds.strstart - state.ds.match_start > TOO_FAR) { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length = MIN_MATCH - 1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (state.ds.prev_length >= MIN_MATCH && match_length <= state.ds.prev_length) { + unsigned max_insert = state.ds.strstart + state.ds.lookahead - MIN_MATCH; + check_match(state, state.ds.strstart - 1, prev_match, state.ds.prev_length); + flush = ct_tally(state, state.ds.strstart - 1 - prev_match, state.ds.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + state.ds.lookahead -= state.ds.prev_length - 1; + state.ds.prev_length -= 2; + do { + if (++state.ds.strstart <= max_insert) { + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } + } while (--state.ds.prev_length != 0); + state.ds.strstart++; + match_available = 0; + match_length = MIN_MATCH - 1; + + if (flush) FLUSH_BLOCK(state, 0), state.ds.block_start = state.ds.strstart; + + } + else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (ct_tally(state, 0, state.ds.window[state.ds.strstart - 1])) { + FLUSH_BLOCK(state, 0), state.ds.block_start = state.ds.strstart; + } + state.ds.strstart++; + state.ds.lookahead--; + } + else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + state.ds.strstart++; + state.ds.lookahead--; + } + // Assert(state,strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + if (match_available) ct_tally(state, 0, state.ds.window[state.ds.strstart - 1]); + + return FLUSH_BLOCK(state, 1); /* eof */ +} + + + + + + + + + + + + +int putlocal(struct zlist far* z, WRITEFUNC wfunc, void* param) +{ // Write a local header described by *z to file *f. Return a ZE_ error code. + PUTLG(LOCSIG, f); + PUTSH(z->ver, f); + PUTSH(z->lflg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->ext, f); + size_t res = (size_t)wfunc(param, z->iname, (unsigned int)z->nam); + if (res != z->nam) return ZE_TEMP; + if (z->ext) + { + res = (size_t)wfunc(param, z->extra, (unsigned int)z->ext); + if (res != z->ext) return ZE_TEMP; + } + return ZE_OK; +} + +int putextended(struct zlist far* z, WRITEFUNC wfunc, void* param) +{ // Write an extended local header described by *z to file *f. Returns a ZE_ code + PUTLG(EXTLOCSIG, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + return ZE_OK; +} + +int putcentral(struct zlist far* z, WRITEFUNC wfunc, void* param) +{ // Write a central header entry of *z to file *f. Returns a ZE_ code. + PUTLG(CENSIG, f); + PUTSH(z->vem, f); + PUTSH(z->ver, f); + PUTSH(z->flg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->cext, f); + PUTSH(z->com, f); + PUTSH(z->dsk, f); + PUTSH(z->att, f); + PUTLG(z->atx, f); + PUTLG(z->off, f); + if ((size_t)wfunc(param, z->iname, (unsigned int)z->nam) != z->nam || + (z->cext && (size_t)wfunc(param, z->cextra, (unsigned int)z->cext) != z->cext) || + (z->com && (size_t)wfunc(param, z->comment, (unsigned int)z->com) != z->com)) + return ZE_TEMP; + return ZE_OK; +} + + +int putend(int n, ulg s, ulg c, extent m, char* z, WRITEFUNC wfunc, void* param) +{ // write the end of the central-directory-data to file *f. + PUTLG(ENDSIG, f); + PUTSH(0, f); + PUTSH(0, f); + PUTSH(n, f); + PUTSH(n, f); + PUTLG(s, f); + PUTLG(c, f); + PUTSH(m, f); + // Write the comment, if any + if (m && wfunc(param, z, (unsigned int)m) != m) return ZE_TEMP; + return ZE_OK; +} + + + + + + +const ulg crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define CRC32(c, b) (crc_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) +#define DO1(buf) crc = CRC32(crc, *buf++) +#define DO2(buf) DO1(buf); DO1(buf) +#define DO4(buf) DO2(buf); DO2(buf) +#define DO8(buf) DO4(buf); DO4(buf) + +ulg crc32(ulg crc, const uch* buf, extent len) +{ + if (buf == NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) { DO8(buf); len -= 8; } + if (len) do { DO1(buf); } while (--len); + return crc ^ 0xffffffffL; // (instead of ~c for 64-bit machines) +} + + +void update_keys(unsigned long* keys, char c) +{ + keys[0] = CRC32(keys[0], c); + keys[1] += keys[0] & 0xFF; + keys[1] = keys[1] * 134775813L + 1; + keys[2] = CRC32(keys[2], keys[1] >> 24); +} +char decrypt_byte(unsigned long* keys) +{ + unsigned temp = ((unsigned)keys[2] & 0xffff) | 2; + return (char)(((temp * (temp ^ 1)) >> 8) & 0xff); +} +char zencode(unsigned long* keys, char c) +{ + int t = decrypt_byte(keys); + update_keys(keys, c); + return (char)(t ^ c); +} + + + + + + + +bool HasZipSuffix(const TCHAR* fn) +{ + const TCHAR* ext = fn + _tcslen(fn); + while (ext > fn && *ext != '.') ext--; + if (ext == fn && *ext != '.') return false; + if (_tcsicmp(ext, _T(".Z")) == 0) return true; + if (_tcsicmp(ext, _T(".zip")) == 0) return true; + if (_tcsicmp(ext, _T(".zoo")) == 0) return true; + if (_tcsicmp(ext, _T(".arc")) == 0) return true; + if (_tcsicmp(ext, _T(".lzh")) == 0) return true; + if (_tcsicmp(ext, _T(".arj")) == 0) return true; + if (_tcsicmp(ext, _T(".gz")) == 0) return true; + if (_tcsicmp(ext, _T(".tgz")) == 0) return true; + return false; +} + + +lutime_t filetime2timet(const FILETIME ft) +{ + __int64 i = *(__int64*)&ft; + return (lutime_t)((i - 116444736000000000) / 10000000); +} + +void filetime2dosdatetime(const FILETIME ft, WORD* dosdate, WORD* dostime) +{ // date: bits 0-4 are day of month 1-31. Bits 5-8 are month 1..12. Bits 9-15 are year-1980 + // time: bits 0-4 are seconds/2, bits 5-10 are minute 0..59. Bits 11-15 are hour 0..23 + SYSTEMTIME st; FileTimeToSystemTime(&ft, &st); + *dosdate = (WORD)(((st.wYear - 1980) & 0x7f) << 9); + *dosdate |= (WORD)((st.wMonth & 0xf) << 5); + *dosdate |= (WORD)((st.wDay & 0x1f)); + *dostime = (WORD)((st.wHour & 0x1f) << 11); + *dostime |= (WORD)((st.wMinute & 0x3f) << 5); + *dostime |= (WORD)((st.wSecond * 2) & 0x1f); +} + + +ZRESULT GetFileInfo(HANDLE hf, ulg* attr, long* size, iztimes* times, ulg* timestamp) +{ // The handle must be a handle to a file + // The date and time is returned in a long with the date most significant to allow + // unsigned integer comparison of absolute times. The attributes have two + // high bytes unix attr, and two low bytes a mapping of that to DOS attr. + //struct stat s; int res=stat(fn,&s); if (res!=0) return false; + // translate windows file attributes into zip ones. + BY_HANDLE_FILE_INFORMATION bhi; BOOL res = GetFileInformationByHandle(hf, &bhi); + if (!res) return ZR_NOFILE; + DWORD fa = bhi.dwFileAttributes; ulg a = 0; + // Zip uses the lower word for its interpretation of windows stuff + if (fa & FILE_ATTRIBUTE_READONLY) a |= 0x01; + if (fa & FILE_ATTRIBUTE_HIDDEN) a |= 0x02; + if (fa & FILE_ATTRIBUTE_SYSTEM) a |= 0x04; + if (fa & FILE_ATTRIBUTE_DIRECTORY)a |= 0x10; + if (fa & FILE_ATTRIBUTE_ARCHIVE) a |= 0x20; + // It uses the upper word for standard unix attr, which we manually construct + if (fa & FILE_ATTRIBUTE_DIRECTORY)a |= 0x40000000; // directory + else a |= 0x80000000; // normal file + a |= 0x01000000; // readable + if (fa & FILE_ATTRIBUTE_READONLY) {} + else a |= 0x00800000; // writeable +// now just a small heuristic to check if it's an executable: + DWORD red, hsize = GetFileSize(hf, NULL); if (hsize > 40) + { + SetFilePointer(hf, 0, NULL, FILE_BEGIN); unsigned short magic; ReadFile(hf, &magic, sizeof(magic), &red, NULL); + SetFilePointer(hf, 36, NULL, FILE_BEGIN); unsigned long hpos; ReadFile(hf, &hpos, sizeof(hpos), &red, NULL); + if (magic == 0x54AD && hsize > hpos + 4 + 20 + 28) + { + SetFilePointer(hf, hpos, NULL, FILE_BEGIN); unsigned long signature; ReadFile(hf, &signature, sizeof(signature), &red, NULL); + if (signature == IMAGE_DOS_SIGNATURE || signature == IMAGE_OS2_SIGNATURE + || signature == IMAGE_OS2_SIGNATURE_LE || signature == IMAGE_NT_SIGNATURE) + { + a |= 0x00400000; // executable + } + } + } + // + if (attr != NULL) *attr = a; + if (size != NULL) *size = hsize; + if (times != NULL) + { // lutime_t is 32bit number of seconds elapsed since 0:0:0GMT, Jan1, 1970. + // but FILETIME is 64bit number of 100-nanosecs since Jan1, 1601 + times->atime = filetime2timet(bhi.ftLastAccessTime); + times->mtime = filetime2timet(bhi.ftLastWriteTime); + times->ctime = filetime2timet(bhi.ftCreationTime); + } + if (timestamp != NULL) + { + WORD dosdate, dostime; + filetime2dosdatetime(bhi.ftLastWriteTime, &dosdate, &dostime); + *timestamp = (WORD)dostime | (((DWORD)dosdate) << 16); + } + return ZR_OK; +} + + + + + + + + +class TZip +{ +public: + TZip(const char* pwd) : hfout(0), mustclosehfout(false), hmapout(0), zfis(0), obuf(0), hfin(0), writ(0), oerr(false), hasputcen(false), ooffset(0), encwriting(false), encbuf(0), password(0), state(0) { if (pwd != 0 && *pwd != 0) { password = new char[strlen(pwd) + 1]; strcpy(password, pwd); } } + ~TZip() { if (state != 0) delete state; state = 0; if (encbuf != 0) delete[] encbuf; encbuf = 0; if (password != 0) delete[] password; password = 0; } + + // These variables say about the file we're writing into + // We can write to pipe, file-by-handle, file-by-name, memory-to-memmapfile + char* password; // keep a copy of the password + HANDLE hfout; // if valid, we'll write here (for files or pipes) + bool mustclosehfout; // if true, we are responsible for closing hfout + HANDLE hmapout; // otherwise, we'll write here (for memmap) + unsigned ooffset; // for hfout, this is where the pointer was initially + ZRESULT oerr; // did a write operation give rise to an error? + unsigned writ; // how far have we written. This is maintained by Add, not write(), to avoid confusion over seeks + bool ocanseek; // can we seek? + char* obuf; // this is where we've locked mmap to view. + unsigned int opos; // current pos in the mmap + unsigned int mapsize; // the size of the map we created + bool hasputcen; // have we yet placed the central directory? + bool encwriting; // if true, then we'll encrypt stuff using 'keys' before we write it to disk + unsigned long keys[3]; // keys are initialised inside Add() + char* encbuf; // if encrypting, then this is a temporary workspace for encrypting the data + unsigned int encbufsize; // (to be used and resized inside write(), and deleted in the destructor) + // + TZipFileInfo* zfis; // each file gets added onto this list, for writing the table at the end + TState* state; // we use just one state object per zip, because it's big (500k) + + ZRESULT Create(void* z, unsigned int len, DWORD flags); + static unsigned sflush(void* param, const char* buf, unsigned* size); + static unsigned swrite(void* param, const char* buf, unsigned size); + unsigned int write(const char* buf, unsigned int size); + bool oseek(unsigned int pos); + ZRESULT GetMemory(void** pbuf, unsigned long* plen); + ZRESULT Close(); + + // some variables to do with the file currently being read: + // I haven't done it object-orientedly here, just put them all + // together, since OO didn't seem to make the design any clearer. + ulg attr; iztimes times; ulg timestamp; // all open_* methods set these + bool iseekable; long isize, ired; // size is not set until close() on pips + ulg crc; // crc is not set until close(). iwrit is cumulative + HANDLE hfin; bool selfclosehf; // for input files and pipes + const char* bufin; unsigned int lenin, posin; // for memory + // and a variable for what we've done with the input: (i.e. compressed it!) + ulg csize; // compressed size, set by the compression routines + // and this is used by some of the compression routines + char buf[16384]; + + + ZRESULT open_file(const TCHAR* fn); + ZRESULT open_handle(HANDLE hf, unsigned int len); + ZRESULT open_mem(void* src, unsigned int len); + ZRESULT open_dir(); + static unsigned sread(TState& s, char* buf, unsigned size); + unsigned read(char* buf, unsigned size); + ZRESULT iclose(); + + ZRESULT ideflate(TZipFileInfo* zfi); + ZRESULT istore(); + + ZRESULT Add(const TCHAR* odstzn, void* src, unsigned int len, DWORD flags); + ZRESULT AddCentral(); + +}; + + + +ZRESULT TZip::Create(void* z, unsigned int len, DWORD flags) +{ + if (hfout != 0 || hmapout != 0 || obuf != 0 || writ != 0 || oerr != ZR_OK || hasputcen) return ZR_NOTINITED; + // + if (flags == ZIP_HANDLE) + { + HANDLE hf = (HANDLE)z; + hfout = hf; mustclosehfout = false; +#ifdef DuplicateHandle + BOOL res = DuplicateHandle(GetCurrentProcess(), hf, GetCurrentProcess(), &hfout, 0, FALSE, DUPLICATE_SAME_ACCESS); + if (res) mustclosehandle = true; +#endif + // now we have hfout. Either we duplicated the handle and we close it ourselves + // (while the caller closes h themselves), or we couldn't duplicate it. + DWORD res = SetFilePointer(hfout, 0, 0, FILE_CURRENT); + ocanseek = (res != 0xFFFFFFFF); + if (ocanseek) ooffset = res; else ooffset = 0; + return ZR_OK; + } + else if (flags == ZIP_FILENAME) + { + const TCHAR* fn = (const TCHAR*)z; + hfout = CreateFile(fn, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hfout == INVALID_HANDLE_VALUE) { hfout = 0; return ZR_NOFILE; } + ocanseek = true; + ooffset = 0; + mustclosehfout = true; + return ZR_OK; + } + else if (flags == ZIP_MEMORY) + { + unsigned int size = len; + if (size == 0) return ZR_MEMSIZE; + if (z != 0) obuf = (char*)z; + else + { + hmapout = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL); + if (hmapout == NULL) return ZR_NOALLOC; + obuf = (char*)MapViewOfFile(hmapout, FILE_MAP_ALL_ACCESS, 0, 0, size); + if (obuf == 0) { CloseHandle(hmapout); hmapout = 0; return ZR_NOALLOC; } + } + ocanseek = true; + opos = 0; mapsize = size; + return ZR_OK; + } + else return ZR_ARGS; +} + +unsigned TZip::sflush(void* param, const char* buf, unsigned* size) +{ // static + if (*size == 0) return 0; + TZip* zip = (TZip*)param; + unsigned int writ = zip->write(buf, *size); + if (writ != 0) *size = 0; + return writ; +} +unsigned TZip::swrite(void* param, const char* buf, unsigned size) +{ // static + if (size == 0) return 0; + TZip* zip = (TZip*)param; return zip->write(buf, size); +} +unsigned int TZip::write(const char* buf, unsigned int size) +{ + const char* srcbuf = buf; + if (encwriting) + { + if (encbuf != 0 && encbufsize < size) { delete[] encbuf; encbuf = 0; } + if (encbuf == 0) { encbuf = new char[size * 2]; encbufsize = size; } + memcpy(encbuf, buf, size); + for (unsigned int i = 0; i < size; i++) encbuf[i] = zencode(keys, encbuf[i]); + srcbuf = encbuf; + } + if (obuf != 0) + { + if (opos + size >= mapsize) { oerr = ZR_MEMSIZE; return 0; } + memcpy(obuf + opos, srcbuf, size); + opos += size; + return size; + } + else if (hfout != 0) + { + DWORD writ; WriteFile(hfout, srcbuf, size, &writ, NULL); + return writ; + } + oerr = ZR_NOTINITED; return 0; +} + +bool TZip::oseek(unsigned int pos) +{ + if (!ocanseek) { oerr = ZR_SEEK; return false; } + if (obuf != 0) + { + if (pos >= mapsize) { oerr = ZR_MEMSIZE; return false; } + opos = pos; + return true; + } + else if (hfout != 0) + { + SetFilePointer(hfout, pos + ooffset, NULL, FILE_BEGIN); + return true; + } + oerr = ZR_NOTINITED; return 0; +} + +ZRESULT TZip::GetMemory(void** pbuf, unsigned long* plen) +{ // When the user calls GetMemory, they're presumably at the end + // of all their adding. In any case, we have to add the central + // directory now, otherwise the memory we tell them won't be complete. + if (!hasputcen) AddCentral(); hasputcen = true; + if (pbuf != NULL) *pbuf = (void*)obuf; + if (plen != NULL) *plen = writ; + if (obuf == NULL) return ZR_NOTMMAP; + return ZR_OK; +} + +ZRESULT TZip::Close() +{ // if the directory hadn't already been added through a call to GetMemory, + // then we do it now + ZRESULT res = ZR_OK; if (!hasputcen) res = AddCentral(); hasputcen = true; + if (obuf != 0 && hmapout != 0) UnmapViewOfFile(obuf); obuf = 0; + if (hmapout != 0) CloseHandle(hmapout); hmapout = 0; + if (hfout != 0 && mustclosehfout) CloseHandle(hfout); hfout = 0; mustclosehfout = false; + return res; +} + + + + +ZRESULT TZip::open_file(const TCHAR* fn) +{ + hfin = 0; bufin = 0; selfclosehf = false; crc = CRCVAL_INITIAL; isize = 0; csize = 0; ired = 0; + if (fn == 0) return ZR_ARGS; + HANDLE hf = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hf == INVALID_HANDLE_VALUE) return ZR_NOFILE; + ZRESULT res = open_handle(hf, 0); + if (res != ZR_OK) { CloseHandle(hf); return res; } + selfclosehf = true; + return ZR_OK; +} +ZRESULT TZip::open_handle(HANDLE hf, unsigned int len) +{ + hfin = 0; bufin = 0; selfclosehf = false; crc = CRCVAL_INITIAL; isize = 0; csize = 0; ired = 0; + if (hf == 0 || hf == INVALID_HANDLE_VALUE) return ZR_ARGS; + DWORD res = SetFilePointer(hfout, 0, 0, FILE_CURRENT); + if (res != 0xFFFFFFFF) + { + ZRESULT res = GetFileInfo(hf, &attr, &isize, ×, ×tamp); + if (res != ZR_OK) return res; + SetFilePointer(hf, 0, NULL, FILE_BEGIN); // because GetFileInfo will have screwed it up + iseekable = true; hfin = hf; + return ZR_OK; + } + else + { + attr = 0x80000000; // just a normal file + isize = -1; // can't know size until at the end + if (len != 0) isize = len; // unless we were told explicitly! + iseekable = false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st, &ft); + WORD dosdate, dostime; filetime2dosdatetime(ft, &dosdate, &dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate) << 16); + hfin = hf; + return ZR_OK; + } +} +ZRESULT TZip::open_mem(void* src, unsigned int len) +{ + hfin = 0; bufin = (const char*)src; selfclosehf = false; crc = CRCVAL_INITIAL; ired = 0; csize = 0; ired = 0; + lenin = len; posin = 0; + if (src == 0 || len == 0) return ZR_ARGS; + attr = 0x80000000; // just a normal file + isize = len; + iseekable = true; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st, &ft); + WORD dosdate, dostime; filetime2dosdatetime(ft, &dosdate, &dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate) << 16); + return ZR_OK; +} +ZRESULT TZip::open_dir() +{ + hfin = 0; bufin = 0; selfclosehf = false; crc = CRCVAL_INITIAL; isize = 0; csize = 0; ired = 0; + attr = 0x41C00010; // a readable writable directory, and again directory + isize = 0; + iseekable = false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st, &ft); + WORD dosdate, dostime; filetime2dosdatetime(ft, &dosdate, &dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate) << 16); + return ZR_OK; +} + +unsigned TZip::sread(TState& s, char* buf, unsigned size) +{ // static + TZip* zip = (TZip*)s.param; + return zip->read(buf, size); +} + +unsigned TZip::read(char* buf, unsigned size) +{ + if (bufin != 0) + { + if (posin >= lenin) return 0; // end of input + ulg red = lenin - posin; + if (red > size) red = size; + memcpy(buf, bufin + posin, red); + posin += red; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else if (hfin != 0) + { + DWORD red; + BOOL ok = ReadFile(hfin, buf, size, &red, NULL); + if (!ok) return 0; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else { oerr = ZR_NOTINITED; return 0; } +} + +ZRESULT TZip::iclose() +{ + if (selfclosehf && hfin != 0) CloseHandle(hfin); hfin = 0; + bool mismatch = (isize != -1 && isize != ired); + isize = ired; // and crc has been being updated anyway + if (mismatch) return ZR_MISSIZE; + else return ZR_OK; +} + + + +ZRESULT TZip::ideflate(TZipFileInfo* zfi) +{ + if (state == 0) state = new TState(); + // It's a very big object! 500k! We allocate it on the heap, because PocketPC's + // stack breaks if we try to put it all on the stack. It will be deleted lazily + state->err = 0; + state->readfunc = sread; state->flush_outbuf = sflush; + state->param = this; state->level = 8; state->seekable = iseekable; state->err = NULL; + // the following line will make ct_init realise it has to perform the init + state->ts.static_dtree[0].dl.len = 0; + // Thanks to Alvin77 for this crucial fix: + state->ds.window_size = 0; + // I think that covers everything that needs to be initted. + // + bi_init(*state, buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here + ct_init(*state, &zfi->att); + lm_init(*state, state->level, &zfi->flg); + ulg sz = deflate(*state); + csize = sz; + ZRESULT r = ZR_OK; if (state->err != NULL) r = ZR_FLATE; + return r; +} + +ZRESULT TZip::istore() +{ + ulg size = 0; + for (;;) + { + unsigned int cin = read(buf, 16384); if (cin <= 0 || cin == (unsigned int)EOF) break; + unsigned int cout = write(buf, cin); if (cout != cin) return ZR_MISSIZE; + size += cin; + } + csize = size; + return ZR_OK; +} + + + + + +bool has_seeded = false; +ZRESULT TZip::Add(const TCHAR* odstzn, void* src, unsigned int len, DWORD flags) +{ + if (oerr) return ZR_FAILED; + if (hasputcen) return ZR_ENDED; + + // if we use password encryption, then every isize and csize is 12 bytes bigger + int passex = 0; if (password != 0 && flags != ZIP_FOLDER) passex = 12; + + // zip has its own notion of what its names should look like: i.e. dir/file.stuff + TCHAR dstzn[MAX_PATH]; _tcscpy(dstzn, odstzn); + if (*dstzn == 0) return ZR_ARGS; + TCHAR* d = dstzn; while (*d != 0) { if (*d == '\\') *d = '/'; d++; } + bool isdir = (flags == ZIP_FOLDER); + bool needs_trailing_slash = (isdir && dstzn[_tcslen(dstzn) - 1] != '/'); + int method = DEFLATE; if (isdir || HasZipSuffix(dstzn)) method = STORE; + + // now open whatever was our input source: + ZRESULT openres; + if (flags == ZIP_FILENAME) openres = open_file((const TCHAR*)src); + else if (flags == ZIP_HANDLE) openres = open_handle((HANDLE)src, len); + else if (flags == ZIP_MEMORY) openres = open_mem(src, len); + else if (flags == ZIP_FOLDER) openres = open_dir(); + else return ZR_ARGS; + if (openres != ZR_OK) return openres; + + // A zip "entry" consists of a local header (which includes the file name), + // then the compressed data, and possibly an extended local header. + + // Initialize the local header + TZipFileInfo zfi; zfi.nxt = NULL; + strcpy(zfi.name, ""); +#ifdef UNICODE + WideCharToMultiByte(CP_UTF8, 0, dstzn, -1, zfi.iname, MAX_PATH, 0, 0); +#else + strcpy(zfi.iname, dstzn); +#endif + zfi.nam = strlen(zfi.iname); + if (needs_trailing_slash) { strcat(zfi.iname, "/"); zfi.nam++; } + strcpy(zfi.zname, ""); + zfi.extra = NULL; zfi.ext = 0; // extra header to go after this compressed data, and its length + zfi.cextra = NULL; zfi.cext = 0; // extra header to go in the central end-of-zip directory, and its length + zfi.comment = NULL; zfi.com = 0; // comment, and its length + zfi.mark = 1; + zfi.dosflag = 0; + zfi.att = (ush)BINARY; + zfi.vem = (ush)0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3 + zfi.ver = (ush)20; // Needs PKUNZIP 2.0 to unzip it + zfi.tim = timestamp; + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + zfi.crc = 0; // to be updated later + zfi.flg = 8; // 8 means 'there is an extra header'. Assume for the moment that we need it. + if (password != 0 && !isdir) zfi.flg = 9; // and 1 means 'password-encrypted' + zfi.lflg = zfi.flg; // to be updated later + zfi.how = (ush)method; // to be updated later + zfi.siz = (ulg)(method == STORE && isize >= 0 ? isize + passex : 0); // to be updated later + zfi.len = (ulg)(isize); // to be updated later + zfi.dsk = 0; + zfi.atx = attr; + zfi.off = writ + ooffset; // offset within file of the start of this local record + // stuff the 'times' structure into zfi.extra + + // nb. apparently there's a problem with PocketPC CE(zip)->CE(unzip) fails. And removing the following block fixes it up. + char xloc[EB_L_UT_SIZE]; zfi.extra = xloc; zfi.ext = EB_L_UT_SIZE; + char xcen[EB_C_UT_SIZE]; zfi.cextra = xcen; zfi.cext = EB_C_UT_SIZE; + xloc[0] = 'U'; + xloc[1] = 'T'; + xloc[2] = EB_UT_LEN(3); // length of data part of e.f. + xloc[3] = 0; + xloc[4] = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME; + xloc[5] = (char)(times.mtime); + xloc[6] = (char)(times.mtime >> 8); + xloc[7] = (char)(times.mtime >> 16); + xloc[8] = (char)(times.mtime >> 24); + xloc[9] = (char)(times.atime); + xloc[10] = (char)(times.atime >> 8); + xloc[11] = (char)(times.atime >> 16); + xloc[12] = (char)(times.atime >> 24); + xloc[13] = (char)(times.ctime); + xloc[14] = (char)(times.ctime >> 8); + xloc[15] = (char)(times.ctime >> 16); + xloc[16] = (char)(times.ctime >> 24); + memcpy(zfi.cextra, zfi.extra, EB_C_UT_SIZE); + zfi.cextra[EB_LEN] = EB_UT_LEN(1); + + + // (1) Start by writing the local header: + int r = putlocal(&zfi, swrite, this); + if (r != ZE_OK) { iclose(); return ZR_WRITE; } + writ += 4 + LOCHEAD + (unsigned int)zfi.nam + (unsigned int)zfi.ext; + if (oerr != ZR_OK) { iclose(); return oerr; } + + // (1.5) if necessary, write the encryption header + keys[0] = 305419896L; + keys[1] = 591751049L; + keys[2] = 878082192L; + for (const char* cp = password; cp != 0 && *cp != 0; cp++) update_keys(keys, *cp); + // generate some random bytes + if (!has_seeded) srand(GetTickCount() ^ (unsigned long)GetDesktopWindow()); + char encbuf[12]; for (int i = 0; i < 12; i++) encbuf[i] = (char)((rand() >> 7) & 0xff); + encbuf[11] = (char)((zfi.tim >> 8) & 0xff); + for (int ei = 0; ei < 12; ei++) encbuf[ei] = zencode(keys, encbuf[ei]); + if (password != 0 && !isdir) { swrite(this, encbuf, 12); writ += 12; } + + //(2) Write deflated/stored file to zip file + ZRESULT writeres = ZR_OK; + encwriting = (password != 0 && !isdir); // an object member variable to say whether we write to disk encrypted + if (!isdir && method == DEFLATE) writeres = ideflate(&zfi); + else if (!isdir && method == STORE) writeres = istore(); + else if (isdir) csize = 0; + encwriting = false; + iclose(); + writ += csize; + if (oerr != ZR_OK) return oerr; + if (writeres != ZR_OK) return ZR_WRITE; + + // (3) Either rewrite the local header with correct information... + bool first_header_has_size_right = (zfi.siz == csize + passex); + zfi.crc = crc; + zfi.siz = csize + passex; + zfi.len = isize; + if (ocanseek && (password == 0 || isdir)) + { + zfi.how = (ush)method; + if ((zfi.flg & 1) == 0) zfi.flg &= ~8; // clear the extended local header flag + zfi.lflg = zfi.flg; + // rewrite the local header: + if (!oseek(zfi.off - ooffset)) return ZR_SEEK; + if ((r = putlocal(&zfi, swrite, this)) != ZE_OK) return ZR_WRITE; + if (!oseek(writ)) return ZR_SEEK; + } + else + { // (4) ... or put an updated header at the end + if (zfi.how != (ush)method) return ZR_NOCHANGE; + if (method == STORE && !first_header_has_size_right) return ZR_NOCHANGE; + if ((r = putextended(&zfi, swrite, this)) != ZE_OK) return ZR_WRITE; + writ += 16L; + zfi.flg = zfi.lflg; // if flg modified by inflate, for the central index + } + if (oerr != ZR_OK) return oerr; + + // Keep a copy of the zipfileinfo, for our end-of-zip directory + char* cextra = new char[zfi.cext]; memcpy(cextra, zfi.cextra, zfi.cext); zfi.cextra = cextra; + TZipFileInfo* pzfi = new TZipFileInfo; memcpy(pzfi, &zfi, sizeof(zfi)); + if (zfis == NULL) zfis = pzfi; + else { TZipFileInfo* z = zfis; while (z->nxt != NULL) z = z->nxt; z->nxt = pzfi; } + return ZR_OK; +} + +ZRESULT TZip::AddCentral() +{ // write central directory + int numentries = 0; + ulg pos_at_start_of_central = writ; + //ulg tot_unc_size=0, tot_compressed_size=0; + bool okay = true; + for (TZipFileInfo* zfi = zfis; zfi != NULL; ) + { + if (okay) + { + int res = putcentral(zfi, swrite, this); + if (res != ZE_OK) okay = false; + } + writ += 4 + CENHEAD + (unsigned int)zfi->nam + (unsigned int)zfi->cext + (unsigned int)zfi->com; + //tot_unc_size += zfi->len; + //tot_compressed_size += zfi->siz; + numentries++; + // + TZipFileInfo* zfinext = zfi->nxt; + if (zfi->cextra != 0) delete[] zfi->cextra; + delete zfi; + zfi = zfinext; + } + ulg center_size = writ - pos_at_start_of_central; + if (okay) + { + int res = putend(numentries, center_size, pos_at_start_of_central + ooffset, 0, NULL, swrite, this); + if (res != ZE_OK) okay = false; + writ += 4 + ENDHEAD + 0; + } + if (!okay) return ZR_WRITE; + return ZR_OK; +} + + + + + +ZRESULT lasterrorZ = ZR_OK; + +unsigned int FormatZipMessageZ(ZRESULT code, char* buf, unsigned int len) +{ + if (code == ZR_RECENT) code = lasterrorZ; + const char* msg = "unknown zip result code"; + switch (code) + { + case ZR_OK: msg = "Success"; break; + case ZR_NODUPH: msg = "Culdn't duplicate handle"; break; + case ZR_NOFILE: msg = "Couldn't create/open file"; break; + case ZR_NOALLOC: msg = "Failed to allocate memory"; break; + case ZR_WRITE: msg = "Error writing to file"; break; + case ZR_NOTFOUND: msg = "File not found in the zipfile"; break; + case ZR_MORE: msg = "Still more data to unzip"; break; + case ZR_CORRUPT: msg = "Zipfile is corrupt or not a zipfile"; break; + case ZR_READ: msg = "Error reading file"; break; + case ZR_ARGS: msg = "Caller: faulty arguments"; break; + case ZR_PARTIALUNZ: msg = "Caller: the file had already been partially unzipped"; break; + case ZR_NOTMMAP: msg = "Caller: can only get memory of a memory zipfile"; break; + case ZR_MEMSIZE: msg = "Caller: not enough space allocated for memory zipfile"; break; + case ZR_FAILED: msg = "Caller: there was a previous error"; break; + case ZR_ENDED: msg = "Caller: additions to the zip have already been ended"; break; + case ZR_ZMODE: msg = "Caller: mixing creation and opening of zip"; break; + case ZR_NOTINITED: msg = "Zip-bug: internal initialisation not completed"; break; + case ZR_SEEK: msg = "Zip-bug: trying to seek the unseekable"; break; + case ZR_MISSIZE: msg = "Zip-bug: the anticipated size turned out wrong"; break; + case ZR_NOCHANGE: msg = "Zip-bug: tried to change mind, but not allowed"; break; + case ZR_FLATE: msg = "Zip-bug: an internal error during flation"; break; + } + unsigned int mlen = (unsigned int)strlen(msg); + if (buf == 0 || len == 0) return mlen; + unsigned int n = mlen; if (n + 1 > len) n = len - 1; + strncpy(buf, msg, n); buf[n] = 0; + return mlen; +} + + + +typedef struct +{ + DWORD flag; + TZip* zip; +} TZipHandleData; + + +HZIP CreateZipInternal(void* z, unsigned int len, DWORD flags, const char* password) +{ + TZip* zip = new TZip(password); + lasterrorZ = zip->Create(z, len, flags); + if (lasterrorZ != ZR_OK) { delete zip; return 0; } + TZipHandleData* han = new TZipHandleData; + han->flag = 2; han->zip = zip; return (HZIP)han; +} +HZIP CreateZipHandle(HANDLE h, const char* password) { return CreateZipInternal(h, 0, ZIP_HANDLE, password); } +HZIP CreateZip(const TCHAR* fn, const char* password) { return CreateZipInternal((void*)fn, 0, ZIP_FILENAME, password); } +HZIP CreateZip(void* z, unsigned int len, const char* password) { return CreateZipInternal(z, len, ZIP_MEMORY, password); } + + +ZRESULT ZipAddInternal(HZIP hz, const TCHAR* dstzn, void* src, unsigned int len, DWORD flags) +{ + if (hz == 0) { lasterrorZ = ZR_ARGS; return ZR_ARGS; } + TZipHandleData* han = (TZipHandleData*)hz; + if (han->flag != 2) { lasterrorZ = ZR_ZMODE; return ZR_ZMODE; } + TZip* zip = han->zip; + lasterrorZ = zip->Add(dstzn, src, len, flags); + return lasterrorZ; +} +ZRESULT ZipAdd(HZIP hz, const TCHAR* dstzn, const TCHAR* fn) { return ZipAddInternal(hz, dstzn, (void*)fn, 0, ZIP_FILENAME); } +ZRESULT ZipAdd(HZIP hz, const TCHAR* dstzn, void* src, unsigned int len) { return ZipAddInternal(hz, dstzn, src, len, ZIP_MEMORY); } +ZRESULT ZipAddHandle(HZIP hz, const TCHAR* dstzn, HANDLE h) { return ZipAddInternal(hz, dstzn, h, 0, ZIP_HANDLE); } +ZRESULT ZipAddHandle(HZIP hz, const TCHAR* dstzn, HANDLE h, unsigned int len) { return ZipAddInternal(hz, dstzn, h, len, ZIP_HANDLE); } +ZRESULT ZipAddFolder(HZIP hz, const TCHAR* dstzn) { return ZipAddInternal(hz, dstzn, 0, 0, ZIP_FOLDER); } + + + +ZRESULT ZipGetMemory(HZIP hz, void** buf, unsigned long* len) +{ + if (hz == 0) { if (buf != 0) *buf = 0; if (len != 0) *len = 0; lasterrorZ = ZR_ARGS; return ZR_ARGS; } + TZipHandleData* han = (TZipHandleData*)hz; + if (han->flag != 2) { lasterrorZ = ZR_ZMODE; return ZR_ZMODE; } + TZip* zip = han->zip; + lasterrorZ = zip->GetMemory(buf, len); + return lasterrorZ; +} + +ZRESULT CloseZipZ(HZIP hz) +{ + if (hz == 0) { lasterrorZ = ZR_ARGS; return ZR_ARGS; } + TZipHandleData* han = (TZipHandleData*)hz; + if (han->flag != 2) { lasterrorZ = ZR_ZMODE; return ZR_ZMODE; } + TZip* zip = han->zip; + lasterrorZ = zip->Close(); + delete zip; + delete han; + return lasterrorZ; +} + +bool IsZipHandleZ(HZIP hz) +{ + if (hz == 0) return false; + TZipHandleData* han = (TZipHandleData*)hz; + return (han->flag == 2); +} diff --git a/client/file/zip.h b/client/file/zip.h new file mode 100644 index 0000000..d49a171 --- /dev/null +++ b/client/file/zip.h @@ -0,0 +1,207 @@ +// +//http://www.codeproject.com/KB/files/zip_utils.aspx +// + +#ifndef _zip_H +#define _zip_H + + +// ZIP functions -- for creating zip files +// This file is a repackaged form of the Info-Zip source code available +// at www.info-zip.org. The original copyright notice may be found in +// zip.cpp. The repackaging was done by Lucian Wischik to simplify and +// extend its use in Windows/C++. Also to add encryption and unicode. + + +#ifndef _unzip_H +DECLARE_HANDLE(HZIP); +#endif +// An HZIP identifies a zip file that is being created + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + + + +HZIP CreateZip(const TCHAR* fn, const char* password); +HZIP CreateZip(void* buf, unsigned int len, const char* password); +HZIP CreateZipHandle(HANDLE h, const char* password); +// CreateZip - call this to start the creation of a zip file. +// As the zip is being created, it will be stored somewhere: +// to a pipe: CreateZipHandle(hpipe_write); +// in a file (by handle): CreateZipHandle(hfile); +// in a file (by name): CreateZip("c:\\test.zip"); +// in memory: CreateZip(buf, len); +// or in pagefile memory: CreateZip(0, len); +// The final case stores it in memory backed by the system paging file, +// where the zip may not exceed len bytes. This is a bit friendlier than +// allocating memory with new[]: it won't lead to fragmentation, and the +// memory won't be touched unless needed. That means you can give very +// large estimates of the maximum-size without too much worry. +// As for the password, it lets you encrypt every file in the archive. +// (This api doesn't support per-file encryption.) +// Note: because pipes don't allow random access, the structure of a zipfile +// created into a pipe is slightly different from that created into a file +// or memory. In particular, the compressed-size of the item cannot be +// stored in the zipfile until after the item itself. (Also, for an item added +// itself via a pipe, the uncompressed-size might not either be known until +// after.) This is not normally a problem. But if you try to unzip via a pipe +// as well, then the unzipper will not know these things about the item until +// after it has been unzipped. Therefore: for unzippers which don't just write +// each item to disk or to a pipe, but instead pre-allocate memory space into +// which to unzip them, then either you have to create the zip not to a pipe, +// or you have to add items not from a pipe, or at least when adding items +// from a pipe you have to specify the length. +// Note: for windows-ce, you cannot close the handle until after CloseZip. +// but for real windows, the zip makes its own copy of your handle, so you +// can close yours anytime. + + +ZRESULT ZipAdd(HZIP hz, const TCHAR* dstzn, const TCHAR* fn); +ZRESULT ZipAdd(HZIP hz, const TCHAR* dstzn, void* src, unsigned int len); +ZRESULT ZipAddHandle(HZIP hz, const TCHAR* dstzn, HANDLE h); +ZRESULT ZipAddHandle(HZIP hz, const TCHAR* dstzn, HANDLE h, unsigned int len); +ZRESULT ZipAddFolder(HZIP hz, const TCHAR* dstzn); +// ZipAdd - call this for each file to be added to the zip. +// dstzn is the name that the file will be stored as in the zip file. +// The file to be added to the zip can come +// from a pipe: ZipAddHandle(hz,"file.dat", hpipe_read); +// from a file: ZipAddHandle(hz,"file.dat", hfile); +// from a filen: ZipAdd(hz,"file.dat", "c:\\docs\\origfile.dat"); +// from memory: ZipAdd(hz,"subdir\\file.dat", buf,len); +// (folder): ZipAddFolder(hz,"subdir"); +// Note: if adding an item from a pipe, and if also creating the zip file itself +// to a pipe, then you might wish to pass a non-zero length to the ZipAddHandle +// function. This will let the zipfile store the item's size ahead of the +// compressed item itself, which in turn makes it easier when unzipping the +// zipfile from a pipe. + +ZRESULT ZipGetMemory(HZIP hz, void** buf, unsigned long* len); +// ZipGetMemory - If the zip was created in memory, via ZipCreate(0,len), +// then this function will return information about that memory block. +// buf will receive a pointer to its start, and len its length. +// Note: you can't add any more after calling this. + +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, TCHAR* buf, unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + + +// e.g. +// +// (1) Traditional use, creating a zipfile from existing files +// HZIP hz = CreateZip("c:\\simple1.zip",0); +// ZipAdd(hz,"znsimple.bmp", "c:\\simple.bmp"); +// ZipAdd(hz,"znsimple.txt", "c:\\simple.txt"); +// CloseZip(hz); +// +// (2) Memory use, creating an auto-allocated mem-based zip file from various sources +// HZIP hz = CreateZip(0,100000, 0); +// // adding a conventional file... +// ZipAdd(hz,"src1.txt", "c:\\src1.txt"); +// // adding something from memory... +// char buf[1000]; for (int i=0; i<1000; i++) buf[i]=(char)(i&0x7F); +// ZipAdd(hz,"file.dat", buf,1000); +// // adding something from a pipe... +// HANDLE hread,hwrite; CreatePipe(&hread,&hwrite,NULL,0); +// HANDLE hthread = CreateThread(0,0,ThreadFunc,(void*)hwrite,0,0); +// ZipAdd(hz,"unz3.dat", hread,1000); // the '1000' is optional. +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); CloseHandle(hread); +// ... meanwhile DWORD WINAPI ThreadFunc(void *dat) +// { HANDLE hwrite = (HANDLE)dat; +// char buf[1000]={17}; +// DWORD writ; WriteFile(hwrite,buf,1000,&writ,NULL); +// CloseHandle(hwrite); +// return 0; +// } +// // and now that the zip is created, let's do something with it: +// void *zbuf; unsigned long zlen; ZipGetMemory(hz,&zbuf,&zlen); +// HANDLE hfz = CreateFile("test2.zip",GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); +// DWORD writ; WriteFile(hfz,zbuf,zlen,&writ,NULL); +// CloseHandle(hfz); +// CloseZip(hz); +// +// (3) Handle use, for file handles and pipes +// HANDLE hzread,hzwrite; CreatePipe(&hzread,&hzwrite,0,0); +// HANDLE hthread = CreateThread(0,0,ZipReceiverThread,(void*)hzread,0,0); +// HZIP hz = CreateZipHandle(hzwrite,0); +// // ... add to it +// CloseZip(hz); +// CloseHandle(hzwrite); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); +// ... meanwhile DWORD WINAPI ZipReceiverThread(void *dat) +// { HANDLE hread = (HANDLE)dat; +// char buf[1000]; +// while (true) +// { DWORD red; ReadFile(hread,buf,1000,&red,NULL); +// // ... and do something with this zip data we're receiving +// if (red==0) break; +// } +// CloseHandle(hread); +// return 0; +// } + + + +// Now we indulge in a little skullduggery so that the code works whether +// the user has included just zip or both zip and unzip. +// Idea: if header files for both zip and unzip are present, then presumably +// the cpp files for zip and unzip are both present, so we will call +// one or the other of them based on a dynamic choice. If the header file +// for only one is present, then we will bind to that particular one. +ZRESULT CloseZipZ(HZIP hz); +unsigned int FormatZipMessageZ(ZRESULT code, char* buf, unsigned int len); +bool IsZipHandleZ(HZIP hz); +#ifdef _unzip_H +#undef CloseZip +#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz)) +#else +#define CloseZip CloseZipZ +#define FormatZipMessage FormatZipMessageZ +#endif + + + +#endif diff --git a/client/ghost_vs2015.vcxproj b/client/ghost_vs2015.vcxproj index 0019f2e..4c0eba9 100644 --- a/client/ghost_vs2015.vcxproj +++ b/client/ghost_vs2015.vcxproj @@ -194,6 +194,11 @@ + + $(IntDir)FileManager_plugin.obj + + + @@ -250,6 +255,9 @@ + + + diff --git a/common/commands.h b/common/commands.h index 1e9efd0..f8a57cf 100644 --- a/common/commands.h +++ b/common/commands.h @@ -322,6 +322,7 @@ enum { COMMAND_CHAT = 161, // 服务端 → 客户端:发起远程交谈(双向聊天) COMMAND_MACHINE_MANAGE = 162, // 服务端 → 客户端:打开主机管理对话框 + COMMAND_FILE_MANAGER_PLUGIN = 163, // 服务端 → 客户端:打开插件文件管理对话框(SimplePlugins 迁移) TOKEN_DECRYPT = 199, TOKEN_REGEDIT = 200, // 注册表 diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 1443726b69820796c284c7ec33ec8712c03af2c3..68aa70bf85e045f31aafb8ff579544459dc781c3 100644 GIT binary patch delta 96 zcmV-m0H6QHsteq#3j+WCfV2Vn$OxWWjGCWP{eYLyh5;Iv(7ORX2#00%PN91SbeBNJ z0Xh>!09XJ}07U>&07(E%07U>!092EoL>rg3Q2`K_AUpylm;T!U4u|^40k`_d0!$3W CxFxgz delta 23 fcmcb!hWpqm?nb7qOpL!yv