{ lib, config, ... }: let cfg = config.plusultra.services.samba; inherit (lib) types mkEnableOption mkIf mapAttrs optionalAttrs; inherit (lib.plusultra) mkOpt mkBoolOpt; bool-to-yes-no = value: if value then "yes" else "no"; shares-submodule = with types; submodule ({ name, ... }: { options = { path = mkOpt str null "The path to serve."; public = mkBoolOpt false "Whether the share is public."; browseable = mkBoolOpt true "Whether the share is browseable."; comment = mkOpt str name "An optional comment."; read-only = mkBoolOpt false "Whether the share should be read only."; only-owner-editable = mkBoolOpt false "Whether the share is only writable by the system owner (plusultra.user.name)."; extra-config = mkOpt attrs { } "Extra configuration options for the share."; }; }); in { options.plusultra.services.samba = with types; { enable = mkEnableOption "Samba"; workgroup = mkOpt str "WORKGROUP" "The workgroup to use."; browseable = mkBoolOpt true "Whether the shares are browseable."; shares = mkOpt (attrsOf shares-submodule) { } "The shares to serve."; }; config = mkIf cfg.enable { networking.firewall = { allowedTCPPorts = [ 5357 ]; allowedUDPPorts = [ 3702 ]; }; services.samba-wsdd = { enable = true; discovery = true; workgroup = "WORKGROUP"; }; services.samba = { enable = true; openFirewall = true; extraConfig = '' browseable = ${bool-to-yes-no cfg.browseable} ''; shares = mapAttrs (name: value: { inherit (value) path comment; public = bool-to-yes-no value.public; browseable = bool-to-yes-no value.browseable; "read only" = bool-to-yes-no value.read-only; } // (optionalAttrs value.only-owner-editable { "write list" = config.plusultra.user.name; "read list" = "guest, nobody"; "create mask" = "0755"; }) // value.extra-config) cfg.shares; }; }; }