Give variable a more expressive name (#15102)
[tails-persistence-setup.git] / lib / Tails / Persistence / Step / Bootstrap.pm
blob8d08ff4f7144ba4df9b2ba7116a38a11b28b3eaf
1 =head1 NAME
3 Tails::Persistence::Step::Bootstrap - bootstrap persistent storage
5 =cut
7 package Tails::Persistence::Step::Bootstrap;
9 use 5.10.1;
10 use strictures 2;
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);
19 use Locale::gettext;
20 use POSIX;
21 setlocale(LC_MESSAGES, "");
22 textdomain("tails-persistence-setup");
24 use Moo;
25 use MooX::late;
26 use namespace::clean;
29 =head1 ATTRIBUTES
31 =cut
33 foreach (qw{intro label verify_label warning_label}) {
34 has $_ => (
35 lazy_build => 1,
36 is => 'rw',
37 isa => 'Gtk3::Label',
41 foreach (qw{passphrase_entry verify_passphrase_entry}) {
42 has $_ => (
43 lazy_build => 1,
44 is => 'rw',
45 isa => 'Gtk3::Entry',
46 builder => '_build_passphrase_entry',
50 has 'table_alignment' => (
51 lazy_build => 1,
52 is => 'rw',
53 isa => 'Gtk3::Alignment',
55 has 'table' => (
56 lazy_build => 1,
57 is => 'rw',
58 isa => 'Gtk3::Table',
60 has 'warning_area' => (
61 lazy_build => 1,
62 is => 'rw',
63 isa => 'Gtk3::HBox',
65 has 'warning_image' => (
66 lazy_build => 1,
67 is => 'rw',
68 isa => 'Gtk3::Image',
70 has 'size_of_free_space' => (
71 required => 1,
72 is => 'ro',
73 isa => 'Int',
75 foreach (qw{mount_persistence_partition_cb create_configuration_cb}) {
76 has $_ => (
77 required => 1,
78 is => 'ro',
79 isa => 'CodeRef',
83 has 'passphrase_check_button' => (
84 lazy_build => 1,
85 is => 'rw',
86 isa => 'Gtk3::CheckButton',
90 =head1 CONSTRUCTORS
92 =cut
94 method BUILD (@args) {
95 $self->title->set_text($self->encoding->decode(gettext(
96 q{Persistence wizard - Persistent volume creation}
97 )));
98 $self->subtitle->set_text($self->encoding->decode(gettext(
99 q{Choose a passphrase to protect the persistent volume}
100 )));
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"),
105 $self->drive_vendor,
106 $self->drive_model,
107 )));
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();
139 return $box;
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.}
152 )));
154 return $intro;
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);
174 return $ext_hbox;
177 method _build_label () {
178 my $label = Gtk3::Label->new($self->encoding->decode(gettext(
179 q{Passphrase:}
180 )));
181 $label->set_alignment(0.0, 0.5);
182 return $label;
185 method _build_verify_label () {
186 my $label = Gtk3::Label->new($self->encoding->decode(gettext(
187 q{Verify Passphrase:}
188 )));
189 $label->set_alignment(0.0, 0.5);
190 return $label;
193 method _build_warning_label () {
194 my $label = Gtk3::Label->new('');
195 $label->set_padding(10, 0);
196 $label->set_markup(
197 "<i>"
198 . $self->encoding->decode(gettext(q{Passphrase can't be empty}))
199 . "</i>"
201 return $label;
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 });
209 return $entry;
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);
228 return $table;
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;
240 =head1 METHODS
242 =cut
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(
252 "<i>"
253 . $self->encoding->decode(gettext(q{Passphrases do not match}))
254 . "</i>"
256 $self->warning_image->show;
257 $self->go_button->set_sensitive(FALSE);
259 elsif (length($passphrase) == 0) {
260 $self->warning_label->set_markup(
261 "<i>"
262 . $self->encoding->decode(gettext(q{Passphrase can't be empty}))
263 . "</i>"
265 $self->warning_image->show;
266 $self->go_button->set_sensitive(FALSE);
268 else {
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.
296 local $@;
297 eval { $replies->{format_reply}->get_result };
298 $error = $@;
301 else {
302 $error = $replies->{create_partition_error};
305 if ($error) {
306 $self->working(0);
307 say STDERR "$error";
308 $self->subtitle->set_text($self->encoding->decode(gettext(q{Failed})));
309 $self->description->set_text($error);
311 else {
312 say STDERR "created ${created_device}.";
313 $self->working(0);
315 $self->subtitle->set_text($self->encoding->decode(gettext(
316 q{Mounting Tails persistence partition.}
317 )));
318 $self->description->set_text($self->encoding->decode(gettext(
319 q{The Tails persistence partition will be mounted.}
320 )));
321 $self->working(1);
322 systemx(qw{/sbin/udevadm settle});
323 my $mountpoint = $self->mount_persistence_partition_cb->();
324 $self->working(0);
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.}
329 )));
330 $self->description->set_text($self->encoding->decode(gettext(
331 q{The permissions of the persistent volume will be corrected.}
332 )));
333 $self->working(1);
334 systemx(qw{sudo -n /usr/bin/tails-fix-persistent-volume-permissions});
335 $self->working(0);
336 say STDERR "fixed permissions.";
338 $self->subtitle->set_text($self->encoding->decode(gettext(
339 q{Creating default persistence configuration.}
340 )));
341 $self->description->set_text($self->encoding->decode(gettext(
342 q{The default persistence configuration will be created.}
343 )));
344 $self->working(1);
345 $self->create_configuration_cb->();
346 $self->working(0);
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);
355 $self->working(1);
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';
372 no Moo;