| /* SfxSetup.c - 7z SFX Setup |
| 2015-11-08 : Igor Pavlov : Public domain */ |
| |
| #include "Precomp.h" |
| |
| #ifndef UNICODE |
| #define UNICODE |
| #endif |
| |
| #ifndef _UNICODE |
| #define _UNICODE |
| #endif |
| |
| #include <windows.h> |
| #include <shellapi.h> |
| |
| #ifdef _CONSOLE |
| #include <stdio.h> |
| #endif |
| |
| #include "7z.h" |
| #include "7zAlloc.h" |
| #include "7zCrc.h" |
| #include "7zFile.h" |
| #include "CpuArch.h" |
| |
| #define k_EXE_ExtIndex 2 |
| |
| static const char * const kExts[] = |
| { |
| "bat" |
| , "cmd" |
| , "exe" |
| , "inf" |
| , "msi" |
| #ifdef UNDER_CE |
| , "cab" |
| #endif |
| , "html" |
| , "htm" |
| }; |
| |
| static const char * const kNames[] = |
| { |
| "setup" |
| , "install" |
| , "run" |
| , "start" |
| }; |
| |
| static unsigned FindExt(const wchar_t *s, unsigned *extLen) |
| { |
| unsigned len = (unsigned)wcslen(s); |
| unsigned i; |
| for (i = len; i > 0; i--) |
| { |
| if (s[i - 1] == '.') |
| { |
| *extLen = len - i; |
| return i - 1; |
| } |
| } |
| *extLen = 0; |
| return len; |
| } |
| |
| #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c))) |
| |
| static unsigned FindItem(const char * const *items, unsigned num, const wchar_t *s, unsigned len) |
| { |
| unsigned i; |
| for (i = 0; i < num; i++) |
| { |
| const char *item = items[i]; |
| unsigned itemLen = (unsigned)strlen(item); |
| unsigned j; |
| if (len != itemLen) |
| continue; |
| for (j = 0; j < len; j++) |
| { |
| unsigned c = (Byte)item[j]; |
| if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j]) |
| break; |
| } |
| if (j == len) |
| return i; |
| } |
| return i; |
| } |
| |
| #ifdef _CONSOLE |
| static BOOL WINAPI HandlerRoutine(DWORD ctrlType) |
| { |
| UNUSED_VAR(ctrlType); |
| return TRUE; |
| } |
| #endif |
| |
| static void PrintErrorMessage(const char *message) |
| { |
| #ifdef _CONSOLE |
| printf("\n7-Zip Error: %s\n", message); |
| #else |
| #ifdef UNDER_CE |
| WCHAR messageW[256 + 4]; |
| unsigned i; |
| for (i = 0; i < 256 && message[i] != 0; i++) |
| messageW[i] = message[i]; |
| messageW[i] = 0; |
| MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR); |
| #else |
| MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR); |
| #endif |
| #endif |
| } |
| |
| static WRes MyCreateDir(const WCHAR *name) |
| { |
| return CreateDirectoryW(name, NULL) ? 0 : GetLastError(); |
| } |
| |
| #ifdef UNDER_CE |
| #define kBufferSize (1 << 13) |
| #else |
| #define kBufferSize (1 << 15) |
| #endif |
| |
| #define kSignatureSearchLimit (1 << 22) |
| |
| static Bool FindSignature(CSzFile *stream, UInt64 *resPos) |
| { |
| Byte buf[kBufferSize]; |
| size_t numPrevBytes = 0; |
| *resPos = 0; |
| for (;;) |
| { |
| size_t processed, pos; |
| if (*resPos > kSignatureSearchLimit) |
| return False; |
| processed = kBufferSize - numPrevBytes; |
| if (File_Read(stream, buf + numPrevBytes, &processed) != 0) |
| return False; |
| processed += numPrevBytes; |
| if (processed < k7zStartHeaderSize || |
| (processed == k7zStartHeaderSize && numPrevBytes != 0)) |
| return False; |
| processed -= k7zStartHeaderSize; |
| for (pos = 0; pos <= processed; pos++) |
| { |
| for (; pos <= processed && buf[pos] != '7'; pos++); |
| if (pos > processed) |
| break; |
| if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0) |
| if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8)) |
| { |
| *resPos += pos; |
| return True; |
| } |
| } |
| *resPos += processed; |
| numPrevBytes = k7zStartHeaderSize; |
| memmove(buf, buf + processed, k7zStartHeaderSize); |
| } |
| } |
| |
| static Bool DoesFileOrDirExist(const WCHAR *path) |
| { |
| WIN32_FIND_DATAW fd; |
| HANDLE handle; |
| handle = FindFirstFileW(path, &fd); |
| if (handle == INVALID_HANDLE_VALUE) |
| return False; |
| FindClose(handle); |
| return True; |
| } |
| |
| static WRes RemoveDirWithSubItems(WCHAR *path) |
| { |
| WIN32_FIND_DATAW fd; |
| HANDLE handle; |
| WRes res = 0; |
| size_t len = wcslen(path); |
| wcscpy(path + len, L"*"); |
| handle = FindFirstFileW(path, &fd); |
| path[len] = L'\0'; |
| if (handle == INVALID_HANDLE_VALUE) |
| return GetLastError(); |
| |
| for (;;) |
| { |
| if (wcscmp(fd.cFileName, L".") != 0 && |
| wcscmp(fd.cFileName, L"..") != 0) |
| { |
| wcscpy(path + len, fd.cFileName); |
| if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) |
| { |
| wcscat(path, WSTRING_PATH_SEPARATOR); |
| res = RemoveDirWithSubItems(path); |
| } |
| else |
| { |
| SetFileAttributesW(path, 0); |
| if (DeleteFileW(path) == 0) |
| res = GetLastError(); |
| } |
| |
| if (res != 0) |
| break; |
| } |
| |
| if (!FindNextFileW(handle, &fd)) |
| { |
| res = GetLastError(); |
| if (res == ERROR_NO_MORE_FILES) |
| res = 0; |
| break; |
| } |
| } |
| |
| path[len] = L'\0'; |
| FindClose(handle); |
| if (res == 0) |
| { |
| if (!RemoveDirectoryW(path)) |
| res = GetLastError(); |
| } |
| return res; |
| } |
| |
| #ifdef _CONSOLE |
| int MY_CDECL main() |
| #else |
| int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, |
| #ifdef UNDER_CE |
| LPWSTR |
| #else |
| LPSTR |
| #endif |
| lpCmdLine, int nCmdShow) |
| #endif |
| { |
| CFileInStream archiveStream; |
| CLookToRead lookStream; |
| CSzArEx db; |
| SRes res = SZ_OK; |
| ISzAlloc allocImp; |
| ISzAlloc allocTempImp; |
| WCHAR sfxPath[MAX_PATH + 2]; |
| WCHAR path[MAX_PATH * 3 + 2]; |
| #ifndef UNDER_CE |
| WCHAR workCurDir[MAX_PATH + 32]; |
| #endif |
| size_t pathLen; |
| DWORD winRes; |
| const wchar_t *cmdLineParams; |
| const char *errorMessage = NULL; |
| Bool useShellExecute = True; |
| DWORD exitCode = 0; |
| |
| #ifdef _CONSOLE |
| SetConsoleCtrlHandler(HandlerRoutine, TRUE); |
| #else |
| UNUSED_VAR(hInstance); |
| UNUSED_VAR(hPrevInstance); |
| UNUSED_VAR(lpCmdLine); |
| UNUSED_VAR(nCmdShow); |
| #endif |
| |
| CrcGenerateTable(); |
| |
| allocImp.Alloc = SzAlloc; |
| allocImp.Free = SzFree; |
| |
| allocTempImp.Alloc = SzAllocTemp; |
| allocTempImp.Free = SzFreeTemp; |
| |
| FileInStream_CreateVTable(&archiveStream); |
| LookToRead_CreateVTable(&lookStream, False); |
| |
| winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH); |
| if (winRes == 0 || winRes > MAX_PATH) |
| return 1; |
| { |
| cmdLineParams = GetCommandLineW(); |
| #ifndef UNDER_CE |
| { |
| Bool quoteMode = False; |
| for (;; cmdLineParams++) |
| { |
| wchar_t c = *cmdLineParams; |
| if (c == L'\"') |
| quoteMode = !quoteMode; |
| else if (c == 0 || (c == L' ' && !quoteMode)) |
| break; |
| } |
| } |
| #endif |
| } |
| |
| { |
| unsigned i; |
| DWORD d; |
| winRes = GetTempPathW(MAX_PATH, path); |
| if (winRes == 0 || winRes > MAX_PATH) |
| return 1; |
| pathLen = wcslen(path); |
| d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId(); |
| |
| for (i = 0;; i++, d += GetTickCount()) |
| { |
| if (i >= 100) |
| { |
| res = SZ_ERROR_FAIL; |
| break; |
| } |
| wcscpy(path + pathLen, L"7z"); |
| |
| { |
| wchar_t *s = path + wcslen(path); |
| UInt32 value = d; |
| unsigned k; |
| for (k = 0; k < 8; k++) |
| { |
| unsigned t = value & 0xF; |
| value >>= 4; |
| s[7 - k] = (wchar_t)((t < 10) ? ('0' + t) : ('A' + (t - 10))); |
| } |
| s[k] = '\0'; |
| } |
| |
| if (DoesFileOrDirExist(path)) |
| continue; |
| if (CreateDirectoryW(path, NULL)) |
| { |
| wcscat(path, WSTRING_PATH_SEPARATOR); |
| pathLen = wcslen(path); |
| break; |
| } |
| if (GetLastError() != ERROR_ALREADY_EXISTS) |
| { |
| res = SZ_ERROR_FAIL; |
| break; |
| } |
| } |
| |
| #ifndef UNDER_CE |
| wcscpy(workCurDir, path); |
| #endif |
| if (res != SZ_OK) |
| errorMessage = "Can't create temp folder"; |
| } |
| |
| if (res != SZ_OK) |
| { |
| if (!errorMessage) |
| errorMessage = "Error"; |
| PrintErrorMessage(errorMessage); |
| return 1; |
| } |
| |
| if (InFile_OpenW(&archiveStream.file, sfxPath) != 0) |
| { |
| errorMessage = "can not open input file"; |
| res = SZ_ERROR_FAIL; |
| } |
| else |
| { |
| UInt64 pos = 0; |
| if (!FindSignature(&archiveStream.file, &pos)) |
| res = SZ_ERROR_FAIL; |
| else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0) |
| res = SZ_ERROR_FAIL; |
| if (res != 0) |
| errorMessage = "Can't find 7z archive"; |
| } |
| |
| if (res == SZ_OK) |
| { |
| lookStream.realStream = &archiveStream.s; |
| LookToRead_Init(&lookStream); |
| } |
| |
| SzArEx_Init(&db); |
| if (res == SZ_OK) |
| { |
| res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); |
| } |
| |
| if (res == SZ_OK) |
| { |
| UInt32 executeFileIndex = (UInt32)(Int32)-1; |
| UInt32 minPrice = 1 << 30; |
| UInt32 i; |
| UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ |
| Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ |
| size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ |
| |
| for (i = 0; i < db.NumFiles; i++) |
| { |
| size_t offset = 0; |
| size_t outSizeProcessed = 0; |
| size_t len; |
| WCHAR *temp; |
| len = SzArEx_GetFileNameUtf16(&db, i, NULL); |
| |
| if (len >= MAX_PATH) |
| { |
| res = SZ_ERROR_FAIL; |
| break; |
| } |
| |
| temp = path + pathLen; |
| |
| SzArEx_GetFileNameUtf16(&db, i, temp); |
| { |
| res = SzArEx_Extract(&db, &lookStream.s, i, |
| &blockIndex, &outBuffer, &outBufferSize, |
| &offset, &outSizeProcessed, |
| &allocImp, &allocTempImp); |
| if (res != SZ_OK) |
| break; |
| } |
| { |
| CSzFile outFile; |
| size_t processedSize; |
| size_t j; |
| size_t nameStartPos = 0; |
| for (j = 0; temp[j] != 0; j++) |
| { |
| if (temp[j] == '/') |
| { |
| temp[j] = 0; |
| MyCreateDir(path); |
| temp[j] = CHAR_PATH_SEPARATOR; |
| nameStartPos = j + 1; |
| } |
| } |
| |
| if (SzArEx_IsDir(&db, i)) |
| { |
| MyCreateDir(path); |
| continue; |
| } |
| else |
| { |
| unsigned extLen; |
| const WCHAR *name = temp + nameStartPos; |
| unsigned len2 = (unsigned)wcslen(name); |
| unsigned nameLen = FindExt(temp + nameStartPos, &extLen); |
| unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len2 - extLen, extLen); |
| unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen); |
| |
| unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12)); |
| if (minPrice > price) |
| { |
| minPrice = price; |
| executeFileIndex = i; |
| useShellExecute = (extPrice != k_EXE_ExtIndex); |
| } |
| |
| if (DoesFileOrDirExist(path)) |
| { |
| errorMessage = "Duplicate file"; |
| res = SZ_ERROR_FAIL; |
| break; |
| } |
| if (OutFile_OpenW(&outFile, path)) |
| { |
| errorMessage = "Can't open output file"; |
| res = SZ_ERROR_FAIL; |
| break; |
| } |
| } |
| |
| processedSize = outSizeProcessed; |
| if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed) |
| { |
| errorMessage = "Can't write output file"; |
| res = SZ_ERROR_FAIL; |
| } |
| |
| #ifdef USE_WINDOWS_FILE |
| if (SzBitWithVals_Check(&db.MTime, i)) |
| { |
| const CNtfsFileTime *t = db.MTime.Vals + i; |
| FILETIME mTime; |
| mTime.dwLowDateTime = t->Low; |
| mTime.dwHighDateTime = t->High; |
| SetFileTime(outFile.handle, NULL, NULL, &mTime); |
| } |
| #endif |
| |
| { |
| SRes res2 = File_Close(&outFile); |
| if (res != SZ_OK) |
| break; |
| if (res2 != SZ_OK) |
| { |
| res = res2; |
| break; |
| } |
| } |
| #ifdef USE_WINDOWS_FILE |
| if (SzBitWithVals_Check(&db.Attribs, i)) |
| SetFileAttributesW(path, db.Attribs.Vals[i]); |
| #endif |
| } |
| } |
| |
| if (res == SZ_OK) |
| { |
| if (executeFileIndex == (UInt32)(Int32)-1) |
| { |
| errorMessage = "There is no file to execute"; |
| res = SZ_ERROR_FAIL; |
| } |
| else |
| { |
| WCHAR *temp = path + pathLen; |
| UInt32 j; |
| SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp); |
| for (j = 0; temp[j] != 0; j++) |
| if (temp[j] == '/') |
| temp[j] = CHAR_PATH_SEPARATOR; |
| } |
| } |
| IAlloc_Free(&allocImp, outBuffer); |
| } |
| SzArEx_Free(&db, &allocImp); |
| |
| File_Close(&archiveStream.file); |
| |
| if (res == SZ_OK) |
| { |
| HANDLE hProcess = 0; |
| |
| #ifndef UNDER_CE |
| WCHAR oldCurDir[MAX_PATH + 2]; |
| oldCurDir[0] = 0; |
| { |
| DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir); |
| if (needLen == 0 || needLen > MAX_PATH) |
| oldCurDir[0] = 0; |
| SetCurrentDirectory(workCurDir); |
| } |
| #endif |
| |
| if (useShellExecute) |
| { |
| SHELLEXECUTEINFO ei; |
| UINT32 executeRes; |
| BOOL success; |
| |
| memset(&ei, 0, sizeof(ei)); |
| ei.cbSize = sizeof(ei); |
| ei.lpFile = path; |
| ei.fMask = SEE_MASK_NOCLOSEPROCESS |
| #ifndef UNDER_CE |
| | SEE_MASK_FLAG_DDEWAIT |
| #endif |
| /* | SEE_MASK_NO_CONSOLE */ |
| ; |
| if (wcslen(cmdLineParams) != 0) |
| ei.lpParameters = cmdLineParams; |
| ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */ |
| success = ShellExecuteEx(&ei); |
| executeRes = (UINT32)(UINT_PTR)ei.hInstApp; |
| if (!success || (executeRes <= 32 && executeRes != 0)) /* executeRes = 0 in Windows CE */ |
| res = SZ_ERROR_FAIL; |
| else |
| hProcess = ei.hProcess; |
| } |
| else |
| { |
| STARTUPINFOW si; |
| PROCESS_INFORMATION pi; |
| WCHAR cmdLine[MAX_PATH * 3]; |
| |
| wcscpy(cmdLine, path); |
| wcscat(cmdLine, cmdLineParams); |
| memset(&si, 0, sizeof(si)); |
| si.cb = sizeof(si); |
| if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0) |
| res = SZ_ERROR_FAIL; |
| else |
| { |
| CloseHandle(pi.hThread); |
| hProcess = pi.hProcess; |
| } |
| } |
| |
| if (hProcess != 0) |
| { |
| WaitForSingleObject(hProcess, INFINITE); |
| if (!GetExitCodeProcess(hProcess, &exitCode)) |
| exitCode = 1; |
| CloseHandle(hProcess); |
| } |
| |
| #ifndef UNDER_CE |
| SetCurrentDirectory(oldCurDir); |
| #endif |
| } |
| |
| path[pathLen] = L'\0'; |
| RemoveDirWithSubItems(path); |
| |
| if (res == SZ_OK) |
| return (int)exitCode; |
| |
| { |
| if (res == SZ_ERROR_UNSUPPORTED) |
| errorMessage = "Decoder doesn't support this archive"; |
| else if (res == SZ_ERROR_MEM) |
| errorMessage = "Can't allocate required memory"; |
| else if (res == SZ_ERROR_CRC) |
| errorMessage = "CRC error"; |
| else |
| { |
| if (!errorMessage) |
| errorMessage = "ERROR"; |
| } |
| |
| if (errorMessage) |
| PrintErrorMessage(errorMessage); |
| } |
| return 1; |
| } |