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