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 <setupapi.h>
12 #include <conutils.h>
13 
14 static BOOL
15 MatchCmdOption(LPWSTR argvOption, LPCWSTR szOptToMacth)
16 {
17     WCHAR FirstCharList[] = {L'-', L'/'};
18 
19     for (UINT i = 0; i < _countof(FirstCharList); i++)
20     {
21         if (argvOption[0] == FirstCharList[i])
22         {
23             return StrCmpIW(argvOption + 1, szOptToMacth) == 0;
24         }
25     }
26     return FALSE;
27 }
28 
29 static void
30 InitRappsConsole()
31 {
32     // First, try to attach to our parent's console
33     if (!AttachConsole(ATTACH_PARENT_PROCESS))
34     {
35         // Did we already have a console?
36         if (GetLastError() != ERROR_ACCESS_DENIED)
37         {
38             // No, try to open a new one
39             AllocConsole();
40         }
41     }
42     ConInitStdStreams(); // Initialize the Console Standard Streams
43 }
44 
45 static BOOL
46 HandleInstallCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
47 {
48     if (argcLeft < 1)
49     {
50         InitRappsConsole();
51         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PACKAGE_NAME, szCommand);
52         return FALSE;
53     }
54 
55     CAtlList<CAppInfo *> Applications;
56     for (int i = 0; i < argcLeft; i++)
57     {
58         LPCWSTR PackageName = argvLeft[i];
59         CAppInfo *AppInfo = db->FindByPackageName(PackageName);
60         if (AppInfo)
61         {
62             Applications.AddTail(AppInfo);
63         }
64     }
65 
66     return DownloadListOfApplications(Applications, TRUE);
67 }
68 
69 static BOOL
70 HandleSetupCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
71 {
72     if (argcLeft != 1)
73     {
74         InitRappsConsole();
75         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_FILE_NAME, szCommand);
76         return FALSE;
77     }
78 
79     CAtlList<CAppInfo *> Applications;
80     HINF InfHandle = SetupOpenInfFileW(argvLeft[0], NULL, INF_STYLE_WIN4, NULL);
81     if (InfHandle == INVALID_HANDLE_VALUE)
82     {
83         return FALSE;
84     }
85 
86     INFCONTEXT Context;
87     if (SetupFindFirstLineW(InfHandle, L"RAPPS", L"Install", &Context))
88     {
89         WCHAR szPkgName[MAX_PATH];
90         do
91         {
92             if (SetupGetStringFieldW(&Context, 1, szPkgName, _countof(szPkgName), NULL))
93             {
94                 CAppInfo *AppInfo = db->FindByPackageName(szPkgName);
95                 if (AppInfo)
96                 {
97                     Applications.AddTail(AppInfo);
98                 }
99             }
100         } while (SetupFindNextLine(&Context, &Context));
101     }
102     SetupCloseInfFile(InfHandle);
103 
104     return DownloadListOfApplications(Applications, TRUE);
105 }
106 
107 static BOOL
108 HandleFindCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
109 {
110     if (argcLeft < 1)
111     {
112         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PARAMS, szCommand);
113         return FALSE;
114     }
115 
116     CAtlList<CAppInfo *> List;
117     db->GetApps(List, ENUM_ALL_AVAILABLE);
118 
119     for (int i = 0; i < argcLeft; i++)
120     {
121         LPCWSTR lpszSearch = argvLeft[i];
122         ConResMsgPrintf(StdOut, NULL, IDS_CMD_FIND_RESULT_FOR, lpszSearch);
123 
124         POSITION CurrentListPosition = List.GetHeadPosition();
125         while (CurrentListPosition)
126         {
127             CAppInfo *Info = List.GetNext(CurrentListPosition);
128 
129             if (SearchPatternMatch(Info->szDisplayName, lpszSearch) || SearchPatternMatch(Info->szComments, lpszSearch))
130             {
131                 ConPrintf(StdOut, L"%s (%s)\n", Info->szDisplayName.GetString(), Info->szIdentifier.GetString());
132             }
133         }
134 
135         ConPrintf(StdOut, L"\n");
136     }
137 
138     return TRUE;
139 }
140 
141 static BOOL
142 HandleInfoCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
143 {
144     if (argcLeft < 1)
145     {
146         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PARAMS, szCommand);
147         return FALSE;
148     }
149 
150     for (int i = 0; i < argcLeft; i++)
151     {
152         LPCWSTR PackageName = argvLeft[i];
153         CAppInfo *AppInfo = db->FindByPackageName(PackageName);
154         if (!AppInfo)
155         {
156             ConResMsgPrintf(StdOut, NULL, IDS_CMD_PACKAGE_NOT_FOUND, PackageName);
157         }
158         else
159         {
160             ConResMsgPrintf(StdOut, NULL, IDS_CMD_PACKAGE_INFO, PackageName);
161 
162             ConPuts(StdOut, AppInfo->szDisplayName);
163 
164             if (!AppInfo->szDisplayVersion.IsEmpty())
165             {
166                 ConResPrintf(StdOut, IDS_AINFO_VERSION);
167                 ConPuts(StdOut, AppInfo->szDisplayVersion);
168             }
169 
170             CStringW License, Size, UrlSite, UrlDownload;
171             AppInfo->GetDisplayInfo(License, Size, UrlSite, UrlDownload);
172 
173             if (!License.IsEmpty())
174             {
175                 ConResPrintf(StdOut, IDS_AINFO_LICENSE);
176                 ConPuts(StdOut, License);
177             }
178 
179             if (!Size.IsEmpty())
180             {
181                 ConResPrintf(StdOut, IDS_AINFO_SIZE);
182                 ConPuts(StdOut, Size);
183             }
184 
185             if (!UrlSite.IsEmpty())
186             {
187                 ConResPrintf(StdOut, IDS_AINFO_URLSITE);
188                 ConPuts(StdOut, UrlSite);
189             }
190 
191             if (AppInfo->szComments)
192             {
193                 ConResPrintf(StdOut, IDS_AINFO_DESCRIPTION);
194                 ConPuts(StdOut, AppInfo->szComments);
195             }
196 
197             if (!UrlDownload.IsEmpty())
198             {
199                 ConResPrintf(StdOut, IDS_AINFO_URLDOWNLOAD);
200                 ConPuts(StdOut, UrlDownload);
201             }
202             ConPuts(StdOut, L"\n");
203         }
204         ConPuts(StdOut, L"\n");
205     }
206     return TRUE;
207 }
208 
209 static VOID
210 PrintHelpCommand()
211 {
212     ConPrintf(StdOut, L"\n");
213     ConResPuts(StdOut, IDS_APPTITLE);
214     ConPrintf(StdOut, L"\n\n");
215 
216     ConResPuts(StdOut, IDS_CMD_USAGE);
217     ConPrintf(StdOut, L"%ls\n", UsageString);
218 }
219 
220 BOOL
221 ParseCmdAndExecute(LPWSTR lpCmdLine, BOOL bIsFirstLaunch, int nCmdShow)
222 {
223     INT argc;
224     LPWSTR *argv = CommandLineToArgvW(lpCmdLine, &argc);
225     if (!argv)
226         return FALSE;
227 
228     CStringW Directory;
229     GetStorageDirectory(Directory);
230     CAppDB db(Directory);
231 
232     BOOL bAppwizMode = (argc > 1 && MatchCmdOption(argv[1], CMD_KEY_APPWIZ));
233     if (!bAppwizMode)
234     {
235         if (SettingsInfo.bUpdateAtStart || bIsFirstLaunch)
236             db.RemoveCached();
237 
238         db.UpdateAvailable();
239     }
240 
241     db.UpdateInstalled();
242 
243     if (argc == 1 || bAppwizMode) // RAPPS is launched without options or APPWIZ mode is requested
244     {
245         // Check whether the RAPPS MainWindow is already launched in another process
246         HANDLE hMutex;
247 
248         hMutex = CreateMutexW(NULL, FALSE, szWindowClass);
249         if ((!hMutex) || (GetLastError() == ERROR_ALREADY_EXISTS))
250         {
251             /* If already started, find its window */
252             HWND hWindow = FindWindowW(szWindowClass, NULL);
253 
254             /* Activate window */
255             ShowWindow(hWindow, SW_SHOWNORMAL);
256             SetForegroundWindow(hWindow);
257             if (bAppwizMode)
258                 PostMessage(hWindow, WM_COMMAND, ID_ACTIVATE_APPWIZ, 0);
259             return FALSE;
260         }
261 
262         CMainWindow wnd(&db, bAppwizMode);
263         MainWindowLoop(&wnd, nCmdShow);
264 
265         if (hMutex)
266             CloseHandle(hMutex);
267 
268         return TRUE;
269     }
270 
271     if (MatchCmdOption(argv[1], CMD_KEY_INSTALL))
272     {
273         return HandleInstallCommand(&db, argv[1], argc - 2, argv + 2);
274     }
275     else if (MatchCmdOption(argv[1], CMD_KEY_SETUP))
276     {
277         return HandleSetupCommand(&db, argv[1], argc - 2, argv + 2);
278     }
279 
280     InitRappsConsole();
281 
282     if (MatchCmdOption(argv[1], CMD_KEY_FIND))
283     {
284         return HandleFindCommand(&db, argv[1], argc - 2, argv + 2);
285     }
286     else if (MatchCmdOption(argv[1], CMD_KEY_INFO))
287     {
288         return HandleInfoCommand(&db, argv[1], argc - 2, argv + 2);
289     }
290     else if (MatchCmdOption(argv[1], CMD_KEY_HELP) || MatchCmdOption(argv[1], CMD_KEY_HELP_ALT))
291     {
292         PrintHelpCommand();
293         return TRUE;
294     }
295     else
296     {
297         // unrecognized/invalid options
298         ConResPuts(StdOut, IDS_CMD_INVALID_OPTION);
299         PrintHelpCommand();
300         return FALSE;
301     }
302 }
303