PIKApp/app/file/file-remote.c

406 lines
12 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* file-remote.c
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
*
* Based on: URI plug-in, GIO/GVfs backend
* Copyright (C) 2008 Sven Neumann <sven@gimp.org>
*
* 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/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libpikabase/pikabase.h"
#include "core/core-types.h"
#include "core/pika.h"
#include "core/pikaprogress.h"
#include "file-remote.h"
#include "pika-intl.h"
typedef enum
{
DOWNLOAD,
UPLOAD
} RemoteCopyMode;
typedef struct
{
PikaProgress *progress;
GCancellable *cancellable;
gboolean cancel;
GMainLoop *main_loop;
GError *error;
} RemoteMount;
typedef struct
{
RemoteCopyMode mode;
PikaProgress *progress;
GCancellable *cancellable;
gboolean cancel;
gint64 last_time;
} RemoteProgress;
static void file_remote_mount_volume_ready (GFile *file,
GAsyncResult *result,
RemoteMount *mount);
static void file_remote_mount_file_cancel (PikaProgress *progress,
RemoteMount *mount);
static GFile * file_remote_get_temp_file (Pika *pika,
GFile *file);
static gboolean file_remote_copy_file (Pika *pika,
GFile *src_file,
GFile *dest_file,
RemoteCopyMode mode,
PikaProgress *progress,
GError **error);
static void file_remote_copy_file_cancel (PikaProgress *progress,
RemoteProgress *remote_progress);
static void file_remote_progress_callback (goffset current_num_bytes,
goffset total_num_bytes,
gpointer user_data);
/* public functions */
gboolean
file_remote_mount_file (Pika *pika,
GFile *file,
PikaProgress *progress,
GError **error)
{
GMountOperation *operation;
RemoteMount mount = { 0, };
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
mount.progress = progress;
mount.main_loop = g_main_loop_new (NULL, FALSE);
operation = pika_get_mount_operation (pika, progress);
if (progress)
{
pika_progress_start (progress, TRUE, _("Mounting remote volume"));
mount.cancellable = g_cancellable_new ();
g_signal_connect (progress, "cancel",
G_CALLBACK (file_remote_mount_file_cancel),
&mount);
}
g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE,
operation, mount.cancellable,
(GAsyncReadyCallback) file_remote_mount_volume_ready,
&mount);
g_main_loop_run (mount.main_loop);
g_main_loop_unref (mount.main_loop);
if (progress)
{
g_signal_handlers_disconnect_by_func (progress,
file_remote_mount_file_cancel,
&mount);
g_object_unref (mount.cancellable);
pika_progress_end (progress);
}
g_object_unref (operation);
if (mount.error)
{
if (mount.error->domain != G_IO_ERROR ||
mount.error->code != G_IO_ERROR_ALREADY_MOUNTED)
{
g_propagate_error (error, mount.error);
return FALSE;
}
else
{
g_clear_error (&mount.error);
}
}
return TRUE;
}
GFile *
file_remote_download_image (Pika *pika,
GFile *file,
PikaProgress *progress,
GError **error)
{
GFile *local_file;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
local_file = file_remote_get_temp_file (pika, file);
if (! file_remote_copy_file (pika, file, local_file, DOWNLOAD,
progress, error))
{
g_object_unref (local_file);
return NULL;
}
return local_file;
}
GFile *
file_remote_upload_image_prepare (Pika *pika,
GFile *file,
PikaProgress *progress,
GError **error)
{
GFile *local_file;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
local_file = file_remote_get_temp_file (pika, file);
return local_file;
}
gboolean
file_remote_upload_image_finish (Pika *pika,
GFile *file,
GFile *local_file,
PikaProgress *progress,
GError **error)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (G_IS_FILE (local_file), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (! file_remote_copy_file (pika, local_file, file, UPLOAD,
progress, error))
{
return FALSE;
}
return TRUE;
}
/* private functions */
static void
file_remote_mount_volume_ready (GFile *file,
GAsyncResult *result,
RemoteMount *mount)
{
g_file_mount_enclosing_volume_finish (file, result, &mount->error);
g_main_loop_quit (mount->main_loop);
}
static void
file_remote_mount_file_cancel (PikaProgress *progress,
RemoteMount *mount)
{
mount->cancel = TRUE;
g_cancellable_cancel (mount->cancellable);
}
static GFile *
file_remote_get_temp_file (Pika *pika,
GFile *file)
{
gchar *basename;
GFile *temp_file = NULL;
basename = g_path_get_basename (pika_file_get_utf8_name (file));
if (basename)
{
const gchar *ext = strchr (basename, '.');
if (ext && strlen (ext))
temp_file = pika_get_temp_file (pika, ext + 1);
g_free (basename);
}
if (! temp_file)
temp_file = pika_get_temp_file (pika, "xxx");
return temp_file;
}
static gboolean
file_remote_copy_file (Pika *pika,
GFile *src_file,
GFile *dest_file,
RemoteCopyMode mode,
PikaProgress *progress,
GError **error)
{
RemoteProgress remote_progress = { 0, };
gboolean success;
GError *my_error = NULL;
remote_progress.mode = mode;
remote_progress.progress = progress;
if (progress)
{
pika_progress_start (progress, TRUE, _("Opening remote file"));
remote_progress.cancellable = g_cancellable_new ();
g_signal_connect (progress, "cancel",
G_CALLBACK (file_remote_copy_file_cancel),
&remote_progress);
success = g_file_copy (src_file, dest_file, G_FILE_COPY_OVERWRITE,
remote_progress.cancellable,
file_remote_progress_callback, &remote_progress,
&my_error);
g_signal_handlers_disconnect_by_func (progress,
file_remote_copy_file_cancel,
&remote_progress);
g_object_unref (remote_progress.cancellable);
pika_progress_set_value (progress, 1.0);
pika_progress_end (progress);
}
else
{
success = g_file_copy (src_file, dest_file, G_FILE_COPY_OVERWRITE,
NULL, NULL, NULL,
&my_error);
}
return success;
}
static void
file_remote_copy_file_cancel (PikaProgress *progress,
RemoteProgress *remote_progress)
{
remote_progress->cancel = TRUE;
g_cancellable_cancel (remote_progress->cancellable);
}
static void
file_remote_progress_callback (goffset current_num_bytes,
goffset total_num_bytes,
gpointer user_data)
{
RemoteProgress *progress = user_data;
gint64 now;
/* update the progress only up to 10 times a second */
now = g_get_monotonic_time ();
if ((now - progress->last_time) / 1000 < 100)
return;
progress->last_time = now;
if (total_num_bytes > 0)
{
const gchar *format;
gchar *done = g_format_size (current_num_bytes);
gchar *total = g_format_size (total_num_bytes);
switch (progress->mode)
{
case DOWNLOAD:
format = _("Downloading image (%s of %s)");
break;
case UPLOAD:
format = _("Uploading image (%s of %s)");
break;
default:
pika_assert_not_reached ();
}
pika_progress_set_text (progress->progress, format, done, total);
g_free (total);
g_free (done);
pika_progress_set_value (progress->progress,
(gdouble) current_num_bytes /
(gdouble) total_num_bytes);
}
else
{
const gchar *format;
gchar *done = g_format_size (current_num_bytes);
switch (progress->mode)
{
case DOWNLOAD:
format = _("Downloaded %s of image data");
break;
case UPLOAD:
format = _("Uploaded %s of image data");
break;
default:
pika_assert_not_reached ();
}
pika_progress_set_text (progress->progress, format, done);
g_free (done);
pika_progress_pulse (progress->progress);
}
while (! progress->cancel && g_main_context_pending (NULL))
g_main_context_iteration (NULL, FALSE);
}