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;
}