/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Copyright (C) 2012 Red Hat, Inc.
*
* This file is part of MATE Utils.
*
* MATE Utils 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 2 of the License, or
* (at your option) any later version.
*
* MATE Utils 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 MATE Utils. If not, see .
*
* Author: David Zeuthen
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
static gboolean have_gtk = FALSE;
static UDisksClient *udisks_client = NULL;
static GMainLoop *main_loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
static void
show_error (const gchar *format, ...)
{
va_list var_args;
gchar *s;
va_start (var_args, format);
s = g_strdup_vprintf (format, var_args);
if (have_gtk)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new_with_markup (NULL,
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s",
_("An error occurred"));
gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), "%s", s);
gtk_window_set_title (GTK_WINDOW (dialog), _("MATE Disk Image Mounter"));
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
else
{
g_printerr ("%s\n", s);
}
g_free (s);
va_end (var_args);
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean opt_writable = FALSE;
static const GOptionEntry opt_entries[] =
{
{ "writable", 'w', 0, G_OPTION_ARG_NONE, &opt_writable, N_("Allow writing to the image"), NULL},
{ NULL }
};
/* ---------------------------------------------------------------------------------------------------- */
/* TODO: keep in sync with src/disks/gduutils.c (ideally in shared lib) */
static void
_gdu_utils_configure_file_chooser_for_disk_images (GtkFileChooser *file_chooser)
{
GtkFileFilter *filter;
const gchar *folder;
/* Default to the "Documents" folder since that's where we save such images */
folder = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
if (folder != NULL)
gtk_file_chooser_set_current_folder (file_chooser, folder);
/* TODO: define proper mime-types */
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("All Files"));
gtk_file_filter_add_pattern (filter, "*");
gtk_file_chooser_add_filter (file_chooser, filter); /* adopts filter */
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Disk Images (*.img, *.iso)"));
gtk_file_filter_add_pattern (filter, "*.img");
gtk_file_filter_add_pattern (filter, "*.iso");
gtk_file_chooser_add_filter (file_chooser, filter); /* adopts filter */
gtk_file_chooser_set_filter (file_chooser, filter);
}
static GSList *
do_filechooser (void)
{
GSList *ret = NULL;
GtkWidget *dialog;
GtkWidget *ro_checkbutton;
ret = NULL;
dialog = gtk_file_chooser_dialog_new (_("Select Disk Image(s) to Mount"),
NULL, /* parent window */
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Mount"), GTK_RESPONSE_ACCEPT,
NULL);
_gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (dialog));
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
/* Add a RO check button that defaults to RO */
ro_checkbutton = gtk_check_button_new_with_mnemonic (_("Set up _read-only mount"));
gtk_widget_set_tooltip_markup (ro_checkbutton, _("If checked, the mount will be read-only. This is useful if you don't want the underlying disk image to be modified"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ro_checkbutton), !opt_writable);
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), ro_checkbutton);
//gtk_widget_show_all (dialog);
if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
goto out;
ret = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
opt_writable = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ro_checkbutton));
out:
gtk_widget_destroy (dialog);
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc, char *argv[])
{
gint ret = 1;
GError *error = NULL;
gchar *s = NULL;
GOptionContext *o = NULL;
gint n;
GSList *uris = NULL;
GSList *l;
#ifdef ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif /* ENABLE_NLS */
have_gtk = gtk_init_check (&argc, &argv);
if (have_gtk)
gtk_window_set_default_icon_name ("drive-removable-media");
main_loop = g_main_loop_new (NULL, FALSE);
udisks_client = udisks_client_new_sync (NULL, &error);
if (udisks_client == NULL)
{
g_printerr (_("Error connecting to udisks daemon: %s (%s, %d)"),
error->message, g_quark_to_string (error->domain), error->code);
g_error_free (error);
goto out;
}
o = g_option_context_new (NULL);
g_option_context_set_help_enabled (o, FALSE);
g_option_context_set_summary (o, _("Attach and mount one or more disk image files."));
g_option_context_add_main_entries (o, opt_entries, GETTEXT_PACKAGE);
if (!g_option_context_parse (o, &argc, &argv, NULL))
{
s = g_option_context_get_help (o, FALSE, NULL);
g_printerr ("%s", s);
g_free (s);
goto out;
}
if (argc > 1)
{
for (n = 1; n < argc; n++)
uris = g_slist_prepend (uris, g_strdup (argv[n]));
uris = g_slist_reverse (uris);
}
else
{
if (!have_gtk)
{
show_error ("No files given and GTK+ not available");
goto out;
}
else
{
uris = do_filechooser ();
}
}
/* Files to attach are positional arguments */
for (l = uris; l != NULL; l = l->next)
{
const gchar *uri;
gchar *filename;
GUnixFDList *fd_list = NULL;
GVariantBuilder options_builder;
gint fd;
gchar *loop_object_path = NULL;
GFile *file;
uri = l->data;
file = g_file_new_for_commandline_arg (uri);
filename = g_file_get_path (file);
g_object_unref (file);
if (filename == NULL)
{
show_error (_("Cannot open `%s' - maybe the volume isn't mounted?"), uri);
goto done_with_image;
}
fd = open (filename, opt_writable ? O_RDWR : O_RDONLY);
if (fd == -1)
{
show_error (_("Error opening `%s': %m"), filename);
goto done_with_image;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE ("a{sv}"));
if (!opt_writable)
g_variant_builder_add (&options_builder, "{sv}", "read-only", g_variant_new_boolean (TRUE));
fd_list = g_unix_fd_list_new_from_array (&fd, 1); /* adopts the fd */
/* Set up the disk image... */
error = NULL;
if (!udisks_manager_call_loop_setup_sync (udisks_client_get_manager (udisks_client),
g_variant_new_handle (0),
g_variant_builder_end (&options_builder),
fd_list,
&loop_object_path,
NULL, /* out_fd_list */
NULL, /* GCancellable */
&error))
{
show_error (_("Error attaching disk image: %s (%s, %d)"),
error->message, g_quark_to_string (error->domain), error->code);
g_clear_error (&error);
goto done_with_image;
}
/* Note that the desktop automounter is responsible for mounting,
* unlocking etc. partitions etc. inside the image...
*/
done_with_image:
g_clear_object (&fd_list);
g_free (filename);
g_free (loop_object_path);
} /* for each image */
ret = 0;
out:
if (main_loop != NULL)
g_main_loop_unref (main_loop);
g_slist_free_full (uris, g_free);
g_clear_object (&udisks_client);
return ret;
}