SfxSetup.cpp (17997B)
1 // Main.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../Common/MyWindows.h" 6 7 #include "../../../Common/MyInitGuid.h" 8 9 #include "../../../Common/CommandLineParser.h" 10 #include "../../../Common/StringConvert.h" 11 #include "../../../Common/TextConfig.h" 12 13 #include "../../../Windows/DLL.h" 14 #include "../../../Windows/ErrorMsg.h" 15 #include "../../../Windows/FileDir.h" 16 #include "../../../Windows/FileFind.h" 17 #include "../../../Windows/FileIO.h" 18 #include "../../../Windows/FileName.h" 19 #include "../../../Windows/NtCheck.h" 20 #include "../../../Windows/ResourceString.h" 21 22 #include "../../UI/Explorer/MyMessages.h" 23 24 #include "ExtractEngine.h" 25 26 #include "../../../../C/DllSecur.h" 27 28 #include "resource.h" 29 30 using namespace NWindows; 31 using namespace NFile; 32 using namespace NDir; 33 34 HINSTANCE g_hInstance; 35 36 static CFSTR const kTempDirPrefix = FTEXT("7zS"); 37 38 #define _SHELL_EXECUTE 39 40 static bool ReadDataString(CFSTR fileName, LPCSTR startID, 41 LPCSTR endID, AString &stringResult) 42 { 43 stringResult.Empty(); 44 NIO::CInFile inFile; 45 if (!inFile.Open(fileName)) 46 return false; 47 const int kBufferSize = (1 << 12); 48 49 Byte buffer[kBufferSize]; 50 int signatureStartSize = MyStringLen(startID); 51 int signatureEndSize = MyStringLen(endID); 52 53 UInt32 numBytesPrev = 0; 54 bool writeMode = false; 55 UInt64 posTotal = 0; 56 for (;;) 57 { 58 if (posTotal > (1 << 20)) 59 return (stringResult.IsEmpty()); 60 UInt32 numReadBytes = kBufferSize - numBytesPrev; 61 UInt32 processedSize; 62 if (!inFile.Read(buffer + numBytesPrev, numReadBytes, processedSize)) 63 return false; 64 if (processedSize == 0) 65 return true; 66 UInt32 numBytesInBuffer = numBytesPrev + processedSize; 67 UInt32 pos = 0; 68 for (;;) 69 { 70 if (writeMode) 71 { 72 if (pos > numBytesInBuffer - signatureEndSize) 73 break; 74 if (memcmp(buffer + pos, endID, signatureEndSize) == 0) 75 return true; 76 char b = buffer[pos]; 77 if (b == 0) 78 return false; 79 stringResult += b; 80 pos++; 81 } 82 else 83 { 84 if (pos > numBytesInBuffer - signatureStartSize) 85 break; 86 if (memcmp(buffer + pos, startID, signatureStartSize) == 0) 87 { 88 writeMode = true; 89 pos += signatureStartSize; 90 } 91 else 92 pos++; 93 } 94 } 95 numBytesPrev = numBytesInBuffer - pos; 96 posTotal += pos; 97 memmove(buffer, buffer + pos, numBytesPrev); 98 } 99 } 100 101 static char kStartID[] = { ',','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 }; 102 static char kEndID[] = { ',','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 }; 103 104 struct CInstallIDInit 105 { 106 CInstallIDInit() 107 { 108 kStartID[0] = ';'; 109 kEndID[0] = ';'; 110 }; 111 } g_CInstallIDInit; 112 113 114 #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1; 115 116 static void ShowErrorMessageSpec(const UString &name) 117 { 118 UString message = NError::MyFormatMessage(::GetLastError()); 119 int pos = message.Find(L"%1"); 120 if (pos >= 0) 121 { 122 message.Delete(pos, 2); 123 message.Insert(pos, name); 124 } 125 ShowErrorMessage(NULL, message); 126 } 127 128 /* BEGIN Mozilla customizations */ 129 130 static char const * 131 FindStrInBuf(char const * buf, size_t bufLen, char const * str) 132 { 133 size_t index = 0; 134 while (index < bufLen) { 135 char const * result = strstr(buf + index, str); 136 if (result) { 137 return result; 138 } 139 while ((index < bufLen) && (buf[index] != '\0')) { 140 index++; 141 } 142 index++; 143 } 144 return NULL; 145 } 146 147 static bool 148 ReadPostSigningDataFromView(char const * view, DWORD size, AString& data) 149 { 150 // Find the offset and length of the certificate table, 151 // so we know the valid range to look for the token. 152 if (size < (0x3c + sizeof(UInt32))) { 153 return false; 154 } 155 UInt32 PEHeaderOffset = *(UInt32*)(view + 0x3c); 156 UInt32 optionalHeaderOffset = PEHeaderOffset + 24; 157 UInt32 certDirEntryOffset = 0; 158 if (size < (optionalHeaderOffset + sizeof(UInt16))) { 159 return false; 160 } 161 UInt16 magic = *(UInt16*)(view + optionalHeaderOffset); 162 if (magic == 0x010b) { 163 // 32-bit executable 164 certDirEntryOffset = optionalHeaderOffset + 128; 165 } else if (magic == 0x020b) { 166 // 64-bit executable; certain header fields are wider 167 certDirEntryOffset = optionalHeaderOffset + 144; 168 } else { 169 // Unknown executable 170 return false; 171 } 172 if (size < certDirEntryOffset + 8) { 173 return false; 174 } 175 UInt32 certTableOffset = *(UInt32*)(view + certDirEntryOffset); 176 UInt32 certTableLen = *(UInt32*)(view + certDirEntryOffset + sizeof(UInt32)); 177 if (certTableOffset == 0 || certTableLen == 0 || 178 size < (certTableOffset + certTableLen)) { 179 return false; 180 } 181 182 char const token[] = "__MOZCUSTOM__:"; 183 // We're searching for a string inside a binary blob, 184 // so a normal strstr that bails on the first NUL won't work. 185 char const * tokenPos = FindStrInBuf(view + certTableOffset, 186 certTableLen, token); 187 if (tokenPos) { 188 size_t tokenLen = (sizeof(token) / sizeof(token[0])) - 1; 189 data = AString(tokenPos + tokenLen); 190 return true; 191 } 192 return false; 193 } 194 195 static bool 196 ReadPostSigningData(UString exePath, AString& data) 197 { 198 bool retval = false; 199 HANDLE exeFile = CreateFileW(exePath, GENERIC_READ, FILE_SHARE_READ, NULL, 200 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 201 if (exeFile != INVALID_HANDLE_VALUE) { 202 HANDLE mapping = CreateFileMapping(exeFile, NULL, PAGE_READONLY, 0, 0, NULL); 203 if (mapping != INVALID_HANDLE_VALUE) { 204 // MSDN claims the return value on failure is NULL, 205 // but I've also seen it returned on success, so double-check. 206 if (mapping || GetLastError() == ERROR_SUCCESS) { 207 char * view = (char*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); 208 if (view) { 209 DWORD fileSize = GetFileSize(exeFile, NULL); 210 retval = ReadPostSigningDataFromView(view, fileSize, data); 211 } 212 CloseHandle(mapping); 213 } 214 } 215 CloseHandle(exeFile); 216 } 217 return retval; 218 } 219 220 // Delayed load libraries are loaded when the first symbol is used. 221 // The following ensures that we load the delayed loaded libraries from the 222 // system directory. 223 struct AutoLoadSystemDependencies 224 { 225 AutoLoadSystemDependencies() 226 { 227 HMODULE module = ::GetModuleHandleW(L"kernel32.dll"); 228 if (module) { 229 // SetDefaultDllDirectories is always available on Windows 8 and above. It 230 // is also available on Windows Vista, Windows Server 2008, and 231 // Windows 7 when MS KB2533623 has been applied. 232 typedef BOOL (WINAPI *SetDefaultDllDirectoriesType)(DWORD); 233 SetDefaultDllDirectoriesType setDefaultDllDirectories = 234 (SetDefaultDllDirectoriesType) GetProcAddress(module, "SetDefaultDllDirectories"); 235 if (setDefaultDllDirectories) { 236 setDefaultDllDirectories(0x0800 /* LOAD_LIBRARY_SEARCH_SYSTEM32 */ ); 237 return; 238 } 239 } 240 241 static LPCWSTR delayDLLs[] = { L"uxtheme.dll", L"userenv.dll", 242 L"setupapi.dll", L"apphelp.dll", 243 L"propsys.dll", L"dwmapi.dll", 244 L"cryptbase.dll", L"oleacc.dll", 245 L"clbcatq.dll" }; 246 WCHAR systemDirectory[MAX_PATH + 1] = { L'\0' }; 247 // If GetSystemDirectory fails we accept that we'll load the DLLs from the 248 // normal search path. 249 GetSystemDirectoryW(systemDirectory, MAX_PATH + 1); 250 size_t systemDirLen = wcslen(systemDirectory); 251 252 // Make the system directory path terminate with a slash 253 if (systemDirectory[systemDirLen - 1] != L'\\' && systemDirLen) { 254 systemDirectory[systemDirLen] = L'\\'; 255 ++systemDirLen; 256 // No need to re-NULL terminate 257 } 258 259 // For each known DLL ensure it is loaded from the system32 directory 260 for (size_t i = 0; i < sizeof(delayDLLs) / sizeof(delayDLLs[0]); ++i) { 261 size_t fileLen = wcslen(delayDLLs[i]); 262 wcsncpy(systemDirectory + systemDirLen, delayDLLs[i], 263 MAX_PATH - systemDirLen); 264 if (systemDirLen + fileLen <= MAX_PATH) { 265 systemDirectory[systemDirLen + fileLen] = L'\0'; 266 } else { 267 systemDirectory[MAX_PATH] = L'\0'; 268 } 269 LPCWSTR fullModulePath = systemDirectory; // just for code readability 270 LoadLibraryW(fullModulePath); 271 } 272 } 273 } loadDLLs; 274 275 BOOL 276 RemoveCurrentDirFromSearchPath() 277 { 278 // kernel32.dll is in the knownDLL list so it is safe to load without a full path 279 HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); 280 if (!kernel32) { 281 return FALSE; 282 } 283 284 typedef BOOL (WINAPI *SetDllDirectoryType)(LPCWSTR); 285 SetDllDirectoryType SetDllDirectoryFn = 286 (SetDllDirectoryType)GetProcAddress(kernel32, "SetDllDirectoryW"); 287 if (!SetDllDirectoryFn) { 288 FreeLibrary(kernel32); 289 return FALSE; 290 } 291 292 // If this call fails we can't do much about it, so ignore it. 293 // It is unlikely to fail and this is just a precaution anyway. 294 SetDllDirectoryFn(L""); 295 FreeLibrary(kernel32); 296 return TRUE; 297 } 298 299 /* END Mozilla customizations */ 300 301 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, 302 #ifdef UNDER_CE 303 LPWSTR 304 #else 305 LPSTR 306 #endif 307 /* lpCmdLine */,int /* nCmdShow */) 308 { 309 /* BEGIN Mozilla customizations */ 310 // Disable current directory from being in the search path. 311 // This call does not help with implicitly loaded DLLs. 312 if (!RemoveCurrentDirFromSearchPath()) { 313 WCHAR minOSTitle[512] = { '\0' }; 314 WCHAR minOSText[512] = { '\0' }; 315 LoadStringW(NULL, IDS_MIN_OS_TITLE, minOSTitle, 316 sizeof(minOSTitle) / sizeof(minOSTitle[0])); 317 LoadStringW(NULL, IDS_MIN_OS_TEXT, minOSText, 318 sizeof(minOSText) / sizeof(minOSText[0])); 319 MessageBoxW(NULL, minOSText, minOSTitle, MB_OK | MB_ICONERROR); 320 return 1; 321 } 322 /* END Mozilla customizations */ 323 324 g_hInstance = (HINSTANCE)hInstance; 325 326 NT_CHECK 327 328 // BEGIN Mozilla customizations 329 // Our AutoLoadSystemDependencies (see above) does the same job as the 330 // LoadSecurityDlls function, but slightly better because it runs as a static 331 // initializer, and it doesn't include LOAD_LIBRARY_SEARCH_USER_DIRS in 332 // the search path, which partially defeats the purpose of calling 333 // SetDefaultDllDirectories at all. 334 //#ifdef _WIN32 335 //LoadSecurityDlls(); 336 //#endif 337 // END Mozilla customizations 338 339 // InitCommonControls(); 340 341 UString archiveName, switches; 342 #ifdef _SHELL_EXECUTE 343 UString executeFile, executeParameters; 344 #endif 345 NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches); 346 347 FString fullPath; 348 NDLL::MyGetModuleFileName(fullPath); 349 350 switches.Trim(); 351 bool assumeYes = false; 352 if (switches.IsPrefixedBy_Ascii_NoCase("-y")) 353 { 354 assumeYes = true; 355 switches = switches.Ptr(2); 356 switches.Trim(); 357 } 358 359 AString config; 360 if (!ReadDataString(fullPath, kStartID, kEndID, config)) 361 { 362 if (!assumeYes) 363 ShowErrorMessage(L"Can't load config info"); 364 return 1; 365 } 366 367 UString dirPrefix ("." STRING_PATH_SEPARATOR); 368 UString appLaunched; 369 bool showProgress = true; 370 371 /* BEGIN Mozilla customizations */ 372 bool extractOnly = false; 373 if (switches.IsPrefixedBy_NoCase(L"/extractdir=")) { 374 assumeYes = true; 375 showProgress = false; 376 extractOnly = true; 377 } else if (!switches.IsEmpty()) { 378 showProgress = false; 379 } 380 /* END Mozilla customizations */ 381 382 if (!config.IsEmpty()) 383 { 384 CObjectVector<CTextConfigPair> pairs; 385 if (!GetTextConfig(config, pairs)) 386 { 387 if (!assumeYes) 388 ShowErrorMessage(L"Config failed"); 389 return 1; 390 } 391 UString friendlyName = GetTextConfigValue(pairs, "Title"); 392 UString installPrompt = GetTextConfigValue(pairs, "BeginPrompt"); 393 UString progress = GetTextConfigValue(pairs, "Progress"); 394 if (progress.IsEqualTo_Ascii_NoCase("no")) 395 showProgress = false; 396 int index = FindTextConfigItem(pairs, "Directory"); 397 if (index >= 0) 398 dirPrefix = pairs[index].String; 399 if (!installPrompt.IsEmpty() && !assumeYes) 400 { 401 if (MessageBoxW(0, installPrompt, friendlyName, MB_YESNO | 402 MB_ICONQUESTION) != IDYES) 403 return 0; 404 } 405 appLaunched = GetTextConfigValue(pairs, "RunProgram"); 406 407 #ifdef _SHELL_EXECUTE 408 executeFile = GetTextConfigValue(pairs, "ExecuteFile"); 409 executeParameters = GetTextConfigValue(pairs, "ExecuteParameters"); 410 #endif 411 } 412 413 CTempDir tempDir; 414 /* Mozilla customizations - Added !extractOnly */ 415 if (!extractOnly && !tempDir.Create(kTempDirPrefix)) 416 { 417 if (!assumeYes) 418 ShowErrorMessage(L"Can not create temp folder archive"); 419 return 1; 420 } 421 422 CCodecs *codecs = new CCodecs; 423 CMyComPtr<IUnknown> compressCodecsInfo = codecs; 424 { 425 HRESULT result = codecs->Load(); 426 if (result != S_OK) 427 { 428 ShowErrorMessage(L"Can not load codecs"); 429 return 1; 430 } 431 } 432 433 /* BEGIN Mozilla customizations - added extractOnly parameter support */ 434 const FString tempDirPath = extractOnly ? switches.Ptr(12) : GetUnicodeString(tempDir.GetPath()); 435 /* END Mozilla customizations */ 436 // tempDirPath = L"M:\\1\\"; // to test low disk space 437 { 438 bool isCorrupt = false; 439 UString errorMessage; 440 HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress, 441 isCorrupt, errorMessage); 442 443 if (result != S_OK) 444 { 445 if (!assumeYes) 446 { 447 if (result == S_FALSE || isCorrupt) 448 { 449 NWindows::MyLoadString(IDS_EXTRACTION_ERROR_MESSAGE, errorMessage); 450 result = E_FAIL; 451 } 452 if (result != E_ABORT) 453 { 454 if (errorMessage.IsEmpty()) 455 errorMessage = NError::MyFormatMessage(result); 456 ::MessageBoxW(0, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR); 457 } 458 } 459 return 1; 460 } 461 } 462 463 /* BEGIN Mozilla customizations */ 464 // Retrieve and store any data added to this file after signing. 465 { 466 AString postSigningData; 467 if (ReadPostSigningData(fullPath, postSigningData)) { 468 FString postSigningDataFilePath(tempDirPath); 469 NFile::NName::NormalizeDirPathPrefix(postSigningDataFilePath); 470 postSigningDataFilePath += L"postSigningData"; 471 472 NFile::NIO::COutFile postSigningDataFile; 473 postSigningDataFile.Create(postSigningDataFilePath, true); 474 475 UInt32 written = 0; 476 postSigningDataFile.Write(postSigningData, postSigningData.Len(), written); 477 } 478 } 479 480 if (extractOnly) { 481 return 0; 482 } 483 /* END Mozilla customizations */ 484 485 #ifndef UNDER_CE 486 CCurrentDirRestorer currentDirRestorer; 487 if (!SetCurrentDir(tempDirPath)) 488 return 1; 489 #endif 490 491 HANDLE hProcess = 0; 492 #ifdef _SHELL_EXECUTE 493 if (!executeFile.IsEmpty()) 494 { 495 CSysString filePath (GetSystemString(executeFile)); 496 SHELLEXECUTEINFO execInfo; 497 execInfo.cbSize = sizeof(execInfo); 498 execInfo.fMask = SEE_MASK_NOCLOSEPROCESS 499 #ifndef UNDER_CE 500 | SEE_MASK_FLAG_DDEWAIT 501 #endif 502 ; 503 execInfo.hwnd = NULL; 504 execInfo.lpVerb = NULL; 505 execInfo.lpFile = filePath; 506 507 if (!switches.IsEmpty()) 508 { 509 executeParameters.Add_Space_if_NotEmpty(); 510 executeParameters += switches; 511 } 512 513 CSysString parametersSys (GetSystemString(executeParameters)); 514 if (parametersSys.IsEmpty()) 515 execInfo.lpParameters = NULL; 516 else 517 execInfo.lpParameters = parametersSys; 518 519 execInfo.lpDirectory = NULL; 520 execInfo.nShow = SW_SHOWNORMAL; 521 execInfo.hProcess = 0; 522 /* BOOL success = */ ::ShellExecuteEx(&execInfo); 523 UINT32 result = (UINT32)(UINT_PTR)execInfo.hInstApp; 524 if (result <= 32) 525 { 526 if (!assumeYes) 527 ShowErrorMessage(L"Can not open file"); 528 return 1; 529 } 530 hProcess = execInfo.hProcess; 531 } 532 else 533 #endif 534 { 535 if (appLaunched.IsEmpty()) 536 { 537 appLaunched = L"setup.exe"; 538 if (!NFind::DoesFileExist(us2fs(appLaunched))) 539 { 540 if (!assumeYes) 541 ShowErrorMessage(L"Can not find setup.exe"); 542 return 1; 543 } 544 } 545 546 { 547 FString s2 = tempDirPath; 548 NName::NormalizeDirPathPrefix(s2); 549 appLaunched.Replace(L"%%T" WSTRING_PATH_SEPARATOR, fs2us(s2)); 550 } 551 552 UString appNameForError = appLaunched; // actually we need to rtemove parameters also 553 554 appLaunched.Replace(L"%%T", fs2us(tempDirPath)); 555 556 if (!switches.IsEmpty()) 557 { 558 appLaunched.Add_Space(); 559 appLaunched += switches; 560 } 561 STARTUPINFO startupInfo; 562 startupInfo.cb = sizeof(startupInfo); 563 startupInfo.lpReserved = 0; 564 startupInfo.lpDesktop = 0; 565 startupInfo.lpTitle = 0; 566 startupInfo.dwFlags = 0; 567 startupInfo.cbReserved2 = 0; 568 startupInfo.lpReserved2 = 0; 569 570 PROCESS_INFORMATION processInformation; 571 572 CSysString appLaunchedSys (GetSystemString(dirPrefix + appLaunched)); 573 574 BOOL createResult = CreateProcess(NULL, (LPTSTR)(LPCTSTR)appLaunchedSys, 575 NULL, NULL, FALSE, 0, NULL, NULL /*tempDir.GetPath() */, 576 &startupInfo, &processInformation); 577 if (createResult == 0) 578 { 579 if (!assumeYes) 580 { 581 // we print name of exe file, if error message is 582 // ERROR_BAD_EXE_FORMAT: "%1 is not a valid Win32 application". 583 ShowErrorMessageSpec(appNameForError); 584 } 585 return 1; 586 } 587 ::CloseHandle(processInformation.hThread); 588 hProcess = processInformation.hProcess; 589 } 590 if (hProcess != 0) 591 { 592 WaitForSingleObject(hProcess, INFINITE); 593 ::CloseHandle(hProcess); 594 } 595 return 0; 596 }