Main.cpp
1 #include <filesystem> 2 #include <fstream> 3 #include <string> 4 #include <vector> 5 #include <Shlobj.h> 6 #include <winrt/Windows.Data.Json.h> 7 #include <winrt/Windows.Foundation.Collections.h> 8 #include <winrt/Windows.System.UserProfile.h> 9 #include <winrt/Windows.Globalization.h> 10 11 #include "ZipTools/ZipFolder.h" 12 #include <common/SettingsAPI/settings_helpers.h> 13 #include <common/utils/json.h> 14 #include <common/utils/timeutil.h> 15 #include <common/utils/exec.h> 16 17 #include "Package.h" 18 #include "ReportMonitorInfo.h" 19 #include "RegistryUtils.h" 20 #include "EventViewer.h" 21 #include "InstallationFolder.h" 22 #include "ReportGPOValues.h" 23 24 using namespace std; 25 using namespace std::filesystem; 26 using namespace winrt::Windows::Data::Json; 27 28 map<wstring, vector<wstring>> escapeInfo = { 29 { L"FancyZones\\app-zone-history.json", { L"app-zone-history/app-path" } }, 30 { L"FancyZones\\settings.json", { L"properties/fancyzones_excluded_apps" } }, 31 { L"MouseWithoutBorders\\settings.json", { L"properties/SecurityKey" } }, // avoid leaking connection key 32 { L"Keyboard Manager\\default.json", { 33 L"remapKeysToText", 34 L"remapShortcutsToText", 35 L"remapShortcuts/global/runProgramFilePath", 36 L"remapShortcuts/global/runProgramArgs", 37 L"remapShortcuts/global/runProgramStartInDir", 38 L"remapShortcuts/global/openUri", 39 L"remapShortcuts/appSpecific/runProgramFilePath", 40 L"remapShortcuts/appSpecific/runProgramArgs", 41 L"remapShortcuts/appSpecific/runProgramStartInDir", 42 L"remapShortcuts/appSpecific/openUri", 43 } }, // avoid leaking personal information from text, URI or application mappings 44 { L"Workspaces/workspaces.json", { L"workspaces/applications/command-line-arguments" } }, 45 { L"AdvancedPaste/settings.json", { 46 L"properties/custom-actions/value/name", 47 L"properties/custom-actions/value/prompt" 48 } }, 49 }; 50 51 vector<wstring> filesToDelete = { 52 L"AdvancedPaste\\lastQuery.json", 53 L"AdvancedPaste\\kernelQueryCache.json", 54 L"PowerToys Run\\Cache", 55 L"PowerRename\\replace-mru.json", 56 L"PowerRename\\search-mru.json", 57 L"PowerToys Run\\Settings\\UserSelectedRecord.json", 58 L"PowerToys Run\\Settings\\QueryHistory.json", 59 L"NewPlus\\Templates", 60 L"etw", 61 }; 62 63 vector<wstring> GetXpathArray(wstring xpath) 64 { 65 vector<wstring> result; 66 wstring cur = L""; 67 for (auto ch : xpath) 68 { 69 if (ch == L'/') 70 { 71 result.push_back(cur); 72 cur = L""; 73 continue; 74 } 75 76 cur += ch; 77 } 78 79 if (!cur.empty()) 80 { 81 result.push_back(cur); 82 } 83 84 return result; 85 } 86 87 void HideByXPath(IJsonValue& val, vector<wstring>& xpathArray, int p) 88 { 89 if (val.ValueType() == JsonValueType::Array) 90 { 91 for (auto it : val.GetArray()) 92 { 93 HideByXPath(it, xpathArray, p); 94 } 95 96 return; 97 } 98 99 if (p == xpathArray.size() - 1) 100 { 101 if (val.ValueType() == JsonValueType::Object) 102 { 103 auto obj = val.GetObjectW(); 104 if (obj.HasKey(xpathArray[p])) 105 { 106 auto privateDatavalue = JsonValue::CreateStringValue(L"<private_data>"); 107 obj.SetNamedValue(xpathArray[p], privateDatavalue); 108 } 109 } 110 111 return; 112 } 113 114 if (val.ValueType() == JsonValueType::Object) 115 { 116 IJsonValue newVal; 117 try 118 { 119 newVal = val.GetObjectW().GetNamedValue(xpathArray[p]); 120 } 121 catch (...) 122 { 123 return; 124 } 125 126 HideByXPath(newVal, xpathArray, p + 1); 127 } 128 } 129 130 void HideForFile(const path& dir, const wstring& relativePath) 131 { 132 path jsonPath = dir; 133 jsonPath.append(relativePath); 134 auto jObject = json::from_file(jsonPath.wstring()); 135 if (!jObject.has_value()) 136 { 137 wprintf(L"Failed to parse file %s\n", jsonPath.c_str()); 138 return; 139 } 140 141 JsonValue jValue = json::value(jObject.value()); 142 for (auto xpath : escapeInfo[relativePath]) 143 { 144 vector<wstring> xpathArray = GetXpathArray(xpath); 145 HideByXPath(jValue, xpathArray, 0); 146 } 147 148 json::to_file(jsonPath.wstring(), jObject.value()); 149 } 150 151 bool DeleteFolder(wstring path) 152 { 153 error_code err; 154 remove_all(path, err); 155 if (err.value() != 0) 156 { 157 wprintf_s(L"Failed to delete %s. Error code: %d\n", path.c_str(), err.value()); 158 return false; 159 } 160 161 return true; 162 } 163 164 void HideUserPrivateInfo(const filesystem::path& dir) 165 { 166 // Replace data in json files 167 for (auto& it : escapeInfo) 168 { 169 HideForFile(dir, it.first); 170 } 171 172 // Delete files 173 for (auto it : filesToDelete) 174 { 175 auto path = dir; 176 path = path.append(it); 177 DeleteFolder(path); 178 } 179 } 180 181 void ReportWindowsVersion(const filesystem::path& tmpDir) 182 { 183 auto versionReportPath = tmpDir; 184 versionReportPath = versionReportPath.append("windows-version.txt"); 185 OSVERSIONINFOEXW osInfo{}; 186 187 try 188 { 189 NTSTATUS(WINAPI * RtlGetVersion) 190 (LPOSVERSIONINFOEXW) = nullptr; 191 *reinterpret_cast<FARPROC*>(& RtlGetVersion) = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); 192 if (RtlGetVersion) 193 { 194 osInfo.dwOSVersionInfoSize = sizeof(osInfo); 195 RtlGetVersion(&osInfo); 196 } 197 } 198 catch (...) 199 { 200 printf("Failed to get windows version info\n"); 201 return; 202 } 203 204 try 205 { 206 wofstream versionReport(versionReportPath); 207 versionReport << "MajorVersion: " << osInfo.dwMajorVersion << endl; 208 versionReport << "MinorVersion: " << osInfo.dwMinorVersion << endl; 209 versionReport << "BuildNumber: " << osInfo.dwBuildNumber << endl; 210 } 211 catch (...) 212 { 213 printf("Failed to write to %s\n", versionReportPath.string().c_str()); 214 } 215 } 216 217 void ReportWindowsSettings(const filesystem::path& tmpDir) 218 { 219 std::wstring userLanguage; 220 std::wstring userLocale; 221 try 222 { 223 const auto lang = winrt::Windows::System::UserProfile::GlobalizationPreferences::Languages().GetAt(0); 224 userLanguage = winrt::Windows::Globalization::Language{ lang }.DisplayName().c_str(); 225 wchar_t localeName[LOCALE_NAME_MAX_LENGTH]{}; 226 if (!LCIDToLocaleName(GetThreadLocale(), localeName, LOCALE_NAME_MAX_LENGTH, 0)) 227 { 228 throw -1; 229 } 230 userLocale = localeName; 231 } 232 catch (...) 233 { 234 printf("Failed to get windows settings\n"); 235 return; 236 } 237 238 try 239 { 240 wofstream settingsReport(tmpDir / "windows-settings.txt"); 241 settingsReport << "Preferred user language: " << userLanguage << endl; 242 settingsReport << "User locale: " << userLocale << endl; 243 } 244 catch (...) 245 { 246 printf("Failed to write windows settings\n"); 247 } 248 } 249 250 void ReportDotNetInstallationInfo(const filesystem::path& tmpDir) 251 { 252 auto dotnetInfoPath = tmpDir; 253 dotnetInfoPath.append("dotnet-installation-info.txt"); 254 try 255 { 256 wofstream dotnetReport(dotnetInfoPath); 257 auto dotnetInfo = exec_and_read_output(LR"(dotnet --list-runtimes)"); 258 if (!dotnetInfo.has_value()) 259 { 260 printf("Failed to get dotnet installation information\n"); 261 return; 262 } 263 264 dotnetReport << dotnetInfo.value().c_str(); 265 } 266 catch (...) 267 { 268 printf("Failed to report dotnet installation information"); 269 } 270 } 271 272 void ReportInstallerLogs(const filesystem::path& tmpDir, const filesystem::path& reportDir) 273 { 274 const char* bootstrapperLogFilePrefix = "powertoys-bootstrapper-msi-"; 275 const char* PTLogFilePrefix = "PowerToysMSIInstaller_"; 276 277 for (auto& entry : directory_iterator{ tmpDir }) 278 { 279 std::error_code ec; 280 if (entry.is_directory(ec) || !entry.path().has_filename()) 281 { 282 continue; 283 } 284 285 const auto fileName = entry.path().filename().string(); 286 if (!fileName.starts_with(bootstrapperLogFilePrefix) && !fileName.starts_with(PTLogFilePrefix)) 287 { 288 continue; 289 } 290 copy(entry.path(), reportDir / fileName, ec); 291 } 292 } 293 294 int wmain(int argc, wchar_t* argv[], wchar_t*) 295 { 296 // Get path to save zip 297 wstring saveZipPath; 298 if (argc > 1) 299 { 300 saveZipPath = argv[1]; 301 } 302 else 303 { 304 wchar_t buffer[MAX_PATH]; 305 if (SHGetSpecialFolderPath(HWND_DESKTOP, buffer, CSIDL_DESKTOP, FALSE)) 306 { 307 saveZipPath = buffer; 308 } 309 else 310 { 311 printf("Failed to retrieve the desktop path. Error code: %d\n", GetLastError()); 312 return 1; 313 } 314 } 315 316 auto settingsRootPath = PTSettingsHelper::get_root_save_folder_location(); 317 settingsRootPath += L"\\"; 318 319 auto localLowPath = PTSettingsHelper::get_local_low_folder_location(); 320 localLowPath += L"\\logs\\"; 321 322 const auto tempDir = temp_directory_path(); 323 auto reportDir = temp_directory_path() / "PowerToys\\"; 324 if (!DeleteFolder(reportDir)) 325 { 326 printf("Failed to delete temp folder\n"); 327 } 328 329 try 330 { 331 copy(settingsRootPath, reportDir, copy_options::recursive); 332 333 // Remove updates folder contents 334 DeleteFolder(reportDir / "Updates"); 335 } 336 337 catch (...) 338 { 339 printf("Failed to copy PowerToys folder\n"); 340 return 1; 341 } 342 343 try 344 { 345 copy(localLowPath, reportDir, copy_options::recursive); 346 } 347 348 catch (...) 349 { 350 printf("Failed to copy logs saved in LocalLow\n"); 351 } 352 353 #ifndef _DEBUG 354 InstallationFolder::ReportStructure(reportDir); 355 #endif 356 357 // Hide sensitive information 358 HideUserPrivateInfo(reportDir); 359 360 // Write windows settings to the temporary folder 361 ReportWindowsSettings(reportDir); 362 363 // Write monitors info to the temporary folder 364 ReportMonitorInfo(reportDir); 365 366 // Write windows version info to the temporary folder 367 ReportWindowsVersion(reportDir); 368 369 // Write dotnet installation info to the temporary folder 370 ReportDotNetInstallationInfo(reportDir); 371 372 // Write registry to the temporary folder 373 ReportRegistry(reportDir); 374 375 // Write gpo policies to the temporary folder 376 ReportGPOValues(reportDir); 377 378 // Write compatibility tab info to the temporary folder 379 ReportCompatibilityTab(reportDir); 380 381 // Write event viewer logs info to the temporary folder 382 EventViewer::ReportEventViewerInfo(reportDir); 383 384 // Write AppXDeployment-Server event logs to the temporary folder 385 EventViewer::ReportAppXDeploymentLogs(reportDir); 386 387 ReportInstallerLogs(tempDir, reportDir); 388 389 ReportInstalledContextMenuPackages(reportDir); 390 391 // Zip folder 392 auto zipPath = path::path(saveZipPath); 393 394 try 395 { 396 ZipFolder(zipPath, reportDir); 397 } 398 catch (...) 399 { 400 printf("Failed to zip folder\n"); 401 return 1; 402 } 403 404 DeleteFolder(reportDir); 405 return 0; 406 }