#!/bin/sh
# Create a user's Windows profile, C:\users\<name>, owned by that user.
#
# Run by pam_exec as root when a session opens (registered through
# pam-auth-update: /usr/share/pam-configs/stained-glass-profile). This is the
# job Windows' profile service does as SYSTEM at first logon, and it cannot be
# done by the user: C:\users belongs to the SYSTEM account, and since ADR 0013
# a user's Windows programs create files with that user's own Unix rights --
# so without this, a new user's programs cannot write their own profile, TEMP
# included (multi-user debt D15).
#
# Only for members of the Windows-side group ($SG_WINE_GROUP); never for the
# SYSTEM account or root. A profile the SYSTEM account made before ADR 0013 --
# when every user's files were created with its rights -- is handed over to
# its user. Always exits 0: a login must not fail over this.
set -u

SG_LIB="${SG_LIB:-/usr/lib/stained-glass}"
# shellcheck source=lib/sg-common.sh
. "$SG_LIB/sg-common.sh"

[ "${PAM_TYPE:-open_session}" = open_session ] || exit 0
user="${PAM_USER:-${1:-}}"
case "$user" in ""|.*|*/*) exit 0 ;; esac
[ "$user" = "$SG_SYSTEM_USER" ] && exit 0
uid=$(id -u "$user" 2>/dev/null) || exit 0
[ "$uid" = 0 ] && exit 0
id -nG "$user" 2>/dev/null | tr ' ' '\n' | grep -qx "$SG_WINE_GROUP" || exit 0

users="$SG_PREFIX/drive_c/users"
# Every component above the profile must be the SYSTEM account's, or whoever
# controls it could point this root-run hook somewhere else.
[ -d "$users" ] && [ ! -L "$users" ] || exit 0
[ "$(stat -c %U "$users")" = "$SG_SYSTEM_USER" ] || exit 0

profile="$users/$user"
[ -L "$profile" ] && exit 0
if [ ! -e "$profile" ]; then
    # 0700 from the start, so it is never open to anyone else
    mkdir -m 0700 "$profile" 2>/dev/null && chown "$user:" "$profile" &&
        sg_log "created the user profile for $user"
elif [ -d "$profile" ] && [ "$(stat -c %U "$profile")" = "$SG_SYSTEM_USER" ]; then
    # -P: never follow a symbolic link inside it
    chown -R -P "$user:" "$profile" && chmod 0700 "$profile" &&
        sg_log "handed the user profile of $user, made by $SG_SYSTEM_USER, to $user"
fi
[ -d "$profile" ] && [ "$(stat -c %U "$profile")" = "$user" ] || exit 0

# The folders Windows' Default profile brings. Nothing creates TEMP on demand,
# and programs fail without it -- csc, and PowerShell, which drops to
# ConstrainedLanguage when it cannot write its policy probe there. The rest
# shell32 would create on first use, but a profile should look like one.
#
# Created *as the user*: the profile is theirs now, and root walking a tree the
# user controls could be sent anywhere with a symbolic link.
# shellcheck disable=SC2016 # $1 is the inner shell's, deliberately
runuser -u "$user" -- sh -c 'umask 077; cd "$1" && exec mkdir -p \
    AppData/Local/Temp AppData/LocalLow AppData/Roaming \
    Desktop Documents Downloads Music Pictures Videos Favorites Links \
    Contacts Searches "Saved Games"' sg-profile-create "$profile" 2>/dev/null ||
    sg_log "WARNING: could not create the standard folders in $user's profile"

# Send to: the items Windows' Default profile brings -- "Compressed (zipped)
# Folder" (wine-sg's shell32 runs the .ZFSendToTarget type's sendto command,
# which sg-shell's sg-zip registers), "Desktop (create shortcut)" and
# "Documents". Empty files, as on Windows: their extensions say what they do.
# Once per profile (a marker in AppData\Local), so an item the user deleted
# stays deleted; profiles made before this get them at their next login.
# shellcheck disable=SC2016 # $1 is the inner shell's, deliberately
runuser -u "$user" -- sh -c 'umask 077; cd "$1" || exit 1
    mark="AppData/Local/Stained Glass/sendto-defaults"
    [ -e "$mark" ] && exit 0
    d="AppData/Roaming/Microsoft/Windows/SendTo"
    mkdir -p "$d" "AppData/Local/Stained Glass" || exit 1
    for f in "Compressed (zipped) Folder.ZFSendToTarget" "Desktop (create shortcut).DeskLink" "Documents.mydocs"; do
        [ -e "$d/$f" ] || : > "$d/$f" || exit 1
    done
    : > "$mark"' sg-profile-create "$profile" 2>/dev/null ||
    sg_log "WARNING: could not create the Send to items in $user's profile"

# The SYSTEM account may read and write the profile, as on Windows (SYSTEM:
# Full control). An elevated program runs as SYSTEM (ADR 0012): an installer
# the user downloaded to Downloads could not even be read by it -- it died
# without a word after the user said Yes. A POSIX ACL (and a default ACL, so
# new files and folders inherit it), set *as the user* -- the profile is
# theirs, and root walking it could be sent elsewhere by a symbolic link;
# setfacl -P does not follow them. The recursive pass is once per profile (a
# marker); the default ACLs carry it from then on.
#
# The profile folder's own entry at every sign-in: a chmod of it (cp -a of a
# home into it, "chmod 700 ~") empties the ACL mask, and SYSTEM loses the whole
# profile. The marker is "-2": profiles whose homes were merged in before that
# was known get the recursive pass once more.
#
# And the person by name too ("-3"): an elevated program is the person,
# elevated (wine-sg 7601), so it writes their AppData, Start menu and Desktop
# -- as the SYSTEM account on disk, and such a file's owner is SYSTEM. The
# person's own entry, inherited, keeps it theirs, as their Full control does
# on Windows.
if command -v setfacl >/dev/null 2>&1; then
    # shellcheck disable=SC2016 # $1..$2 are the inner shell's, deliberately
    runuser -u "$user" -- sh -c 'cd "$1" || exit 1
        setfacl -P -m "u:$2:rwX" -m "d:u:$2:rwX" -m "u:$3:rwX" -m "d:u:$3:rwX" . 2>/dev/null || exit 1
        mark="AppData/Local/Stained Glass/system-access-3"
        [ -e "$mark" ] && exit 0
        # files an elevated program made are SYSTEM'"'"'s already: the user
        # cannot change them, and that is not a failure
        setfacl -R -P -m "u:$2:rwX" -m "d:u:$2:rwX" -m "u:$3:rwX" -m "d:u:$3:rwX" . 2>/dev/null
        mkdir -p "AppData/Local/Stained Glass" && : > "$mark"' sg-profile-create "$profile" "$SG_SYSTEM_USER" "$user" 2>/dev/null ||
        sg_log "WARNING: could not give the SYSTEM account access to $user's profile"
fi
# The settings a program installed for the whole machine needs in the
# person's own profile before they first use it (Everything's folder index):
# as the user, the profile is theirs.
runuser -u "$user" -- env SG_LIB="$SG_LIB" "${SG_APP_DEFAULTS:-/usr/libexec/stained-glass/sg-app-defaults}" "$profile" 2>/dev/null ||
    sg_log "WARNING: could not apply the program defaults in $user's profile"
# One home for Linux and Windows programs: a new account's /home/<user> is
# made a link to this profile now; a home already in use waits for the next
# boot (sg-shared-home.service).
"${SG_SHARED_HOME:-/usr/libexec/stained-glass/sg-shared-home}" --login "$user" 2>/dev/null
exit 0
