IAsyncAction CodePushPackage::DownloadPackageAsync()

in windows/CodePush/CodePushPackage.cpp [33:192]


    /*static*/ IAsyncAction CodePushPackage::DownloadPackageAsync(
        JsonObject& updatePackage,
        std::wstring_view expectedBundleFileName,
        std::wstring_view publicKey,
        std::function<void(int64_t, int64_t)> progressCallback)
    {
        auto newUpdateHash{ updatePackage.GetNamedString(L"packageHash") };
        auto codePushFolder{ co_await GetCodePushFolderAsync() };

        auto downloadFile{ co_await codePushFolder.CreateFileAsync(DownloadFileName, CreationCollisionOption::ReplaceExisting) };

        CodePushDownloadHandler downloadHandler{
            downloadFile,
            progressCallback };

        auto isZip{ co_await downloadHandler.Download(updatePackage.GetNamedString(L"downloadUrl")) };

        StorageFolder newUpdateFolder{ co_await codePushFolder.CreateFolderAsync(newUpdateHash, CreationCollisionOption::ReplaceExisting) };
        StorageFile newUpdateMetadataFile{ nullptr };
        auto mutableUpdatePackage{ updatePackage };
        if (isZip)
        {
            auto unzippedFolder{ co_await codePushFolder.CreateFolderAsync(UnzippedFolderName, CreationCollisionOption::ReplaceExisting) };
            co_await FileUtils::UnzipAsync(downloadFile, unzippedFolder);
            downloadFile.DeleteAsync();

            auto isDiffUpdate{ false };

            auto diffManifestFile{ (co_await unzippedFolder.TryGetItemAsync(DiffManifestFileName)).try_as<StorageFile>() };
            if (diffManifestFile != nullptr)
            {
                isDiffUpdate = true;
            }

            if (isDiffUpdate)
            {
                // Copy the current package to the new package.
                auto currentPackageFolder{ co_await GetCurrentPackageFolderAsync() };

                if (currentPackageFolder == nullptr)
                {
                    // Currently running the binary version, copy files from the bundled resources
                    auto newUpdateCodePushFolder{ co_await newUpdateFolder.CreateFolderAsync(CodePushUpdateUtils::ManifestFolderPrefix) };

                    auto binaryAssetsFolder{ co_await CodePushNativeModule::GetBundleAssetsFolderAsync() };
                    auto newUpdateAssetsFolder{ co_await newUpdateCodePushFolder.CreateFolderAsync(CodePushUpdateUtils::AssetsFolderName) };
                    CodePushUpdateUtils::CopyEntriesInFolderAsync(binaryAssetsFolder, newUpdateAssetsFolder);

                    auto binaryBundleFile{ co_await CodePushNativeModule::GetBinaryBundleAsync() };
                    co_await binaryBundleFile.CopyAsync(newUpdateCodePushFolder);
                }
                else
                {
                    // Copy the contents of the current package to the new package. (how are conflicts resolved?)
                    co_await CodePushUpdateUtils::CopyEntriesInFolderAsync(currentPackageFolder, newUpdateFolder);
                }

                auto manifestContent{ co_await FileIO::ReadTextAsync(diffManifestFile, UnicodeEncoding::Utf8) };
                auto manifestJson{ JsonObject::Parse(manifestContent) };
                auto deletedFiles{ manifestJson.TryLookup(L"deletedFiles") };
                auto deletedFilesArray{ deletedFiles.try_as<JsonArray>() };

                if (deletedFilesArray != nullptr)
                {
                    for (const auto& deletedFileName : deletedFilesArray)
                    {
                        auto fileToDelete{ (co_await newUpdateFolder.TryGetItemAsync(deletedFileName.GetString())).try_as<StorageFile>() };
                        if (fileToDelete != nullptr)
                        {
                            co_await fileToDelete.DeleteAsync();
                        }
                    }
                }

                co_await diffManifestFile.DeleteAsync();
            }

            co_await CodePushUpdateUtils::CopyEntriesInFolderAsync(unzippedFolder, newUpdateFolder);
            co_await unzippedFolder.DeleteAsync();

            auto relativeBundlePath{ co_await FileUtils::FindFilePathAsync(newUpdateFolder, expectedBundleFileName) };
            if (!relativeBundlePath.empty())
            {
                mutableUpdatePackage.Insert(RelativeBundlePathKey, JsonValue::CreateStringValue(relativeBundlePath));
            }
            else
            {
                auto errorMessage{ L"Error: Unable to find JS bundle in downloaded package." };
                hresult_error error{ E_INVALIDARG, errorMessage };
                CodePushUtils::Log(error);
                throw error;
            }

            auto newUpdateMetadata{ co_await newUpdateFolder.TryGetItemAsync(UpdateMetadataFileName) };
            if (newUpdateMetadata != nullptr)
            {
                co_await newUpdateMetadata.DeleteAsync();
            }

            CodePushUtils::Log((isDiffUpdate) ? L"Applying diff update." : L"Applying full update.");
            auto isSignatureVerificationEnabled{ !publicKey.empty() };

            auto signatureFile{ co_await CodePushUpdateUtils::GetSignatureFileAsync(newUpdateFolder) };
            auto isSignatureAppearedInBundle{ signatureFile != nullptr };

            if (isSignatureVerificationEnabled)
            {
                if (isSignatureAppearedInBundle)
                {
                    auto errorMessage{ L"Error: Signature Verification is not currently supported." };
                    hresult_error error{ E_NOTIMPL, errorMessage };
                    CodePushUtils::Log(error);
                    throw error;
                }
                else
                {
                    auto errorMessage{ L"Error! Public key was provided but there is no JWT signature within app bundle to verify " \
                                L"Possible reasons, why that might happen: \n" \
                                L"1. You've been released CodePush bundle update using a version of the CodePush CLI that does not support code signing.\n" \
                                L"2. You've been released CodePush bundle update without providing --privateKeyPath option." };
                    hresult_error error{ E_FAIL, errorMessage };
                    CodePushUtils::Log(error);
                    throw error;
                }
            }
            else
            {
                bool needToVerifyHash;
                if (isSignatureAppearedInBundle)
                {
                    CodePushUtils::Log(L"Warning! JWT signature exists in codepush update but code integrity check couldn't be performed" \
                        L" because there is no public key configured. " \
                        L"Please ensure that a public key is properly configured within your application.");
                    needToVerifyHash = true;
                }
                else
                {
                    needToVerifyHash = isDiffUpdate;
                }

                if (needToVerifyHash)
                {
                    auto errorMessage{ L"Error: package content verification is not currently supported." };
                    hresult_error error{ E_NOTIMPL, errorMessage };
                    CodePushUtils::Log(error);
                }
            }
        }
        else
        {
            co_await downloadFile.MoveAsync(newUpdateFolder, UpdateBundleFileName, NameCollisionOption::ReplaceExisting);
        }

        newUpdateMetadataFile = co_await newUpdateFolder.CreateFileAsync(UpdateMetadataFileName, CreationCollisionOption::ReplaceExisting);

        auto packageJsonString{ mutableUpdatePackage.Stringify() };
        co_await FileIO::WriteTextAsync(newUpdateMetadataFile, packageJsonString);

        co_return;
    }