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