1 // Main.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../../C/DllSecur.h"
6 
7 #include "../../../Common/MyWindows.h"
8 #include "../../../Common/MyInitGuid.h"
9 
10 #include "../../../Common/CommandLineParser.h"
11 #include "../../../Common/StringConvert.h"
12 #include "../../../Common/TextConfig.h"
13 
14 #include "../../../Windows/DLL.h"
15 #include "../../../Windows/ErrorMsg.h"
16 #include "../../../Windows/FileDir.h"
17 #include "../../../Windows/FileFind.h"
18 #include "../../../Windows/FileIO.h"
19 #include "../../../Windows/FileName.h"
20 #include "../../../Windows/NtCheck.h"
21 #include "../../../Windows/ResourceString.h"
22 
23 #include "../../UI/Explorer/MyMessages.h"
24 
25 #include "ExtractEngine.h"
26 
27 #include "resource.h"
28 
29 using namespace NWindows;
30 using namespace NFile;
31 using namespace NDir;
32 
33 extern
34 HINSTANCE g_hInstance;
35 HINSTANCE g_hInstance;
36 
37 static CFSTR const kTempDirPrefix = FTEXT("7zS");
38 
39 #define MY_SHELL_EXECUTE
40 
ReadDataString(CFSTR fileName, LPCSTR startID, LPCSTR endID, AString &stringResult)41 static bool ReadDataString(CFSTR fileName, LPCSTR startID,
42     LPCSTR endID, AString &stringResult)
43 {
44   stringResult.Empty();
45   NIO::CInFile inFile;
46   if (!inFile.Open(fileName))
47     return false;
48   const size_t kBufferSize = (1 << 12);
49 
50   Byte buffer[kBufferSize];
51   const unsigned signatureStartSize = MyStringLen(startID);
52   const unsigned signatureEndSize = MyStringLen(endID);
53 
54   size_t numBytesPrev = 0;
55   bool writeMode = false;
56   UInt64 posTotal = 0;
57   for (;;)
58   {
59     if (posTotal > (1 << 20))
60       return (stringResult.IsEmpty());
61     const size_t numReadBytes = kBufferSize - numBytesPrev;
62     size_t processedSize;
63     if (!inFile.ReadFull(buffer + numBytesPrev, numReadBytes, processedSize))
64       return false;
65     if (processedSize == 0)
66       return true;
67     const size_t numBytesInBuffer = numBytesPrev + processedSize;
68     UInt32 pos = 0;
69     for (;;)
70     {
71       if (writeMode)
72       {
73         if (pos + signatureEndSize > numBytesInBuffer)
74           break;
75         if (memcmp(buffer + pos, endID, signatureEndSize) == 0)
76           return true;
77         const Byte b = buffer[pos];
78         if (b == 0)
79           return false;
80         stringResult += (char)b;
81         pos++;
82       }
83       else
84       {
85         if (pos + signatureStartSize > numBytesInBuffer)
86           break;
87         if (memcmp(buffer + pos, startID, signatureStartSize) == 0)
88         {
89           writeMode = true;
90           pos += signatureStartSize;
91         }
92         else
93           pos++;
94       }
95     }
96     numBytesPrev = numBytesInBuffer - pos;
97     posTotal += pos;
98     memmove(buffer, buffer + pos, numBytesPrev);
99   }
100 }
101 
102 static char kStartID[] = { ',','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 };
103 static char kEndID[]   = { ',','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 };
104 
105 static struct CInstallIDInit
106 {
CInstallIDInitCInstallIDInit107   CInstallIDInit()
108   {
109     kStartID[0] = ';';
110     kEndID[0] = ';';
111   }
112 } g_CInstallIDInit;
113 
114 
115 #if defined(_WIN32) && defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE)
116 #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1;
117 #endif
118 
ShowErrorMessageSpec(const UString &name)119 static void ShowErrorMessageSpec(const UString &name)
120 {
121   UString message = NError::MyFormatMessage(::GetLastError());
122   const int pos = message.Find(L"%1");
123   if (pos >= 0)
124   {
125     message.Delete((unsigned)pos, 2);
126     message.Insert((unsigned)pos, name);
127   }
128   ShowErrorMessage(NULL, message);
129 }
130 
WinMain(HINSTANCE hInstance, HINSTANCE , LPWSTR ,int )131 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
132     #ifdef UNDER_CE
133     LPWSTR
134     #else
135     LPSTR
136     #endif
137     /* lpCmdLine */,int /* nCmdShow */)
138 {
139   g_hInstance = (HINSTANCE)hInstance;
140 
141   NT_CHECK
142 
143   #ifdef _WIN32
144   LoadSecurityDlls();
145   #endif
146 
147   // InitCommonControls();
148 
149   UString archiveName, switches;
150   #ifdef MY_SHELL_EXECUTE
151   UString executeFile, executeParameters;
152   #endif
153   NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches);
154 
155   FString fullPath;
156   NDLL::MyGetModuleFileName(fullPath);
157 
158   switches.Trim();
159   bool assumeYes = false;
160   if (switches.IsPrefixedBy_Ascii_NoCase("-y"))
161   {
162     assumeYes = true;
163     switches = switches.Ptr(2);
164     switches.Trim();
165   }
166 
167   AString config;
168   if (!ReadDataString(fullPath, kStartID, kEndID, config))
169   {
170     if (!assumeYes)
171       ShowErrorMessage(L"Can't load config info");
172     return 1;
173   }
174 
175   UString dirPrefix ("." STRING_PATH_SEPARATOR);
176   UString appLaunched;
177   bool showProgress = true;
178   if (!config.IsEmpty())
179   {
180     CObjectVector<CTextConfigPair> pairs;
181     if (!GetTextConfig(config, pairs))
182     {
183       if (!assumeYes)
184         ShowErrorMessage(L"Config failed");
185       return 1;
186     }
187     const UString friendlyName = GetTextConfigValue(pairs, "Title");
188     const UString installPrompt = GetTextConfigValue(pairs, "BeginPrompt");
189     const UString progress = GetTextConfigValue(pairs, "Progress");
190     if (progress.IsEqualTo_Ascii_NoCase("no"))
191       showProgress = false;
192     const int index = FindTextConfigItem(pairs, "Directory");
193     if (index >= 0)
194       dirPrefix = pairs[index].String;
195     if (!installPrompt.IsEmpty() && !assumeYes)
196     {
197       if (MessageBoxW(NULL, installPrompt, friendlyName, MB_YESNO |
198           MB_ICONQUESTION) != IDYES)
199         return 0;
200     }
201     appLaunched = GetTextConfigValue(pairs, "RunProgram");
202 
203     #ifdef MY_SHELL_EXECUTE
204     executeFile = GetTextConfigValue(pairs, "ExecuteFile");
205     executeParameters = GetTextConfigValue(pairs, "ExecuteParameters");
206     #endif
207   }
208 
209   CTempDir tempDir;
210   if (!tempDir.Create(kTempDirPrefix))
211   {
212     if (!assumeYes)
213       ShowErrorMessage(L"Cannot create temp folder archive");
214     return 1;
215   }
216 
217   CCodecs *codecs = new CCodecs;
218   CMyComPtr<IUnknown> compressCodecsInfo = codecs;
219   {
220     const HRESULT result = codecs->Load();
221     if (result != S_OK)
222     {
223       ShowErrorMessage(L"Cannot load codecs");
224       return 1;
225     }
226   }
227 
228   const FString tempDirPath = tempDir.GetPath();
229   // tempDirPath = L"M:\\1\\"; // to test low disk space
230   {
231     bool isCorrupt = false;
232     UString errorMessage;
233     HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress,
234       isCorrupt, errorMessage);
235 
236     if (result != S_OK)
237     {
238       if (!assumeYes)
239       {
240         if (result == S_FALSE || isCorrupt)
241         {
242           NWindows::MyLoadString(IDS_EXTRACTION_ERROR_MESSAGE, errorMessage);
243           result = E_FAIL;
244         }
245         if (result != E_ABORT)
246         {
247           if (errorMessage.IsEmpty())
248             errorMessage = NError::MyFormatMessage(result);
249           ::MessageBoxW(NULL, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR);
250         }
251       }
252       return 1;
253     }
254   }
255 
256   #ifndef UNDER_CE
257   CCurrentDirRestorer currentDirRestorer;
258   if (!SetCurrentDir(tempDirPath))
259     return 1;
260   #endif
261 
262   HANDLE hProcess = NULL;
263 #ifdef MY_SHELL_EXECUTE
264   if (!executeFile.IsEmpty())
265   {
266     CSysString filePath (GetSystemString(executeFile));
267     SHELLEXECUTEINFO execInfo;
268     execInfo.cbSize = sizeof(execInfo);
269     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS
270       #ifndef UNDER_CE
271       | SEE_MASK_FLAG_DDEWAIT
272       #endif
273       ;
274     execInfo.hwnd = NULL;
275     execInfo.lpVerb = NULL;
276     execInfo.lpFile = filePath;
277 
278     if (!switches.IsEmpty())
279     {
280       executeParameters.Add_Space_if_NotEmpty();
281       executeParameters += switches;
282     }
283 
284     const CSysString parametersSys (GetSystemString(executeParameters));
285     if (parametersSys.IsEmpty())
286       execInfo.lpParameters = NULL;
287     else
288       execInfo.lpParameters = parametersSys;
289 
290     execInfo.lpDirectory = NULL;
291     execInfo.nShow = SW_SHOWNORMAL;
292     execInfo.hProcess = NULL;
293     /* BOOL success = */ ::ShellExecuteEx(&execInfo);
294     UINT32 result = (UINT32)(UINT_PTR)execInfo.hInstApp;
295     if (result <= 32)
296     {
297       if (!assumeYes)
298         ShowErrorMessage(L"Cannot open file");
299       return 1;
300     }
301     hProcess = execInfo.hProcess;
302   }
303   else
304 #endif
305   {
306     if (appLaunched.IsEmpty())
307     {
308       appLaunched = L"setup.exe";
309       if (!NFind::DoesFileExist_FollowLink(us2fs(appLaunched)))
310       {
311         if (!assumeYes)
312           ShowErrorMessage(L"Cannot find setup.exe");
313         return 1;
314       }
315     }
316 
317     {
318       FString s2 = tempDirPath;
319       NName::NormalizeDirPathPrefix(s2);
320       appLaunched.Replace(L"%%T" WSTRING_PATH_SEPARATOR, fs2us(s2));
321     }
322 
323     const UString appNameForError = appLaunched; // actually we need to rtemove parameters also
324 
325     appLaunched.Replace(L"%%T", fs2us(tempDirPath));
326 
327     if (!switches.IsEmpty())
328     {
329       appLaunched.Add_Space();
330       appLaunched += switches;
331     }
332     STARTUPINFO startupInfo;
333     startupInfo.cb = sizeof(startupInfo);
334     startupInfo.lpReserved = NULL;
335     startupInfo.lpDesktop = NULL;
336     startupInfo.lpTitle = NULL;
337     startupInfo.dwFlags = 0;
338     startupInfo.cbReserved2 = 0;
339     startupInfo.lpReserved2 = NULL;
340 
341     PROCESS_INFORMATION processInformation;
342 
343     const CSysString appLaunchedSys (GetSystemString(dirPrefix + appLaunched));
344 
345     const BOOL createResult = CreateProcess(NULL,
346         appLaunchedSys.Ptr_non_const(),
347         NULL, NULL, FALSE, 0, NULL, NULL /*tempDir.GetPath() */,
348         &startupInfo, &processInformation);
349     if (createResult == 0)
350     {
351       if (!assumeYes)
352       {
353         // we print name of exe file, if error message is
354         // ERROR_BAD_EXE_FORMAT: "%1 is not a valid Win32 application".
355         ShowErrorMessageSpec(appNameForError);
356       }
357       return 1;
358     }
359     ::CloseHandle(processInformation.hThread);
360     hProcess = processInformation.hProcess;
361   }
362   if (hProcess)
363   {
364     WaitForSingleObject(hProcess, INFINITE);
365     ::CloseHandle(hProcess);
366   }
367   return 0;
368 }
369