Consistently "use strictures 2" in all source files.
[tails-persistence-setup.git] / lib / Tails / Persistence / Step / Bootstrap.pm
blobbdd9a143eecc888553c89484be3a921dca81a427
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 Glib qw{TRUE FALSE};
14 use IPC::System::Simple qw{systemx};
15 use Number::Format qw(:subs);
17 use Locale::gettext;
18 use POSIX;
19 setlocale(LC_MESSAGES, "");
20 textdomain("tails-persistence-setup");
22 use Moo;
23 use MooX::late;
24 use namespace::clean;
27 =head1 ATTRIBUTES
29 =cut
31 foreach (qw{intro label verify_label warning_label}) {
32 has $_ => (
33 lazy_build => 1,
34 is => 'rw',
35 isa => 'Gtk3::Label',
39 foreach (qw{passphrase_entry verify_passphrase_entry}) {
40 has $_ => (
41 lazy_build => 1,
42 is => 'rw',
43 isa => 'Gtk3::Entry',
44 builder => '_build_passphrase_entry',
48 has 'table_alignment' => (
49 lazy_build => 1,
50 is => 'rw',
51 isa => 'Gtk3::Alignment',
53 has 'table' => (
54 lazy_build => 1,
55 is => 'rw',
56 isa => 'Gtk3::Table',
58 has 'warning_area' => (
59 lazy_build => 1,
60 is => 'rw',
61 isa => 'Gtk3::HBox',
63 has 'warning_image' => (
64 lazy_build => 1,
65 is => 'rw',
66 isa => 'Gtk3::Image',
68 has 'size_of_free_space' => (
69 required => 1,
70 is => 'ro',
71 isa => 'Int',
73 foreach (qw{mount_persistence_partition_cb create_configuration_cb}) {
74 has $_ => (
75 required => 1,
76 is => 'ro',
77 isa => 'CodeRef',
82 =head1 CONSTRUCTORS
84 =cut
86 sub BUILD {
87 my $self = shift;
89 $self->title->set_text($self->encoding->decode(gettext(
90 q{Persistence wizard - Persistent volume creation}
91 )));
92 $self->subtitle->set_text($self->encoding->decode(gettext(
93 q{Choose a passphrase to protect the persistent volume}
94 )));
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"),
99 $self->drive_vendor,
100 $self->drive_model,
101 )));
102 $self->go_button->set_label($self->encoding->decode(gettext(q{Create})));
105 sub _build_main_box {
106 my $self = shift;
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();
133 return $box;
136 sub _build_intro {
137 my $self = shift;
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.}
147 )));
149 return $intro;
152 sub _build_warning_image {
153 my $self = shift;
155 Gtk3::Image->new_from_stock('gtk-dialog-info', 'menu');
158 sub _build_warning_area {
159 my $self = shift;
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);
173 return $ext_hbox;
176 sub _build_label {
177 my $self = shift;
179 my $label = Gtk3::Label->new($self->encoding->decode(gettext(
180 q{Passphrase:}
181 )));
182 $label->set_alignment(0.0, 0.5);
183 return $label;
186 sub _build_verify_label {
187 my $self = shift;
189 my $label = Gtk3::Label->new($self->encoding->decode(gettext(
190 q{Verify Passphrase:}
191 )));
192 $label->set_alignment(0.0, 0.5);
193 return $label;
196 sub _build_warning_label {
197 my $self = shift;
199 my $label = Gtk3::Label->new('');
200 $label->set_padding(10, 0);
201 $label->set_markup(
202 "<i>"
203 . $self->encoding->decode(gettext(q{Passphrase can't be empty}))
204 . "</i>"
206 return $label;
209 sub _build_passphrase_entry {
210 my $self = shift;
212 my $entry = Gtk3::Entry->new;
213 $entry->set_visibility(FALSE);
214 $entry->signal_connect("changed" => sub { $self->passphrase_entry_changed });
216 return $entry;
219 sub _build_table_alignment {
220 my $self = shift;
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;
227 sub _build_table {
228 my $self = shift;
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);
239 return $table;
243 =head1 METHODS
245 =cut
247 sub update_passphrase_ui {
248 my $self = shift;
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(
257 "<i>"
258 . $self->encoding->decode(gettext(q{Passphrases do not match}))
259 . "</i>"
261 $self->warning_image->show;
262 $self->go_button->set_sensitive(FALSE);
264 elsif (length($passphrase) == 0) {
265 $self->warning_label->set_markup(
266 "<i>"
267 . $self->encoding->decode(gettext(q{Passphrase can't be empty}))
268 . "</i>"
270 $self->warning_image->show;
271 $self->go_button->set_sensitive(FALSE);
273 else {
274 $self->warning_image->hide;
275 $self->warning_label->set_text(' ');
276 $self->go_button->set_sensitive(TRUE);
280 sub passphrase_entry_changed {
281 my $self = shift;
283 $self->update_passphrase_ui;
286 sub operation_finished {
287 my $self = shift;
288 my $replies = shift;
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.
298 local $@;
299 eval { $replies->{format_reply}->get_result };
300 $error = $@;
303 else {
304 $error = $replies->{create_partition_error};
307 if ($error) {
308 $self->working(0);
309 say STDERR "$error";
310 $self->subtitle->set_text($self->encoding->decode(gettext(q{Failed})));
311 $self->description->set_text($error);
313 else {
314 say STDERR "created ${created_device}.";
315 $self->working(0);
317 $self->subtitle->set_text($self->encoding->decode(gettext(
318 q{Mounting Tails persistence partition.}
319 )));
320 $self->description->set_text($self->encoding->decode(gettext(
321 q{The Tails persistence partition will be mounted.}
322 )));
323 $self->working(1);
324 systemx(qw{/sbin/udevadm settle});
325 my $mountpoint = $self->mount_persistence_partition_cb->();
326 $self->working(0);
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.}
331 )));
332 $self->description->set_text($self->encoding->decode(gettext(
333 q{The permissions of the persistent volume will be corrected.}
334 )));
335 $self->working(1);
336 systemx(qw{sudo -n /usr/bin/tails-fix-persistent-volume-permissions});
337 $self->working(0);
338 say STDERR "fixed permissions.";
340 $self->subtitle->set_text($self->encoding->decode(gettext(
341 q{Creating default persistence configuration.}
342 )));
343 $self->description->set_text($self->encoding->decode(gettext(
344 q{The default persistence configuration will be created.}
345 )));
346 $self->working(1);
347 $self->create_configuration_cb->();
348 $self->working(0);
349 say STDERR "created default persistence configuration.";
351 $self->success_callback->();
355 sub go_button_pressed {
356 my $self = shift;
358 $_->hide foreach ($self->intro, $self->warning_area, $self->table);
359 $self->working(1);
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';
376 no Moo;