1 /* 2 * PROJECT: ReactOS Zip Shell Extension 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Create a zip file 5 * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) 6 * Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 7 */ 8 9 #include "precomp.h" 10 #include "atlsimpcoll.h" 11 #include "minizip/zip.h" 12 #include "minizip/iowin32.h" 13 #include <process.h> 14 15 static CStringW DoGetZipName(LPCWSTR filename) 16 { 17 WCHAR szPath[MAX_PATH]; 18 StringCbCopyW(szPath, sizeof(szPath), filename); 19 PathRemoveExtensionW(szPath); 20 21 CStringW ret = szPath; 22 ret += L".zip"; 23 24 UINT i = 2; 25 while (PathFileExistsW(ret)) 26 { 27 CStringW str; 28 str.Format(L" (%u).zip", i++); 29 30 ret = szPath; 31 ret += str; 32 } 33 34 return ret; 35 } 36 37 static CStringA DoGetAnsiName(LPCWSTR filename) 38 { 39 CHAR buf[MAX_PATH]; 40 WideCharToMultiByte(CP_ACP, 0, filename, -1, buf, _countof(buf), NULL, NULL); 41 return buf; 42 } 43 44 static CStringW DoGetBaseName(LPCWSTR filename) 45 { 46 WCHAR szBaseName[MAX_PATH]; 47 StringCbCopyW(szBaseName, sizeof(szBaseName), filename); 48 PathRemoveFileSpecW(szBaseName); 49 PathAddBackslashW(szBaseName); 50 return szBaseName; 51 } 52 53 static CStringA 54 DoGetNameInZip(const CStringW& basename, const CStringW& filename) 55 { 56 CStringW basenameI = basename, filenameI = filename; 57 basenameI.MakeUpper(); 58 filenameI.MakeUpper(); 59 60 CStringW ret; 61 if (filenameI.Find(basenameI) == 0) 62 ret = filename.Mid(basename.GetLength()); 63 else 64 ret = filename; 65 66 ret.Replace(L'\\', L'/'); 67 68 return DoGetAnsiName(ret); 69 } 70 71 static BOOL 72 DoReadAllOfFile(LPCWSTR filename, CSimpleArray<BYTE>& contents, 73 zip_fileinfo *pzi) 74 { 75 contents.RemoveAll(); 76 77 HANDLE hFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, 78 NULL, OPEN_EXISTING, 79 FILE_FLAG_SEQUENTIAL_SCAN, NULL); 80 if (hFile == INVALID_HANDLE_VALUE) 81 { 82 DPRINT1("%S: cannot open\n", filename); 83 return FALSE; 84 } 85 86 FILETIME ft, ftLocal; 87 ZeroMemory(pzi, sizeof(*pzi)); 88 if (GetFileTime(hFile, NULL, NULL, &ft)) 89 { 90 SYSTEMTIME st; 91 FileTimeToLocalFileTime(&ft, &ftLocal); 92 FileTimeToSystemTime(&ftLocal, &st); 93 pzi->tmz_date.tm_sec = st.wSecond; 94 pzi->tmz_date.tm_min = st.wMinute; 95 pzi->tmz_date.tm_hour = st.wHour; 96 pzi->tmz_date.tm_mday = st.wDay; 97 pzi->tmz_date.tm_mon = st.wMonth - 1; 98 pzi->tmz_date.tm_year = st.wYear; 99 } 100 101 const DWORD cbBuff = 0x7FFF; 102 LPBYTE pbBuff = reinterpret_cast<LPBYTE>(CoTaskMemAlloc(cbBuff)); 103 if (!pbBuff) 104 { 105 DPRINT1("Out of memory\n"); 106 CloseHandle(hFile); 107 return FALSE; 108 } 109 110 for (;;) 111 { 112 DWORD cbRead; 113 if (!ReadFile(hFile, pbBuff, cbBuff, &cbRead, NULL) || !cbRead) 114 break; 115 116 for (DWORD i = 0; i < cbRead; ++i) 117 contents.Add(pbBuff[i]); 118 } 119 120 CoTaskMemFree(pbBuff); 121 CloseHandle(hFile); 122 123 return TRUE; 124 } 125 126 static void 127 DoAddFilesFromItem(CSimpleArray<CStringW>& files, LPCWSTR item) 128 { 129 if (!PathIsDirectoryW(item)) 130 { 131 files.Add(item); 132 return; 133 } 134 135 WCHAR szPath[MAX_PATH]; 136 StringCbCopyW(szPath, sizeof(szPath), item); 137 PathAppendW(szPath, L"*"); 138 139 WIN32_FIND_DATAW find; 140 HANDLE hFind = FindFirstFileW(szPath, &find); 141 if (hFind == INVALID_HANDLE_VALUE) 142 return; 143 144 do 145 { 146 if (wcscmp(find.cFileName, L".") == 0 || 147 wcscmp(find.cFileName, L"..") == 0) 148 { 149 continue; 150 } 151 152 StringCbCopyW(szPath, sizeof(szPath), item); 153 PathAppendW(szPath, find.cFileName); 154 155 if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 156 DoAddFilesFromItem(files, szPath); 157 else 158 files.Add(szPath); 159 } while (FindNextFileW(hFind, &find)); 160 161 FindClose(hFind); 162 } 163 164 struct CZipCreatorImpl 165 { 166 CSimpleArray<CStringW> m_items; 167 168 unsigned JustDoIt(); 169 }; 170 171 CZipCreator::CZipCreator() : m_pimpl(new CZipCreatorImpl) 172 { 173 InterlockedIncrement(&g_ModuleRefCnt); 174 } 175 176 CZipCreator::~CZipCreator() 177 { 178 InterlockedDecrement(&g_ModuleRefCnt); 179 delete m_pimpl; 180 } 181 182 static unsigned __stdcall 183 create_zip_function(void *arg) 184 { 185 CZipCreator *pCreator = reinterpret_cast<CZipCreator *>(arg); 186 return pCreator->m_pimpl->JustDoIt(); 187 } 188 189 BOOL CZipCreator::runThread(CZipCreator *pCreator) 190 { 191 unsigned tid = 0; 192 HANDLE hThread = reinterpret_cast<HANDLE>( 193 _beginthreadex(NULL, 0, create_zip_function, pCreator, 0, &tid)); 194 195 if (hThread) 196 { 197 CloseHandle(hThread); 198 return TRUE; 199 } 200 201 DPRINT1("hThread == NULL\n"); 202 203 CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE)); 204 CStringW strText(MAKEINTRESOURCEW(IDS_CANTSTARTTHREAD)); 205 MessageBoxW(NULL, strText, strTitle, MB_ICONERROR); 206 207 delete pCreator; 208 return FALSE; 209 } 210 211 void CZipCreator::DoAddItem(LPCWSTR pszFile) 212 { 213 // canonicalize path 214 WCHAR szPath[MAX_PATH]; 215 GetFullPathNameW(pszFile, _countof(szPath), szPath, NULL); 216 217 m_pimpl->m_items.Add(szPath); 218 } 219 220 enum CZC_ERROR 221 { 222 CZCERR_ZEROITEMS = 1, 223 CZCERR_NOFILES, 224 CZCERR_CREATE, 225 CZCERR_READ 226 }; 227 228 unsigned CZipCreatorImpl::JustDoIt() 229 { 230 // TODO: Show progress. 231 232 if (m_items.GetSize() <= 0) 233 { 234 DPRINT1("GetSize() <= 0\n"); 235 return CZCERR_ZEROITEMS; 236 } 237 238 CSimpleArray<CStringW> files; 239 for (INT iItem = 0; iItem < m_items.GetSize(); ++iItem) 240 { 241 DoAddFilesFromItem(files, m_items[iItem]); 242 } 243 244 if (files.GetSize() <= 0) 245 { 246 DPRINT1("files.GetSize() <= 0\n"); 247 248 CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE)); 249 CStringW strText; 250 strText.Format(IDS_NOFILES, static_cast<LPCWSTR>(m_items[0])); 251 MessageBoxW(NULL, strText, strTitle, MB_ICONERROR); 252 253 return CZCERR_NOFILES; 254 } 255 256 zlib_filefunc64_def ffunc; 257 fill_win32_filefunc64W(&ffunc); 258 259 CStringW strZipName = DoGetZipName(m_items[0]); 260 zipFile zf = zipOpen2_64(strZipName, APPEND_STATUS_CREATE, NULL, &ffunc); 261 if (zf == 0) 262 { 263 DPRINT1("zf == 0\n"); 264 265 int err = CZCERR_CREATE; 266 267 CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE)); 268 CStringW strText; 269 strText.Format(IDS_CANTCREATEZIP, static_cast<LPCWSTR>(strZipName), err); 270 MessageBoxW(NULL, strText, strTitle, MB_ICONERROR); 271 272 return err; 273 } 274 275 // TODO: password 276 const char *password = NULL; 277 int zip64 = 1; // always zip64 278 zip_fileinfo zi; 279 280 int err = 0; 281 CStringW strTarget, strBaseName = DoGetBaseName(m_items[0]); 282 for (INT iFile = 0; iFile < files.GetSize(); ++iFile) 283 { 284 const CStringW& strFile = files[iFile]; 285 286 CSimpleArray<BYTE> contents; 287 if (!DoReadAllOfFile(strFile, contents, &zi)) 288 { 289 DPRINT1("DoReadAllOfFile failed\n"); 290 err = CZCERR_READ; 291 strTarget = strFile; 292 break; 293 } 294 295 unsigned long crc = 0; 296 if (password) 297 { 298 // TODO: crc = ...; 299 } 300 301 CStringA strNameInZip = DoGetNameInZip(strBaseName, strFile); 302 err = zipOpenNewFileInZip3_64(zf, 303 strNameInZip, 304 &zi, 305 NULL, 306 0, 307 NULL, 308 0, 309 NULL, 310 Z_DEFLATED, 311 Z_DEFAULT_COMPRESSION, 312 0, 313 -MAX_WBITS, 314 DEF_MEM_LEVEL, 315 Z_DEFAULT_STRATEGY, 316 password, 317 crc, 318 zip64); 319 if (err) 320 { 321 DPRINT1("zipOpenNewFileInZip3_64\n"); 322 break; 323 } 324 325 err = zipWriteInFileInZip(zf, contents.GetData(), contents.GetSize()); 326 if (err) 327 { 328 DPRINT1("zipWriteInFileInZip\n"); 329 break; 330 } 331 332 err = zipCloseFileInZip(zf); 333 if (err) 334 { 335 DPRINT1("zipCloseFileInZip\n"); 336 break; 337 } 338 } 339 340 zipClose(zf, NULL); 341 342 if (err) 343 { 344 DeleteFileW(strZipName); 345 346 CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE)); 347 348 CStringW strText; 349 if (err < 0) 350 strText.Format(IDS_CANTCREATEZIP, static_cast<LPCWSTR>(strZipName), err); 351 else 352 strText.Format(IDS_CANTREADFILE, static_cast<LPCWSTR>(strTarget)); 353 354 MessageBoxW(NULL, strText, strTitle, MB_ICONERROR); 355 } 356 else 357 { 358 WCHAR szFullPath[MAX_PATH]; 359 GetFullPathNameW(strZipName, _countof(szFullPath), szFullPath, NULL); 360 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, szFullPath, NULL); 361 } 362 363 return err; 364 } 365