[gnome-disk-utility/new-ui] Handle LUKS volumes and show spinner in grid



commit b671bc977f2c7fd8104e28cf1e0c7a1d849fb368
Author: David Zeuthen <davidz redhat com>
Date:   Sat Oct 24 18:08:49 2009 -0400

    Handle LUKS volumes and show spinner in grid
    
    We still need to port the code for the passphrase dialogs to
    sub-classes of GduDialog but that's for another day.

 src/gdu-gtk/gdu-volume-grid.c        |  464 +++++++++++++++++++++++++---------
 src/palimpsest/gdu-section-volumes.c |  447 ++++++++++++++++++++++++++++++++
 2 files changed, 797 insertions(+), 114 deletions(-)
---
diff --git a/src/gdu-gtk/gdu-volume-grid.c b/src/gdu-gtk/gdu-volume-grid.c
index cdbafa4..e1281c7 100644
--- a/src/gdu-gtk/gdu-volume-grid.c
+++ b/src/gdu-gtk/gdu-volume-grid.c
@@ -53,11 +53,14 @@ struct GridElement
         GridElement *next;
 
         /* these values are set in recompute_size() */
-        gint x;
-        gint y;
-        gint width;
-        gint height;
+        guint x;
+        guint y;
+        guint width;
+        guint height;
         GridEdgeFlags edge_flags;
+
+        /* used for the job spinner */
+        guint spinner_current;
 };
 
 static void
@@ -84,6 +87,8 @@ struct GduVolumeGridPrivate
 
         GridElement *selected;
         GridElement *focused;
+
+        guint animation_timeout_id;
 };
 
 enum
@@ -105,15 +110,15 @@ G_DEFINE_TYPE (GduVolumeGrid, gdu_volume_grid, GTK_TYPE_DRAWING_AREA)
 static void recompute_grid (GduVolumeGrid *grid);
 
 static void recompute_size (GduVolumeGrid *grid,
-                            gint           width,
-                            gint           height);
+                            guint          width,
+                            guint          height);
 
 static GridElement *find_element_for_presentable (GduVolumeGrid *grid,
                                                   GduPresentable *presentable);
 
 static GridElement *find_element_for_position (GduVolumeGrid *grid,
-                                               gint x,
-                                               gint y);
+                                               guint x,
+                                               guint y);
 
 static gboolean gdu_volume_grid_expose_event (GtkWidget           *widget,
                                               GdkEventExpose      *event);
@@ -157,6 +162,11 @@ gdu_volume_grid_finalize (GObject *object)
                                               grid);
         g_object_unref (grid->priv->pool);
 
+        if (grid->priv->animation_timeout_id > 0) {
+                g_source_remove (grid->priv->animation_timeout_id);
+                grid->priv->animation_timeout_id = 0;
+        }
+
         if (G_OBJECT_CLASS (gdu_volume_grid_parent_class)->finalize != NULL)
                 G_OBJECT_CLASS (gdu_volume_grid_parent_class)->finalize (object);
 }
@@ -497,10 +507,37 @@ presentable_sort_offset (GduPresentable *a, GduPresentable *b)
                 return 0;
 }
 
+static guint
+get_depth (GList *elements)
+{
+        guint depth;
+        GList *l;
+
+        depth = 0;
+        if (elements == NULL)
+                goto out;
+
+        for (l = elements; l != NULL; l = l->next) {
+                GridElement *ee = l->data;
+                guint ee_depth;
+
+                ee_depth = get_depth (ee->embedded_elements) + 1;
+                if (ee_depth > depth)
+                        depth = ee_depth;
+        }
+
+ out:
+        return depth;
+}
+
 static void
-recompute_size (GduVolumeGrid *grid,
-                gint           width,
-                gint           height)
+recompute_size_for_slice (GList  *elements,
+                          guint   width,
+                          guint   height,
+                          guint   total_width,
+                          guint   total_height,
+                          guint   offset_x,
+                          guint   offset_y)
 {
         GList *l;
         gint x;
@@ -508,15 +545,17 @@ recompute_size (GduVolumeGrid *grid,
 
         x = 0;
         pixels_left = width;
-        for (l = grid->priv->elements; l != NULL; l = l->next) {
+        for (l = elements; l != NULL; l = l->next) {
                 GridElement *element = l->data;
                 gint element_width;
-                gboolean is_first;
                 gboolean is_last;
+                guint element_depth;
 
-                is_first = (l == grid->priv->elements);
                 is_last  = (l->next == NULL);
 
+                element_depth = get_depth (element->embedded_elements);
+                //g_debug ("element_depth = %d (x,y)=(%d,%d) height=%d", element_depth, offset_x, offset_y, height);
+
                 if (is_last) {
                         element_width = pixels_left;
                         pixels_left = 0;
@@ -527,67 +566,111 @@ recompute_size (GduVolumeGrid *grid,
                         pixels_left -= element_width;
                 }
 
-                element->x = x;
-                element->y = 0;
+                element->x = x + offset_x;
+                element->y = offset_y;
                 element->width = element_width;
-                element->height = height;
-                element->edge_flags = GRID_EDGE_TOP | GRID_EDGE_BOTTOM;
-                if (is_first)
+                if (element_depth > 0) {
+                        element->height = height / (element_depth + 1);
+                } else {
+                        element->height = height;
+                }
+
+                if (element->x == 0)
                         element->edge_flags |= GRID_EDGE_LEFT;
-                if (is_last)
+                if (element->y == 0)
+                        element->edge_flags |= GRID_EDGE_TOP;
+                if (element->x + element->width == total_width)
                         element->edge_flags |= GRID_EDGE_RIGHT;
+                if (element->y + element->height == total_height)
+                        element->edge_flags |= GRID_EDGE_BOTTOM;
 
                 x += element_width;
 
-                /* for now we don't recurse - we only handle embedded element for toplevel elements */
-                if (element->embedded_elements != NULL) {
-                        gint e_x;
-                        gint e_width;
-                        gint e_pixels_left;
-                        GList *ll;
+                recompute_size_for_slice (element->embedded_elements,
+                                          element->width,
+                                          height - element->height,
+                                          total_width,
+                                          total_height,
+                                          element->x,
+                                          element->height + element->y);
+        }
+}
 
-                        element->height = height/3;
-                        element->edge_flags &= ~(GRID_EDGE_BOTTOM);
-
-                        e_x = element->x;
-                        e_width = element->width;
-                        e_pixels_left = e_width;
-                        for (ll = element->embedded_elements; ll != NULL; ll = ll->next) {
-                                GridElement *e_element = ll->data;
-                                gint e_element_width;
-                                gboolean e_is_first;
-                                gboolean e_is_last;
-
-                                e_is_first = (ll == element->embedded_elements);
-                                e_is_last  = (ll->next == NULL);
-
-                                if (e_is_last) {
-                                        e_element_width = e_pixels_left;
-                                        e_pixels_left = 0;
-                                } else {
-                                        e_element_width = e_element->size_ratio * e_width;
-                                        if (e_element_width > e_pixels_left)
-                                                element_width = e_pixels_left;
-                                        e_pixels_left -= e_element_width;
-                                }
+static void
+recompute_size (GduVolumeGrid *grid,
+                guint          width,
+                guint          height)
+{
+        recompute_size_for_slice (grid->priv->elements,
+                                  width,
+                                  height,
+                                  width,
+                                  height,
+                                  0,
+                                  0);
+}
 
-                                e_element->x = e_x;
-                                e_element->y = element->height;
-                                e_element->width = e_element_width;
-                                e_element->height = height - e_element->y;
-                                e_element->edge_flags = GRID_EDGE_BOTTOM;
-                                if (is_first && e_is_first)
-                                        e_element->edge_flags |= GRID_EDGE_LEFT;
-                                if (is_last && e_is_last)
-                                        e_element->edge_flags |= GRID_EDGE_RIGHT;
+static void
+add_luks_holders (GduVolumeGrid *grid,
+                  GridElement   *element)
+{
+        GduDevice *d;
+        GduDevice *dholder;
+        GduPresentable *pholder;
+        const gchar *holder;
+        GridElement *holder_element;
+
+        d = NULL;
+        dholder = NULL;
+        pholder = NULL;
+
+        d = gdu_presentable_get_device (element->presentable);
+        if (d == NULL)
+                goto out;
 
-                                e_x += e_element_width;
+        if (g_strcmp0 (gdu_device_id_get_usage (d), "crypto") != 0)
+                goto out;
 
-                        }
-                }
-        }
+        holder = gdu_device_luks_get_holder (d);
+        if (holder == NULL || g_strcmp0 (holder, "/") == 0)
+                goto out;
+
+        dholder = gdu_pool_get_by_object_path (grid->priv->pool, holder);
+        if (dholder == NULL)
+                goto out;
+
+        pholder = gdu_pool_get_volume_by_device (grid->priv->pool, dholder);
+        if (pholder == NULL)
+                goto out;
+
+        holder_element = g_new0 (GridElement, 1);
+        holder_element->size_ratio = 1.0;
+        holder_element->presentable = g_object_ref (pholder);
+        holder_element->parent = element;
+
+        g_assert (element->embedded_elements == NULL);
+
+        //g_debug ("added holder %s for %s", gdu_device_get_device_file (dholder), gdu_device_get_device_file (d));
+
+        element->embedded_elements = g_list_append (element->embedded_elements,
+                                                    holder_element);
+
+        /* recurse - because we might have more than one level of encryption - for example
+         *
+         *   sda1 -> LUKS -> LUKS -> filesystem
+         */
+        add_luks_holders (grid, holder_element);
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (dholder != NULL)
+                g_object_unref (dholder);
+        if (pholder != NULL)
+                g_object_unref (pholder);
 }
 
+
 static void
 recompute_grid (GduVolumeGrid *grid)
 {
@@ -668,10 +751,17 @@ recompute_grid (GduVolumeGrid *grid)
 
                                 element->embedded_elements = g_list_append (element->embedded_elements,
                                                                             logical_element);
+
+                                add_luks_holders (grid, logical_element);
+
                         }
                         g_list_foreach (enclosed_logical_partitions, (GFunc) g_object_unref, NULL);
                         g_list_free (enclosed_logical_partitions);
-                        }
+
+                }
+
+                add_luks_holders (grid, element);
+
                 if (ed != NULL)
                         g_object_unref (ed);
 
@@ -713,6 +803,63 @@ recompute_grid (GduVolumeGrid *grid)
 }
 
 static void
+render_spinner (cairo_t   *cr,
+                guint      size,
+                guint      num_lines,
+                guint      current,
+                gdouble    x,
+                gdouble    y)
+{
+        guint n;
+        gdouble radius;
+        gdouble cx;
+        gdouble cy;
+        gdouble half;
+
+        cx = x + size/2.0;
+        cy = y + size/2.0;
+        radius = size/2.0;
+        half = num_lines / 2;
+
+        current = current % num_lines;
+
+        for (n = 0; n < num_lines; n++) {
+                gdouble inset;
+                gdouble t;
+
+                inset = 0.7 * radius;
+
+                /* transparency is a function of time and intial value */
+                t = (gdouble) ((n + num_lines - current) % num_lines) / num_lines;
+
+                cairo_set_source_rgba (cr, 0, 0, 0, t);
+                cairo_set_line_width (cr, 2.0);
+                cairo_move_to (cr,
+                               cx + (radius - inset) * cos (n * M_PI / half),
+                               cy + (radius - inset) * sin (n * M_PI / half));
+                cairo_line_to (cr,
+                               cx + radius * cos (n * M_PI / half),
+                               cy + radius * sin (n * M_PI / half));
+                cairo_stroke (cr);
+        }
+}
+
+static void
+render_pixbuf (cairo_t   *cr,
+               gdouble    x,
+               gdouble    y,
+               GdkPixbuf *pixbuf)
+{
+        gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
+        cairo_rectangle (cr,
+                         x,
+                         y,
+                         gdk_pixbuf_get_width (pixbuf),
+                         gdk_pixbuf_get_height (pixbuf));
+        cairo_fill (cr);
+}
+
+static void
 round_rect (cairo_t *cr,
             gdouble x, gdouble y,
             gdouble w, gdouble h,
@@ -786,7 +933,8 @@ round_rect (cairo_t *cr,
         }
 }
 
-static void
+/* returns true if an animation timeout is needed */
+static gboolean
 render_element (GduVolumeGrid *grid,
                 cairo_t       *cr,
                 GridElement   *element,
@@ -794,6 +942,7 @@ render_element (GduVolumeGrid *grid,
                 gboolean       is_focused,
                 gboolean       is_grid_focused)
 {
+        gboolean need_animation_timeout;
         gdouble fill_red;
         gdouble fill_green;
         gdouble fill_blue;
@@ -831,6 +980,8 @@ render_element (GduVolumeGrid *grid,
         gdouble text_selected_not_focused_green;
         gdouble text_selected_not_focused_blue;
 
+        need_animation_timeout = FALSE;
+
         fill_red     = 1;
         fill_green   = 1;
         fill_blue    = 1;
@@ -1000,16 +1151,29 @@ render_element (GduVolumeGrid *grid,
                                ceil (element->y + element->height / 2 - 2 - te.height/2 - te.y_bearing));
                 cairo_show_text (cr, text);
 
-        } else { /* render descriptive text for the presentable */
+        } else { /* render descriptive text + icons for the presentable */
                 gchar *s;
                 gchar *s1;
                 cairo_text_extents_t te;
                 cairo_text_extents_t te1;
                 GduDevice *d;
                 gdouble text_height;
+                gboolean render_padlock_closed;
+                gboolean render_padlock_open;
+                gboolean render_job_in_progress;
+                GPtrArray *pixbufs_to_render;
+                guint icon_offset;
+                guint n;
+
+                render_padlock_closed = FALSE;
+                render_padlock_open = FALSE;
+                render_job_in_progress = FALSE;
 
                 d = gdu_presentable_get_device (element->presentable);
 
+                if (d != NULL && gdu_device_job_in_progress (d))
+                        render_job_in_progress = TRUE;
+
                 s = NULL;
                 s1 = NULL;
                 if (d != NULL && g_strcmp0 (gdu_device_id_get_usage (d), "filesystem") == 0) {
@@ -1039,6 +1203,11 @@ render_element (GduVolumeGrid *grid,
                         s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->presentable),
                                                             FALSE,
                                                             FALSE);
+                        if (g_strcmp0 (gdu_device_luks_get_holder (d), "/") == 0) {
+                                render_padlock_closed = TRUE;
+                        } else {
+                                render_padlock_open = TRUE;
+                        }
                 } else if (!gdu_presentable_is_allocated (element->presentable)) {
                         s = g_strdup (_("Free"));
                         s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->presentable),
@@ -1092,42 +1261,87 @@ render_element (GduVolumeGrid *grid,
                 g_free (s);
                 g_free (s1);
 
+                /* OK, done with the text - now render spinner and icons */
+                icon_offset = 0;
+
+                if (render_job_in_progress) {
+                        render_spinner (cr,
+                                        16,
+                                        12,
+                                        element->spinner_current,
+                                        ceil (element->x + element->width - 16 - icon_offset - 4),
+                                        ceil (element->y + element->height - 16 - 4));
+
+                        icon_offset += 16 + 2; /* padding */
+
+                        element->spinner_current += 1;
+
+                        need_animation_timeout = TRUE;
+                }
+
+                /* icons */
+                pixbufs_to_render = g_ptr_array_new_with_free_func (g_object_unref);
+                if (render_padlock_open)
+                        g_ptr_array_add (pixbufs_to_render,
+                                         gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                                   "gdu-encrypted-unlock",
+                                                                   16, 0, NULL));
+                if (render_padlock_closed)
+                        g_ptr_array_add (pixbufs_to_render,
+                                         gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                                   "gdu-encrypted-lock",
+                                                                   16, 0, NULL));
+                for (n = 0; n < pixbufs_to_render->len; n++) {
+                        GdkPixbuf *pixbuf = GDK_PIXBUF (pixbufs_to_render->pdata[n]);
+                        guint icon_width;
+                        guint icon_height;
+
+                        icon_width = gdk_pixbuf_get_width (pixbuf);
+                        icon_height = gdk_pixbuf_get_height (pixbuf);
+
+                        render_pixbuf (cr,
+                                       ceil (element->x + element->width - icon_width - icon_offset - 4),
+                                       ceil (element->y + element->height - icon_height - 4),
+                                       pixbuf);
+
+                        icon_offset += icon_width + 2; /* padding */
+                }
+                g_ptr_array_free (pixbufs_to_render, TRUE);
+
+
                 if (d != NULL)
                         g_object_unref (d);
         }
 
         cairo_restore (cr);
+
+        return need_animation_timeout;
 }
 
 static gboolean
-gdu_volume_grid_expose_event (GtkWidget           *widget,
-                              GdkEventExpose      *event)
+on_animation_timeout (gpointer data)
 {
-        GduVolumeGrid *grid = GDU_VOLUME_GRID (widget);
-        GList *l;
-        cairo_t *cr;
-        gdouble width;
-        gdouble height;
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (data);
 
-        width = widget->allocation.width;
-        height = widget->allocation.height;
+        gtk_widget_queue_draw (GTK_WIDGET (grid));
 
-        recompute_size (grid,
-                        width - 1,
-                        height -1);
+        return TRUE; /* keep timeout around */
+}
 
-        cr = gdk_cairo_create (widget->window);
-        cairo_rectangle (cr,
-                         event->area.x, event->area.y,
-                         event->area.width, event->area.height);
-        cairo_clip (cr);
+static gboolean
+render_slice (GduVolumeGrid *grid,
+              cairo_t       *cr,
+              GList         *elements)
+{
+        GList *l;
+        gboolean need_animation_timeout;
 
-        for (l = grid->priv->elements; l != NULL; l = l->next) {
+        need_animation_timeout = FALSE;
+        for (l = elements; l != NULL; l = l->next) {
                 GridElement *element = l->data;
                 gboolean is_selected;
                 gboolean is_focused;
                 gboolean is_grid_focused;
-                GList *ll;
 
                 is_selected = FALSE;
                 is_focused = FALSE;
@@ -1141,39 +1355,61 @@ gdu_volume_grid_expose_event (GtkWidget           *widget,
                                 is_focused = TRUE;
                 }
 
-                render_element (grid,
-                                cr,
-                                element,
-                                is_selected,
-                                is_focused,
-                                is_grid_focused);
+                need_animation_timeout |= render_element (grid,
+                                                          cr,
+                                                          element,
+                                                          is_selected,
+                                                          is_focused,
+                                                          is_grid_focused);
 
-                for (ll = element->embedded_elements; ll != NULL; ll = ll->next) {
-                        GridElement *element = ll->data;
+                need_animation_timeout |= render_slice (grid,
+                                                        cr,
+                                                        element->embedded_elements);
+        }
 
-                        is_selected = FALSE;
-                        is_focused = FALSE;
-                        is_grid_focused = GTK_WIDGET_HAS_FOCUS (grid);
+        return need_animation_timeout;
+}
 
-                        if (element == grid->priv->selected)
-                                is_selected = TRUE;
+static gboolean
+gdu_volume_grid_expose_event (GtkWidget           *widget,
+                              GdkEventExpose      *event)
+{
+        GduVolumeGrid *grid = GDU_VOLUME_GRID (widget);
+        cairo_t *cr;
+        gdouble width;
+        gdouble height;
+        gboolean need_animation_timeout;
 
-                        if (element == grid->priv->focused) {
-                                if (grid->priv->focused != grid->priv->selected && is_grid_focused)
-                                        is_focused = TRUE;
-                        }
+        width = widget->allocation.width;
+        height = widget->allocation.height;
 
-                        render_element (grid,
-                                        cr,
-                                        element,
-                                        is_selected,
-                                        is_focused,
-                                        is_grid_focused);
-                }
-        }
+        recompute_size (grid,
+                        width - 1,
+                        height -1);
+
+        cr = gdk_cairo_create (widget->window);
+        cairo_rectangle (cr,
+                         event->area.x, event->area.y,
+                         event->area.width, event->area.height);
+        cairo_clip (cr);
+
+        need_animation_timeout = render_slice (grid, cr, grid->priv->elements);
 
         cairo_destroy (cr);
 
+        if (need_animation_timeout) {
+                if (grid->priv->animation_timeout_id == 0) {
+                        grid->priv->animation_timeout_id = g_timeout_add (80,
+                                                                          on_animation_timeout,
+                                                                          grid);
+                }
+        } else {
+                if (grid->priv->animation_timeout_id > 0) {
+                        g_source_remove (grid->priv->animation_timeout_id);
+                        grid->priv->animation_timeout_id = 0;
+                }
+        }
+
         return FALSE;
 }
 
@@ -1212,8 +1448,8 @@ find_element_for_presentable (GduVolumeGrid *grid,
 
 static GridElement *
 do_find_element_for_position (GList *elements,
-                              gint   x,
-                              gint   y)
+                              guint  x,
+                              guint  y)
 {
         GList *l;
         GridElement *ret;
@@ -1243,8 +1479,8 @@ do_find_element_for_position (GList *elements,
 
 static GridElement *
 find_element_for_position (GduVolumeGrid *grid,
-                           gint x,
-                           gint y)
+                           guint x,
+                           guint y)
 {
         return do_find_element_for_position (grid->priv->elements, x, y);
 }
diff --git a/src/palimpsest/gdu-section-volumes.c b/src/palimpsest/gdu-section-volumes.c
index 7ef4971..9c9935c 100644
--- a/src/palimpsest/gdu-section-volumes.c
+++ b/src/palimpsest/gdu-section-volumes.c
@@ -56,6 +56,10 @@ struct _GduSectionVolumesPrivate
         GduButtonElement *partition_edit_button;
         GduButtonElement *partition_delete_button;
         GduButtonElement *partition_create_button;
+        GduButtonElement *luks_lock_button;
+        GduButtonElement *luks_unlock_button;
+        GduButtonElement *luks_forget_passphrase_button;
+        GduButtonElement *luks_change_passphrase_button;
 };
 
 G_DEFINE_TYPE (GduSectionVolumes, gdu_section_volumes, GDU_TYPE_SECTION)
@@ -457,6 +461,383 @@ on_partition_edit_button_clicked (GduButtonElement *button_element,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+on_luks_forget_passphrase_button_clicked (GduButtonElement *button_element,
+                                          gpointer          user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+        GtkWidget *dialog;
+        GduPresentable *v;
+        GduDevice *d;
+        gint response;
+
+        v = NULL;
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+        if (v == NULL)
+                goto out;
+
+        d = gdu_presentable_get_device (v);
+        if (d == NULL)
+                goto out;
+
+        dialog = gdu_confirmation_dialog_new (GTK_WINDOW (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section)))),
+                                              v,
+                                              _("Are you sure you want to forget the passphrase?"),
+                                              _("_Forget"));
+        gtk_widget_show_all (dialog);
+        response = gtk_dialog_run (GTK_DIALOG (dialog));
+        if (response == GTK_RESPONSE_OK) {
+                gdu_util_delete_secret (d);
+        }
+        gtk_widget_destroy (dialog);
+
+        gdu_section_update (GDU_SECTION (section));
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+luks_lock_op_callback (GduDevice *device,
+                       GError    *error,
+                       gpointer   user_data)
+{
+        GduShell *shell = GDU_SHELL (user_data);
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+                dialog = gdu_error_dialog_for_volume (GTK_WINDOW (gdu_shell_get_toplevel (shell)),
+                                                      device,
+                                                      _("Error locking LUKS volume"),
+                                                      error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+                g_error_free (error);
+        }
+        g_object_unref (shell);
+}
+
+static void
+on_luks_lock_button_clicked (GduButtonElement *button_element,
+                             gpointer          user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+        GduPresentable *v;
+        GduDevice *d;
+
+        v = NULL;
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+        if (v == NULL)
+                goto out;
+
+        d = gdu_presentable_get_device (v);
+        if (d == NULL)
+                goto out;
+
+        gdu_device_op_luks_lock (d,
+                                 luks_lock_op_callback,
+                                 g_object_ref (gdu_section_get_shell (GDU_SECTION (section))));
+
+ out:
+        if (d != NULL)
+                g_object_unref (d);
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void unlock_action_do (GduSectionVolumes *section,
+                              GduPresentable *presentable,
+                              gboolean bypass_keyring,
+                              gboolean indicate_wrong_passphrase);
+
+typedef struct {
+        GduSectionVolumes *section;
+        GduPresentable *presentable;
+        gboolean asked_user;
+} UnlockData;
+
+static UnlockData *
+unlock_data_new (GduSectionVolumes *section,
+                 GduPresentable    *presentable,
+                 gboolean           asked_user)
+{
+        UnlockData *data;
+        data = g_new0 (UnlockData, 1);
+        data->section = g_object_ref (section);
+        data->presentable = g_object_ref (presentable);
+        data->asked_user = asked_user;
+        return data;
+}
+
+static void
+unlock_data_free (UnlockData *data)
+{
+        g_object_unref (data->section);
+        g_object_unref (data->presentable);
+        g_free (data);
+}
+
+static gboolean
+unlock_retry (gpointer user_data)
+{
+        UnlockData *data = user_data;
+        GduDevice *device;
+        gboolean indicate_wrong_passphrase;
+
+        device = gdu_presentable_get_device (data->presentable);
+        if (device != NULL) {
+                indicate_wrong_passphrase = FALSE;
+
+                if (!data->asked_user) {
+                        /* if we attempted to unlock the device without asking the user
+                         * then the password must have come from the keyring.. hence,
+                         * since we failed, the password in the keyring is bad. Remove
+                         * it.
+                         */
+                        g_warning ("removing bad password from keyring");
+                        gdu_util_delete_secret (device);
+                } else {
+                        /* we did ask the user on the last try and that passphrase
+                         * didn't work.. make sure the new dialog tells him that
+                         */
+                        indicate_wrong_passphrase = TRUE;
+                }
+
+                unlock_action_do (data->section, data->presentable, TRUE, indicate_wrong_passphrase);
+                g_object_unref (device);
+        }
+        unlock_data_free (data);
+        return FALSE;
+}
+
+static void
+unlock_op_cb (GduDevice *device,
+              char      *object_path_of_cleartext_device,
+              GError    *error,
+              gpointer   user_data)
+{
+        UnlockData *data = user_data;
+        GduShell *shell;
+
+        shell = gdu_section_get_shell (GDU_SECTION (data->section));
+
+        if (error != NULL && error->code == GDU_ERROR_INHIBITED) {
+                GtkWidget *dialog;
+
+                dialog = gdu_error_dialog_for_volume (GTK_WINDOW (gdu_shell_get_toplevel (shell)),
+                                                      device,
+                                                      _("Error unlocking LUKS volume"),
+                                                      error);
+                gtk_widget_show_all (dialog);
+                gtk_window_present (GTK_WINDOW (dialog));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+
+                g_error_free (error);
+        } else if (error != NULL) {
+                /* retry */
+                g_idle_add (unlock_retry, data);
+                g_error_free (error);
+        } else {
+                unlock_data_free (data);
+                g_free (object_path_of_cleartext_device);
+        }
+}
+
+static void
+unlock_action_do (GduSectionVolumes *section,
+                  GduPresentable    *presentable,
+                  gboolean           bypass_keyring,
+                  gboolean           indicate_wrong_passphrase)
+{
+        gchar *secret;
+        gboolean asked_user;
+        GduDevice *device;
+
+        device = gdu_presentable_get_device (presentable);
+        if (device != NULL) {
+                GduShell *shell;
+
+                shell = gdu_section_get_shell (GDU_SECTION (section));
+
+                secret = gdu_util_dialog_ask_for_secret (GTK_WIDGET (gdu_shell_get_toplevel (shell)),
+                                                         presentable,
+                                                         bypass_keyring,
+                                                         indicate_wrong_passphrase,
+                                                         &asked_user);
+                if (secret != NULL) {
+                        gdu_device_op_luks_unlock (device,
+                                                   secret,
+                                                   unlock_op_cb,
+                                                   unlock_data_new (section,
+                                                                    presentable,
+                                                                    asked_user));
+                        /* scrub the password */
+                        memset (secret, '\0', strlen (secret));
+                        g_free (secret);
+                }
+
+                g_object_unref (device);
+        }
+}
+
+
+static void
+on_luks_unlock_button_clicked (GduButtonElement *button_element,
+                               gpointer          user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+        GduPresentable *v;
+
+        v = NULL;
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+        if (v == NULL)
+                goto out;
+
+        unlock_action_do (section, v, FALSE, FALSE);
+
+ out:
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+        char *old_secret;
+        char *new_secret;
+        gboolean save_in_keyring;
+        gboolean save_in_keyring_session;
+        GduPresentable *presentable;
+        GduSectionVolumes *section;
+} ChangePassphraseData;
+
+static void
+change_passphrase_data_free (ChangePassphraseData *data)
+{
+        /* scrub the secrets */
+        if (data->old_secret != NULL) {
+                memset (data->old_secret, '\0', strlen (data->old_secret));
+                g_free (data->old_secret);
+        }
+        if (data->new_secret != NULL) {
+                memset (data->new_secret, '\0', strlen (data->new_secret));
+                g_free (data->new_secret);
+        }
+        if (data->presentable != NULL)
+                g_object_unref (data->presentable);
+        if (data->section != NULL)
+                g_object_unref (data->section);
+        g_free (data);
+}
+
+static void change_passphrase_do (GduSectionVolumes *section,
+                                  GduPresentable    *presentable,
+                                  gboolean           bypass_keyring,
+                                  gboolean           indicate_wrong_passphrase);
+
+static void
+change_passphrase_completed (GduDevice  *device,
+                             GError     *error,
+                             gpointer    user_data)
+{
+        ChangePassphraseData *data = user_data;
+
+        if (error == NULL) {
+                /* It worked! Now update the keyring */
+
+                if (data->save_in_keyring || data->save_in_keyring_session)
+                        gdu_util_save_secret (device, data->new_secret, data->save_in_keyring_session);
+                else
+                        gdu_util_delete_secret (device);
+
+                change_passphrase_data_free (data);
+        } else {
+                /* It didn't work. Because the given passphrase was wrong. Try again,
+                 * this time forcibly bypassing the keyring and telling the user
+                 * the given passphrase was wrong.
+                 */
+                change_passphrase_do (data->section, data->presentable, TRUE, TRUE);
+                change_passphrase_data_free (data);
+        }
+}
+
+static void
+change_passphrase_do (GduSectionVolumes *section,
+                      GduPresentable    *presentable,
+                      gboolean           bypass_keyring,
+                      gboolean           indicate_wrong_passphrase)
+{
+        GduDevice *device;
+        ChangePassphraseData *data;
+
+        device = gdu_presentable_get_device (presentable);
+        if (device == NULL) {
+                goto out;
+        }
+
+        data = g_new0 (ChangePassphraseData, 1);
+        data->presentable = g_object_ref (presentable);
+        data->section = g_object_ref (section);
+
+        if (!gdu_util_dialog_change_secret (gdu_shell_get_toplevel (gdu_section_get_shell (GDU_SECTION (section))),
+                                            presentable,
+                                            &data->old_secret,
+                                            &data->new_secret,
+                                            &data->save_in_keyring,
+                                            &data->save_in_keyring_session,
+                                            bypass_keyring,
+                                            indicate_wrong_passphrase)) {
+                change_passphrase_data_free (data);
+                goto out;
+        }
+
+        gdu_device_op_luks_change_passphrase (device,
+                                              data->old_secret,
+                                              data->new_secret,
+                                              change_passphrase_completed,
+                                              data);
+
+out:
+        if (device != NULL) {
+                g_object_unref (device);
+        }
+}
+
+static void
+on_luks_change_passphrase_button_clicked (GduButtonElement *button_element,
+                                          gpointer          user_data)
+{
+        GduSectionVolumes *section = GDU_SECTION_VOLUMES (user_data);
+        GduPresentable *v;
+
+        v = NULL;
+
+        v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
+        if (v == NULL)
+                goto out;
+
+        change_passphrase_do (section, v, FALSE, FALSE);
+
+ out:
+        if (v != NULL)
+                g_object_unref (v);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 gdu_section_volumes_update (GduSection *_section)
 {
         GduSectionVolumes *section = GDU_SECTION_VOLUMES (_section);
@@ -472,6 +853,10 @@ gdu_section_volumes_update (GduSection *_section)
         gboolean show_partition_edit_button;
         gboolean show_partition_delete_button;
         gboolean show_partition_create_button;
+        gboolean show_luks_lock_button;
+        gboolean show_luks_unlock_button;
+        gboolean show_luks_forget_passphrase_button;
+        gboolean show_luks_change_passphrase_button;
 
         v = NULL;
         d = NULL;
@@ -483,6 +868,10 @@ gdu_section_volumes_update (GduSection *_section)
         show_partition_edit_button = FALSE;
         show_partition_delete_button = FALSE;
         show_partition_create_button = FALSE;
+        show_luks_lock_button = FALSE;
+        show_luks_unlock_button = FALSE;
+        show_luks_forget_passphrase_button = FALSE;
+        show_luks_change_passphrase_button = FALSE;
 
         v = gdu_volume_grid_get_selected (GDU_VOLUME_GRID (section->priv->grid));
 
@@ -692,6 +1081,19 @@ gdu_section_volumes_update (GduSection *_section)
 
                 show_fs_check_button = TRUE;
 
+        } else if (g_strcmp0 (usage, "crypto") == 0) {
+
+                if (g_strcmp0 (gdu_device_luks_get_holder (d), "/") == 0) {
+                        show_luks_unlock_button = TRUE;
+                        gdu_details_element_set_text (section->priv->usage_element, _("Encrypted Volume (Locked)"));
+                } else {
+                        show_luks_lock_button = TRUE;
+                        gdu_details_element_set_text (section->priv->usage_element, _("Encrypted Volume (Unlocked)"));
+                }
+                if (gdu_util_have_secret (d))
+                        show_luks_forget_passphrase_button = TRUE;
+                show_luks_change_passphrase_button = TRUE;
+
         } else if (g_strcmp0 (usage, "") == 0 &&
                    d != NULL && gdu_device_is_partition (d) &&
                    g_strcmp0 (gdu_device_partition_get_scheme (d), "mbr") == 0 &&
@@ -720,6 +1122,10 @@ gdu_section_volumes_update (GduSection *_section)
         gdu_button_element_set_visible (section->priv->partition_edit_button, show_partition_edit_button);
         gdu_button_element_set_visible (section->priv->partition_delete_button, show_partition_delete_button);
         gdu_button_element_set_visible (section->priv->partition_create_button, show_partition_create_button);
+        gdu_button_element_set_visible (section->priv->luks_lock_button, show_luks_lock_button);
+        gdu_button_element_set_visible (section->priv->luks_unlock_button, show_luks_unlock_button);
+        gdu_button_element_set_visible (section->priv->luks_forget_passphrase_button, show_luks_forget_passphrase_button);
+        gdu_button_element_set_visible (section->priv->luks_change_passphrase_button, show_luks_change_passphrase_button);
 
  out:
         if (d != NULL)
@@ -873,6 +1279,47 @@ gdu_section_volumes_constructed (GObject *object)
         g_ptr_array_add (button_elements, button_element);
         section->priv->partition_create_button = button_element;
 
+        button_element = gdu_button_element_new ("gdu-encrypted-lock",
+                                                 _("_Lock Volume"),
+                                                 _("Make encrypted data unavailable"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_luks_lock_button_clicked),
+                          section);
+        g_ptr_array_add (button_elements, button_element);
+        section->priv->luks_lock_button = button_element;
+
+        button_element = gdu_button_element_new ("gdu-encrypted-unlock",
+                                                 _("_Unlock Volume"),
+                                                 _("Make encrypted data available"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_luks_unlock_button_clicked),
+                          section);
+        g_ptr_array_add (button_elements, button_element);
+        section->priv->luks_unlock_button = button_element;
+
+        button_element = gdu_button_element_new (GTK_STOCK_CLEAR,
+                                                 _("Forge_t Passphrase"),
+                                                 _("Delete passphrase from keyring"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_luks_forget_passphrase_button_clicked),
+                          section);
+        g_ptr_array_add (button_elements, button_element);
+        section->priv->luks_forget_passphrase_button = button_element;
+
+        /* TODO: not a great choice for the icon but _EDIT would conflict with "Edit Partition */
+        button_element = gdu_button_element_new (GTK_STOCK_FIND_AND_REPLACE,
+                                                 _("Change _Passphrase"),
+                                                 _("Change passphrase"));
+        g_signal_connect (button_element,
+                          "clicked",
+                          G_CALLBACK (on_luks_change_passphrase_button_clicked),
+                          section);
+        g_ptr_array_add (button_elements, button_element);
+        section->priv->luks_change_passphrase_button = button_element;
+
         gdu_button_table_set_elements (GDU_BUTTON_TABLE (section->priv->button_table), button_elements);
         g_ptr_array_unref (button_elements);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]