3 Tails::Persistence::Step::Bootstrap - bootstrap persistent storage
7 package Tails
::Persistence
::Step
::Bootstrap
;
12 use Function
::Parameters
;
13 use Glib
qw{TRUE FALSE
};
15 use IPC
::System
::Simple
qw{systemx
};
16 use Number
::Format
qw(:subs);
17 use Types
::Standard
qw(HashRef);
21 setlocale
(LC_MESSAGES
, "");
22 textdomain
("tails-persistence-setup");
33 foreach (qw{intro label verify_label warning_label
}) {
41 foreach (qw{passphrase_entry verify_passphrase_entry
}) {
46 builder
=> '_build_passphrase_entry',
50 has
'table_alignment' => (
53 isa
=> 'Gtk3::Alignment',
60 has
'warning_area' => (
65 has
'warning_image' => (
70 has
'size_of_free_space' => (
75 foreach (qw{mount_persistence_partition_cb create_configuration_cb
}) {
83 has
'passphrase_check_button' => (
86 isa
=> 'Gtk3::CheckButton',
94 method BUILD
(@args) {
95 $self->title->set_text($self->encoding->decode(gettext
(
96 q{Persistence wizard - Persistent volume creation}
98 $self->subtitle->set_text($self->encoding->decode(gettext
(
99 q{Choose a passphrase to protect the persistent volume}
101 $self->description->set_markup($self->encoding->decode(sprintf(
102 # TRANSLATORS: size, device vendor, device model
103 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.}),
104 format_bytes($self->size_of_free_space, mode => "iec"),
108 $self->go_button->set_label($self->encoding->decode(gettext(q{Create})));
111 method _build_main_widget
() {
112 my $box = Gtk3
::VBox
->new();
113 $box->set_spacing(6);
114 $box->pack_start($self->title, FALSE
, FALSE
, 0);
115 $box->pack_start($self->intro, FALSE
, FALSE
, 0);
116 $box->pack_start($self->subtitle, FALSE
, FALSE
, 0);
117 $box->pack_start($self->description, FALSE
, FALSE
, 0);
119 my $show_passphrase_box = Gtk3
::Box
->new("horizontal", 0);
120 $show_passphrase_box->pack_end($self->passphrase_check_button, FALSE
, FALSE
, 0);
121 $box->add($show_passphrase_box);
122 my $passphrase_box = Gtk3
::VBox
->new(FALSE
, 6);
123 $passphrase_box->set_spacing(6);
124 $passphrase_box->pack_start($self->table_alignment, FALSE
, FALSE
, 0);
125 $passphrase_box->pack_start($self->warning_area, FALSE
, FALSE
, 0);
127 $box->pack_start($passphrase_box, FALSE
, FALSE
, 0);
129 $self->verify_passphrase_entry->set_activates_default(TRUE
);
131 $box->pack_start($self->status_area, FALSE
, FALSE
, 0);
133 my $button_alignment = Gtk3
::Alignment
->new(1.0, 0, 0.2, 1.0);
134 $button_alignment->set_padding(0, 0, 10, 10);
135 $button_alignment->add($self->go_button);
136 $box->pack_start($button_alignment, FALSE
, FALSE
, 0);
137 $self->passphrase_entry->grab_focus();
142 method _build_intro
() {
143 my $intro = Gtk3
::Label
->new('');
144 $intro->set_alignment(0.0, 0.5);
145 $intro->set_padding(10, 10);
146 $intro->set_line_wrap(TRUE
);
147 $intro->set_line_wrap_mode('word');
148 $intro->set_single_line_mode(FALSE
);
149 $intro->set_max_width_chars(72);
150 $intro->set_markup($self->encoding->decode(gettext
(
151 q{<b>Beware!</b> Using persistence has consequences that must be well understood. Tails can't help you if you use it wrong! See the <i>Encrypted persistence</i> page of the Tails documentation to learn more.}
157 method _build_warning_image
() {
158 Gtk3
::Image
->new_from_stock('gtk-dialog-info', 'menu');
161 method _build_warning_area
() {
162 my $ext_hbox = Gtk3
::HBox
->new(FALSE
, 0);
163 $ext_hbox->set_border_width(10);
164 $ext_hbox->set_spacing(6);
166 my $box = Gtk3
::HBox
->new(FALSE
, 12);
167 $box->set_spacing(6);
168 $box->pack_start($self->warning_image, FALSE
, FALSE
, 0);
169 $box->pack_start($self->warning_label, FALSE
, FALSE
, 0);
171 $ext_hbox->pack_start($box, FALSE
, FALSE
, 0);
172 $ext_hbox->pack_start(Gtk3
::Label
->new(' '), FALSE
, FALSE
, 0);
177 method _build_label
() {
178 my $label = Gtk3
::Label
->new($self->encoding->decode(gettext
(
181 $label->set_alignment(0.0, 0.5);
185 method _build_verify_label
() {
186 my $label = Gtk3
::Label
->new($self->encoding->decode(gettext
(
187 q{Verify Passphrase:}
189 $label->set_alignment(0.0, 0.5);
193 method _build_warning_label
() {
194 my $label = Gtk3
::Label
->new('');
195 $label->set_padding(10, 0);
198 . $self->encoding->decode(gettext
(q{Passphrase can't be empty}))
204 method _build_passphrase_entry
() {
205 my $entry = Gtk3
::Entry
->new;
206 $entry->set_visibility(FALSE
);
207 $entry->signal_connect("changed" => sub { $self->passphrase_entry_changed });
212 method _build_table_alignment
() {
213 my $table_alignment = Gtk3
::Alignment
->new(0.0, 0.0, 1.0, 1.0);
214 $table_alignment->add($self->table);
215 return $table_alignment;
218 method _build_table
() {
219 my $table = Gtk3
::Table
->new(1, 2, FALSE
);
220 $table->set_border_width(10);
221 $table->set_col_spacings(12);
222 $table->set_row_spacings(6);
223 $table->attach($self->label, 0, 1, 0, 1, 'fill', [qw{expand fill
}], 0, 0);
224 $table->attach_defaults($self->passphrase_entry, 1, 2, 0, 1);
225 $table->attach($self->verify_label, 0, 1, 1, 2, 'fill', [qw{expand fill
}], 0, 0);
226 $table->attach_defaults($self->verify_passphrase_entry, 1, 2, 1, 2);
231 method _build_passphrase_check_button
() {
232 my $check_button = Gtk3
::CheckButton
->new_with_label('Show Passphrase');
233 $check_button->set_active(FALSE
);
234 $check_button->signal_connect(toggled
=> sub { $self->passphrase_check_button_toggled });
236 return $check_button;
244 method update_passphrase_ui
() {
245 my $passphrase = $self->passphrase_entry->get_text;
246 my $passphrase_verify = $self->verify_passphrase_entry->get_text;
248 # TODO: check passphrase strength (#7002)
250 if ($passphrase ne $passphrase_verify) {
251 $self->warning_label->set_markup(
253 . $self->encoding->decode(gettext
(q{Passphrases do not match}))
256 $self->warning_image->show;
257 $self->go_button->set_sensitive(FALSE
);
259 elsif (length($passphrase) == 0) {
260 $self->warning_label->set_markup(
262 . $self->encoding->decode(gettext
(q{Passphrase can't be empty}))
265 $self->warning_image->show;
266 $self->go_button->set_sensitive(FALSE
);
269 $self->warning_image->hide;
270 $self->warning_label->set_text(' ');
271 $self->go_button->set_sensitive(TRUE
);
275 method passphrase_entry_changed
() {
276 $self->update_passphrase_ui;
279 method passphrase_check_button_toggled
() {
280 my $show_passphrase_op = $self->passphrase_check_button->get_active;
281 foreach my $entry ($self->passphrase_entry, $self->verify_passphrase_entry) {
282 $entry->set_visibility($show_passphrase_op);
286 method operation_finished
(HashRef
$replies) {
287 my ($created_device, $error);
289 say STDERR
"Entering Bootstrap::operation_finished";
291 if (exists($replies->{created_device
}) && defined($replies->{created_device
})) {
292 $created_device = $replies->{created_device
};
293 # For some reason, we cannot get the exception when Try::Tiny is used,
294 # so let's do it by hand.
297 eval { $replies->{format_reply
}->get_result };
302 $error = $replies->{create_partition_error
};
308 $self->subtitle->set_text($self->encoding->decode(gettext
(q{Failed})));
309 $self->description->set_text($error);
312 say STDERR
"created ${created_device}.";
315 $self->subtitle->set_text($self->encoding->decode(gettext
(
316 q{Mounting Tails persistence partition.}
318 $self->description->set_text($self->encoding->decode(gettext
(
319 q{The Tails persistence partition will be mounted.}
322 systemx
(qw{/sbin/udevadm settle
});
323 my $mountpoint = $self->mount_persistence_partition_cb->();
325 say STDERR
"mounted persistence partition on $mountpoint";
327 $self->subtitle->set_text($self->encoding->decode(gettext
(
328 q{Correcting permissions of the persistent volume.}
330 $self->description->set_text($self->encoding->decode(gettext
(
331 q{The permissions of the persistent volume will be corrected.}
334 systemx
(qw{sudo
-n
/usr/bin
/tails
-fix
-persistent
-volume
-permissions
});
336 say STDERR
"fixed permissions.";
338 $self->subtitle->set_text($self->encoding->decode(gettext
(
339 q{Creating default persistence configuration.}
341 $self->description->set_text($self->encoding->decode(gettext
(
342 q{The default persistence configuration will be created.}
345 $self->create_configuration_cb->();
347 say STDERR
"created default persistence configuration.";
349 $self->success_callback->();
353 method go_button_pressed
() {
354 $_->hide foreach ($self->intro, $self->warning_area, $self->table,$self->passphrase_check_button);
356 $self->subtitle->set_text(
357 $self->encoding->decode(gettext
(q{Creating...})),
359 $self->description->set_text(
360 $self->encoding->decode(gettext
(q{Creating the persistent volume...})),
363 $self->go_callback->(
364 passphrase
=> $self->passphrase_entry->get_text,
365 end_cb
=> sub { $self->operation_finished(@_) },
369 with
'Tails::Persistence::Role::StatusArea';
370 with
'Tails::Persistence::Role::SetupStep';