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 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 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 * 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 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 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 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 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 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 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 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 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 if (SettingsInfo.bUpdateAtStart || bIsFirstLaunch) 342 db.RemoveCached(); 343 344 db.UpdateAvailable(); 345 } 346 347 db.UpdateInstalled(); 348 349 if (argc == 1 || bAppwizMode) // RAPPS is launched without options or APPWIZ mode is requested 350 { 351 // Check whether the RAPPS MainWindow is already launched in another process 352 HANDLE hMutex; 353 354 hMutex = CreateMutexW(NULL, FALSE, szWindowClass); 355 if ((!hMutex) || (GetLastError() == ERROR_ALREADY_EXISTS)) 356 { 357 /* If already started, find its window */ 358 HWND hWindow; 359 for (int wait = 2500, inter = 250; wait > 0; wait -= inter) 360 { 361 if ((hWindow = FindWindowW(szWindowClass, NULL)) != NULL) 362 break; 363 Sleep(inter); 364 } 365 366 if (hWindow) 367 { 368 /* Activate the window in the other instance */ 369 SwitchToThisWindow(hWindow, TRUE); 370 if (bAppwizMode) 371 PostMessage(hWindow, WM_COMMAND, ID_ACTIVATE_APPWIZ, 0); 372 373 if (hMutex) 374 CloseHandle(hMutex); 375 376 return FALSE; 377 } 378 } 379 380 CMainWindow wnd(&db, bAppwizMode); 381 MainWindowLoop(&wnd, nCmdShow); 382 383 if (hMutex) 384 CloseHandle(hMutex); 385 386 return TRUE; 387 } 388 389 if (MatchCmdOption(argv[1], CMD_KEY_INSTALL)) 390 { 391 return HandleInstallCommand(&db, argv[1], argc - 2, argv + 2); 392 } 393 else if (MatchCmdOption(argv[1], CMD_KEY_SETUP)) 394 { 395 return HandleSetupCommand(&db, argv[1], argc - 2, argv + 2); 396 } 397 else if (MatchCmdOption(argv[1], CMD_KEY_UNINSTALL)) 398 { 399 return HandleUninstallCommand(db, argc - 2, argv + 2); 400 } 401 else if (MatchCmdOption(argv[1], CMD_KEY_GENINST)) 402 { 403 return HandleGenerateInstallerCommand(db, argc - 2, argv + 2); 404 } 405 406 InitRappsConsole(); 407 408 if (MatchCmdOption(argv[1], CMD_KEY_FIND)) 409 { 410 return HandleFindCommand(&db, argv[1], argc - 2, argv + 2); 411 } 412 else if (MatchCmdOption(argv[1], CMD_KEY_INFO)) 413 { 414 return HandleInfoCommand(&db, argv[1], argc - 2, argv + 2); 415 } 416 else if (MatchCmdOption(argv[1], CMD_KEY_HELP) || MatchCmdOption(argv[1], CMD_KEY_HELP_ALT)) 417 { 418 PrintHelpCommand(); 419 return TRUE; 420 } 421 else 422 { 423 // unrecognized/invalid options 424 ConResPuts(StdOut, IDS_CMD_INVALID_OPTION); 425 PrintHelpCommand(); 426 return FALSE; 427 } 428 } 429