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 #include "rapps.h"
9 
10 #include "unattended.h"
11 
12 #include "winmain.h"
13 
14 #include <setupapi.h>
15 
16 #include <conutils.h>
17 
18 BOOL MatchCmdOption(LPWSTR argvOption, LPCWSTR szOptToMacth)
19 {
20     WCHAR FirstCharList[] = { L'-', L'/' };
21 
22     for (UINT i = 0; i < _countof(FirstCharList); i++)
23     {
24         if (argvOption[0] == FirstCharList[i])
25         {
26             if (StrCmpIW(argvOption + 1, szOptToMacth) == 0)
27             {
28                 return TRUE;
29             }
30             else
31             {
32                 return FALSE;
33             }
34         }
35     }
36     return FALSE;
37 }
38 
39 BOOL HandleInstallCommand(LPWSTR szCommand, int argcLeft, LPWSTR * argvLeft)
40 {
41     if (argcLeft == 0)
42     {
43         ConInitStdStreams(); // Initialize the Console Standard Streams
44         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PACKAGE_NAME, szCommand);
45         return FALSE;
46     }
47     FreeConsole();
48 
49     ATL::CSimpleArray<ATL::CStringW> PkgNameList;
50 
51     for (int i = 0; i < argcLeft; i++)
52     {
53         PkgNameList.Add(argvLeft[i]);
54     }
55 
56     CAvailableApps apps;
57     apps.UpdateAppsDB();
58     apps.Enum(ENUM_ALL_AVAILABLE, NULL, NULL);
59 
60     ATL::CSimpleArray<CAvailableApplicationInfo> arrAppInfo = apps.FindAppsByPkgNameList(PkgNameList);
61     if (arrAppInfo.GetSize() > 0)
62     {
63         DownloadListOfApplications(arrAppInfo, TRUE);
64         return TRUE;
65     }
66     else
67     {
68         return FALSE;
69     }
70 }
71 
72 BOOL HandleSetupCommand(LPWSTR szCommand, int argcLeft, LPWSTR * argvLeft)
73 {
74     if (argcLeft != 1)
75     {
76         ConInitStdStreams(); // Initialize the Console Standard Streams
77         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_FILE_NAME, szCommand);
78         return FALSE;
79     }
80     FreeConsole();
81 
82     ATL::CSimpleArray<ATL::CStringW> PkgNameList;
83     HINF InfHandle = SetupOpenInfFileW(argvLeft[0], NULL, INF_STYLE_WIN4, NULL);
84     if (InfHandle == INVALID_HANDLE_VALUE)
85     {
86         return FALSE;
87     }
88 
89     INFCONTEXT Context;
90     if (SetupFindFirstLineW(InfHandle, L"RAPPS", L"Install", &Context))
91     {
92         WCHAR szPkgName[MAX_PATH];
93         do
94         {
95             if (SetupGetStringFieldW(&Context, 1, szPkgName, _countof(szPkgName), NULL))
96             {
97                 PkgNameList.Add(szPkgName);
98             }
99         } while (SetupFindNextLine(&Context, &Context));
100     }
101     SetupCloseInfFile(InfHandle);
102 
103     CAvailableApps apps;
104     apps.UpdateAppsDB();
105     apps.Enum(ENUM_ALL_AVAILABLE, NULL, NULL);
106 
107     ATL::CSimpleArray<CAvailableApplicationInfo> arrAppInfo = apps.FindAppsByPkgNameList(PkgNameList);
108     if (arrAppInfo.GetSize() > 0)
109     {
110         DownloadListOfApplications(arrAppInfo, TRUE);
111         return TRUE;
112     }
113     else
114     {
115         return FALSE;
116     }
117 }
118 
119 BOOL CALLBACK CmdFindAppEnum(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param)
120 {
121     LPCWSTR lpszSearch = (LPCWSTR)param;
122     if (!SearchPatternMatch(Info->m_szName.GetString(), lpszSearch) &&
123         !SearchPatternMatch(Info->m_szDesc.GetString(), lpszSearch))
124     {
125         return TRUE;
126     }
127 
128     ConPrintf(StdOut, (LPWSTR)L"%s (%s)\n", (LPCWSTR)(Info->m_szName), (LPCWSTR)(Info->m_szPkgName));
129     return TRUE;
130 }
131 
132 BOOL HandleFindCommand(LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
133 {
134     if (argcLeft < 1)
135     {
136         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PARAMS, szCommand);
137         return FALSE;
138     }
139 
140     CAvailableApps apps;
141     apps.UpdateAppsDB();
142 
143     for (int i = 0; i < argcLeft; i++)
144     {
145         ConResMsgPrintf(StdOut, NULL, IDS_CMD_FIND_RESULT_FOR, argvLeft[i]);
146         apps.Enum(ENUM_ALL_AVAILABLE, CmdFindAppEnum, argvLeft[i]);
147         ConPrintf(StdOut, (LPWSTR)L"\n");
148     }
149 
150     return TRUE;
151 }
152 
153 BOOL HandleInfoCommand(LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
154 {
155     if (argcLeft < 1)
156     {
157         ConResMsgPrintf(StdOut, NULL, IDS_CMD_NEED_PARAMS, szCommand);
158         return FALSE;
159     }
160 
161     CAvailableApps apps;
162     apps.UpdateAppsDB();
163     apps.Enum(ENUM_ALL_AVAILABLE, NULL, NULL);
164 
165     for (int i = 0; i < argcLeft; i++)
166     {
167         CAvailableApplicationInfo *AppInfo = apps.FindAppByPkgName(argvLeft[i]);
168         if (!AppInfo)
169         {
170             ConResMsgPrintf(StdOut, NULL, IDS_CMD_PACKAGE_NOT_FOUND, argvLeft[i]);
171         }
172         else
173         {
174             ConResMsgPrintf(StdOut, NULL, IDS_CMD_PACKAGE_INFO, argvLeft[i]);
175             // TODO: code about extracting information from CAvailableApplicationInfo (in appview.cpp, class CAppRichEdit)
176             // is in a mess. It should be refactored, and should not placed in class CAppRichEdit.
177             // and the code here should reused that code after refactor.
178 
179             ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szName);
180 
181             if (AppInfo->m_szVersion)
182             {
183                 ConResPrintf(StdOut, IDS_AINFO_VERSION);
184                 ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szVersion);
185             }
186 
187             if (AppInfo->m_szLicense)
188             {
189                 ConResPrintf(StdOut, IDS_AINFO_LICENSE);
190                 ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szLicense);
191             }
192 
193             if (AppInfo->m_szSize)
194             {
195                 ConResPrintf(StdOut, IDS_AINFO_SIZE);
196                 ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szSize);
197             }
198 
199             if (AppInfo->m_szUrlSite)
200             {
201                 ConResPrintf(StdOut, IDS_AINFO_URLSITE);
202                 ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szUrlSite);
203             }
204 
205             if (AppInfo->m_szDesc)
206             {
207                 ConResPrintf(StdOut, IDS_AINFO_DESCRIPTION);
208                 ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szDesc);
209             }
210 
211             if (AppInfo->m_szUrlDownload)
212             {
213                 ConResPrintf(StdOut, IDS_AINFO_URLDOWNLOAD);
214                 ConPuts(StdOut, (LPWSTR)(LPCWSTR)AppInfo->m_szUrlDownload);
215             }
216 
217             ConPrintf(StdOut, (LPWSTR)L"\n");
218         }
219         ConPrintf(StdOut, (LPWSTR)L"\n");
220     }
221     return TRUE;
222 }
223 
224 BOOL HandleHelpCommand(LPWSTR szCommand, int argcLeft, LPWSTR * argvLeft)
225 {
226     if (argcLeft != 0)
227     {
228         return FALSE;
229     }
230 
231     ConPrintf(StdOut, (LPWSTR)L"\n");
232     ConResPuts(StdOut, IDS_APPTITLE);
233     ConPrintf(StdOut, (LPWSTR)L"\n\n");
234 
235     ConResPuts(StdOut, IDS_CMD_USAGE);
236     ConPrintf(StdOut, (LPWSTR)L"%ls\n", UsageString);
237     return TRUE;
238 }
239 
240 BOOL ParseCmdAndExecute(LPWSTR lpCmdLine, BOOL bIsFirstLaunch, int nCmdShow)
241 {
242     INT argc;
243     LPWSTR *argv = CommandLineToArgvW(lpCmdLine, &argc);
244 
245     if (!argv)
246     {
247         return FALSE;
248     }
249 
250     if (argc == 1) // RAPPS is launched without options
251     {
252         // Close the console, and open MainWindow
253         FreeConsole();
254 
255 
256         // Check for if rapps MainWindow is already launched in another process
257         HANDLE hMutex;
258 
259         hMutex = CreateMutexW(NULL, FALSE, szWindowClass);
260         if ((!hMutex) || (GetLastError() == ERROR_ALREADY_EXISTS))
261         {
262             /* If already started, it is found its window */
263             HWND hWindow = FindWindowW(szWindowClass, NULL);
264 
265             /* Activate window */
266             ShowWindow(hWindow, SW_SHOWNORMAL);
267             SetForegroundWindow(hWindow);
268             return FALSE;
269         }
270 
271         if (SettingsInfo.bUpdateAtStart || bIsFirstLaunch)
272             CAvailableApps::ForceUpdateAppsDB();
273 
274         MainWindowLoop(nCmdShow);
275 
276         if (hMutex)
277             CloseHandle(hMutex);
278 
279         return TRUE;
280     }
281     else if (MatchCmdOption(argv[1], CMD_KEY_INSTALL))
282     {
283         return HandleInstallCommand(argv[1], argc - 2, argv + 2);
284     }
285     else if (MatchCmdOption(argv[1], CMD_KEY_SETUP))
286     {
287         return HandleSetupCommand(argv[1], argc - 2, argv + 2);
288     }
289 
290 
291     ConInitStdStreams(); // Initialize the Console Standard Streams
292 
293     if (MatchCmdOption(argv[1], CMD_KEY_FIND))
294     {
295         return HandleFindCommand(argv[1], argc - 2, argv + 2);
296     }
297     else if (MatchCmdOption(argv[1], CMD_KEY_INFO))
298     {
299         return HandleInfoCommand(argv[1], argc - 2, argv + 2);
300     }
301     else if (MatchCmdOption(argv[1], CMD_KEY_HELP))
302     {
303         return HandleHelpCommand(argv[1], argc - 2, argv + 2);
304     }
305     else
306     {
307         // unrecognized/invalid options
308         ConResPuts(StdOut, IDS_CMD_INVALID_OPTION);
309         return FALSE;
310     }
311 }
312