• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SfxSetup.c - 7z SFX Setup
2 2014-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 
26 static 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 
40 static const char *kNames[] =
41 {
42     "setup"
43   , "install"
44   , "run"
45   , "start"
46 };
47 
FindExt(const wchar_t * s,unsigned * extLen)48 static 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 
FindItem(const char ** items,unsigned num,const wchar_t * s,unsigned len)66 static 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
HandlerRoutine(DWORD ctrlType)89 static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
90 {
91   ctrlType = ctrlType;
92   return TRUE;
93 }
94 #endif
95 
PrintErrorMessage(const char * message)96 static 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 
MyCreateDir(const WCHAR * name)114 static 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 
FindSignature(CSzFile * stream,UInt64 * resPos)127 static 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 
DoesFileOrDirExist(const WCHAR * path)163 static 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 
RemoveDirWithSubItems(WCHAR * path)174 static 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
main()224 int MY_CDECL main()
225 #else
226 int 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 }
618