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     BOOL bAppwizMode = FALSE;
226 
227     if (!argv)
228     {
229         return FALSE;
230     }
231 
232     CStringW Directory;
233     GetStorageDirectory(Directory);
234     CAppDB db(Directory);
235 
236     if (argc > 1 && MatchCmdOption(argv[1], CMD_KEY_APPWIZ))
237     {
238         bAppwizMode = TRUE;
239     }
240 
241     if (SettingsInfo.bUpdateAtStart || bIsFirstLaunch)
242     {
243         db.RemoveCached();
244     }
245     db.UpdateAvailable();
246     db.UpdateInstalled();
247 
248     if (argc == 1 || bAppwizMode) // RAPPS is launched without options or APPWIZ mode is requested
249     {
250         // Check whether the RAPPS MainWindow is already launched in another process
251         HANDLE hMutex;
252 
253         hMutex = CreateMutexW(NULL, FALSE, szWindowClass);
254         if ((!hMutex) || (GetLastError() == ERROR_ALREADY_EXISTS))
255         {
256             /* If already started, find its window */
257             HWND hWindow = FindWindowW(szWindowClass, NULL);
258 
259             /* Activate window */
260             ShowWindow(hWindow, SW_SHOWNORMAL);
261             SetForegroundWindow(hWindow);
262             if (bAppwizMode)
263                 PostMessage(hWindow, WM_COMMAND, ID_ACTIVATE_APPWIZ, 0);
264             return FALSE;
265         }
266 
267         CMainWindow wnd(&db, bAppwizMode);
268         MainWindowLoop(&wnd, nCmdShow);
269 
270         if (hMutex)
271             CloseHandle(hMutex);
272 
273         return TRUE;
274     }
275 
276     if (MatchCmdOption(argv[1], CMD_KEY_INSTALL))
277     {
278         return HandleInstallCommand(&db, argv[1], argc - 2, argv + 2);
279     }
280     else if (MatchCmdOption(argv[1], CMD_KEY_SETUP))
281     {
282         return HandleSetupCommand(&db, argv[1], argc - 2, argv + 2);
283     }
284 
285     InitRappsConsole();
286 
287     if (MatchCmdOption(argv[1], CMD_KEY_FIND))
288     {
289         return HandleFindCommand(&db, argv[1], argc - 2, argv + 2);
290     }
291     else if (MatchCmdOption(argv[1], CMD_KEY_INFO))
292     {
293         return HandleInfoCommand(&db, argv[1], argc - 2, argv + 2);
294     }
295     else if (MatchCmdOption(argv[1], CMD_KEY_HELP) || MatchCmdOption(argv[1], CMD_KEY_HELP_ALT))
296     {
297         PrintHelpCommand();
298         return TRUE;
299     }
300     else
301     {
302         // unrecognized/invalid options
303         ConResPuts(StdOut, IDS_CMD_INVALID_OPTION);
304         PrintHelpCommand();
305         return FALSE;
306     }
307 }
308