static ADUC_Result StepsHandler_Download()

in src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp [499:690]


static ADUC_Result StepsHandler_Download(const tagADUC_WorkflowData* workflowData)
{
    ADUC_Result result;
    result.ResultCode = ADUC_Result_Failure;
    result.ExtendedResultCode = 0;
    ADUC_WorkflowHandle handle = workflowData->WorkflowHandle;
    ADUC_WorkflowHandle stepHandle = nullptr;
    char* workFolder = workflow_get_workfolder(handle);
    JSON_Array* selectedComponentsArray = nullptr;
    int workflowLevel = workflow_get_level(handle);
    int workflowStep = workflow_get_step_index(handle);
    int selectedComponentsCount = 0;
    char* serializedComponentString = nullptr;
    bool isComponentsEnumeratorRegistered = ExtensionManager::IsComponentsEnumeratorRegistered();
    int createResult = 0;

    if (workflow_is_cancel_requested(handle))
    {
        result.ResultCode = ADUC_Result_Failure_Cancelled;
        result.ExtendedResultCode = 0;
        goto done;
    }

    Log_Debug(
        "\n#\n#Download task begin (level: %d, step:%d, wfid:%s, h_addr:0x%x).",
        workflowLevel,
        workflowStep,
        workflow_peek_id(handle),
        handle);

    createResult = ADUC_SystemUtils_MkSandboxDirRecursive(workFolder);
    if (createResult != 0)
    {
        Log_Error("Unable to create folder %s, error %d", workFolder, createResult);
        result = { ADUC_Result_Failure, ADUC_ERC_STEPS_HANDLER_CREATE_SANDBOX_FAILURE };
        goto done;
    }

    result = PrepareStepsWorkflowDataObject(handle);
    if (IsAducResultCodeFailure(result.ResultCode))
    {
        workflow_set_result_details(handle, "Invalid steps workflow collection");
        goto done;
    }

    result = HandleComponents(
        workflowLevel,
        workflowStep,
        isComponentsEnumeratorRegistered,
        handle,
        selectedComponentsArray,
        &selectedComponentsCount);

    if (IsAducResultCodeFailure(result.ResultCode))
    {
        goto done;
    }

    // For each selected component, perform step's backup, install & apply phase, restore phase if needed, in order.
    for (size_t iCom = 0, stepsCount = workflow_get_children_count(handle); iCom < selectedComponentsCount; iCom++)
    {
        serializedComponentString = CreateComponentSerializedString(selectedComponentsArray, iCom);

        //
        // For each step (child workflow), invoke backup, install and apply actions.
        // if install or apply fails, invoke restore action.
        //
        for (size_t i = 0; i < stepsCount; i++)
        {
            if (IsStepsHandlerExtraDebugLogsEnabled())
            {
                Log_Debug(
                    "Perform download action of child step #%lu on component #%d.\n#### Component ####\n%s\n###################\n",
                    i,
                    iCom,
                    serializedComponentString);
            }

            // Use a wrapper workflow to hold a stepHandle.
            ADUC_WorkflowData stepWorkflow = {};

            stepHandle = workflow_get_child(handle, i);
            if (stepHandle == nullptr)
            {
                const char* errorFmt = "Cannot process step #%lu due to missing (child) workflow data.";
                Log_Error(errorFmt, i);
                result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_DOWNLOAD_FAILURE_MISSING_CHILD_WORKFLOW;
                workflow_set_result_details(handle, errorFmt, i);
                goto done;
            }
            stepWorkflow.WorkflowHandle = stepHandle;

            // For inline step - set current component info on the workflow.
            if (serializedComponentString != nullptr && workflow_is_inline_step(handle, i))
            {
                if (!workflow_set_selected_components(stepHandle, serializedComponentString))
                {
                    result.ResultCode = ADUC_Result_Failure;
                    result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_SET_SELECTED_COMPONENTS_FAILURE;
                    workflow_set_result_details(handle, "Cannot select target component(s) for step #%lu", i);
                    goto done;
                }
            }

            ContentHandler* contentHandler = nullptr;
            const char* stepUpdateType = workflow_is_inline_step(handle, i)
                ? workflow_peek_update_manifest_step_handler(handle, i)
                : DEFAULT_REF_STEP_HANDLER;

            Log_Info("Loading handler for step #%lu (handler: '%s')", i, stepUpdateType);

            result = ExtensionManager::LoadUpdateContentHandlerExtension(stepUpdateType, &contentHandler);

            if (IsAducResultCodeFailure(result.ResultCode))
            {
                const char* errorFmt = "Cannot load a handler for step #%lu (handler :%s)";
                Log_Error(errorFmt, i, stepUpdateType);
                workflow_set_result(stepHandle, result);
                workflow_set_result_details(handle, errorFmt, i, stepUpdateType == nullptr ? "NULL" : stepUpdateType);
                goto done;
            }

            ADUC_ExtensionContractInfo contractInfo = contentHandler->GetContractInfo();
            if (ADUC_ContractUtils_IsV1Contract(&contractInfo))
            {
                result = DoV1DownloadWork(&stepWorkflow, contentHandler, handle, stepHandle);

                if (IsAducResultCodeFailure(result.ResultCode))
                {
                    goto done;
                }

                if (result.ResultCode == ADUC_Result_Install_Skipped_UpdateAlreadyInstalled)
                {
                    goto instanceDone;
                }
            }
            else
            {
                result = handleUnsupportedContractVersion(&contractInfo, stepUpdateType, handle);
                goto done;
            }

        instanceDone:
            stepHandle = nullptr;

            if (IsAducResultCodeFailure(result.ResultCode))
            {
                goto componentDone;
            }
        } // instances loop

    componentDone:
        json_free_serialized_string(serializedComponentString);
        serializedComponentString = nullptr;

        if (IsAducResultCodeFailure(result.ResultCode))
        {
            goto done;
        }

        // Set step's result.
    }

    result.ResultCode = ADUC_Result_Download_Success;
    result.ExtendedResultCode = 0;

done:

    // NOTE: Do not free child workflow here, so that it can be reused in the next phase.
    // Only free child handle when the workflow is done.
    //
    // Alternatively, we can persist child workflow state, to free up some memory, and
    // load the state when needed in the next phase

    workflow_set_result(handle, result);

    if (IsAducResultCodeSuccess(result.ResultCode))
    {
        workflow_set_state(handle, ADUCITF_State_DownloadSucceeded);
    }
    else
    {
        workflow_set_state(handle, ADUCITF_State_Failed);
    }

    json_free_serialized_string(serializedComponentString);
    workflow_free_string(workFolder);

    Log_Debug("Steps_Handler Download end (level %d).", workflowLevel);
    return result;
}