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