You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
617 lines
14 KiB
617 lines
14 KiB
/* SfxSetup.c - 7z SFX Setup |
|
2014-12-07 : Igor Pavlov : Public domain */ |
|
|
|
#include "Precomp.h" |
|
|
|
#ifndef UNICODE |
|
#define UNICODE |
|
#endif |
|
|
|
#ifndef _UNICODE |
|
#define _UNICODE |
|
#endif |
|
|
|
#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 *kExts[] = |
|
{ |
|
"bat" |
|
, "cmd" |
|
, "exe" |
|
, "inf" |
|
, "msi" |
|
#ifdef UNDER_CE |
|
, "cab" |
|
#endif |
|
, "html" |
|
, "htm" |
|
}; |
|
|
|
static const char *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 **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 = 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) |
|
{ |
|
ctrlType = 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 (; buf[pos] != '7' && pos <= processed; 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; |
|
|
|
#ifdef _CONSOLE |
|
SetConsoleCtrlHandler(HandlerRoutine, TRUE); |
|
#else |
|
hInstance = hInstance; |
|
hPrevInstance = hPrevInstance; |
|
lpCmdLine = lpCmdLine; |
|
nCmdShow = 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] = (char)((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 len = (unsigned)wcslen(name); |
|
unsigned nameLen = FindExt(temp + nameStartPos, &extLen); |
|
unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - 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); |
|
CloseHandle(hProcess); |
|
} |
|
|
|
#ifndef UNDER_CE |
|
SetCurrentDirectory(oldCurDir); |
|
#endif |
|
} |
|
|
|
path[pathLen] = L'\0'; |
|
RemoveDirWithSubItems(path); |
|
|
|
if (res == SZ_OK) |
|
return 0; |
|
|
|
{ |
|
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; |
|
}
|
|
|