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