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