🏡 index : quine-global-labs/bazaar.git

/* dl-worker.c
 *
 * Copyright 2025 Adam Masciola
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#define G_LOG_DOMAIN "BAZAAR::DL-WORKER-SUBPROCESS"

#include "env.h"
#include "global-net.h"

static DexFuture *
read_stdin_fiber (GMainLoop  *loop,
                  GIOChannel *stdout_channel);

static DexFuture *
download_fiber (const char *src_uri,
                const char *dest_path,
                GIOChannel *stdout_channel);

int
main (int   argc,
      char *argv[])
{
  g_autoptr (GIOChannel) stdout_channel = NULL;
  g_autoptr (GMainLoop) main_loop       = NULL;
  g_autoptr (DexFuture) future          = NULL;

  g_log_writer_default_set_use_stderr (TRUE);
  dex_init ();

  stdout_channel = g_io_channel_unix_new (STDOUT_FILENO);
  g_assert (g_io_channel_set_encoding (stdout_channel, NULL, NULL));
  g_io_channel_set_buffered (stdout_channel, FALSE);

  main_loop = g_main_loop_new (NULL, FALSE);

  future = dex_scheduler_spawnv (
      dex_thread_pool_scheduler_get_default (),
      bz_get_dex_stack_size (),
      G_CALLBACK (read_stdin_fiber),
      2,
      G_TYPE_MAIN_LOOP, main_loop,
      G_TYPE_IO_CHANNEL, stdout_channel);
  g_main_loop_run (main_loop);

  return EXIT_SUCCESS;
}

static DexFuture *
read_stdin_fiber (GMainLoop  *loop,
                  GIOChannel *stdout_channel)
{
  g_autoptr (GIOChannel) stdin_channel = NULL;

  stdin_channel = g_io_channel_unix_new (STDIN_FILENO);
  for (;;)
    {
      g_autoptr (GError) local_error = NULL;
      g_autofree char *string        = NULL;
      char            *newline       = NULL;
      g_autoptr (GVariant) variant   = NULL;
      g_autofree char *src_uri       = NULL;
      g_autofree char *dst_path      = NULL;

      g_io_channel_read_line (
          stdin_channel, &string, NULL, NULL, &local_error);
      if (string == NULL)
        {
          if (local_error != NULL)
            g_warning ("FATAL: Failure reading stdin channel: %s", local_error->message);
          g_main_loop_quit (loop);
          return NULL;
        }

      newline = g_utf8_strchr (string, -1, '\n');
      if (newline != NULL)
        *newline = '\0';

      variant = g_variant_parse (
          G_VARIANT_TYPE ("(ss)"),
          string, NULL, NULL,
          &local_error);
      if (variant == NULL)
        {
          g_warning ("Failure parsing variant text '%s' into structure: %s\n",
                     string, local_error->message);
          g_main_loop_quit (loop);
          continue;
        }

      g_variant_get (variant, "(ss)", &src_uri, &dst_path);

      dex_future_disown (dex_scheduler_spawnv (
          dex_scheduler_get_default (),
          bz_get_dex_stack_size (),
          G_CALLBACK (download_fiber),
          3,
          G_TYPE_STRING, src_uri,
          G_TYPE_STRING, dst_path,
          G_TYPE_IO_CHANNEL, stdout_channel));
    }

  return NULL;
}

static DexFuture *
download_fiber (const char *src_uri,
                const char *dst_path,
                GIOChannel *stdout_channel)
{
  gboolean success                          = FALSE;
  g_autoptr (GError) local_error            = NULL;
  g_autoptr (GFile) dest_file               = NULL;
  g_autoptr (GFileOutputStream) dest_output = NULL;
  g_autoptr (SoupMessage) message           = NULL;
  SoupMessageHeaders *headers               = NULL;
  g_autoptr (GVariant) variant              = NULL;
  g_autofree char *output                   = NULL;
  g_autofree char *output_plus_nl           = NULL;

  dest_file   = g_file_new_for_path (dst_path);
  dest_output = g_file_replace (
      dest_file, NULL, FALSE,
      G_FILE_CREATE_REPLACE_DESTINATION,
      NULL, &local_error);
  if (dest_output == NULL)
    {
      g_warning ("%s", local_error->message);
      goto done;
    }

  message = soup_message_new (SOUP_METHOD_GET, src_uri);

  headers = soup_message_get_request_headers (message);
  soup_message_headers_append (headers, "Accept", "*/*");
  soup_message_headers_append (headers, "User-Agent", "Bazaar");

  success = dex_await (bz_send_with_global_http_session_then_splice_into (
                           message, G_OUTPUT_STREAM (dest_output)),
                       &local_error);
  if (!success)
    {
      g_warning ("%s", local_error->message);
      goto done;
    }

done:
  variant        = g_variant_new ("(sb)", dst_path, success);
  output         = g_variant_print (variant, TRUE);
  output_plus_nl = g_strdup_printf ("%s\n", output);

  g_io_channel_write_chars (stdout_channel, output_plus_nl, -1, NULL, NULL);

  return dex_future_new_true ();
}