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