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