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