summaryrefslogtreecommitdiffstats
path: root/toolkit/system/windowsPackageManager/nsWindowsPackageManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/system/windowsPackageManager/nsWindowsPackageManager.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/toolkit/system/windowsPackageManager/nsWindowsPackageManager.cpp b/toolkit/system/windowsPackageManager/nsWindowsPackageManager.cpp
new file mode 100644
index 0000000000..98cff7db40
--- /dev/null
+++ b/toolkit/system/windowsPackageManager/nsWindowsPackageManager.cpp
@@ -0,0 +1,341 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWindowsPackageManager.h"
+#include "mozilla/Logging.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "mozilla/mscom/EnsureMTA.h"
+#ifndef __MINGW32__
+# include <comutil.h>
+# include <wrl.h>
+# include <windows.applicationmodel.store.h>
+# include <windows.management.deployment.h>
+# include <windows.services.store.h>
+#endif // __MINGW32__
+#include "nsError.h"
+#include "nsString.h"
+#include "nsISupportsPrimitives.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsXPCOMCID.h"
+#include "json/json.h"
+
+#ifndef __MINGW32__ // WinRT headers not yet supported by MinGW
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Management;
+using namespace ABI::Windows::Services::Store;
+#endif
+
+// Campaign IDs are stored in a JSON data structure under this key
+// for installs done without the user being signed in to the Microsoft
+// store.
+#define CAMPAIGN_ID_JSON_FIELD_NAME "customPolicyField1"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+NS_IMPL_ISUPPORTS(nsWindowsPackageManager, nsIWindowsPackageManager)
+
+NS_IMETHODIMP
+nsWindowsPackageManager::FindUserInstalledPackages(
+ const nsTArray<nsString>& aNamePrefixes, nsTArray<nsString>& aPackages) {
+#ifdef __MINGW32__
+ return NS_ERROR_NOT_IMPLEMENTED;
+#else
+ // The classes we're using are only available beginning with Windows 10
+ if (!mozilla::IsWin10OrLater()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ ComPtr<IInspectable> pmInspectable;
+ ComPtr<Deployment::IPackageManager> pm;
+ HRESULT hr = RoActivateInstance(
+ HStringReference(
+ RuntimeClass_Windows_Management_Deployment_PackageManager)
+ .Get(),
+ &pmInspectable);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ hr = pmInspectable.As(&pm);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ComPtr<Collections::IIterable<ApplicationModel::Package*> > pkgs;
+ hr = pm->FindPackagesByUserSecurityId(NULL, &pkgs);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ ComPtr<Collections::IIterator<ApplicationModel::Package*> > iterator;
+ hr = pkgs->First(&iterator);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ boolean hasCurrent;
+ hr = iterator->get_HasCurrent(&hasCurrent);
+ while (SUCCEEDED(hr) && hasCurrent) {
+ ComPtr<ApplicationModel::IPackage> package;
+ hr = iterator->get_Current(&package);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ ComPtr<ApplicationModel::IPackageId> packageId;
+ hr = package->get_Id(&packageId);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ HString rawName;
+ hr = packageId->get_FamilyName(rawName.GetAddressOf());
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ unsigned int tmp;
+ nsString name(rawName.GetRawBuffer(&tmp));
+ for (uint32_t i = 0; i < aNamePrefixes.Length(); i++) {
+ if (name.Find(aNamePrefixes.ElementAt(i)) != kNotFound) {
+ aPackages.AppendElement(name);
+ break;
+ }
+ }
+ hr = iterator->MoveNext(&hasCurrent);
+ }
+ return NS_OK;
+#endif // __MINGW32__
+}
+
+NS_IMETHODIMP
+nsWindowsPackageManager::GetInstalledDate(uint64_t* ts) {
+#ifdef __MINGW32__
+ return NS_ERROR_NOT_IMPLEMENTED;
+#else
+ // The classes we're using are only available beginning with Windows 10
+ if (!mozilla::IsWin10OrLater()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ ComPtr<ApplicationModel::IPackageStatics> pkgStatics;
+ HRESULT hr = RoGetActivationFactory(
+ HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(),
+ IID_PPV_ARGS(&pkgStatics));
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ComPtr<ApplicationModel::IPackage> package;
+ hr = pkgStatics->get_Current(&package);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ComPtr<ApplicationModel::IPackage3> package3;
+ hr = package.As(&package3);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DateTime installedDate;
+ hr = package3->get_InstalledDate(&installedDate);
+ if (!SUCCEEDED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *ts = installedDate.UniversalTime;
+ return NS_OK;
+#endif // __MINGW32__
+}
+
+NS_IMETHODIMP
+nsWindowsPackageManager::GetCampaignId(nsAString& aCampaignId) {
+#ifdef __MINGW32__
+ return NS_ERROR_NOT_IMPLEMENTED;
+#else
+ // The classes we're using are only available beginning with Windows 10,
+ // and this is only relevant for MSIX packaged builds.
+ if (!mozilla::IsWin10OrLater() || !mozilla::HasPackageIdentity()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ ComPtr<IStoreContextStatics> scStatics = nullptr;
+ HRESULT hr = RoGetActivationFactory(
+ HStringReference(RuntimeClass_Windows_Services_Store_StoreContext).Get(),
+ IID_PPV_ARGS(&scStatics));
+ /* Per
+ * https://docs.microsoft.com/en-us/windows/uwp/publish/create-a-custom-app-promotion-campaign#programmatically-retrieve-the-custom-campaign-id-for-an-app
+ * there are three ways to find a campaign ID.
+ * 1) If the user was logged into the Microsoft Store when they installed
+ * it will be available in a SKU.
+ * 2) If they were not logged in, it will be available through the
+ * StoreAppLicense
+ *
+ * There's also a third way, in theory, to retrieve it on very old versions of
+ * Windows 10 (1511 or earlier). However, these versions don't appear to be
+ * able to use the Windows Store at all anymore - so there's no point in
+ * supporting that scenario.
+ *
+ */
+ if (!SUCCEEDED(hr) || scStatics == nullptr) return NS_ERROR_FAILURE;
+ ComPtr<IStoreContext> storeContext = nullptr;
+ hr = scStatics->GetDefault(&storeContext);
+ if (!SUCCEEDED(hr) || storeContext == nullptr) return NS_ERROR_FAILURE;
+
+ ComPtr<IAsyncOperation<StoreProductResult*> > asyncSpr = nullptr;
+
+ {
+ nsAutoHandle event(CreateEventW(nullptr, true, false, nullptr));
+ bool asyncOpSucceeded = false;
+
+ // Despite the documentation indicating otherwise, the async operations
+ // and callbacks used here don't seem to work outside of a COM MTA.
+ mozilla::mscom::EnsureMTA(
+ [&event, &asyncOpSucceeded, &hr, &storeContext, &asyncSpr]() -> void {
+ auto callback =
+ Callback<IAsyncOperationCompletedHandler<StoreProductResult*> >(
+ [&asyncOpSucceeded, &event](
+ IAsyncOperation<StoreProductResult*>* asyncInfo,
+ AsyncStatus status) -> HRESULT {
+ asyncOpSucceeded = status == AsyncStatus::Completed;
+ return SetEvent(event.get());
+ });
+
+ hr = storeContext->GetStoreProductForCurrentAppAsync(&asyncSpr);
+ if (!SUCCEEDED(hr) || asyncSpr == nullptr) {
+ asyncOpSucceeded = false;
+ return;
+ }
+ hr = asyncSpr->put_Completed(callback.Get());
+ if (!SUCCEEDED(hr)) {
+ asyncOpSucceeded = false;
+ return;
+ }
+
+ DWORD ret = WaitForSingleObject(event.get(), 30000);
+ if (ret != WAIT_OBJECT_0) {
+ asyncOpSucceeded = false;
+ }
+ });
+ if (!asyncOpSucceeded) return NS_ERROR_FAILURE;
+ }
+
+ ComPtr<IStoreProductResult> productResult = nullptr;
+ hr = asyncSpr->GetResults(&productResult);
+ if (!SUCCEEDED(hr) || productResult == nullptr) return NS_ERROR_FAILURE;
+
+ ComPtr<IStoreProduct> product = nullptr;
+ hr = productResult->get_Product(&product);
+ if (!SUCCEEDED(hr) || product == nullptr) return NS_ERROR_FAILURE;
+ ComPtr<Collections::IVectorView<StoreSku*> > skus = nullptr;
+ hr = product->get_Skus(&skus);
+ if (!SUCCEEDED(hr) || skus == nullptr) return NS_ERROR_FAILURE;
+
+ unsigned int size;
+ hr = skus->get_Size(&size);
+ if (!SUCCEEDED(hr)) return NS_ERROR_FAILURE;
+
+ for (unsigned int i = 0; i < size; i++) {
+ ComPtr<IStoreSku> sku = nullptr;
+ hr = skus->GetAt(i, &sku);
+ if (!SUCCEEDED(hr) || sku == nullptr) return NS_ERROR_FAILURE;
+
+ boolean isInUserCollection = false;
+ hr = sku->get_IsInUserCollection(&isInUserCollection);
+ if (!SUCCEEDED(hr) || !isInUserCollection) continue;
+
+ ComPtr<IStoreCollectionData> scd = nullptr;
+ hr = sku->get_CollectionData(&scd);
+ if (!SUCCEEDED(hr) || scd == nullptr) continue;
+
+ HString campaignId;
+ hr = scd->get_CampaignId(campaignId.GetAddressOf());
+ if (!SUCCEEDED(hr)) continue;
+
+ unsigned int tmp;
+ aCampaignId.Assign(campaignId.GetRawBuffer(&tmp));
+ if (aCampaignId.Length() > 0) {
+ aCampaignId.AppendLiteral("&msstoresignedin=true");
+ }
+ }
+
+ // There's various points above that could exit without a failure.
+ // If we get here without a campaignId we may as well just check
+ // the AppStoreLicense.
+ if (aCampaignId.IsEmpty()) {
+ ComPtr<IAsyncOperation<StoreAppLicense*> > asyncSal = nullptr;
+ bool asyncOpSucceeded = false;
+ nsAutoHandle event(CreateEventW(nullptr, true, false, nullptr));
+ mozilla::mscom::EnsureMTA(
+ [&event, &asyncOpSucceeded, &hr, &storeContext, &asyncSal]() -> void {
+ auto callback =
+ Callback<IAsyncOperationCompletedHandler<StoreAppLicense*> >(
+ [&asyncOpSucceeded, &event](
+ IAsyncOperation<StoreAppLicense*>* asyncInfo,
+ AsyncStatus status) -> HRESULT {
+ asyncOpSucceeded = status == AsyncStatus::Completed;
+ return SetEvent(event.get());
+ });
+
+ hr = storeContext->GetAppLicenseAsync(&asyncSal);
+ if (!SUCCEEDED(hr) || asyncSal == nullptr) {
+ asyncOpSucceeded = false;
+ return;
+ }
+ hr = asyncSal->put_Completed(callback.Get());
+ if (!SUCCEEDED(hr)) {
+ asyncOpSucceeded = false;
+ return;
+ }
+
+ DWORD ret = WaitForSingleObject(event.get(), 30000);
+ if (ret != WAIT_OBJECT_0) {
+ asyncOpSucceeded = false;
+ }
+ });
+ if (!asyncOpSucceeded) return NS_ERROR_FAILURE;
+
+ ComPtr<IStoreAppLicense> license = nullptr;
+ hr = asyncSal->GetResults(&license);
+ if (!SUCCEEDED(hr) || license == nullptr) return NS_ERROR_FAILURE;
+
+ HString extendedData;
+ hr = license->get_ExtendedJsonData(extendedData.GetAddressOf());
+ if (!SUCCEEDED(hr)) return NS_ERROR_FAILURE;
+
+ Json::Value jsonData;
+ Json::Reader jsonReader;
+
+ unsigned int tmp;
+ nsAutoString key(extendedData.GetRawBuffer(&tmp));
+ if (!jsonReader.parse(NS_ConvertUTF16toUTF8(key).get(), jsonData, false)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (jsonData.isMember(CAMPAIGN_ID_JSON_FIELD_NAME) &&
+ jsonData[CAMPAIGN_ID_JSON_FIELD_NAME].isString()) {
+ aCampaignId.Assign(
+ NS_ConvertUTF8toUTF16(
+ jsonData[CAMPAIGN_ID_JSON_FIELD_NAME].asString().c_str())
+ .get());
+ if (aCampaignId.Length() > 0) {
+ aCampaignId.AppendLiteral("&msstoresignedin=false");
+ }
+ }
+ }
+
+ // No matter what happens in either block above, if they don't exit with a
+ // failure we managed to successfully pull the campaignId from somewhere
+ // (even if its empty).
+ return NS_OK;
+
+#endif // __MINGW32__
+}
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla