resolve_ops (FlatpakTransaction *self,
             GCancellable       *cancellable,
             GError            **error)
{
  FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
  GList *l;

  for (l = priv->ops; l != NULL; l = l->next)
    {
      FlatpakTransactionOperation *op = l->data;
      g_autoptr(FlatpakRemoteState) state = NULL;
      g_autofree char *checksum = NULL;
      g_autoptr(GBytes) metadata_bytes = NULL;

      if (op->resolved)
        continue;

      if (op->skip)
        {
          /* We're not yet resolved, but marked skip anyway, this can happen if during
           * request_required_tokens() we were normalized away even though not fully resolved.
           * For example we got the checksum but need to auth to get the commit, but the
           * checksum we got was the version already installed.
           */
          g_assert (op->resolved_commit != NULL);
          if (!mark_op_resolved (op, op->resolved_commit, NULL, NULL, NULL, error))
            return FALSE;
          continue;
        }

      if (op->kind == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
        {
          /* We resolve to the deployed metadata, because we need it to uninstall related ops */

          metadata_bytes = load_deployed_metadata (self, op->ref, &checksum, NULL);
          if (metadata_bytes == NULL)
            {
              op->skip = TRUE;
              continue;
            }
          if (!mark_op_resolved (op, checksum, NULL, metadata_bytes, NULL, error))
            return FALSE;
          continue;
        }

      if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE)
        {
          g_assert (op->commit != NULL);
          if (!mark_op_resolved (op, op->commit, NULL, op->external_metadata, NULL, error))
            return FALSE;
          continue;
        }

      /* op->kind is INSTALL or UPDATE */

      if (flatpak_decomposed_is_app (op->ref))
        {
          if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL)
            priv->max_op = APP_INSTALL;
          else
            priv->max_op = MAX (priv->max_op, APP_UPDATE);
        }
      else if (flatpak_decomposed_is_runtime (op->ref))
        {
          if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL)
            priv->max_op = MAX (priv->max_op, RUNTIME_INSTALL);
        }

      state = flatpak_transaction_ensure_remote_state (self, op->kind, op->remote, NULL, error);
      if (state == NULL)
        return FALSE;

      /* Should we use local state */
      if (transaction_is_local_only (self, op->kind))
        {
          g_autoptr(GVariant) commit_data = flatpak_dir_read_latest_commit (priv->dir, op->remote, op->ref,
                                                                            &checksum, NULL, error);
          if (commit_data == NULL)
            return FALSE;

          if (!resolve_op_from_commit (self, op, checksum, NULL, commit_data, error))
            return FALSE;
        }
      else
        {
          g_autoptr(GError) local_error = NULL;
          g_autoptr(GFile) sideload_path = NULL;

          if (op->commit != NULL)
            {
              checksum = g_strdup (op->commit);
              /* Check if this is available offline and if so, use that */
              sideload_path = flatpak_remote_state_lookup_sideload_checksum (state, op->commit);
            }
          else
            {
              g_autofree char *latest_checksum = NULL;
              g_autoptr(GFile) latest_sideload_path = NULL;
              g_autofree char *local_checksum = NULL;
              guint64 latest_timestamp;
              g_autoptr(GVariant) local_commit_data = flatpak_dir_read_latest_commit (priv->dir, op->remote, op->ref,
                                                                                      &local_checksum, NULL, NULL);

              if (flatpak_dir_find_latest_rev (priv->dir, state, flatpak_decomposed_get_ref (op->ref), op->commit,
                                               &latest_checksum, &latest_timestamp, &latest_sideload_path,
                                               cancellable, &local_error))
                {
                  /* If we found the latest in a sideload repo, it may be older that what is locally available, check timestamps.
                   * Note: If the timestamps are equal (timestamp granularity issue), assume we want to update */
                  if (latest_sideload_path != NULL && local_commit_data && latest_timestamp != 0 &&
                      ostree_commit_get_timestamp (local_commit_data) > latest_timestamp)
                    {
                      g_debug ("Installed commit %s newer than sideloaded %s, ignoring", local_checksum, latest_checksum);
                      checksum = g_steal_pointer (&local_checksum);
                    }
                  else
                    {
                      /* Otherwise, use whatever we found */
                      checksum = g_steal_pointer (&latest_checksum);
                      sideload_path = g_steal_pointer (&latest_sideload_path);
                    }
                }
              else
                {
                  /* Ref not available in the remote (maybe offline), resolve to local version if installed */
                  if (local_commit_data == NULL)
                    {
                      g_propagate_error (error, g_steal_pointer (&local_error));
                      return FALSE;
                    }

                  g_message (_("Warning: Treating remote fetch error as non-fatal since %s is already installed: %s"),
                             flatpak_decomposed_get_ref (op->ref), local_error->message);
                  g_clear_error (&local_error);

                  checksum = g_steal_pointer (&local_checksum);
                }
            }

          /* First try to resolve via metadata (if remote is available and its metadata matches the commit version) */
          if (!try_resolve_op_from_metadata (self, op, checksum, sideload_path, state, &local_error))
            {
              if (local_error)
                {
                  /* Actual error, not just missing from summary */
                  g_propagate_error (error, g_steal_pointer (&local_error));
                  return FALSE;
                }

              /* Missing from summary, try to load the commit object.
               * Note, we don't have a token here, so this will not work for authenticated apps.
               * We handle this by catching the 401 http status and retrying. */
              g_autoptr(GVariant) commit_data = NULL;
              VarRefInfoRef ref_info;

              /* OCI needs this to get the oci repository for the ref to request the token, so lets always set it here */
              if (op->summary_metadata == NULL &&
                  flatpak_remote_state_lookup_ref (state, flatpak_decomposed_get_ref (op->ref),
                                                   NULL, NULL, &ref_info, NULL, NULL))
                op->summary_metadata = var_metadata_dup_to_gvariant (var_ref_info_get_metadata (ref_info));

              commit_data = flatpak_remote_state_load_ref_commit (state, priv->dir,
                                                                  flatpak_decomposed_get_ref (op->ref),
                                                                  checksum, /* initially NULL */ op->resolved_token,
                                                                  NULL, NULL, &local_error);
              if (commit_data == NULL)
                {
                  if (g_error_matches (local_error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) && !op->requested_token)
                    {

                      g_debug ("Unauthorized access during resolve by commit of %s, retrying with token", flatpak_decomposed_get_ref (op->ref));
                      priv->needs_resolve = TRUE;
                      priv->needs_tokens = TRUE;

                      /* Token type maxint32 means we don't know the type */
                      op->token_type = G_MAXINT32;
                      op->resolved_commit = g_strdup (checksum);

                      g_clear_error (&local_error);
                      continue;
                    }
                  g_propagate_error (error, g_steal_pointer (&local_error));
                  return FALSE;
                }

              if (!resolve_op_from_commit (self, op, checksum, sideload_path, commit_data, error))
                return FALSE;
            }
        }
    }

  return TRUE;
}
resolve_ops (FlatpakTransaction *self,
             GCancellable       *cancellable,
             GError            **error)
{
  FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
  GList *l;

  for (l = priv->ops; l != NULL; l = l->next)
    {
      FlatpakTransactionOperation *op = l->data;
      g_autoptr(FlatpakRemoteState) state = NULL;
      g_autofree char *checksum = NULL;
      g_autoptr(GBytes) metadata_bytes = NULL;

      if (op->resolved)
        continue;

      if (op->skip)
        {
           
          g_assert (op->resolved_commit != NULL);
          if (!mark_op_resolved (op, op->resolved_commit, NULL, NULL, NULL, error))
            return FALSE;
          continue;
        }

      if (op->kind == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
        {
           

          metadata_bytes = load_deployed_metadata (self, op->ref, &checksum, NULL);
          if (metadata_bytes == NULL)
            {
              op->skip = TRUE;
              continue;
            }
          if (!mark_op_resolved (op, checksum, NULL, metadata_bytes, NULL, error))
            return FALSE;
          continue;
        }

      if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE)
        {
          g_assert (op->commit != NULL);
          if (!mark_op_resolved (op, op->commit, NULL, op->external_metadata, NULL, error))
            return FALSE;
          continue;
        }

       

      if (flatpak_decomposed_is_app (op->ref))
        {
          if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL)
            priv->max_op = APP_INSTALL;
          else
            priv->max_op = MAX (priv->max_op, APP_UPDATE);
        }
      else if (flatpak_decomposed_is_runtime (op->ref))
        {
          if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL)
            priv->max_op = MAX (priv->max_op, RUNTIME_INSTALL);
        }

      state = flatpak_transaction_ensure_remote_state (self, op->kind, op->remote, NULL, error);
      if (state == NULL)
        return FALSE;

       
      if (transaction_is_local_only (self, op->kind))
        {
          g_autoptr(GVariant) commit_data = flatpak_dir_read_latest_commit (priv->dir, op->remote, op->ref,
                                                                            &checksum, NULL, error);
          if (commit_data == NULL)
            return FALSE;

          if (!resolve_op_from_commit (self, op, checksum, NULL, commit_data, error))
            return FALSE;
        }
      else
        {
          g_autoptr(GError) local_error = NULL;
          g_autoptr(GFile) sideload_path = NULL;

          if (op->commit != NULL)
            {
              checksum = g_strdup (op->commit);
               
              sideload_path = flatpak_remote_state_lookup_sideload_checksum (state, op->commit);
            }
          else
            {
              g_autofree char *latest_checksum = NULL;
              g_autoptr(GFile) latest_sideload_path = NULL;
              g_autofree char *local_checksum = NULL;
              guint64 latest_timestamp;
              g_autoptr(GVariant) local_commit_data = flatpak_dir_read_latest_commit (priv->dir, op->remote, op->ref,
                                                                                      &local_checksum, NULL, NULL);

              if (flatpak_dir_find_latest_rev (priv->dir, state, flatpak_decomposed_get_ref (op->ref), op->commit,
                                               &latest_checksum, &latest_timestamp, &latest_sideload_path,
                                               cancellable, &local_error))
                {
                   
                  if (latest_sideload_path != NULL && local_commit_data && latest_timestamp != 0 &&
                      ostree_commit_get_timestamp (local_commit_data) > latest_timestamp)
                    {
                      g_debug ("Installed commit %s newer than sideloaded %s, ignoring", local_checksum, latest_checksum);
                      checksum = g_steal_pointer (&local_checksum);
                    }
                  else
                    {
                       
                      checksum = g_steal_pointer (&latest_checksum);
                      sideload_path = g_steal_pointer (&latest_sideload_path);
                    }
                }
              else
                {
                   
                  if (local_commit_data == NULL)
                    {
                      g_propagate_error (error, g_steal_pointer (&local_error));
                      return FALSE;
                    }

                  g_message (_("Warning: Treating remote fetch error as non-fatal since %s is already installed: %s"),
                             flatpak_decomposed_get_ref (op->ref), local_error->message);
                  g_clear_error (&local_error);

                  checksum = g_steal_pointer (&local_checksum);
                }
            }

           
          if (!try_resolve_op_from_metadata (self, op, checksum, sideload_path, state, &local_error))
            {
              if (local_error)
                {
                   
                  g_propagate_error (error, g_steal_pointer (&local_error));
                  return FALSE;
                }

               
              g_autoptr(GVariant) commit_data = NULL;
              VarRefInfoRef ref_info;

               
              if (op->summary_metadata == NULL &&
                  flatpak_remote_state_lookup_ref (state, flatpak_decomposed_get_ref (op->ref),
                                                   NULL, NULL, &ref_info, NULL, NULL))
                op->summary_metadata = var_metadata_dup_to_gvariant (var_ref_info_get_metadata (ref_info));

              commit_data = flatpak_remote_state_load_ref_commit (state, priv->dir,
                                                                  flatpak_decomposed_get_ref (op->ref),
                                                                  checksum,   op->resolved_token,
                                                                  NULL, NULL, &local_error);
              if (commit_data == NULL)
                {
                  if (g_error_matches (local_error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) && !op->requested_token)
                    {

                      g_debug ("Unauthorized access during resolve by commit of %s, retrying with token", flatpak_decomposed_get_ref (op->ref));
                      priv->needs_resolve = TRUE;
                      priv->needs_tokens = TRUE;

                       
                      op->token_type = G_MAXINT32;
                      op->resolved_commit = g_strdup (checksum);

                      g_clear_error (&local_error);
                      continue;
                    }
                  g_propagate_error (error, g_steal_pointer (&local_error));
                  return FALSE;
                }

              if (!resolve_op_from_commit (self, op, checksum, sideload_path, commit_data, error))
                return FALSE;
            }
        }
    }

  return TRUE;
}