aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/user/userctl
blob: d9720d8aa456181926e1858a8dcef43c6893796c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/bin/bash
#
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The user's details, read and written where other programs look for them.
#
#   userctl get              key=value: name email avatar language location signingkey
#   userctl keys             fingerprint<TAB>uid per gpg secret key
#   userctl set <key> <val>  write one field to every store holding it
#
# Stores: AccountsService (and through SetRealName the passwd GECOS that
# LibreOffice reads), KDE's ~/.config/emaildefaults, the global git config.
# Polkit lets a user change their own AccountsService entry without a prompt.
#
# set prints nothing on success. On a failing write it prints the command and
# its error on stdout, for the drawer page, and exits 1 without trying the
# remaining stores. A new field is one case below and one entry in the
# page's field list.

set -u
# Character-wise ${x:0:1}, so an accented initial is not cut in half.
export LC_ALL=C.UTF-8

ACC=org.freedesktop.Accounts
OBJ="/org/freedesktop/Accounts/User$(id -u)"
IFACE=org.freedesktop.Accounts.User

usage() {
    printf 'usage: %s get | keys | set <name|email|avatar|language|location|signingkey> <value>\n' \
           "${0##*/}" >&2
    exit 2
}

run() {
    local err
    if ! err="$("$@" 2>&1 >/dev/null)"; then
        printf '%s: %s\n' "$1" "${err:-failed}"
        exit 1
    fi
}

# --json, because plain get-property prints non-ASCII as octal escapes.
# Fails with the first line of stderr as its output, so get can report it.
prop() {
    local out
    out="$(busctl --system --json=short get-property "$ACC" "$OBJ" "$IFACE" "$1" 2>&1)" \
        || { head -n1 <<< "${out:-failed}"; return 1; }
    jq -r .data <<< "$out"
}

acc() { run busctl --system call -- "$ACC" "$OBJ" "$IFACE" "$1" s "$2"; }
kde() { run kwriteconfig6 --file emaildefaults --group PROFILE_Default --key "$1" -- "$2"; }

# The surname as an initial, for commits: "Jane Doèr" -> "Jane D.".
gitname() {
    local -a w
    read -ra w <<< "$1"
    local n=${#w[@]}
    if (( n < 2 )); then printf '%s' "${w[*]}"; return; fi
    printf '%s %s.' "${w[*]:0:n-1}" "${w[n-1]:0:1}"
}

[[ $# -ge 1 ]] || usage

case "$1" in
    get)
        out=
        # Read everything first: one error line and exit 1, never blank fields.
        for p in RealName:name Email:email IconFile:avatar Language:language Location:location; do
            val="$(prop "${p%%:*}")" || { printf 'error=%s\n' "$val"; exit 1; }
            out+="${p#*:}=$val"$'\n'
        done
        printf '%s' "$out"
        printf 'signingkey=%s\n' "$(git config --global --get user.signingkey)"
        ;;
    keys)
        # The first fpr after a sec is the primary key's; the first uid
        # after that names it. ssb fingerprints come later and are skipped.
        gpg --list-secret-keys --with-colons 2>/dev/null | awk -F: '
            $1 == "sec"              { want = ($2 !~ /^[rei]$/ && $12 ~ /S/) ? 1 : 0; next }
            $1 == "fpr" && want == 1 { fpr = $10; want = 2; next }
            $1 == "uid" && want == 2 && $2 !~ /^[re]$/ { print fpr "\t" $10; want = 0 }'
        ;;
    set)
        [[ $# -eq 3 ]] || usage
        v="$3"
        case "$2" in
            name)
                acc SetRealName "$v"
                kde FullName "$v"
                run git config --global user.name "$(gitname "$v")"
                ;;
            email)
                acc SetEmail "$v"
                kde EmailAddress "$v"
                run git config --global user.email "$v"
                ;;
            avatar)   acc SetIconFile "$v" ;;
            language) acc SetLanguage "$v" ;;
            location) acc SetLocation "$v" ;;
            signingkey)
                if [[ -n "$v" ]]; then
                    run git config --global user.signingkey "$v"
                elif git config --global --get user.signingkey >/dev/null; then
                    run git config --global --unset-all user.signingkey
                fi
                ;;
            *) usage ;;
        esac
        ;;
    *) usage ;;
esac