From a178510f63e4e391479c8eee6cb87e6c062861f5 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 12 Sep 2026 18:50:43 +0200 Subject: [PATCH] chore: update --- Makefile | 4 +- config.scm | 170 +++++++++---------- home/dotfiles/foot.ini | 2 +- home/dotfiles/waybar/config.jsonc | 26 +++ home/dotfiles/waybar/style.css | 5 + home/home-configuration.scm | 20 +-- home/modules/ruther/home/dwl/config.h | 46 +++++ home/modules/ruther/home/dwl/scripts.scm | 7 +- home/modules/ruther/home/dwl/wm.scm | 6 +- home/patches/msmtp-from-utf8-encoding.patch | 119 ------------- home/patches/offlineimap3.patch | 178 ++++++++++++++++++-- isos/simple.scm | 2 +- modules/ruther/services/auto-login.scm | 49 ++++++ modules/ruther/services/nix.scm | 58 +++++++ udev/51-android.rules | 1 + 15 files changed, 445 insertions(+), 248 deletions(-) delete mode 100644 home/patches/msmtp-from-utf8-encoding.patch create mode 100644 modules/ruther/services/auto-login.scm create mode 100644 modules/ruther/services/nix.scm create mode 100644 udev/51-android.rules diff --git a/Makefile b/Makefile index 1c4565c9d917af9ee23edc565ec933d4dc577020..8473b9517cecec6f44a83994ade44eb22d9c27d8 100644 --- a/Makefile +++ b/Makefile @@ -51,4 +51,6 @@ system-repl: channels-lock.scm $(GUIX) repl -L modules -i ./repl-init.scm $(EXTRA_ARGS) channels-lock.scm: channels.scm - guix time-machine -C channels.scm $(EXTRA_TIMEMACHINE_ARGS) -- describe -f channels $(EXTRA_ARGS) > channels-lock.scm + guix time-machine -C channels.scm $(EXTRA_TIMEMACHINE_ARGS) -- \ + describe -f channels $(EXTRA_ARGS) > channels-lock.scm \ + || (rm channels-lock.scm && exit 1) diff --git a/config.scm b/config.scm index 1d51b791ad5c3cddfea827930ec75d4cbd1d82bf..fd608f69c488a903af32775a9a7218f1b0882044 100644 --- a/config.scm +++ b/config.scm @@ -5,6 +5,7 @@ (define-module (config)) (use-modules + (guix gexp) (nongnu packages linux) (nongnu system linux-initrd) (nongnu packages firmware) @@ -19,11 +20,18 @@ (guix utils) (guix packages) (guix build-system gnu) + (guix build-system trivial) (gnu system accounts) (ruther services system) (ruther services bind) (ruther services nix-gl) - (ruther bootloader grub)) + (ruther services nix) + (ruther services auto-login) + (ruther services netbird) + (ruther packages netbird) + (ruther bootloader grub) + (dfsg contrib services tailscale)) + (use-service-modules desktop sddm xorg base nix pm virtualization vpn sound dbus cups containers security-token networking ssh databases linux docker) @@ -85,6 +93,9 @@ (udev-rules-service 'pluto-sdr (file->udev-rule "53-adi-plutosdr-usb.rules" (local-file "udev/53-adi-plutosdr-usb.rules"))) + (udev-rules-service + 'android + (file->udev-rule "51-android.rules" (local-file "udev/51-android.rules"))) (udev-rules-service 'ftdi (file->udev-rule "51-ftdi.rules" (local-file "udev/51-ftdi.rules"))))) @@ -123,7 +134,9 @@ (endpoint "159.195.49.157:51820") (keep-alive 25) (public-key "ZOVjmgUak67kLhNVgZwyb0bro3Yi4vCJbGArv+35IWQ=") - (allowed-ips '("192.168.32.0/24"))))))))) + (allowed-ips '("192.168.32.0/24"))))))) + (service tailscaled-service-type) + (service netbird-service-type))) (define %ruther/laptop-gui-essential-services (list @@ -175,7 +188,7 @@ (define %ruther/base-laptop-os (operating-system - (kernel linux-6.18) + (kernel linux-7.2) (kernel-arguments (cons* "amdgpu.dcdebugmask=0x10" %default-kernel-arguments)) @@ -236,10 +249,10 @@ (program (file-append inetutils "/bin/traceroute")) (capabilities "cap_net_raw=eip")) - (privileged-program - (program - (file-append wireshark-patched "/bin/dumpcap")) - (capabilities "cap_net_raw,cap_net_admin=eip")) + ;; (privileged-program + ;; (program + ;; (file-append wireshark-patched "/bin/dumpcap")) + ;; (capabilities "cap_net_raw,cap_net_admin=eip")) %default-privileged-programs)) ;; This is where we specify system-wide packages. @@ -251,10 +264,11 @@ zip unzip wget curl vim - nix polkit ;; get pktty fwupd-nonfree - wireshark-patched) + netbird + ;; wireshark-patched + ) %base-packages)) (services @@ -278,13 +292,6 @@ (home "/home/fbw")) %default-guix-shared-users)))) - ;; Prevent me from going to Codeberg too often. - (simple-service 'disable-codeberg - hosts-service-type - (list - (host "127.0.0.1" "www.codeberg.org") - (host "127.0.0.1" "codeberg.org"))) - ;; I give up! Let the download binaries run. (extra-special-file "/lib64/ld-linux-x86-64.so.2" (file-append glibc "/lib/ld-linux-x86-64.so.2")) @@ -294,20 +301,48 @@ ;; Let's use GUI apps from Nixpkgs, because, why not? nixos-opengl-driver-service - (service nix-service-type + ;; Prefer the builder's LAN address, then fall back to WireGuard. + (extra-special-file + "/root/.ssh/config" + (mixed-text-file + "root-ssh-config" + "Host edge-ruther-builder\n" + " HostName edge-ruther.local\n" + " HostKeyAlias edge-ruther.local\n" + " BatchMode yes\n" + " ConnectTimeout 5\n" + " ProxyUseFdpass yes\n" + " ProxyCommand " + (file-append bash "/bin/sh") + " -c '" + (file-append netcat-openbsd "/bin/nc") + " -F -w 2 edge-ruther.local %p || exec " + (file-append netcat-openbsd "/bin/nc") + " -F -w 10 192.168.32.12 %p'\n")) + + (service nix-service-type/raised-nofile (nix-configuration (extra-config '("experimental-features = nix-command flakes\n" "extra-platforms = i686-linux aarch64-linux\n" "keep-outputs = true\n" - "keep-derivations = true\n")))) + "keep-derivations = true\n" + + "extra-trusted-public-keys = edge-1:sGPnKl9kRZ1Tv8xqC8qdMfdpPpeZ0HDE38/eEmGOCGk=\n" + "fallback = true\n" + + "builders = ssh-ng://nixremote@edge-ruther-builder x86_64-linux /root/.ssh/id_ed25519 8 2 big-parallel,kvm\n" + "builders-use-substitutes = true\n")))) ;; TODO: contribute this to the nix service (simple-service 'nix-etc-d etc-profile-d-service-type (list (file-append nix "/etc/profile.d/nix-daemon.sh") (file-append nix "/etc/profile.d/nix.sh"))) - ;; Vivado or Matlab can crash because they open too many files + ;; Vivado or Matlab can crash because they open too many files. + ;; Note: @nixbld limits are set on the nix-daemon shepherd + ;; service itself (see (ruther services nix)); PAM limits don't + ;; apply because nixbld users never log in. (service pam-limits-service-type (list (pam-limits-entry "@wheel" 'hard 'nofile '50000) @@ -322,7 +357,9 @@ (local-file "keys/nonguix-signing-key.pub") (local-file "keys/rpi-local-key.pub") (local-file "keys/orv2-local-key.pub"))) - (substitute-urls '("https://substitutes.nonguix.org")))) + ;; (substitute-urls '("https://nonguix-proxy.ditigal.xyz")) + (substitute-urls '("https://substitutes.nonguix.org")) + )) (service earlyoom-service-type (earlyoom-configuration @@ -355,8 +392,9 @@ (mingetty-service-type config => (if (string=? (mingetty-configuration-tty config) "tty1") (mingetty-configuration (inherit config) - (auto-login "ruther") - (login-pause? #t)) + (auto-login "auto") + (login-program (make-auto-login-program #:users '("ruther" "fbw"))) + (login-pause? #f)) config)) (elogind-service-type config => (elogind-configuration (handle-lid-switch-external-power 'ignore))) @@ -380,93 +418,39 @@ (ice-9 match) (guix records)) -(define-record-type - (non-grafted derivation) - non-grafted? - (derivation non-grafted-derivation)) - -(define-gexp-compiler (non-grafted-compiler (non-grafted ) system target) - (mlet* %store-monad ((drv (non-grafted-derivation non-grafted))) - (without-grafting - (gexp->derivation "non-grafted" - #~(symlink #$drv #$output))))) - -(define-record-type* - drv-name-record drv-name - drv-name? - (derivation drv-name-derivation) - (override-grafts? drv-name-override-grafts? - (default #f))) - -(define-gexp-compiler (drv-name-compiler (drv-name ) system target) - (let ((drv-name-store (derivation->drv-name (drv-name-derivation drv-name)))) - (if (drv-name-override-grafts? drv-name) - (without-grafting drv-name-store) - drv-name-store))) +(define (non-grafted file) + (computed-file + "non-grafted" + #~(symlink + #$(with-parameters + ((%graft? #f)) + file) + #$output))) (define (@@ (gnu system) )) (define-gexp-compiler (os-compiler (os ) system target) (operating-system-derivation os)) -;; I don't know why the inputs aren't getting grafted here, but they aren't, that's good here. -(define (make-non-grafted-package pkgs) - (package - (name "non-grafted-pkg") - (version "0") - (synopsis #f) - (source #f) - (description #f) - (home-page #f) - (license #f) - (build-system (@ (guix build-system trivial) trivial-build-system)) - ;; (inputs pkgs) - (arguments - (list - #:builder #~(begin - (use-modules - (srfi srfi-19)) - - (mkdir #$output) - - (for-each (lambda (pkg) - (symlink - pkg - (string-append #$output "/" (basename pkg)))) - '#$pkgs) - - (symlink #$guile-3.0:debug (string-append #$output "/" "guile-debug")) - (symlink #$(libc-utf8-locales-for-target):static (string-append #$output "/" "guile-debug")) - - ;; (for-each (lambda (pkg) - ;; (unless (file-exists? (string-append #$output "/" (basename pkg))) - ;; (symlink - ;; pkg - ;; (string-append #$output "/" (basename pkg))))) - ;; (map cdr %build-inputs)) - ))))) - ;; Takes an operating system and gc roots its derivation (define (operating-system-with-build-inputs os) (operating-system (inherit os) - (services (operating-system-user-services os)) (services (cons* (simple-service 'gc-root-system-derivation gc-root-service-type (list - (drv-name (operating-system-derivation os) #t) - (drv-name (operating-system-derivation os) #f) - (non-grafted (operating-system-derivation os)) + (non-grafted os) (libc-utf8-locales-for-target) guile-3.0 - (make-non-grafted-package - (list - guile-3.0 - ;; `(,guile-3.0 "debug") - (libc-utf8-locales-for-target) - texinfo)))) + ;; (map + ;; non-grafted + ;; (list + ;; guile-3.0 + ;; (libc-utf8-locales-for-target) + ;; texinfo)) + )) (operating-system-user-services os))))) -(operating-system-with-build-inputs %ruther/base-laptop-os) +;; (operating-system-with-build-inputs %ruther/base-laptop-os) %ruther/base-laptop-os diff --git a/home/dotfiles/foot.ini b/home/dotfiles/foot.ini index f98d3255d8e103cb37feea984a3f3543a8230caf..32ee19029b127272d647cf7973284138ccf86097 100644 --- a/home/dotfiles/foot.ini +++ b/home/dotfiles/foot.ini @@ -1 +1 @@ -font=Hack:size=12 \ No newline at end of file +font=Comic Shanns Mono:size=14 \ No newline at end of file diff --git a/home/dotfiles/waybar/config.jsonc b/home/dotfiles/waybar/config.jsonc index dd060afdcb2266a83c579a62af6baa9684e12f05..0a0f9ae7ebaecace1f5f85e9c48d3d5fefeb4208 100644 --- a/home/dotfiles/waybar/config.jsonc +++ b/home/dotfiles/waybar/config.jsonc @@ -16,6 +16,7 @@ "modules-center": [ ], "modules-right": [ + "custom/dnd", "idle_inhibitor", "pulseaudio", "network", @@ -51,6 +52,18 @@ "tooltip": true, "tooltip-format": "{app}: {title}" }, + "custom/dnd": { + "exec": "if makoctl mode | grep -qx do-not-disturb; then echo '{\"alt\":\"on\",\"class\":\"on\",\"tooltip\":\"Do not disturb: on\"}'; else echo '{\"alt\":\"off\",\"class\":\"off\",\"tooltip\":\"Do not disturb: off\"}'; fi", + "return-type": "json", + "format": "{icon}", + "format-icons": { + "on": "", + "off": "" + }, + "on-click": "makoctl mode -t do-not-disturb; pkill -RTMIN+8 waybar", + "signal": 8, + "interval": 60 + }, "idle_inhibitor": { "format": "{icon}", "format-icons": { @@ -155,6 +168,7 @@ "modules-center": [ ], "modules-right": [ + "custom/dnd", "idle_inhibitor", "pulseaudio", "network", @@ -191,6 +205,18 @@ "tooltip": true, "tooltip-format": "{app}: {title}" }, + "custom/dnd": { + "exec": "if makoctl mode | grep -qx do-not-disturb; then echo '{\"alt\":\"on\",\"class\":\"on\",\"tooltip\":\"Do not disturb: on\"}'; else echo '{\"alt\":\"off\",\"class\":\"off\",\"tooltip\":\"Do not disturb: off\"}'; fi", + "return-type": "json", + "format": "{icon}", + "format-icons": { + "on": "", + "off": "" + }, + "on-click": "makoctl mode -t do-not-disturb; pkill -RTMIN+8 waybar", + "signal": 8, + "interval": 60 + }, "idle_inhibitor": { "format": "{icon}", "format-icons": { diff --git a/home/dotfiles/waybar/style.css b/home/dotfiles/waybar/style.css index 33d110a169fe42729c17d5646fe46319cb55b063..37b5f12847a7fc0bec4af2a8a2aa184bfa67deaa 100644 --- a/home/dotfiles/waybar/style.css +++ b/home/dotfiles/waybar/style.css @@ -84,6 +84,7 @@ button:hover { #custom-media, #tray, #mode, +#custom-dnd, #idle_inhibitor, #scratchpad, #power-profiles-daemon, @@ -135,6 +136,10 @@ button:hover { padding-right: 15px; } +#custom-dnd.on { + color: #f53c3c; +} + label:focus { background-color: #000000; } diff --git a/home/home-configuration.scm b/home/home-configuration.scm index c1690bed80bec949768158734c7156cdaaf24366..9f7c3d18dcc97d52cb006f144a98bc6867605b46 100644 --- a/home/home-configuration.scm +++ b/home/home-configuration.scm @@ -119,21 +119,6 @@ (find-files source "\\.(otf|otc)$")) #t)))))))) -(define (apply-patches pkg patches) - (let ((src (package-source pkg)) - (source-patches - (if (list? patches) - patches - (list patches)))) - (package - (inherit pkg) - (source - (origin - (inherit src) - (patches - (append source-patches - (origin-patches src)))))))) - (home-environment (packages (append @@ -149,8 +134,7 @@ dwm offlineimap - (apply-patches msmtp - (local-file "./patches/msmtp-from-utf8-encoding.patch")) + msmtp (emacs-for-tty) (emacs-packages)) (specifications->packages @@ -263,7 +247,7 @@ $XDG_DATA_DIRS") (service home-gtk-service-type (let ((gtk-theme (make-graphite-gtk-theme #:theme "orange" #:color "dark")) (icon-theme (make-tela-circle-icon-theme #:variant "orange")) - (cursor-theme (make-catppuccin-cursors #:flavor "frappe" #:accent "sapphire"))) + (cursor-theme (make-catppuccin-cursors #:flavor "latte" #:accent "maroon"))) (home-gtk-configuration (gtk-theme (gtk-theme-configuration diff --git a/home/modules/ruther/home/dwl/config.h b/home/modules/ruther/home/dwl/config.h index f06c9ba23504acd4b072e15e176439369a9921af..557a0ff8e8460ff7d175cc0ed6ffd8706460a1b0 100644 --- a/home/modules/ruther/home/dwl/config.h +++ b/home/modules/ruther/home/dwl/config.h @@ -287,3 +287,49 @@ static const Button buttons[] = { { MODKEY, BTN_MIDDLE, togglefloating, {0} }, { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, }; + +bool applycustomrules(const char *appid, const char *title, Rule *r) +{ + static int opened_librewolf = 0; // appid librewolf + static int opened_firefox = 0; // appid Firefox + static int opened_vesktop = 0; // appid vesktop + static int opened_element = 0; // appid Element + + if (strstr(appid, "librewolf")) { + if (opened_librewolf < 4) { + r->tags = 1 << (opened_librewolf / 2); + r->allow_activation = 1; + opened_librewolf++; + return true; + } + } + + if (strstr(appid, "Firefox")) { + if (opened_firefox < 2) { + r->tags = 1 << 7; + r->allow_activation = 1; + opened_firefox++; + return true; + } + } + + if (strstr(appid, "Element")) { + if (opened_element < 2) { + r->tags = 1 << 8; + r->allow_activation = 1; + opened_element++; + return true; + } + } + + if (strstr(appid, "vesktop")) { + if (opened_vesktop < 2) { + r->tags = 1 << 8; + r->allow_activation = 1; + opened_vesktop++; + return true; + } + } + + return false; +} diff --git a/home/modules/ruther/home/dwl/scripts.scm b/home/modules/ruther/home/dwl/scripts.scm index 1657ddb36c74d1842096969385dfa3a63884b69a..077b526ff89f0d04b6e5457fb8ed0189d50104e1 100644 --- a/home/modules/ruther/home/dwl/scripts.scm +++ b/home/modules/ruther/home/dwl/scripts.scm @@ -76,7 +76,12 @@ "start" "wlr-services" (getenv "WAYLAND_DISPLAY") - (getenv "DISPLAY")))) + (getenv "DISPLAY")) + + (system "librewolf &") + (system "firefox &") + (system "element-desktop &") + (system "dev.vencord.Vesktop &"))) (define (start-dwl-gexp dwl waybar) #~(begin diff --git a/home/modules/ruther/home/dwl/wm.scm b/home/modules/ruther/home/dwl/wm.scm index 20fbf0000affb9de54f23227c24994ffaddfa7b4..511a1b17d781d06a266f42ef41c2d0f375a4feb7 100644 --- a/home/modules/ruther/home/dwl/wm.scm +++ b/home/modules/ruther/home/dwl/wm.scm @@ -120,14 +120,14 @@ (method git-fetch) (uri (git-reference (url "https://git.ditigal.xyz/~ruther/dwl") - (commit "2daeeb4ffd1b7b382b80976a14f0ae5f23b920b9"))) + (commit "c5333e44d94f9d3cf439ac7e257d64f3f3014cfb"))) (file-name (git-file-name name "0.8-dev")) (sha256 - (base32 "1r5k6vzmgwwlwd3giym1fy3y633i770a838fbzrc41afnbwrzz8s")))) + (base32 "1cbhan45jznpgwgrn9xab5605i8l4ldrlswwrj5a5pg7w4a1y2cv")))) (inputs (modify-inputs (package-inputs base) (append foot) - (append rofi-wayland) + (append rofi) (append swaylock) (append mew) (append dmenu-wl) diff --git a/home/patches/msmtp-from-utf8-encoding.patch b/home/patches/msmtp-from-utf8-encoding.patch deleted file mode 100644 index 269c94c83be8bb0f9907c5aa7c098eb6d653a6a7..0000000000000000000000000000000000000000 --- a/home/patches/msmtp-from-utf8-encoding.patch +++ /dev/null @@ -1,119 +0,0 @@ -From d4e93269f280a57863f55f30a910afabaf424c24 Mon Sep 17 00:00:00 2001 -From: Martin Lambers -Date: Sat, 28 Sep 2024 13:13:37 +0200 -Subject: [PATCH] Properly encode non-ASCII strings in the From header - -This fixes Github issue #162. ---- - src/msmtp.c | 4 +++- - src/tools.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ - src/tools.h | 8 ++++++++ - 3 files changed, 67 insertions(+), 1 deletion(-) - -diff --git a/src/msmtp.c b/src/msmtp.c -index 95297a6..75a4a29 100644 ---- a/src/msmtp.c -+++ b/src/msmtp.c -@@ -4187,8 +4187,10 @@ int main(int argc, char *argv[]) - { - if (account->from_full_name) - { -+ char *enc_name = encode_for_header(account->from_full_name); - fprintf(prepend_header_tmpfile, "From: %s <%s>\n", -- account->from_full_name, account->from); -+ enc_name, account->from); -+ free(enc_name); - } - else - { -diff --git a/src/tools.c b/src/tools.c -index d029f24..11718d3 100644 ---- a/src/tools.c -+++ b/src/tools.c -@@ -50,9 +50,11 @@ - #include - #ifdef ENABLE_NLS - # include -+# include - #endif - - #include "xalloc.h" -+#include "base64.h" - #include "tools.h" - - -@@ -907,3 +909,57 @@ int check_hostname_matches_domain(const char *hostname, const char *domain) - return (hostname[hostname_len - 1 - domain_len] == '.' - && strcasecmp(hostname + (hostname_len - domain_len), domain) == 0) ? 1 : 0; - } -+ -+ -+/* -+ * encode_for_header() -+ * -+ * see tools.h -+ */ -+ -+char *encode_for_header(const char *s) -+{ -+ int needsEncoding = 0; -+ for (int i = 0; s[i]; i++) -+ { -+ if (s[i] < 32 || s[i] >= 127) -+ { -+ needsEncoding = 1; -+ break; -+ } -+ } -+ if (needsEncoding) -+ { -+ /* create a string of the form "=?ENCODING?B?BASE64STRING?=" */ -+ size_t s_len = strlen(s); -+ size_t b64_s_len = BASE64_LENGTH(s_len); -+ char* encoding = xstrdup( -+#ifdef ENABLE_NLS -+ nl_langinfo(CODESET) -+#else -+ "UTF-8"; -+#endif -+ ); -+ size_t e_len = strlen(encoding); -+ size_t enc_len = 2 + e_len + 3 + b64_s_len + 3; -+ char *enc = xmalloc(enc_len + 1); -+ size_t i = 0; -+ enc[i++] = '='; -+ enc[i++] = '?'; -+ for (size_t j = 0; j < e_len; j++) -+ { -+ enc[i++] = tolower(encoding[j]); -+ } -+ free(encoding); -+ enc[i++] = '?'; -+ enc[i++] = 'B'; -+ enc[i++] = '?'; -+ base64_encode(s, s_len, enc + i, enc_len - i + 1); -+ strcat(enc, "?="); -+ return enc; -+ } -+ else -+ { -+ return xstrdup(s); -+ } -+} -diff --git a/src/tools.h b/src/tools.h -index 86ba9ff..ebdeb73 100644 ---- a/src/tools.h -+++ b/src/tools.h -@@ -259,4 +259,12 @@ void split_mail_address(const char *address, char **local_part, char **domain_pa - */ - int check_hostname_matches_domain(const char *hostname, const char *domain); - -+/* -+ * encode_for_header() -+ * -+ * Encode a string so that it can be used in a mail header. -+ * The result is allocated and needs to be freed. -+ */ -+char *encode_for_header(const char *s); -+ - #endif diff --git a/home/patches/offlineimap3.patch b/home/patches/offlineimap3.patch index 4127b74e45341197931266ea3cf5f2b19e0f36bb..872e97e0de53c1b0827f83fcfb33ca8a2bec5b02 100644 --- a/home/patches/offlineimap3.patch +++ b/home/patches/offlineimap3.patch @@ -1,11 +1,167 @@ ---- a/offlineimap/localeval.py 2025-05-04 12:19:37.739860684 +0200 -+++ b/offlineimap/localeval.py 2025-05-04 12:26:07.838113251 +0200 -@@ -26,6 +26,8 @@ - self.namespace = {} - - if path is not None: -+ import importlib.machinery -+ - # FIXME: limit opening files owned by current user with rights set - # to fixed mode 644. - foo = open(path, 'r') +From 1c4f9b4f05de575cd99ac5bad68c6e0bbad25374 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rodolfo=20Garc=C3=ADa=20Pe=C3=B1as=20=28kix=29?= + +Date: Wed, 13 May 2026 18:35:54 +0200 +Subject: [PATCH] Enhance STARTTLS handling with fallback for non-standard IMAP + servers + +- Implement tolerance for non-standard IMAP servers that do not provide reliable CAPABILITY after STARTTLS. +- Introduce configuration option 'allow_nonstandard_capabilities' to enable fallback using pre-TLS capabilities. +--- + offlineimap/imapserver.py | 110 +++++++++++++++++++++++++++------ + offlineimap/repository/IMAP.py | 7 +++ + 2 files changed, 98 insertions(+), 19 deletions(-) + +diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py +index fd866867..b29b5d6c 100644 +--- a/offlineimap/imapserver.py ++++ b/offlineimap/imapserver.py +@@ -363,32 +363,95 @@ def __gsshandler(self, token): + return None + + def __start_tls(self, imapobj): +- if 'STARTTLS' in imapobj.capabilities and not self.usessl: +- self.ui.debug('imap', 'Using STARTTLS connection') +- # imaplib2.starttls() unconditionally calls _get_capabilities() right +- # after the TLS handshake (before setting _tls_established=True). +- # Servers like Protonmail Bridge do not respond to CAPABILITY at +- # that stage, causing a 60-second hang. We bypass it by temporarily +- # substituting a no-op; imaplib2 still sets _tls_established=True +- # normally, and acquireconnection() already guards its own +- # post-login CAPABILITY refresh against that flag (see below). +- _orig_get_cap = imapobj._get_capabilities +- imapobj._get_capabilities = lambda: None ++ """Upgrade connection to TLS if STARTTLS is configured. ++ ++ - Uses pre-TLS capabilities only to decide whether to perform STARTTLS. ++ - After STARTTLS, forces a CAPABILITY command and replaces internal ++ capabilities with the post-TLS response. ++ - If no reliable post-TLS CAPABILITY is available: ++ - In strict mode (allow_nonstandard_capabilities = no): abort. ++ - In tolerant mode (allow_nonstandard_capabilities = yes): ++ reuse pre-TLS capabilities, removing LOGINDISABLED. ++ """ ++ ++ # If the repository does not want STARTTLS, do nothing. ++ if not self.starttls or self.usessl: ++ return ++ ++ # Pre-TLS capabilities (from initial banner / CAPABILITY). ++ caps_pre = set(getattr(imapobj, '_offlineimap_capabilities_pre_tls', ++ getattr(imapobj, 'capabilities', []))) ++ ++ # If the server does not advertise STARTTLS, respect that. ++ if 'STARTTLS' not in caps_pre: ++ return ++ ++ # Execute STARTTLS. ++ self.ui.debug('imap', 'Using STARTTLS connection') ++ ++ # Disable _get_capabilities() during starttls() to prevent imaplib2 ++ # from issuing CAPABILITY before we can do it ourselves. ++ _orig_get_cap = imapobj._get_capabilities ++ imapobj._get_capabilities = lambda: None ++ try: + try: + imapobj.starttls() + except imapobj.error as e: + err = "Failed to start TLS connection: %s" % str(e) + raise OfflineImapError(err, OfflineImapError.ERROR.REPO, + exc_info()[2]) +- finally: +- # Always restore the original method, even on success. +- imapobj._get_capabilities = _orig_get_cap +- elif self.starttls and not self.usessl: ++ finally: ++ # Always restore the original method, even on error. ++ imapobj._get_capabilities = _orig_get_cap ++ ++ # At this point, the socket is encrypted. Now we must refresh CAPABILITY. ++ caps_post = None ++ try: ++ typ, data = imapobj.capability() ++ if typ == 'OK' and data: ++ # data is a list of bytes, e.g. [b'IMAP4rev1 IDLE AUTH=PLAIN'] ++ line = data[0] ++ if isinstance(line, bytes): ++ line = line.decode('ascii', 'ignore') ++ caps_post = set(line.upper().split()) ++ except Exception: ++ caps_post = None ++ ++ if caps_post is not None: ++ # Standard path: completely replace capabilities ++ imapobj.capabilities = caps_post ++ imapobj._offlineimap_capabilities_post_tls = caps_post ++ return ++ ++ # If we reach here, we do not have reliable post-TLS capabilities. ++ # Decide based on repository configuration. ++ allow_nonstandard = getattr(self.repos, ++ 'allow_nonstandard_capabilities', False) ++ ++ if not allow_nonstandard: ++ # Strict mode: abort with clear error. + raise OfflineImapError( +- "STARTTLS requested but server does not advertise STARTTLS capability. " +- "Aborting to prevent sending password in plaintext (possible MITM attack). " +- "If you REALLY want an insecure connection, set 'starttls = no'.", +- OfflineImapError.ERROR.REPO) ++ "Server did not provide valid CAPABILITY after STARTTLS; " ++ "set 'allow_nonstandard_capabilities = yes' in repository " ++ "configuration to enable a non-standard fallback.", ++ OfflineImapError.ERROR.REPO ++ ) ++ ++ # Tolerant mode: best-effort using caps_pre. ++ caps_fallback = set(caps_pre) ++ if 'LOGINDISABLED' in caps_fallback: ++ # We assume that after STARTTLS, LOGINDISABLED no longer applies, ++ # so we remove it to allow LOGIN/AUTH configured by the user. ++ caps_fallback.remove('LOGINDISABLED') ++ ++ imapobj.capabilities = caps_fallback ++ imapobj._offlineimap_capabilities_post_tls = caps_fallback ++ ++ self.ui.warn( ++ "Server did not provide CAPABILITY after STARTTLS; " ++ "falling back to pre-TLS capabilities without LOGINDISABLED " ++ "due to allow_nonstandard_capabilities = yes." ++ ) + + # All __authn_* procedures are helpers that do authentication. + # They are class methods that take one parameter, IMAP object. +@@ -605,6 +668,15 @@ def acquireconnection(self): + self.lastowner[imapobj] = curThread.ident + self.connectionlock.release() + ++ # Store the pre-TLS capabilities (from banner and initial CAPABILITY). ++ # This will be used only to decide STARTTLS and, in case of non-standard ++ # fallback, to reconstruct an approximate post-TLS capability list. ++ try: ++ caps_pre = set(getattr(imapobj, 'capabilities', [])) ++ except Exception: ++ caps_pre = set() ++ imapobj._offlineimap_capabilities_pre_tls = caps_pre ++ + # Verify that the connection is still alive before returning it + # to the caller. If not, clean up and recursively call + # acquireconnection() to get a new one. +diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py +index b1afe861..db64dae1 100644 +--- a/offlineimap/repository/IMAP.py ++++ b/offlineimap/repository/IMAP.py +@@ -38,6 +38,13 @@ def __init__(self, reposname, account): + self.idlefolders = None + BaseRepository.__init__(self, reposname, account) + # self.ui is being set by the BaseRepository ++ ++ # Allow tolerance for non-standard IMAP servers that do not provide ++ # reliable CAPABILITY after STARTTLS. Default is strict (False). ++ # If set to True, heuristics using pre-TLS capabilities are applied ++ # as a fallback. ++ self.allow_nonstandard_capabilities = self.getconfboolean( ++ 'allow_nonstandard_capabilities', False) + self._host = None + # Must be set before calling imapserver.IMAPServer(self) + self.oauth2_request_url = None diff --git a/isos/simple.scm b/isos/simple.scm index 77607b14b5c2b67a210a465ce89b7f5cbad5e6ec..e7a5ede379a2b84be02906adb56d63eb391d6d3f 100644 --- a/isos/simple.scm +++ b/isos/simple.scm @@ -77,7 +77,7 @@ (package-version guix)))) - (kernel linux-6.13) + (kernel linux-lts) (firmware (cons* linux-firmware %base-firmware)) diff --git a/modules/ruther/services/auto-login.scm b/modules/ruther/services/auto-login.scm new file mode 100644 index 0000000000000000000000000000000000000000..7fa63df67ac990a26b1ebb7ffed483afee69acb4 --- /dev/null +++ b/modules/ruther/services/auto-login.scm @@ -0,0 +1,49 @@ +(define-module (ruther services auto-login) + #:use-module (guix gexp) + #:use-module (gnu packages admin) + #:use-module (gnu packages base) + #:export (make-auto-login-program)) + +(define* (make-auto-login-program #:key (users '())) + (program-file + "auto-login" + #~(begin + (define users (list #$@users)) + (define marker "/tmp/.auto-login-done") + (define stty-bin #$(file-append coreutils "/bin/stty")) + + (define (show-menu!) + (display "\nAuto-login — choose account:\n") + (let loop ((us users) (i 0)) + (when (pair? us) + (format #t " ~a) ~a\n" (integer->char (+ i (char->integer #\a))) (car us)) + (loop (cdr us) (+ i 1)))) + (display "> ")) + + (define (read-key) + (dynamic-wind + (lambda () (system* stty-bin "-echo" "cbreak")) + (lambda () (read-char)) + (lambda () (system* stty-bin "echo" "-cbreak")))) + + (define (read-choice) + (show-menu!) + (let* ((ch (read-key)) + (idx (- (char->integer ch) (char->integer #\a)))) + (newline) + (if (and (>= idx 0) (< idx (length users))) + (list-ref users idx) + (begin + (display "Invalid choice, try again.\n") + (read-choice))))) + + (let ((login-bin #$(file-append shadow "/bin/login")) + (args (filter (lambda (a) (not (or (string=? a "-f") + (string=? a "auto")))) + (cdr (command-line))))) ; strip -f and dummy username injected by mingetty + (if (file-exists? marker) + (apply execlp login-bin login-bin args) + (let ((user (read-choice))) + (call-with-output-file marker + (lambda (p) (display (getpid) p))) + (apply execlp login-bin login-bin "-f" user args))))))) diff --git a/modules/ruther/services/nix.scm b/modules/ruther/services/nix.scm new file mode 100644 index 0000000000000000000000000000000000000000..909d1bcd1b249675a2537aafc7f2b7e00b2e0cbc --- /dev/null +++ b/modules/ruther/services/nix.scm @@ -0,0 +1,58 @@ +;; Custom nix-daemon service that raises its NOFILE rlimit. +;; +;; The stock (gnu services nix) nix-daemon is started by shepherd with the +;; default 1024/4096 open-files rlimit. Build processes run as @nixbld are +;; spawned as children of the daemon, so they inherit that limit — PAM limits +;; for @nixbld are ineffective because nixbld users never log in. +;; +;; This module re-exports a service type that inherits from nix-service-type +;; but replaces its shepherd-root-service-type extension with one that passes +;; #:resource-limits to make-forkexec-constructor. + +(define-module (ruther services nix) + #:use-module (srfi srfi-1) + #:use-module (guix gexp) + #:use-module (gnu services) + #:use-module (gnu services nix) + #:use-module (gnu services shepherd) + #:export (nix-service-type/raised-nofile)) + +;; The nix-configuration-* accessors are not exported from (gnu services nix), +;; so reach in with @@. +(define nix-configuration-package + (@@ (gnu services nix) nix-configuration-package)) +(define nix-configuration-build-directory + (@@ (gnu services nix) nix-configuration-build-directory)) +(define nix-configuration-extra-options + (@@ (gnu services nix) nix-configuration-extra-options)) + +(define (nix-shepherd-service/raised-nofile config) + (let ((package (nix-configuration-package config)) + (build-directory (nix-configuration-build-directory config)) + (extra-options (nix-configuration-extra-options config))) + (list + (shepherd-service + (provision '(nix-daemon)) + (documentation "Run nix-daemon with a raised NOFILE rlimit.") + (requirement '(user-processes file-system-/nix/store)) + (start #~(make-forkexec-constructor + (list (string-append #$package "/bin/nix-daemon") + #$@extra-options) + #:environment-variables + (list (string-append "TMPDIR=" #$build-directory) + "PATH=/run/current-system/profile/bin") + #:resource-limits + '((nofile 1048576 1048576)))) + (respawn? #f) + (stop #~(make-kill-destructor)))))) + +(define nix-service-type/raised-nofile + (service-type + (inherit nix-service-type) + (extensions + (cons (service-extension shepherd-root-service-type + nix-shepherd-service/raised-nofile) + (filter (lambda (ext) + (not (eq? (service-extension-target ext) + shepherd-root-service-type))) + (service-type-extensions nix-service-type)))))) diff --git a/udev/51-android.rules b/udev/51-android.rules new file mode 100644 index 0000000000000000000000000000000000000000..32a79315f9dd86721fc7e871897af72412d80e40 --- /dev/null +++ b/udev/51-android.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee1", MODE="0666", TAG+="uaccess", SYMLINK+="android%n" \ No newline at end of file