From bd16b5e6f91c4855345c12e3588657d8e21d8469 Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 20:58:20 +0100
Subject: [PATCH 01/10] Add wrapper script for testing wizard through chroot.

---
 test_with_chroot.sh | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100755 test_with_chroot.sh

diff --git a/test_with_chroot.sh b/test_with_chroot.sh
new file mode 100755
index 0000000..1ad27e5
--- /dev/null
+++ b/test_with_chroot.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+set -e
+
+# set up variables and check existence of chroot system
+if [ -z "${CHROOT_PATH}" ]; then
+    echo "Need to set CHROOT_PATH!"
+    exit 1
+fi
+wizard_filename="reform-setup"
+cleanup_script_filename="cleanup.sh"
+repo_path_wizard="./target/debug/${wizard_filename}"
+repo_path_cleanup_script="./${cleanup_script_filename}"
+chroot_dir_root="${CHROOT_PATH}/root"
+chroot_path_wizard="./root/${wizard_filename}"
+if [ ! -d "${chroot_dir_root}" ]; then
+    echo "Can't find ${chroot_dir_root}, suspecting no system for chrooting set up."
+    echo "To set it up, run something like this:"
+    echo "# mkdir -p ${CHROOT_PATH}"
+    echo "# debootstrap unstable ${CHROOT_PATH} http://deb.debian.org/debian/"
+    exit 1
+fi
+
+# ensure the current variants of the needed executables are inside the chrooted system
+cp "${repo_path_wizard}" "${chroot_dir_root}/"
+cp "${repo_path_cleanup_script}" "${chroot_dir_root}/"
+
+# add whatever packages not provided by debootstrap on default are expected
+chroot "${CHROOT_PATH}" /bin/apt install -y libgtk-4-dev libadwaita-1-dev
+
+# xhost +/- allows the Wizard to access the X Server for display
+xhost +
+chroot "${CHROOT_PATH}" "${chroot_path_wizard}" 
+xhost -
+
+exit 0
-- 
GitLab


From aef1069cec0104a0898332f7b6c37eeadcfc4de7 Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:11:37 +0100
Subject: [PATCH 02/10] Close wizard on finish.

---
 src/main.rs | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 7520d07..50db81d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -156,7 +156,7 @@ fn build_time_page(stack: &Stack) -> StackPage {
     return stack.add_titled(&page_box, Some("time"), "Time");
 }
 
-fn build_desktop_page(stack: &Stack) -> StackPage {
+fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage {
     let button_next = Button::builder().label("Next").build();
     let page_box = build_box();
 
@@ -170,20 +170,19 @@ fn build_desktop_page(stack: &Stack) -> StackPage {
     page_box.append(&choice_sway);
     page_box.append(&button_next);
 
+    button_next.connect_clicked(
+        clone!(@weak window => move |_| {
+            let _ = window.close();
+        })
+    );
+
     return stack.add_titled(&page_box, Some("desktop"), "Desktop");
 }
 
 fn build_ui(app: &Application) {
     let header_bar = HeaderBar::default();
 
-    // main content stack
     let stack = Stack::builder().build();
-    let _welcome_page = build_welcome_page(&stack);
-    let _keyboard_page = build_keyboard_page(&stack);
-    let _root_pw_page = build_root_pw_page(&stack);
-    let _account_page = build_account_page(&stack);
-    let _time_page = build_time_page(&stack);
-    let _desktop_page = build_desktop_page(&stack);
 
     let toolbar_view = ToolbarView::builder()
         .content(&stack)
@@ -219,4 +218,12 @@ fn build_ui(app: &Application) {
     window.set_titlebar(Some(&header_bar));
 
     window.present();
+
+    // main content stack
+    let _welcome_page = build_welcome_page(&stack);
+    let _keyboard_page = build_keyboard_page(&stack);
+    let _root_pw_page = build_root_pw_page(&stack);
+    let _account_page = build_account_page(&stack);
+    let _time_page = build_time_page(&stack);
+    let _desktop_page = build_desktop_page(&stack, &window);
 }
-- 
GitLab


From c978dbdb8a041abed42cc0154ffd5272841f087d Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:20:24 +0100
Subject: [PATCH 03/10] Add cleanup script dummy and call it on wizard finish.

---
 cleanup.sh  | 4 ++++
 src/main.rs | 3 +++
 2 files changed, 7 insertions(+)
 create mode 100644 cleanup.sh

diff --git a/cleanup.sh b/cleanup.sh
new file mode 100644
index 0000000..8e67d30
--- /dev/null
+++ b/cleanup.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -e
+
+echo "I wish I was a proper clean-up script, for now I'm just a dummy. – $(date)" >> /root/cleanup_script.log
diff --git a/src/main.rs b/src/main.rs
index 50db81d..9850233 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -172,6 +172,9 @@ fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak window => move |_| {
+            let output = Command::new("sh").args(["/root/cleanup.sh"]).output().expect("failed to run cleanup script");
+            println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output of timedatectl"));
+            println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output of timedatectl"));
             let _ = window.close();
         })
     );
-- 
GitLab


From 85eb5d3616eb646d400cfa77efde6f579f21463d Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:29:23 +0100
Subject: [PATCH 04/10] Fix timezone selection in chroot tests, and make it
 actually set timezone and localtime.

---
 src/main.rs | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 9850233..174fb35 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -130,11 +130,18 @@ fn build_time_page(stack: &Stack) -> StackPage {
     let button_next = Button::builder().label("Next").build();
     let page_box = build_box();
 
-    let output = String::from_utf8(Command::new("timedatectl")
-                                   .args(["list-timezones"])
+    // let output = String::from_utf8(Command::new("timedatectl")
+    //                                .args(["list-timezones"])
+    //                                .output()
+    //                                .expect("failed to execute timedatectl").stdout)
+    //     .expect("failed to parse utf8 output of timedatectl");
+
+    // above wouldn't run easily within chroot, so for now let's use this
+    let output = String::from_utf8(Command::new("sh")
+                                   .args(["-c", "awk '/^Z/ { print $2 }; /^L/ { print $3 }' /usr/share/zoneinfo/tzdata.zi"])
                                    .output()
-                                   .expect("failed to execute timedatectl").stdout)
-        .expect("failed to parse utf8 output of timedatectl");
+                                   .expect("failed to collect timezones").stdout)
+        .expect("failed to parse utf8 output of awk");
 
     let lines:Vec<&str> = output.lines().collect();
     let list = StringList::new(&lines);
@@ -149,6 +156,16 @@ fn build_time_page(stack: &Stack) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
+
+            // in a non-chroot environment we might want to use timedatectl instead
+            let tz = list.string(dropdown.selected()).unwrap();
+            let mut output = Command::new("sh").args(["-c", &format!("echo '{}' > /etc/timezone", tz)]).output().expect("failed to set timezone");
+            println!("set timezone: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
+            println!("set timezone: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
+            output = Command::new("ln").args(["-sf", &format!("/usr/share/zoneinfo/{}", tz), "/etc/localtime"]).output().expect("failed to set localtime");
+            println!("set localtime: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
+            println!("set localtime: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
+
             stack.set_visible_child_full("desktop", gtk::StackTransitionType::SlideLeft);
         })
     );
@@ -172,9 +189,11 @@ fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak window => move |_| {
+
             let output = Command::new("sh").args(["/root/cleanup.sh"]).output().expect("failed to run cleanup script");
-            println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output of timedatectl"));
-            println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output of timedatectl"));
+            println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
+            println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
+
             let _ = window.close();
         })
     );
-- 
GitLab


From 3b6518c4cc66b18cbdf945fc1cd641c1b8e62e1c Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:32:33 +0100
Subject: [PATCH 05/10] Add adduser call on username setting.

---
 src/main.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index 174fb35..13a60c4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -119,6 +119,12 @@ fn build_account_page(stack: &Stack) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
+
+            let username = username_entry.text();
+            let output = Command::new("adduser").args(["--disabled-password", "--comment", "", &username]).output().expect("failed to adduser");
+            println!("adduser: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
+            println!("adduser: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
+
             stack.set_visible_child_full("time", gtk::StackTransitionType::SlideLeft);
         })
     );
-- 
GitLab


From e859051bd4d9570ba5e6f4bf881db094b8b0d6a8 Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:41:56 +0100
Subject: [PATCH 06/10] Some refactoring.

---
 src/main.rs | 29 +++++++++++------------------
 1 file changed, 11 insertions(+), 18 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 13a60c4..94f8131 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,13 @@ const MARGIN: i32 = 48;
 // - https://gtk-rs.org/gtk4-rs/stable/latest/docs/gtk4/
 // - https://kerkour.com/rust-cross-compilation
 
+fn run_command(description: &str, executable: &str, params: Vec<&str>) {
+    println!("STEP: {}", &description);
+    let output = Command::new(executable).args(params).output().expect(&format!("failed to {}", description));
+    println!("STDOUT: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
+    println!("STDERR: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
+}
+
 fn main() -> glib::ExitCode {
     let app = Application::builder().application_id(APP_ID).build();
 
@@ -119,12 +126,8 @@ fn build_account_page(stack: &Stack) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
-
             let username = username_entry.text();
-            let output = Command::new("adduser").args(["--disabled-password", "--comment", "", &username]).output().expect("failed to adduser");
-            println!("adduser: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
-            println!("adduser: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
-
+            run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec());
             stack.set_visible_child_full("time", gtk::StackTransitionType::SlideLeft);
         })
     );
@@ -162,16 +165,10 @@ fn build_time_page(stack: &Stack) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
-
             // in a non-chroot environment we might want to use timedatectl instead
             let tz = list.string(dropdown.selected()).unwrap();
-            let mut output = Command::new("sh").args(["-c", &format!("echo '{}' > /etc/timezone", tz)]).output().expect("failed to set timezone");
-            println!("set timezone: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
-            println!("set timezone: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
-            output = Command::new("ln").args(["-sf", &format!("/usr/share/zoneinfo/{}", tz), "/etc/localtime"]).output().expect("failed to set localtime");
-            println!("set localtime: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
-            println!("set localtime: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
-
+            run_command("set timezone", "sh", ["-c", &format!("echo '{}' > /etc/timezone", tz)].to_vec());
+            run_command("set localtime", "ln", ["-sf", &format!("/usr/share/zoneinfo/{}", tz), "/etc/localtime"].to_vec());
             stack.set_visible_child_full("desktop", gtk::StackTransitionType::SlideLeft);
         })
     );
@@ -195,11 +192,7 @@ fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak window => move |_| {
-
-            let output = Command::new("sh").args(["/root/cleanup.sh"]).output().expect("failed to run cleanup script");
-            println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output"));
-            println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output"));
-
+            run_command("cleanup.sh", "sh", ["/root/cleanup.sh"].to_vec());
             let _ = window.close();
         })
     );
-- 
GitLab


From a6134c83fabd66799828cfe47b88699c6461507c Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:46:36 +0100
Subject: [PATCH 07/10] Add very hacky password setting.

---
 src/main.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index 94f8131..0494845 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -101,6 +101,9 @@ fn build_root_pw_page(stack: &Stack) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
+            let pw = pw_entry.text();
+            // TODO: find less hacky approach to automatized password setting
+            run_command("set root pw", "sh", ["-c", &format!("echo -n 'root:{}' | chpasswd", &pw)].to_vec());
             stack.set_visible_child_full("account", gtk::StackTransitionType::SlideLeft);
         })
     );
@@ -127,7 +130,10 @@ fn build_account_page(stack: &Stack) -> StackPage {
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
             let username = username_entry.text();
+            let pw = pw_entry.text();
             run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec());
+            // TODO: find less hacky approach to automatized password setting
+            run_command("set user pw", "sh", ["-c", &format!("echo -n '{}:{}' | chpasswd", &username, &pw)].to_vec());
             stack.set_visible_child_full("time", gtk::StackTransitionType::SlideLeft);
         })
     );
-- 
GitLab


From 709dfa6f35df10c76522ac5a318bb8879fd302d8 Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 21:58:26 +0100
Subject: [PATCH 08/10] Add most basic input validation.

---
 src/main.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index 0494845..6b2dfb5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,14 @@ const MARGIN: i32 = 48;
 // - https://gtk-rs.org/gtk4-rs/stable/latest/docs/gtk4/
 // - https://kerkour.com/rust-cross-compilation
 
+fn valid_username(name: &str) -> bool {
+    return name.len() > 0
+}
+
+fn valid_password(pw: &str) -> bool {
+    return pw.len() > 0
+}
+
 fn run_command(description: &str, executable: &str, params: Vec<&str>) {
     println!("STEP: {}", &description);
     let output = Command::new(executable).args(params).output().expect(&format!("failed to {}", description));
@@ -102,6 +110,10 @@ fn build_root_pw_page(stack: &Stack) -> StackPage {
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
             let pw = pw_entry.text();
+            let pw_retry = pw_entry_rep.text();
+            if pw != pw_retry || !valid_password(&pw) {
+                return;
+            }
             // TODO: find less hacky approach to automatized password setting
             run_command("set root pw", "sh", ["-c", &format!("echo -n 'root:{}' | chpasswd", &pw)].to_vec());
             stack.set_visible_child_full("account", gtk::StackTransitionType::SlideLeft);
@@ -131,6 +143,10 @@ fn build_account_page(stack: &Stack) -> StackPage {
         clone!(@weak stack => move |_| {
             let username = username_entry.text();
             let pw = pw_entry.text();
+            let pw_retry = pw_entry_rep.text();
+            if pw != pw_retry || !valid_username(&username) || !valid_password(&pw) {
+                return;
+            }
             run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec());
             // TODO: find less hacky approach to automatized password setting
             run_command("set user pw", "sh", ["-c", &format!("echo -n '{}:{}' | chpasswd", &username, &pw)].to_vec());
-- 
GitLab


From 7c61a2aeda9eab201d9b49df35ebfb7f68d78b8c Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 22:22:50 +0100
Subject: [PATCH 09/10] Add draft for keyboard setup, seemingly not yet
 working.

---
 src/main.rs         | 6 ++++++
 test_with_chroot.sh | 5 ++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index 6b2dfb5..138dd6a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -77,6 +77,7 @@ fn build_keyboard_page(stack: &Stack) -> StackPage {
         "QWERTY-JP (Japanese)",
         "AZERTY-FR (French)",
     ]);
+    let keymaps = ["us", "de", "uk", "jp", "fr"];
     let dropdown = DropDown::builder()
         .model(&list)
         .build();
@@ -87,6 +88,11 @@ fn build_keyboard_page(stack: &Stack) -> StackPage {
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
+            // FIXME: so far this does not seem to do enough to actually set up the keyboard-layout
+            let layout = keymaps[dropdown.selected() as usize];
+            let debconf_selection = format!("keyboard-configuration keyboard-configuration/layout select {}", &layout);
+            run_command("set keyboard layout", "sh", ["-c", &format!("echo '{}' | debconf-set-selections", debconf_selection)].to_vec());
+            run_command("run dpkg-reconfigure", "dpkg-reconfigure", ["-f", "noninteractive", "keyboard-configuration"].to_vec());
             stack.set_visible_child_full("root-password", gtk::StackTransitionType::SlideLeft);
         })
     );
diff --git a/test_with_chroot.sh b/test_with_chroot.sh
index 1ad27e5..ffe2fa1 100755
--- a/test_with_chroot.sh
+++ b/test_with_chroot.sh
@@ -24,9 +24,12 @@ fi
 cp "${repo_path_wizard}" "${chroot_dir_root}/"
 cp "${repo_path_cleanup_script}" "${chroot_dir_root}/"
 
-# add whatever packages not provided by debootstrap on default are expected
+# packages needed for GTK4
 chroot "${CHROOT_PATH}" /bin/apt install -y libgtk-4-dev libadwaita-1-dev
 
+# packages needed for keyboard configuration (?)
+chroot "${CHROOT_PATH}" /bin/apt install -y keyboard-configuration
+
 # xhost +/- allows the Wizard to access the X Server for display
 xhost +
 chroot "${CHROOT_PATH}" "${chroot_path_wizard}" 
-- 
GitLab


From 6c493ab48ca0dff9d7d7bd9a5be318f0e4451c6f Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 5 Mar 2024 22:30:45 +0100
Subject: [PATCH 10/10] Add display of error messages on failed input
 validations.

---
 src/main.rs | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 138dd6a..2b9530a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -111,13 +111,19 @@ fn build_root_pw_page(stack: &Stack) -> StackPage {
     page_box.append(&pw_entry);
     page_box.append(&Label::new(Some("And repeat it for safety:")));
     page_box.append(&pw_entry_rep);
+    let error_label = Label::new(None);
+    page_box.append(&error_label);
     page_box.append(&button_next);
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
             let pw = pw_entry.text();
             let pw_retry = pw_entry_rep.text();
-            if pw != pw_retry || !valid_password(&pw) {
+            if pw != pw_retry {
+                error_label.set_label("ERROR! Passwords don't match!");
+                return;
+            } else if !valid_password(&pw) {
+                error_label.set_label("ERROR! Invalid password format!");
                 return;
             }
             // TODO: find less hacky approach to automatized password setting
@@ -143,14 +149,24 @@ fn build_account_page(stack: &Stack) -> StackPage {
     page_box.append(&pw_entry);
     page_box.append(&Label::new(Some("And repeat it for safety:")));
     page_box.append(&pw_entry_rep);
+    let error_label = Label::new(None);
+    page_box.append(&error_label);
     page_box.append(&button_next);
 
     button_next.connect_clicked(
         clone!(@weak stack => move |_| {
             let username = username_entry.text();
+            if !valid_username(&username) {
+                error_label.set_label("ERROR! Invalid username format!");
+                return;
+            }
             let pw = pw_entry.text();
             let pw_retry = pw_entry_rep.text();
-            if pw != pw_retry || !valid_username(&username) || !valid_password(&pw) {
+            if pw != pw_retry {
+                error_label.set_label("ERROR! Passwords don't match!");
+                return;
+            } else if !valid_password(&pw) {
+                error_label.set_label("ERROR! Invalid password format!");
                 return;
             }
             run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec());
-- 
GitLab