• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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