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