Index: exo-mount/main.c =================================================================== --- exo-mount/main.c (revision 27040) +++ exo-mount/main.c (working copy) @@ -306,12 +306,12 @@ "but kernel doesn't list the volume as mounted"); } +#ifdef HAVE_HAL +err0: +#endif /* check if we failed */ if (G_UNLIKELY (err != NULL)) { -#ifdef HAVE_HAL -err0: -#endif /* check if we should display an error dialog */ if (G_LIKELY (!opt_noui)) { Index: exo-mount/exo-mount-hal.c =================================================================== --- exo-mount/exo-mount-hal.c (revision 27040) +++ exo-mount/exo-mount-hal.c (working copy) @@ -145,8 +145,352 @@ } +static void +exo_prompt_update_text (GtkEditable *editable, + gchar *new_text, + gint new_text_len, + gint *position, + gpointer data) +{ + gchar **response = (gchar **)data; + if (response == NULL) + return; + g_free(*response); + *response = gtk_editable_get_chars(editable, 0, -1); +} +typedef struct _ExoDialogValidate { + GtkWidget *dialog; + gint response; +} ExoDialogValidate; + +void +exo_prompt_validate_text (GtkEntry *entry, + gpointer data) +{ + ExoDialogValidate *validate_data = (ExoDialogValidate *)data; + gtk_dialog_response(GTK_DIALOG(validate_data->dialog), validate_data->response); +} + +static void +exo_hal_device_removed (LibHalContext *context, + const gchar *udi) +{ + const gchar *dialog_udi; + GtkWidget *dialog = libhal_ctx_get_user_data (context); + + /* check if the active UDI of the dialog was removed */ + dialog_udi = g_object_get_data (G_OBJECT (dialog), "udi"); + if (exo_str_is_equal (dialog_udi, udi)) + { + /* cancel the dialog */ + gtk_dialog_response (GTK_DIALOG (dialog), 0); + } +} + /** + * exo_ask_password: + * @context : a #LibHalContext. + * @udi : the UDI of the device being added, which is watched for removal. + * @icon : the icon or %NULL. + * @title : the prompt title. + * @response_text : the response text. + * @visible : whether text typed should be visible. + * @default_response : Which response to return on Enter. + * @primary_text : the primary prompt text. + * @secondary_text : the secondary prompt text. + * @first_button_text : the first button text. + * @... : %NULL-terminated list of button text, response id pairs. + * + * Return value: the selected response. + **/ +static gint +exo_ask_password (LibHalContext *context, + const gchar *udi, + const gchar *icon, + const gchar *title, + gchar **response_text, + gboolean visible, + gint default_response, + const gchar *primary_text, + const gchar *secondary_text, + const gchar *first_button_text, + ...) +{ + GtkWidget *dialog; + GtkWidget *image; + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *entry; + DBusError derror; + va_list args; + gint response; + ExoDialogValidate validate_data; + + g_return_val_if_fail (exo_hal_udi_validate (udi, -1, NULL), 0); + g_return_val_if_fail (context != NULL, GTK_RESPONSE_CANCEL); + + /* allocate a new dialog */ + dialog = gtk_dialog_new (); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + g_object_set_data_full (G_OBJECT (dialog), "udi", g_strdup (udi), g_free); + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 6); + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 12); + + /* setup the specified title */ + if (G_LIKELY (title != NULL)) + gtk_window_set_title (GTK_WINDOW (dialog), title); + + /* setup the specified buttons */ + if (G_LIKELY (first_button_text != NULL)) + { + va_start (args, first_button_text); + for (response = va_arg (args, gint); first_button_text != NULL; ) + { + /* insert the button */ + gtk_dialog_add_button (GTK_DIALOG (dialog), first_button_text, response); + first_button_text = va_arg (args, const gchar *); + if (G_UNLIKELY (first_button_text == NULL)) + break; + response = va_arg (args, gint); + } + va_end (args); + } + + /* setup the hbox */ + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE, 0); + gtk_widget_show (hbox); + + /* setup the specified icon */ + if (G_LIKELY (icon != NULL)) + { + /* setup an image for the icon */ + image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.0f, 0.0f); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_widget_show (image); + } + + /* setup the vbox */ + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + /* setup the primary text */ + label = gtk_label_new (primary_text); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0f, 0.5f); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + /* setup the secondary text */ + if (G_LIKELY (secondary_text != NULL)) + { + label = gtk_label_new (secondary_text); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0f, 0.5f); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + } + + entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entry), visible); + gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); + gtk_widget_show (entry); + + /* initialize D-Bus error */ + dbus_error_init (&derror); + + /* setup HAL to watch the UDI for removal */ + libhal_ctx_set_user_data (context, dialog); + libhal_ctx_set_device_removed (context, exo_hal_device_removed); + libhal_device_property_watch_all (context, &derror); + + /* setup handler for the entered text */ + if (response_text) + *response_text = NULL; + g_signal_connect_after(G_OBJECT(entry), "insert-text", + G_CALLBACK(exo_prompt_update_text), + (gpointer) response_text); + validate_data.dialog = dialog; + validate_data.response = default_response; + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(exo_prompt_validate_text), + (gpointer) &validate_data); + + /* run the dialog */ + response = gtk_dialog_run (GTK_DIALOG (dialog)); + /* cleanup */ + libhal_ctx_set_device_removed (context, NULL); + libhal_ctx_set_user_data (context, NULL); + gtk_widget_destroy (dialog); + dbus_error_free (&derror); + + return response; +} + +static gchar * +exo_mount_install_crypto_volume (LibHalContext *context, + const gchar *udi, + LibHalVolume *volume, + const gchar *password, + gboolean *pass_error, + GError **error) +{ + gchar *plain_udi = NULL; + DBusMessage *msg = NULL, *reply = NULL; + DBusError err; + DBusConnection *dbus_connection = NULL; + + g_clear_error(error); + *pass_error = FALSE; + msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, + "org.freedesktop.Hal.Device.Volume.Crypto", + "Setup"); + if (msg == NULL) { + g_warning ("Could not create dbus message for %s", udi); + goto out; + } + + if (!dbus_message_append_args (msg, + DBUS_TYPE_STRING, &password, + DBUS_TYPE_INVALID)) { + g_warning ("Could not append args to dbus message for %s", udi); + goto out; + } + + dbus_error_init (&err); + dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &err); + if (!dbus_connection) { + if (dbus_error_is_set (&err)) { + g_message ("Dbus connection failed for %s: %s : %s\n", udi, err.name, err.message); + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, err.message); + dbus_error_free(&err); + } + goto out; + } + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &err)) || + dbus_error_is_set (&err)) { + g_message ("Setup failed for %s: %s : %s\n", udi, err.name, err.message); + if (strcmp (err.name, "org.freedesktop.Hal.Device.Volume.Crypto.SetupPasswordError") == 0) { + *pass_error = TRUE; + } + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, err.message); + dbus_error_free (&err); + goto out; + } else { + plain_udi = libhal_volume_crypto_get_clear_volume_udi (context, volume); + } + + +out: + if (msg != NULL) + dbus_message_unref (msg); + if (reply != NULL) + dbus_message_unref (reply); + if (plain_udi) + g_clear_error(error); + return plain_udi; +} + +static gchar * +exo_mount_setup_crypto_volume(LibHalContext *context, + const gchar *udi, + GError **error) +{ + gchar *password = NULL; + gboolean pass_error = FALSE; + gint num_tries = 0; + gchar *plain_udi = NULL; + LibHalVolume *volume = NULL; + + volume = libhal_volume_from_udi (context, udi); + g_return_val_if_fail(volume != NULL, NULL); + plain_udi = libhal_volume_crypto_get_clear_volume_udi (context, volume); + if (plain_udi != NULL) { + libhal_volume_free(volume); + return plain_udi; + } + + for (num_tries = 0; num_tries < 3; num_tries++) { + gint response,len; + response = exo_ask_password (context, udi, "gtk-dialog-authentication", _("Encrypted volume"), + &password, FALSE, GTK_RESPONSE_OK, + pass_error? _("Wrong password.") + :_("This volume is encrypted."), + _("Please enter your password to decrypt and mount the volume."), + _("Ig_nore"), GTK_RESPONSE_CANCEL, + _("_Mount"), GTK_RESPONSE_OK, + NULL); + if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) { + num_tries = 3; + } else if (password) { + plain_udi = exo_mount_install_crypto_volume(context, udi, volume, password, &pass_error, error); + } + if (password) { + len = strlen(password); + memset(password, 0, len); + g_debug("zeroed password %p", password); + g_free(password); + } + if (plain_udi != NULL) + break; + } + return plain_udi; +} + +static void +exo_mount_teardown_crypto_volume(const gchar *udi) +{ + DBusMessage *msg = NULL, *reply = NULL; + DBusError err; + DBusConnection *dbus_connection = NULL; + + msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, + "org.freedesktop.Hal.Device.Volume.Crypto", + "Teardown"); + if (msg == NULL) { + g_warning ("Could not create dbus message for %s", udi); + goto out; + } + + if (!dbus_message_append_args (msg, + DBUS_TYPE_INVALID)) { + g_warning ("Could not append args to dbus message for %s", udi); + goto out; + } + + dbus_error_init (&err); + dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &err); + if (!dbus_connection) { + if (dbus_error_is_set (&err)) { + g_message ("Dbus connection failed for %s: %s : %s\n", udi, err.name, err.message); + dbus_error_free(&err); + } + goto out; + } + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &err)) || + dbus_error_is_set (&err)) { + g_message ("Teardown failed for %s: %s : %s\n", udi, err.name, err.message); + dbus_error_free (&err); + goto out; + } else { + g_debug("Teardown done\n"); + } + + +out: + if (msg != NULL) + dbus_message_unref (msg); + if (reply != NULL) + dbus_message_unref (reply); +} +/** * exo_mount_hal_device_from_udi: * @udi : UDI of a volume or drive. * @error : return location for errors or %NULL. @@ -215,9 +559,18 @@ } /* verify that we have a mountable device here */ - for (n = 0; interfaces[n] != NULL; ++n) + for (n = 0; interfaces[n] != NULL; ++n) { + if (strcmp (interfaces[n], "org.freedesktop.Hal.Device.Volume.Crypto") == 0) { + gchar *plain_udi = exo_mount_setup_crypto_volume(hal_context, udi,error); + if (plain_udi) { + device = exo_mount_hal_device_from_udi(plain_udi, error); + libhal_free_string(plain_udi); + } + goto out; + } if (strcmp (interfaces[n], "org.freedesktop.Hal.Device.Volume") == 0) break; + } if (G_UNLIKELY (interfaces[n] == NULL)) { /* definitely not a device that we're able to mount, eject or unmount */ @@ -350,6 +703,8 @@ for (m = 0; interfaces[m] != NULL; ++m) if (strcmp (interfaces[m], "org.freedesktop.Hal.Device.Volume") == 0) break; + else if (strcmp (interfaces[m], "org.freedesktop.Hal.Device.Volume.Crypto") == 0) + break; /* check if it's a usable device */ if (interfaces[m] != NULL) @@ -547,10 +902,19 @@ DBusMessage *message; DBusMessage *result; DBusError derror; + const gchar *backing_udi = NULL; g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + /* see if the udi is a crypto fs, in which case we'll have to teardown the crypto layer. */ + backing_udi = libhal_volume_crypto_get_backing_volume_udi(device->volume); + + if (backing_udi) + { + /* never eject a LUKS-encrypted device */ + return exo_mount_hal_device_unmount(device, error); + } /* allocate the D-Bus message for the "Eject" method */ message = dbus_message_new_method_call ("org.freedesktop.Hal", device->udi, "org.freedesktop.Hal.Device.Volume", "Eject"); if (G_UNLIKELY (message == NULL)) @@ -721,7 +1085,7 @@ DBUS_TYPE_STRING, &mount_point, DBUS_TYPE_STRING, &fstype, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, n, - DBUS_TYPE_INVALID)) + DBUS_TYPE_INVALID)) { dbus_message_unref (message); goto oom; @@ -894,10 +1258,14 @@ DBusMessage *message; DBusMessage *result; DBusError derror; + const gchar *backing_udi = NULL; g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + /* see if the udi is a crypto fs, in which case we'll have to teardown the crypto layer. */ + backing_udi = libhal_volume_crypto_get_backing_volume_udi(device->volume); + /* allocate the D-Bus message for the "Unmount" method */ message = dbus_message_new_method_call ("org.freedesktop.Hal", device->udi, "org.freedesktop.Hal.Device.Volume", "Unmount"); if (G_UNLIKELY (message == NULL)) @@ -955,7 +1323,7 @@ { /* Ups, volume not mounted, we succeed! */ dbus_error_free (&derror); - return TRUE; + goto finish; } else { @@ -967,7 +1335,11 @@ dbus_error_free (&derror); return FALSE; } - +finish: + if (G_UNLIKELY(backing_udi != NULL)) + { + exo_mount_teardown_crypto_volume(backing_udi); + } return TRUE; }