src/third-party/lzma/Util/SfxSetup/SfxSetup.c (view raw)
1/* SfxSetup.c - 7z SFX Setup
22014-12-07 : 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 *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 *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 **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 = 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 ctrlType = 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 (; buf[pos] != '7' && pos <= processed; 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 for (;;)
186 {
187 if (wcscmp(fd.cFileName, L".") != 0 &&
188 wcscmp(fd.cFileName, L"..") != 0)
189 {
190 wcscpy(path + len, fd.cFileName);
191 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
192 {
193 wcscat(path, WSTRING_PATH_SEPARATOR);
194 res = RemoveDirWithSubItems(path);
195 }
196 else
197 {
198 SetFileAttributesW(path, 0);
199 if (DeleteFileW(path) == 0)
200 res = GetLastError();
201 }
202 if (res != 0)
203 break;
204 }
205 if (!FindNextFileW(handle, &fd))
206 {
207 res = GetLastError();
208 if (res == ERROR_NO_MORE_FILES)
209 res = 0;
210 break;
211 }
212 }
213 path[len] = L'\0';
214 FindClose(handle);
215 if (res == 0)
216 {
217 if (!RemoveDirectoryW(path))
218 res = GetLastError();
219 }
220 return res;
221}
222
223#ifdef _CONSOLE
224int MY_CDECL main()
225#else
226int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
227 #ifdef UNDER_CE
228 LPWSTR
229 #else
230 LPSTR
231 #endif
232 lpCmdLine, int nCmdShow)
233#endif
234{
235 CFileInStream archiveStream;
236 CLookToRead lookStream;
237 CSzArEx db;
238 SRes res = SZ_OK;
239 ISzAlloc allocImp;
240 ISzAlloc allocTempImp;
241 WCHAR sfxPath[MAX_PATH + 2];
242 WCHAR path[MAX_PATH * 3 + 2];
243 #ifndef UNDER_CE
244 WCHAR workCurDir[MAX_PATH + 32];
245 #endif
246 size_t pathLen;
247 DWORD winRes;
248 const wchar_t *cmdLineParams;
249 const char *errorMessage = NULL;
250 Bool useShellExecute = True;
251
252 #ifdef _CONSOLE
253 SetConsoleCtrlHandler(HandlerRoutine, TRUE);
254 #else
255 hInstance = hInstance;
256 hPrevInstance = hPrevInstance;
257 lpCmdLine = lpCmdLine;
258 nCmdShow = nCmdShow;
259 #endif
260
261 CrcGenerateTable();
262
263 allocImp.Alloc = SzAlloc;
264 allocImp.Free = SzFree;
265
266 allocTempImp.Alloc = SzAllocTemp;
267 allocTempImp.Free = SzFreeTemp;
268
269 FileInStream_CreateVTable(&archiveStream);
270 LookToRead_CreateVTable(&lookStream, False);
271
272 winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
273 if (winRes == 0 || winRes > MAX_PATH)
274 return 1;
275 {
276 cmdLineParams = GetCommandLineW();
277 #ifndef UNDER_CE
278 {
279 Bool quoteMode = False;
280 for (;; cmdLineParams++)
281 {
282 wchar_t c = *cmdLineParams;
283 if (c == L'\"')
284 quoteMode = !quoteMode;
285 else if (c == 0 || (c == L' ' && !quoteMode))
286 break;
287 }
288 }
289 #endif
290 }
291
292 {
293 unsigned i;
294 DWORD d;
295 winRes = GetTempPathW(MAX_PATH, path);
296 if (winRes == 0 || winRes > MAX_PATH)
297 return 1;
298 pathLen = wcslen(path);
299 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
300
301 for (i = 0;; i++, d += GetTickCount())
302 {
303 if (i >= 100)
304 {
305 res = SZ_ERROR_FAIL;
306 break;
307 }
308 wcscpy(path + pathLen, L"7z");
309
310 {
311 wchar_t *s = path + wcslen(path);
312 UInt32 value = d;
313 unsigned k;
314 for (k = 0; k < 8; k++)
315 {
316 unsigned t = value & 0xF;
317 value >>= 4;
318 s[7 - k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
319 }
320 s[k] = '\0';
321 }
322
323 if (DoesFileOrDirExist(path))
324 continue;
325 if (CreateDirectoryW(path, NULL))
326 {
327 wcscat(path, WSTRING_PATH_SEPARATOR);
328 pathLen = wcslen(path);
329 break;
330 }
331 if (GetLastError() != ERROR_ALREADY_EXISTS)
332 {
333 res = SZ_ERROR_FAIL;
334 break;
335 }
336 }
337
338 #ifndef UNDER_CE
339 wcscpy(workCurDir, path);
340 #endif
341 if (res != SZ_OK)
342 errorMessage = "Can't create temp folder";
343 }
344
345 if (res != SZ_OK)
346 {
347 if (!errorMessage)
348 errorMessage = "Error";
349 PrintErrorMessage(errorMessage);
350 return 1;
351 }
352
353 if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
354 {
355 errorMessage = "can not open input file";
356 res = SZ_ERROR_FAIL;
357 }
358 else
359 {
360 UInt64 pos = 0;
361 if (!FindSignature(&archiveStream.file, &pos))
362 res = SZ_ERROR_FAIL;
363 else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)
364 res = SZ_ERROR_FAIL;
365 if (res != 0)
366 errorMessage = "Can't find 7z archive";
367 }
368
369 if (res == SZ_OK)
370 {
371 lookStream.realStream = &archiveStream.s;
372 LookToRead_Init(&lookStream);
373 }
374
375 SzArEx_Init(&db);
376 if (res == SZ_OK)
377 {
378 res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
379 }
380
381 if (res == SZ_OK)
382 {
383 UInt32 executeFileIndex = (UInt32)(Int32)-1;
384 UInt32 minPrice = 1 << 30;
385 UInt32 i;
386 UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
387 Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
388 size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */
389
390 for (i = 0; i < db.NumFiles; i++)
391 {
392 size_t offset = 0;
393 size_t outSizeProcessed = 0;
394 size_t len;
395 WCHAR *temp;
396 len = SzArEx_GetFileNameUtf16(&db, i, NULL);
397
398 if (len >= MAX_PATH)
399 {
400 res = SZ_ERROR_FAIL;
401 break;
402 }
403
404 temp = path + pathLen;
405
406 SzArEx_GetFileNameUtf16(&db, i, temp);
407 {
408 res = SzArEx_Extract(&db, &lookStream.s, i,
409 &blockIndex, &outBuffer, &outBufferSize,
410 &offset, &outSizeProcessed,
411 &allocImp, &allocTempImp);
412 if (res != SZ_OK)
413 break;
414 }
415 {
416 CSzFile outFile;
417 size_t processedSize;
418 size_t j;
419 size_t nameStartPos = 0;
420 for (j = 0; temp[j] != 0; j++)
421 {
422 if (temp[j] == '/')
423 {
424 temp[j] = 0;
425 MyCreateDir(path);
426 temp[j] = CHAR_PATH_SEPARATOR;
427 nameStartPos = j + 1;
428 }
429 }
430
431 if (SzArEx_IsDir(&db, i))
432 {
433 MyCreateDir(path);
434 continue;
435 }
436 else
437 {
438 unsigned extLen;
439 const WCHAR *name = temp + nameStartPos;
440 unsigned len = (unsigned)wcslen(name);
441 unsigned nameLen = FindExt(temp + nameStartPos, &extLen);
442 unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);
443 unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);
444
445 unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
446 if (minPrice > price)
447 {
448 minPrice = price;
449 executeFileIndex = i;
450 useShellExecute = (extPrice != k_EXE_ExtIndex);
451 }
452
453 if (DoesFileOrDirExist(path))
454 {
455 errorMessage = "Duplicate file";
456 res = SZ_ERROR_FAIL;
457 break;
458 }
459 if (OutFile_OpenW(&outFile, path))
460 {
461 errorMessage = "Can't open output file";
462 res = SZ_ERROR_FAIL;
463 break;
464 }
465 }
466
467 processedSize = outSizeProcessed;
468 if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
469 {
470 errorMessage = "Can't write output file";
471 res = SZ_ERROR_FAIL;
472 }
473
474 #ifdef USE_WINDOWS_FILE
475 if (SzBitWithVals_Check(&db.MTime, i))
476 {
477 const CNtfsFileTime *t = db.MTime.Vals + i;
478 FILETIME mTime;
479 mTime.dwLowDateTime = t->Low;
480 mTime.dwHighDateTime = t->High;
481 SetFileTime(outFile.handle, NULL, NULL, &mTime);
482 }
483 #endif
484
485 {
486 SRes res2 = File_Close(&outFile);
487 if (res != SZ_OK)
488 break;
489 if (res2 != SZ_OK)
490 {
491 res = res2;
492 break;
493 }
494 }
495 #ifdef USE_WINDOWS_FILE
496 if (SzBitWithVals_Check(&db.Attribs, i))
497 SetFileAttributesW(path, db.Attribs.Vals[i]);
498 #endif
499 }
500 }
501
502 if (res == SZ_OK)
503 {
504 if (executeFileIndex == (UInt32)(Int32)-1)
505 {
506 errorMessage = "There is no file to execute";
507 res = SZ_ERROR_FAIL;
508 }
509 else
510 {
511 WCHAR *temp = path + pathLen;
512 UInt32 j;
513 SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);
514 for (j = 0; temp[j] != 0; j++)
515 if (temp[j] == '/')
516 temp[j] = CHAR_PATH_SEPARATOR;
517 }
518 }
519 IAlloc_Free(&allocImp, outBuffer);
520 }
521 SzArEx_Free(&db, &allocImp);
522
523 File_Close(&archiveStream.file);
524
525 if (res == SZ_OK)
526 {
527 HANDLE hProcess = 0;
528
529 #ifndef UNDER_CE
530 WCHAR oldCurDir[MAX_PATH + 2];
531 oldCurDir[0] = 0;
532 {
533 DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);
534 if (needLen == 0 || needLen > MAX_PATH)
535 oldCurDir[0] = 0;
536 SetCurrentDirectory(workCurDir);
537 }
538 #endif
539
540 if (useShellExecute)
541 {
542 SHELLEXECUTEINFO ei;
543 UINT32 executeRes;
544 BOOL success;
545
546 memset(&ei, 0, sizeof(ei));
547 ei.cbSize = sizeof(ei);
548 ei.lpFile = path;
549 ei.fMask = SEE_MASK_NOCLOSEPROCESS
550 #ifndef UNDER_CE
551 | SEE_MASK_FLAG_DDEWAIT
552 #endif
553 /* | SEE_MASK_NO_CONSOLE */
554 ;
555 if (wcslen(cmdLineParams) != 0)
556 ei.lpParameters = cmdLineParams;
557 ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */
558 success = ShellExecuteEx(&ei);
559 executeRes = (UINT32)(UINT_PTR)ei.hInstApp;
560 if (!success || (executeRes <= 32 && executeRes != 0)) /* executeRes = 0 in Windows CE */
561 res = SZ_ERROR_FAIL;
562 else
563 hProcess = ei.hProcess;
564 }
565 else
566 {
567 STARTUPINFOW si;
568 PROCESS_INFORMATION pi;
569 WCHAR cmdLine[MAX_PATH * 3];
570
571 wcscpy(cmdLine, path);
572 wcscat(cmdLine, cmdLineParams);
573 memset(&si, 0, sizeof(si));
574 si.cb = sizeof(si);
575 if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
576 res = SZ_ERROR_FAIL;
577 else
578 {
579 CloseHandle(pi.hThread);
580 hProcess = pi.hProcess;
581 }
582 }
583
584 if (hProcess != 0)
585 {
586 WaitForSingleObject(hProcess, INFINITE);
587 CloseHandle(hProcess);
588 }
589
590 #ifndef UNDER_CE
591 SetCurrentDirectory(oldCurDir);
592 #endif
593 }
594
595 path[pathLen] = L'\0';
596 RemoveDirWithSubItems(path);
597
598 if (res == SZ_OK)
599 return 0;
600
601 {
602 if (res == SZ_ERROR_UNSUPPORTED)
603 errorMessage = "Decoder doesn't support this archive";
604 else if (res == SZ_ERROR_MEM)
605 errorMessage = "Can't allocate required memory";
606 else if (res == SZ_ERROR_CRC)
607 errorMessage = "CRC error";
608 else
609 {
610 if (!errorMessage)
611 errorMessage = "ERROR";
612 }
613 if (errorMessage)
614 PrintErrorMessage(errorMessage);
615 }
616 return 1;
617}