Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

617 lines
14 KiB

  1. /* SfxSetup.c - 7z SFX Setup
  2. 2014-12-07 : Igor Pavlov : Public domain */
  3. #include "Precomp.h"
  4. #ifndef UNICODE
  5. #define UNICODE
  6. #endif
  7. #ifndef _UNICODE
  8. #define _UNICODE
  9. #endif
  10. #ifdef _CONSOLE
  11. #include <stdio.h>
  12. #endif
  13. #include "../../7z.h"
  14. #include "../../7zAlloc.h"
  15. #include "../../7zCrc.h"
  16. #include "../../7zFile.h"
  17. #include "../../CpuArch.h"
  18. #define k_EXE_ExtIndex 2
  19. static const char *kExts[] =
  20. {
  21. "bat"
  22. , "cmd"
  23. , "exe"
  24. , "inf"
  25. , "msi"
  26. #ifdef UNDER_CE
  27. , "cab"
  28. #endif
  29. , "html"
  30. , "htm"
  31. };
  32. static const char *kNames[] =
  33. {
  34. "setup"
  35. , "install"
  36. , "run"
  37. , "start"
  38. };
  39. static unsigned FindExt(const wchar_t *s, unsigned *extLen)
  40. {
  41. unsigned len = (unsigned)wcslen(s);
  42. unsigned i;
  43. for (i = len; i > 0; i--)
  44. {
  45. if (s[i - 1] == '.')
  46. {
  47. *extLen = len - i;
  48. return i - 1;
  49. }
  50. }
  51. *extLen = 0;
  52. return len;
  53. }
  54. #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c)))
  55. static unsigned FindItem(const char **items, unsigned num, const wchar_t *s, unsigned len)
  56. {
  57. unsigned i;
  58. for (i = 0; i < num; i++)
  59. {
  60. const char *item = items[i];
  61. unsigned itemLen = (unsigned)strlen(item);
  62. unsigned j;
  63. if (len != itemLen)
  64. continue;
  65. for (j = 0; j < len; j++)
  66. {
  67. unsigned c = item[j];
  68. if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j])
  69. break;
  70. }
  71. if (j == len)
  72. return i;
  73. }
  74. return i;
  75. }
  76. #ifdef _CONSOLE
  77. static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
  78. {
  79. ctrlType = ctrlType;
  80. return TRUE;
  81. }
  82. #endif
  83. static void PrintErrorMessage(const char *message)
  84. {
  85. #ifdef _CONSOLE
  86. printf("\n7-Zip Error: %s\n", message);
  87. #else
  88. #ifdef UNDER_CE
  89. WCHAR messageW[256 + 4];
  90. unsigned i;
  91. for (i = 0; i < 256 && message[i] != 0; i++)
  92. messageW[i] = message[i];
  93. messageW[i] = 0;
  94. MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR);
  95. #else
  96. MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR);
  97. #endif
  98. #endif
  99. }
  100. static WRes MyCreateDir(const WCHAR *name)
  101. {
  102. return CreateDirectoryW(name, NULL) ? 0 : GetLastError();
  103. }
  104. #ifdef UNDER_CE
  105. #define kBufferSize (1 << 13)
  106. #else
  107. #define kBufferSize (1 << 15)
  108. #endif
  109. #define kSignatureSearchLimit (1 << 22)
  110. static Bool FindSignature(CSzFile *stream, UInt64 *resPos)
  111. {
  112. Byte buf[kBufferSize];
  113. size_t numPrevBytes = 0;
  114. *resPos = 0;
  115. for (;;)
  116. {
  117. size_t processed, pos;
  118. if (*resPos > kSignatureSearchLimit)
  119. return False;
  120. processed = kBufferSize - numPrevBytes;
  121. if (File_Read(stream, buf + numPrevBytes, &processed) != 0)
  122. return False;
  123. processed += numPrevBytes;
  124. if (processed < k7zStartHeaderSize ||
  125. (processed == k7zStartHeaderSize && numPrevBytes != 0))
  126. return False;
  127. processed -= k7zStartHeaderSize;
  128. for (pos = 0; pos <= processed; pos++)
  129. {
  130. for (; buf[pos] != '7' && pos <= processed; pos++);
  131. if (pos > processed)
  132. break;
  133. if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0)
  134. if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8))
  135. {
  136. *resPos += pos;
  137. return True;
  138. }
  139. }
  140. *resPos += processed;
  141. numPrevBytes = k7zStartHeaderSize;
  142. memmove(buf, buf + processed, k7zStartHeaderSize);
  143. }
  144. }
  145. static Bool DoesFileOrDirExist(const WCHAR *path)
  146. {
  147. WIN32_FIND_DATAW fd;
  148. HANDLE handle;
  149. handle = FindFirstFileW(path, &fd);
  150. if (handle == INVALID_HANDLE_VALUE)
  151. return False;
  152. FindClose(handle);
  153. return True;
  154. }
  155. static WRes RemoveDirWithSubItems(WCHAR *path)
  156. {
  157. WIN32_FIND_DATAW fd;
  158. HANDLE handle;
  159. WRes res = 0;
  160. size_t len = wcslen(path);
  161. wcscpy(path + len, L"*");
  162. handle = FindFirstFileW(path, &fd);
  163. path[len] = L'\0';
  164. if (handle == INVALID_HANDLE_VALUE)
  165. return GetLastError();
  166. for (;;)
  167. {
  168. if (wcscmp(fd.cFileName, L".") != 0 &&
  169. wcscmp(fd.cFileName, L"..") != 0)
  170. {
  171. wcscpy(path + len, fd.cFileName);
  172. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
  173. {
  174. wcscat(path, WSTRING_PATH_SEPARATOR);
  175. res = RemoveDirWithSubItems(path);
  176. }
  177. else
  178. {
  179. SetFileAttributesW(path, 0);
  180. if (DeleteFileW(path) == 0)
  181. res = GetLastError();
  182. }
  183. if (res != 0)
  184. break;
  185. }
  186. if (!FindNextFileW(handle, &fd))
  187. {
  188. res = GetLastError();
  189. if (res == ERROR_NO_MORE_FILES)
  190. res = 0;
  191. break;
  192. }
  193. }
  194. path[len] = L'\0';
  195. FindClose(handle);
  196. if (res == 0)
  197. {
  198. if (!RemoveDirectoryW(path))
  199. res = GetLastError();
  200. }
  201. return res;
  202. }
  203. #ifdef _CONSOLE
  204. int MY_CDECL main()
  205. #else
  206. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  207. #ifdef UNDER_CE
  208. LPWSTR
  209. #else
  210. LPSTR
  211. #endif
  212. lpCmdLine, int nCmdShow)
  213. #endif
  214. {
  215. CFileInStream archiveStream;
  216. CLookToRead lookStream;
  217. CSzArEx db;
  218. SRes res = SZ_OK;
  219. ISzAlloc allocImp;
  220. ISzAlloc allocTempImp;
  221. WCHAR sfxPath[MAX_PATH + 2];
  222. WCHAR path[MAX_PATH * 3 + 2];
  223. #ifndef UNDER_CE
  224. WCHAR workCurDir[MAX_PATH + 32];
  225. #endif
  226. size_t pathLen;
  227. DWORD winRes;
  228. const wchar_t *cmdLineParams;
  229. const char *errorMessage = NULL;
  230. Bool useShellExecute = True;
  231. #ifdef _CONSOLE
  232. SetConsoleCtrlHandler(HandlerRoutine, TRUE);
  233. #else
  234. hInstance = hInstance;
  235. hPrevInstance = hPrevInstance;
  236. lpCmdLine = lpCmdLine;
  237. nCmdShow = nCmdShow;
  238. #endif
  239. CrcGenerateTable();
  240. allocImp.Alloc = SzAlloc;
  241. allocImp.Free = SzFree;
  242. allocTempImp.Alloc = SzAllocTemp;
  243. allocTempImp.Free = SzFreeTemp;
  244. FileInStream_CreateVTable(&archiveStream);
  245. LookToRead_CreateVTable(&lookStream, False);
  246. winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
  247. if (winRes == 0 || winRes > MAX_PATH)
  248. return 1;
  249. {
  250. cmdLineParams = GetCommandLineW();
  251. #ifndef UNDER_CE
  252. {
  253. Bool quoteMode = False;
  254. for (;; cmdLineParams++)
  255. {
  256. wchar_t c = *cmdLineParams;
  257. if (c == L'\"')
  258. quoteMode = !quoteMode;
  259. else if (c == 0 || (c == L' ' && !quoteMode))
  260. break;
  261. }
  262. }
  263. #endif
  264. }
  265. {
  266. unsigned i;
  267. DWORD d;
  268. winRes = GetTempPathW(MAX_PATH, path);
  269. if (winRes == 0 || winRes > MAX_PATH)
  270. return 1;
  271. pathLen = wcslen(path);
  272. d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
  273. for (i = 0;; i++, d += GetTickCount())
  274. {
  275. if (i >= 100)
  276. {
  277. res = SZ_ERROR_FAIL;
  278. break;
  279. }
  280. wcscpy(path + pathLen, L"7z");
  281. {
  282. wchar_t *s = path + wcslen(path);
  283. UInt32 value = d;
  284. unsigned k;
  285. for (k = 0; k < 8; k++)
  286. {
  287. unsigned t = value & 0xF;
  288. value >>= 4;
  289. s[7 - k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
  290. }
  291. s[k] = '\0';
  292. }
  293. if (DoesFileOrDirExist(path))
  294. continue;
  295. if (CreateDirectoryW(path, NULL))
  296. {
  297. wcscat(path, WSTRING_PATH_SEPARATOR);
  298. pathLen = wcslen(path);
  299. break;
  300. }
  301. if (GetLastError() != ERROR_ALREADY_EXISTS)
  302. {
  303. res = SZ_ERROR_FAIL;
  304. break;
  305. }
  306. }
  307. #ifndef UNDER_CE
  308. wcscpy(workCurDir, path);
  309. #endif
  310. if (res != SZ_OK)
  311. errorMessage = "Can't create temp folder";
  312. }
  313. if (res != SZ_OK)
  314. {
  315. if (!errorMessage)
  316. errorMessage = "Error";
  317. PrintErrorMessage(errorMessage);
  318. return 1;
  319. }
  320. if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
  321. {
  322. errorMessage = "can not open input file";
  323. res = SZ_ERROR_FAIL;
  324. }
  325. else
  326. {
  327. UInt64 pos = 0;
  328. if (!FindSignature(&archiveStream.file, &pos))
  329. res = SZ_ERROR_FAIL;
  330. else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)
  331. res = SZ_ERROR_FAIL;
  332. if (res != 0)
  333. errorMessage = "Can't find 7z archive";
  334. }
  335. if (res == SZ_OK)
  336. {
  337. lookStream.realStream = &archiveStream.s;
  338. LookToRead_Init(&lookStream);
  339. }
  340. SzArEx_Init(&db);
  341. if (res == SZ_OK)
  342. {
  343. res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
  344. }
  345. if (res == SZ_OK)
  346. {
  347. UInt32 executeFileIndex = (UInt32)(Int32)-1;
  348. UInt32 minPrice = 1 << 30;
  349. UInt32 i;
  350. UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
  351. Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
  352. size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */
  353. for (i = 0; i < db.NumFiles; i++)
  354. {
  355. size_t offset = 0;
  356. size_t outSizeProcessed = 0;
  357. size_t len;
  358. WCHAR *temp;
  359. len = SzArEx_GetFileNameUtf16(&db, i, NULL);
  360. if (len >= MAX_PATH)
  361. {
  362. res = SZ_ERROR_FAIL;
  363. break;
  364. }
  365. temp = path + pathLen;
  366. SzArEx_GetFileNameUtf16(&db, i, temp);
  367. {
  368. res = SzArEx_Extract(&db, &lookStream.s, i,
  369. &blockIndex, &outBuffer, &outBufferSize,
  370. &offset, &outSizeProcessed,
  371. &allocImp, &allocTempImp);
  372. if (res != SZ_OK)
  373. break;
  374. }
  375. {
  376. CSzFile outFile;
  377. size_t processedSize;
  378. size_t j;
  379. size_t nameStartPos = 0;
  380. for (j = 0; temp[j] != 0; j++)
  381. {
  382. if (temp[j] == '/')
  383. {
  384. temp[j] = 0;
  385. MyCreateDir(path);
  386. temp[j] = CHAR_PATH_SEPARATOR;
  387. nameStartPos = j + 1;
  388. }
  389. }
  390. if (SzArEx_IsDir(&db, i))
  391. {
  392. MyCreateDir(path);
  393. continue;
  394. }
  395. else
  396. {
  397. unsigned extLen;
  398. const WCHAR *name = temp + nameStartPos;
  399. unsigned len = (unsigned)wcslen(name);
  400. unsigned nameLen = FindExt(temp + nameStartPos, &extLen);
  401. unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);
  402. unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);
  403. unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
  404. if (minPrice > price)
  405. {
  406. minPrice = price;
  407. executeFileIndex = i;
  408. useShellExecute = (extPrice != k_EXE_ExtIndex);
  409. }
  410. if (DoesFileOrDirExist(path))
  411. {
  412. errorMessage = "Duplicate file";
  413. res = SZ_ERROR_FAIL;
  414. break;
  415. }
  416. if (OutFile_OpenW(&outFile, path))
  417. {
  418. errorMessage = "Can't open output file";
  419. res = SZ_ERROR_FAIL;
  420. break;
  421. }
  422. }
  423. processedSize = outSizeProcessed;
  424. if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
  425. {
  426. errorMessage = "Can't write output file";
  427. res = SZ_ERROR_FAIL;
  428. }
  429. #ifdef USE_WINDOWS_FILE
  430. if (SzBitWithVals_Check(&db.MTime, i))
  431. {
  432. const CNtfsFileTime *t = db.MTime.Vals + i;
  433. FILETIME mTime;
  434. mTime.dwLowDateTime = t->Low;
  435. mTime.dwHighDateTime = t->High;
  436. SetFileTime(outFile.handle, NULL, NULL, &mTime);
  437. }
  438. #endif
  439. {
  440. SRes res2 = File_Close(&outFile);
  441. if (res != SZ_OK)
  442. break;
  443. if (res2 != SZ_OK)
  444. {
  445. res = res2;
  446. break;
  447. }
  448. }
  449. #ifdef USE_WINDOWS_FILE
  450. if (SzBitWithVals_Check(&db.Attribs, i))
  451. SetFileAttributesW(path, db.Attribs.Vals[i]);
  452. #endif
  453. }
  454. }
  455. if (res == SZ_OK)
  456. {
  457. if (executeFileIndex == (UInt32)(Int32)-1)
  458. {
  459. errorMessage = "There is no file to execute";
  460. res = SZ_ERROR_FAIL;
  461. }
  462. else
  463. {
  464. WCHAR *temp = path + pathLen;
  465. UInt32 j;
  466. SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);
  467. for (j = 0; temp[j] != 0; j++)
  468. if (temp[j] == '/')
  469. temp[j] = CHAR_PATH_SEPARATOR;
  470. }
  471. }
  472. IAlloc_Free(&allocImp, outBuffer);
  473. }
  474. SzArEx_Free(&db, &allocImp);
  475. File_Close(&archiveStream.file);
  476. if (res == SZ_OK)
  477. {
  478. HANDLE hProcess = 0;
  479. #ifndef UNDER_CE
  480. WCHAR oldCurDir[MAX_PATH + 2];
  481. oldCurDir[0] = 0;
  482. {
  483. DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);
  484. if (needLen == 0 || needLen > MAX_PATH)
  485. oldCurDir[0] = 0;
  486. SetCurrentDirectory(workCurDir);
  487. }
  488. #endif
  489. if (useShellExecute)
  490. {
  491. SHELLEXECUTEINFO ei;
  492. UINT32 executeRes;
  493. BOOL success;
  494. memset(&ei, 0, sizeof(ei));
  495. ei.cbSize = sizeof(ei);
  496. ei.lpFile = path;
  497. ei.fMask = SEE_MASK_NOCLOSEPROCESS
  498. #ifndef UNDER_CE
  499. | SEE_MASK_FLAG_DDEWAIT
  500. #endif
  501. /* | SEE_MASK_NO_CONSOLE */
  502. ;
  503. if (wcslen(cmdLineParams) != 0)
  504. ei.lpParameters = cmdLineParams;
  505. ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */
  506. success = ShellExecuteEx(&ei);
  507. executeRes = (UINT32)(UINT_PTR)ei.hInstApp;
  508. if (!success || (executeRes <= 32 && executeRes != 0)) /* executeRes = 0 in Windows CE */
  509. res = SZ_ERROR_FAIL;
  510. else
  511. hProcess = ei.hProcess;
  512. }
  513. else
  514. {
  515. STARTUPINFOW si;
  516. PROCESS_INFORMATION pi;
  517. WCHAR cmdLine[MAX_PATH * 3];
  518. wcscpy(cmdLine, path);
  519. wcscat(cmdLine, cmdLineParams);
  520. memset(&si, 0, sizeof(si));
  521. si.cb = sizeof(si);
  522. if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
  523. res = SZ_ERROR_FAIL;
  524. else
  525. {
  526. CloseHandle(pi.hThread);
  527. hProcess = pi.hProcess;
  528. }
  529. }
  530. if (hProcess != 0)
  531. {
  532. WaitForSingleObject(hProcess, INFINITE);
  533. CloseHandle(hProcess);
  534. }
  535. #ifndef UNDER_CE
  536. SetCurrentDirectory(oldCurDir);
  537. #endif
  538. }
  539. path[pathLen] = L'\0';
  540. RemoveDirWithSubItems(path);
  541. if (res == SZ_OK)
  542. return 0;
  543. {
  544. if (res == SZ_ERROR_UNSUPPORTED)
  545. errorMessage = "Decoder doesn't support this archive";
  546. else if (res == SZ_ERROR_MEM)
  547. errorMessage = "Can't allocate required memory";
  548. else if (res == SZ_ERROR_CRC)
  549. errorMessage = "CRC error";
  550. else
  551. {
  552. if (!errorMessage)
  553. errorMessage = "ERROR";
  554. }
  555. if (errorMessage)
  556. PrintErrorMessage(errorMessage);
  557. }
  558. return 1;
  559. }