3 Tails::Persistence::Step::Bootstrap - bootstrap persistent storage
7 package Tails
::Persistence
::Step
::Bootstrap
;
12 use Glib
qw{TRUE FALSE
};
14 use IPC
::System
::Simple
qw{systemx
};
15 use Number
::Format
qw(:subs);
19 setlocale
(LC_MESSAGES
, "");
20 textdomain
("tails-persistence-setup");
31 foreach (qw{intro label verify_label warning_label
}) {
39 foreach (qw{passphrase_entry verify_passphrase_entry
}) {
44 builder
=> '_build_passphrase_entry',
48 has
'table_alignment' => (
51 isa
=> 'Gtk3::Alignment',
58 has
'warning_area' => (
63 has
'warning_image' => (
68 has
'size_of_free_space' => (
73 foreach (qw{mount_persistence_partition_cb create_configuration_cb
}) {
89 $self->title->set_text($self->encoding->decode(gettext
(
90 q{Persistence wizard - Persistent volume creation}
92 $self->subtitle->set_text($self->encoding->decode(gettext
(
93 q{Choose a passphrase to protect the persistent volume}
95 $self->description->set_markup($self->encoding->decode(sprintf(
96 # TRANSLATORS: size, device vendor, device model
97 gettext
(q{A %s persistent volume will be created on the <b>%s %s</b> device. Data on this volume will be stored in an encrypted form protected by a passphrase.}),
98 format_bytes($self->size_of_free_space, mode => "iec"),
102 $self->go_button->set_label($self->encoding->decode(gettext(q{Create})));
105 sub _build_main_box
{
109 my $box = Gtk3
::VBox
->new();
110 $box->set_spacing(6);
111 $box->pack_start($self->title, FALSE
, FALSE
, 0);
112 $box->pack_start($self->intro, FALSE
, FALSE
, 0);
113 $box->pack_start($self->subtitle, FALSE
, FALSE
, 0);
114 $box->pack_start($self->description, FALSE
, FALSE
, 0);
116 my $passphrase_box = Gtk3
::VBox
->new(FALSE
, 6);
117 $passphrase_box->set_spacing(6);
118 $passphrase_box->pack_start($self->table_alignment, FALSE
, FALSE
, 0);
119 $passphrase_box->pack_start($self->warning_area, FALSE
, FALSE
, 0);
121 $box->pack_start($passphrase_box, FALSE
, FALSE
, 0);
123 $self->verify_passphrase_entry->set_activates_default(TRUE
);
125 $box->pack_start($self->status_area, FALSE
, FALSE
, 0);
127 my $button_alignment = Gtk3
::Alignment
->new(1.0, 0, 0.2, 1.0);
128 $button_alignment->set_padding(0, 0, 10, 10);
129 $button_alignment->add($self->go_button);
130 $box->pack_start($button_alignment, FALSE
, FALSE
, 0);
131 $self->passphrase_entry->grab_focus();
139 my $intro = Gtk3
::Label
->new('');
140 $intro->set_alignment(0.0, 0.5);
141 $intro->set_padding(10, 10);
142 $intro->set_line_wrap(TRUE
);
143 $intro->set_line_wrap_mode('word');
144 $intro->set_single_line_mode(FALSE
);
145 $intro->set_markup($self->encoding->decode(gettext
(
146 q{<b>Beware!</b> Using persistence has consequences that must be well understood. Tails can't help you if you use it wrong! See <a href='file:///usr/share/doc/tails/website/doc/first_steps/persistence.en.html'>Tails documentation about persistence</a> to learn more.}
152 sub _build_warning_image
{
155 Gtk3
::Image
->new_from_stock('gtk-dialog-info', 'menu');
158 sub _build_warning_area
{
161 my $ext_hbox = Gtk3
::HBox
->new(FALSE
, 0);
162 $ext_hbox->set_border_width(10);
163 $ext_hbox->set_spacing(6);
165 my $box = Gtk3
::HBox
->new(FALSE
, 12);
166 $box->set_spacing(6);
167 $box->pack_start($self->warning_image, FALSE
, FALSE
, 0);
168 $box->pack_start($self->warning_label, FALSE
, FALSE
, 0);
170 $ext_hbox->pack_start($box, FALSE
, FALSE
, 0);
171 $ext_hbox->pack_start(Gtk3
::Label
->new(' '), FALSE
, FALSE
, 0);
179 my $label = Gtk3
::Label
->new($self->encoding->decode(gettext
(
182 $label->set_alignment(0.0, 0.5);
186 sub _build_verify_label
{
189 my $label = Gtk3
::Label
->new($self->encoding->decode(gettext
(
190 q{Verify Passphrase:}
192 $label->set_alignment(0.0, 0.5);
196 sub _build_warning_label
{
199 my $label = Gtk3
::Label
->new('');
200 $label->set_padding(10, 0);
203 . $self->encoding->decode(gettext
(q{Passphrase can't be empty}))
209 sub _build_passphrase_entry
{
212 my $entry = Gtk3
::Entry
->new;
213 $entry->set_visibility(FALSE
);
214 $entry->signal_connect("changed" => sub { $self->passphrase_entry_changed });
219 sub _build_table_alignment
{
222 my $table_alignment = Gtk3
::Alignment
->new(0.0, 0.0, 1.0, 1.0);
223 $table_alignment->add($self->table);
224 return $table_alignment;
230 my $table = Gtk3
::Table
->new(1, 2, FALSE
);
231 $table->set_border_width(10);
232 $table->set_col_spacings(12);
233 $table->set_row_spacings(6);
234 $table->attach($self->label, 0, 1, 0, 1, 'fill', [qw{expand fill
}], 0, 0);
235 $table->attach_defaults($self->passphrase_entry, 1, 2, 0, 1);
236 $table->attach($self->verify_label, 0, 1, 1, 2, 'fill', [qw{expand fill
}], 0, 0);
237 $table->attach_defaults($self->verify_passphrase_entry, 1, 2, 1, 2);
247 sub update_passphrase_ui
{
250 my $passphrase = $self->passphrase_entry->get_text;
251 my $passphrase_verify = $self->verify_passphrase_entry->get_text;
253 # TODO: check passphrase strength (#7002)
255 if ($passphrase ne $passphrase_verify) {
256 $self->warning_label->set_markup(
258 . $self->encoding->decode(gettext
(q{Passphrases do not match}))
261 $self->warning_image->show;
262 $self->go_button->set_sensitive(FALSE
);
264 elsif (length($passphrase) == 0) {
265 $self->warning_label->set_markup(
267 . $self->encoding->decode(gettext
(q{Passphrase can't be empty}))
270 $self->warning_image->show;
271 $self->go_button->set_sensitive(FALSE
);
274 $self->warning_image->hide;
275 $self->warning_label->set_text(' ');
276 $self->go_button->set_sensitive(TRUE
);
280 sub passphrase_entry_changed
{
283 $self->update_passphrase_ui;
286 sub operation_finished
{
289 my ($created_device, $error);
291 say STDERR
"Entering Bootstrap::operation_finished";
293 if (exists($replies->{created_device
}) && defined($replies->{created_device
})) {
294 $created_device = $replies->{created_device
};
295 # For some reason, we cannot get the exception when Try::Tiny is used,
296 # so let's do it by hand.
299 eval { $replies->{format_reply
}->get_result };
304 $error = $replies->{create_partition_error
};
310 $self->subtitle->set_text($self->encoding->decode(gettext
(q{Failed})));
311 $self->description->set_text($error);
314 say STDERR
"created ${created_device}.";
317 $self->subtitle->set_text($self->encoding->decode(gettext
(
318 q{Mounting Tails persistence partition.}
320 $self->description->set_text($self->encoding->decode(gettext
(
321 q{The Tails persistence partition will be mounted.}
324 systemx
(qw{/sbin/udevadm settle
});
325 my $mountpoint = $self->mount_persistence_partition_cb->();
327 say STDERR
"mounted persistence partition on $mountpoint";
329 $self->subtitle->set_text($self->encoding->decode(gettext
(
330 q{Correcting permissions of the persistent volume.}
332 $self->description->set_text($self->encoding->decode(gettext
(
333 q{The permissions of the persistent volume will be corrected.}
336 systemx
(qw{sudo
-n
/usr/bin
/tails
-fix
-persistent
-volume
-permissions
});
338 say STDERR
"fixed permissions.";
340 $self->subtitle->set_text($self->encoding->decode(gettext
(
341 q{Creating default persistence configuration.}
343 $self->description->set_text($self->encoding->decode(gettext
(
344 q{The default persistence configuration will be created.}
347 $self->create_configuration_cb->();
349 say STDERR
"created default persistence configuration.";
351 $self->success_callback->();
355 sub go_button_pressed
{
358 $_->hide foreach ($self->intro, $self->warning_area, $self->table);
360 $self->subtitle->set_text(
361 $self->encoding->decode(gettext
(q{Creating...})),
363 $self->description->set_text(
364 $self->encoding->decode(gettext
(q{Creating the persistent volume...})),
367 $self->go_callback->(
368 passphrase
=> $self->passphrase_entry->get_text,
369 end_cb
=> sub { $self->operation_finished(@_) },
373 with
'Tails::Persistence::Role::StatusArea';
374 with
'Tails::Persistence::Role::SetupStep';