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