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