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