1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Functions to parse command-line flags and process them
5 * COPYRIGHT: Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
6 * Copyright 2020 He Yang (1160386205@qq.com)
7 */
8
9 #include "gui.h"
10 #include "unattended.h"
11 #include "configparser.h"
12 #include <setupapi.h>
13 #include <conutils.h>
14
15 static BOOL
MatchCmdOption(LPWSTR argvOption,LPCWSTR szOptToMacth)16 MatchCmdOption(LPWSTR argvOption, LPCWSTR szOptToMacth)
17 {
18 WCHAR FirstCharList[] = {L'-', L'/'};
19
20 for (UINT i = 0; i < _countof(FirstCharList); i++)
21 {
22 if (argvOption[0] == FirstCharList[i])
23 {
24 return StrCmpIW(argvOption + 1, szOptToMacth) == 0;
25 }
26 }
27 return FALSE;
28 }
29
30 static void
InitRappsConsole()31 InitRappsConsole()
32 {
33 // First, try to attach to our parent's console
34 if (!AttachConsole(ATTACH_PARENT_PROCESS))
35 {
36 // Did we already have a console?
37 if (GetLastError() != ERROR_ACCESS_DENIED)
38 {
39 // No, try to open a new one
40 AllocConsole();
41 }
42 }
43 ConInitStdStreams(); // Initialize the Console Standard Streams
44 }
45
46 static CAppInfo *
SearchForAppWithDisplayName(CAppDB & db,AppsCategories Type,LPCWSTR Name)47 SearchForAppWithDisplayName(CAppDB &db, AppsCategories Type, LPCWSTR Name)
48 {
49 CAtlList<CAppInfo *> List;
50 db.GetApps(List, Type);
51 for (POSITION it = List.GetHeadPosition(); it;)
52 {
53 CAppInfo *Info = List.GetNext(it);
54 if (SearchPatternMatch(Info->szDisplayName, Name))
55 {
56 return Info;
57 }
58 }
59 return NULL;
60 }
61
62 static BOOL
HandleInstallCommand(CAppDB * db,LPWSTR szCommand,int argcLeft,LPWSTR * argvLeft)63 HandleInstallCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
64 {
65 if (argcLeft < 1)
66 {
67 InitRappsConsole();
68 ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PACKAGE_NAME, szCommand);
69 return FALSE;
70 }
71
72 CAtlList<CAppInfo *> Applications;
73 for (int i = 0; i < argcLeft; i++)
74 {
75 LPCWSTR PackageName = argvLeft[i];
76 CAppInfo *AppInfo = db->FindByPackageName(PackageName);
77 if (AppInfo)
78 {
79 Applications.AddTail(AppInfo);
80 }
81 }
82
83 return DownloadListOfApplications(Applications, TRUE);
84 }
85
86 static BOOL
HandleSetupCommand(CAppDB * db,LPWSTR szCommand,int argcLeft,LPWSTR * argvLeft)87 HandleSetupCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
88 {
89 if (argcLeft != 1)
90 {
91 InitRappsConsole();
92 ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_FILE_NAME, szCommand);
93 return FALSE;
94 }
95
96 CAtlList<CAppInfo *> Applications;
97 HINF InfHandle = SetupOpenInfFileW(argvLeft[0], NULL, INF_STYLE_WIN4, NULL);
98 if (InfHandle == INVALID_HANDLE_VALUE)
99 {
100 return FALSE;
101 }
102
103 INFCONTEXT Context;
104 if (SetupFindFirstLineW(InfHandle, L"RAPPS", L"Install", &Context))
105 {
106 WCHAR szPkgName[MAX_PATH];
107 do
108 {
109 if (SetupGetStringFieldW(&Context, 1, szPkgName, _countof(szPkgName), NULL))
110 {
111 CAppInfo *AppInfo = db->FindByPackageName(szPkgName);
112 if (AppInfo)
113 {
114 Applications.AddTail(AppInfo);
115 }
116 }
117 } while (SetupFindNextLine(&Context, &Context));
118 }
119 SetupCloseInfFile(InfHandle);
120
121 return DownloadListOfApplications(Applications, TRUE);
122 }
123
124 static BOOL
HandleUninstallCommand(CAppDB & db,UINT argcLeft,LPWSTR * argvLeft)125 HandleUninstallCommand(CAppDB &db, UINT argcLeft, LPWSTR *argvLeft)
126 {
127 UINT argi = 0, silent = FALSE, byregkeyname = FALSE;
128 for (; argcLeft; ++argi, --argcLeft)
129 {
130 if (!StrCmpIW(argvLeft[argi], L"/S"))
131 ++silent;
132 else if (!StrCmpIW(argvLeft[argi], L"/K"))
133 ++byregkeyname;
134 else
135 break;
136 }
137 if (argcLeft != 1)
138 return FALSE;
139
140 CStringW buf;
141 LPCWSTR name = argvLeft[argi];
142 BOOL retval = FALSE;
143 CAppInfo *pInfo = NULL, *pDelete = NULL;
144
145 if (!byregkeyname)
146 {
147 for (UINT i = ENUM_INSTALLED_MIN; !pInfo && i <= ENUM_INSTALLED_MAX; ++i)
148 {
149 pInfo = SearchForAppWithDisplayName(db, AppsCategories(i), name);
150 }
151
152 if (!pInfo)
153 {
154 CAvailableApplicationInfo *p = db.FindAvailableByPackageName(name);
155 if (p)
156 {
157 CConfigParser *cp = p->GetConfigParser();
158 if (cp && cp->GetString(DB_REGNAME, buf) && !buf.IsEmpty())
159 {
160 name = buf.GetString();
161 byregkeyname = TRUE;
162 }
163 }
164 }
165 }
166
167 if (byregkeyname)
168 {
169 // Force a specific key type if requested (<M|U>[32|64]<\\KeyName>)
170 if (name[0])
171 {
172 REGSAM wow = 0;
173 UINT i = 1;
174 if (name[i] == '3' && name[i + 1])
175 wow = KEY_WOW64_32KEY, i += 2;
176 else if (name[i] == '6' && name[i + 1])
177 wow = KEY_WOW64_64KEY, i += 2;
178
179 if (name[i++] == '\\')
180 {
181 pInfo = CAppDB::CreateInstalledAppInstance(name + i, name[0] == 'U', wow);
182 }
183 }
184
185 if (!pInfo)
186 {
187 pInfo = CAppDB::CreateInstalledAppByRegistryKey(name);
188 }
189 pDelete = pInfo;
190 }
191
192 if (pInfo)
193 {
194 retval = pInfo->UninstallApplication(silent ? UCF_SILENT : UCF_NONE);
195 }
196 delete pDelete;
197 return retval;
198 }
199
200 static BOOL
HandleGenerateInstallerCommand(CAppDB & db,UINT argcLeft,LPWSTR * argvLeft)201 HandleGenerateInstallerCommand(CAppDB &db, UINT argcLeft, LPWSTR *argvLeft)
202 {
203 if (argcLeft != 2)
204 return FALSE;
205
206 CAvailableApplicationInfo *pAI = db.FindAvailableByPackageName(argvLeft[0]);
207 if (!pAI)
208 return FALSE;
209
210 return ExtractAndRunGeneratedInstaller(*pAI, argvLeft[1]);
211 }
212
213 static BOOL
HandleFindCommand(CAppDB * db,LPWSTR szCommand,int argcLeft,LPWSTR * argvLeft)214 HandleFindCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
215 {
216 if (argcLeft < 1)
217 {
218 ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PARAMS, szCommand);
219 return FALSE;
220 }
221
222 CAtlList<CAppInfo *> List;
223 db->GetApps(List, ENUM_ALL_AVAILABLE);
224
225 for (int i = 0; i < argcLeft; i++)
226 {
227 LPCWSTR lpszSearch = argvLeft[i];
228 ConResMsgPrintf(StdOut, NULL, IDS_CMD_FIND_RESULT_FOR, lpszSearch);
229
230 POSITION CurrentListPosition = List.GetHeadPosition();
231 while (CurrentListPosition)
232 {
233 CAppInfo *Info = List.GetNext(CurrentListPosition);
234
235 if (SearchPatternMatch(Info->szDisplayName, lpszSearch) || SearchPatternMatch(Info->szComments, lpszSearch))
236 {
237 ConPrintf(StdOut, L"%s (%s)\n", Info->szDisplayName.GetString(), Info->szIdentifier.GetString());
238 }
239 }
240
241 ConPrintf(StdOut, L"\n");
242 }
243
244 return TRUE;
245 }
246
247 static BOOL
HandleInfoCommand(CAppDB * db,LPWSTR szCommand,int argcLeft,LPWSTR * argvLeft)248 HandleInfoCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
249 {
250 if (argcLeft < 1)
251 {
252 ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PARAMS, szCommand);
253 return FALSE;
254 }
255
256 for (int i = 0; i < argcLeft; i++)
257 {
258 LPCWSTR PackageName = argvLeft[i];
259 CAppInfo *AppInfo = db->FindByPackageName(PackageName);
260 if (!AppInfo)
261 {
262 ConResMsgPrintf(StdOut, NULL, IDS_CMD_PACKAGE_NOT_FOUND, PackageName);
263 }
264 else
265 {
266 ConResMsgPrintf(StdOut, NULL, IDS_CMD_PACKAGE_INFO, PackageName);
267
268 ConPuts(StdOut, AppInfo->szDisplayName);
269
270 if (!AppInfo->szDisplayVersion.IsEmpty())
271 {
272 ConResPrintf(StdOut, IDS_AINFO_VERSION);
273 ConPuts(StdOut, AppInfo->szDisplayVersion);
274 }
275
276 CStringW License, Size, UrlSite, UrlDownload;
277 AppInfo->GetDisplayInfo(License, Size, UrlSite, UrlDownload);
278
279 if (!License.IsEmpty())
280 {
281 ConResPrintf(StdOut, IDS_AINFO_LICENSE);
282 ConPuts(StdOut, License);
283 }
284
285 if (!Size.IsEmpty())
286 {
287 ConResPrintf(StdOut, IDS_AINFO_SIZE);
288 ConPuts(StdOut, Size);
289 }
290
291 if (!UrlSite.IsEmpty())
292 {
293 ConResPrintf(StdOut, IDS_AINFO_URLSITE);
294 ConPuts(StdOut, UrlSite);
295 }
296
297 if (AppInfo->szComments)
298 {
299 ConResPrintf(StdOut, IDS_AINFO_DESCRIPTION);
300 ConPuts(StdOut, AppInfo->szComments);
301 }
302
303 if (!UrlDownload.IsEmpty())
304 {
305 ConResPrintf(StdOut, IDS_AINFO_URLDOWNLOAD);
306 ConPuts(StdOut, UrlDownload);
307 }
308 ConPuts(StdOut, L"\n");
309 }
310 ConPuts(StdOut, L"\n");
311 }
312 return TRUE;
313 }
314
315 static VOID
PrintHelpCommand()316 PrintHelpCommand()
317 {
318 ConPrintf(StdOut, L"\n");
319 ConResPuts(StdOut, IDS_APPTITLE);
320 ConPrintf(StdOut, L"\n\n");
321
322 ConResPuts(StdOut, IDS_CMD_USAGE);
323 ConPrintf(StdOut, L"%ls\n", UsageString);
324 }
325
326 BOOL
ParseCmdAndExecute(LPWSTR lpCmdLine,BOOL bIsFirstLaunch,int nCmdShow)327 ParseCmdAndExecute(LPWSTR lpCmdLine, BOOL bIsFirstLaunch, int nCmdShow)
328 {
329 INT argc;
330 LPWSTR *argv = CommandLineToArgvW(lpCmdLine, &argc);
331 if (!argv)
332 return FALSE;
333
334 CStringW Directory;
335 GetStorageDirectory(Directory);
336 CAppDB db(Directory);
337
338 BOOL bAppwizMode = (argc > 1 && MatchCmdOption(argv[1], CMD_KEY_APPWIZ));
339 if (!bAppwizMode)
340 {
341 CUpdateDatabaseMutex lock;
342 if (SettingsInfo.bUpdateAtStart || bIsFirstLaunch)
343 db.RemoveCached();
344
345 db.UpdateAvailable();
346 }
347
348 db.UpdateInstalled();
349
350 if (argc == 1 || bAppwizMode) // RAPPS is launched without options or APPWIZ mode is requested
351 {
352 // Check whether the RAPPS MainWindow is already launched in another process
353 CStringW szWindowText(MAKEINTRESOURCEW(bAppwizMode ? IDS_APPWIZ_TITLE : IDS_APPTITLE));
354 LPCWSTR pszMutex = bAppwizMode ? L"RAPPWIZ" : MAINWINDOWMUTEX;
355
356 HANDLE hMutex = CreateMutexW(NULL, FALSE, pszMutex);
357 if ((!hMutex) || (GetLastError() == ERROR_ALREADY_EXISTS))
358 {
359 /* If already started, find its window */
360 HWND hWindow;
361 for (int wait = 2500, inter = 250; wait > 0; wait -= inter)
362 {
363 if ((hWindow = FindWindowW(szWindowClass, szWindowText)) != NULL)
364 break;
365 Sleep(inter);
366 }
367
368 if (hWindow)
369 {
370 /* Activate the window in the other instance */
371 ShowWindow(hWindow, SW_SHOW);
372 SwitchToThisWindow(hWindow, TRUE);
373 if (bAppwizMode)
374 PostMessage(hWindow, WM_COMMAND, ID_ACTIVATE_APPWIZ, 0);
375
376 if (hMutex)
377 CloseHandle(hMutex);
378
379 return FALSE;
380 }
381 }
382 szWindowText.Empty();
383
384 CMainWindow wnd(&db, bAppwizMode);
385 MainWindowLoop(&wnd, nCmdShow);
386
387 if (hMutex)
388 CloseHandle(hMutex);
389
390 return TRUE;
391 }
392
393 if (MatchCmdOption(argv[1], CMD_KEY_INSTALL))
394 {
395 return HandleInstallCommand(&db, argv[1], argc - 2, argv + 2);
396 }
397 else if (MatchCmdOption(argv[1], CMD_KEY_SETUP))
398 {
399 return HandleSetupCommand(&db, argv[1], argc - 2, argv + 2);
400 }
401 else if (MatchCmdOption(argv[1], CMD_KEY_UNINSTALL))
402 {
403 return HandleUninstallCommand(db, argc - 2, argv + 2);
404 }
405 else if (MatchCmdOption(argv[1], CMD_KEY_GENINST))
406 {
407 return HandleGenerateInstallerCommand(db, argc - 2, argv + 2);
408 }
409
410 InitRappsConsole();
411
412 if (MatchCmdOption(argv[1], CMD_KEY_FIND))
413 {
414 return HandleFindCommand(&db, argv[1], argc - 2, argv + 2);
415 }
416 else if (MatchCmdOption(argv[1], CMD_KEY_INFO))
417 {
418 return HandleInfoCommand(&db, argv[1], argc - 2, argv + 2);
419 }
420 else if (MatchCmdOption(argv[1], CMD_KEY_HELP) || MatchCmdOption(argv[1], CMD_KEY_HELP_ALT))
421 {
422 PrintHelpCommand();
423 return TRUE;
424 }
425 else
426 {
427 // unrecognized/invalid options
428 ConResPuts(StdOut, IDS_CMD_INVALID_OPTION);
429 PrintHelpCommand();
430 return FALSE;
431 }
432 }
433