Files
coredesktop/configure.d/10-core.sh
T

309 lines
11 KiB
Bash
Executable File

#!/bin/bash
TITLE="Federated Computer Core"
DESCRIPTION="Superpanel, Nextcloud, email, files and Element"
# What you entered last time, so running this again only does what's missing
CORE_CONF="$HOME/.config/coredesktop/core.conf"
GOA_ACCOUNT="$COREDESKTOP/configure.d/lib/goa-account"
source "$COREDESKTOP/configure.d/lib/nextcloud.sh"
[ -f "$CORE_CONF" ] && source "$CORE_CONF"
# CORE_DOMAIN, CORE_EMAIL and CORE_NAME come from core.conf; the rest follow
core_names() {
CORE_USER="${CORE_EMAIL%@*}"
NEXTCLOUD="https://nextcloud.$CORE_DOMAIN"
WEBDAV="$NEXTCLOUD/remote.php/dav/files/$CORE_USER"
MOUNT="/mnt/$CORE_DOMAIN"
SUPERPANEL_DESKTOP="$HOME/.local/share/applications/core-$CORE_DOMAIN-superpanel.desktop"
MATRIX="https://matrix.$CORE_DOMAIN"
ELEMENT_CONFIG="$HOME/.config/Element/config.json"
}
superpanel_done() {
[ -f "$SUPERPANEL_DESKTOP" ]
}
nextcloud_done() {
"$GOA_ACCOUNT" exists owncloud "$CORE_USER@nextcloud.$CORE_DOMAIN" 2> /dev/null
}
email_done() {
"$GOA_ACCOUNT" exists imap_smtp "$CORE_EMAIL" 2> /dev/null
}
files_done() {
grep -qs " $MOUNT davfs " /etc/fstab && grep -qs "^$MOUNT " "$HOME/.davfs2/secrets"
}
element_installed() {
command -v element-desktop > /dev/null
}
# Done when Element isn't installed, as there's nothing to set up
element_done() {
element_installed || return 0
jq -e --arg url "$MATRIX" \
'.default_server_config["m.homeserver"].base_url | rtrimstr("/") == $url' \
"$ELEMENT_CONFIG" &> /dev/null
}
is_configured() {
[ -n "$CORE_DOMAIN" ] || return 1
core_names
superpanel_done && nextcloud_done && email_done && files_done && element_done
}
save_conf() {
mkdir -p "$(dirname "$CORE_CONF")"
{
printf 'CORE_DOMAIN=%q\n' "$CORE_DOMAIN"
printf 'CORE_EMAIL=%q\n' "$CORE_EMAIL"
printf 'CORE_NAME=%q\n' "$CORE_NAME"
} > "$CORE_CONF"
}
desktop_entry() {
local file="$1" name="$2" comment="$3" url="$4" icon="$5"
mkdir -p "$(dirname "$file")"
cat > "$file" << EOF
[Desktop Entry]
Type=Application
Name=$name
Comment=$comment
Exec=xdg-open $url
Icon=$icon
Categories=Network;
EOF
}
setup_superpanel() {
local url="https://superpanel.$CORE_DOMAIN"
local icon="$HOME/.local/share/icons/core-$CORE_DOMAIN-superpanel.png"
# The login page carries Superpanel's icon inline
mkdir -p "$(dirname "$icon")"
if ! curl -s "$url/" | grep -o 'rel="icon"[^>]*base64,[^"]*' | head -n 1 \
| sed 's/.*base64,//' | base64 -d > "$icon" 2> /dev/null || [ ! -s "$icon" ]; then
rm -f "$icon"
icon="applications-internet"
fi
desktop_entry "$SUPERPANEL_DESKTOP" "Superpanel" "Manage your Federated Computer Core ($CORE_DOMAIN)" "$url" "$icon"
echo "Added Superpanel to your applications."
}
setup_nextcloud() {
[ -n "$APP_PASSWORD" ] || { echo "This needs your Core password, and Nextcloud didn't accept it."; return 1; }
"$GOA_ACCOUNT" add << EOF || return 1
$(jq -n --arg user "$CORE_USER" --arg host "nextcloud.$CORE_DOMAIN" --arg url "$NEXTCLOUD" \
--arg password "$APP_PASSWORD" '{
provider: "owncloud",
identity: $user,
presentation_identity: "\($user)@\($host)",
credentials: {password: $password},
details: {
Uri: "\($url)/remote.php/webdav",
FilesEnabled: "true",
CalendarEnabled: "true",
CalDavUri: "\($url)/remote.php/dav",
ContactsEnabled: "true",
CardDavUri: "\($url)/remote.php/dav",
AcceptSslErrors: "false"
}}')
EOF
echo "Added Nextcloud to Online Accounts (calendar, contacts and files)."
}
# The mail server publishes its settings (Thunderbird's autoconfig format).
# Prints: imap_host imap_port imap_socket smtp_host smtp_port smtp_socket username
mail_settings() {
curl -sf --max-time 15 \
"https://autoconfig.$CORE_DOMAIN/mail/config-v1.1.xml?emailaddress=$CORE_EMAIL" \
| python3 -I -c '
import sys, xml.etree.ElementTree as ET
provider = ET.parse(sys.stdin).getroot().find("emailProvider")
imap = provider.find("incomingServer[@type=\"imap\"]")
smtp = provider.find("outgoingServer[@type=\"smtp\"]")
print(*(s.findtext(f) for s in (imap, smtp) for f in ("hostname", "port", "socketType")),
imap.findtext("username"))'
}
# Online Accounts takes host:port, and only the host for the usual port
host_port() {
if [ "$2" = "$3" ]; then echo "$1"; else echo "$1:$2"; fi
}
setup_email() {
[ -n "$PASSWORD" ] || { echo "This needs your Core password."; return 1; }
local imap_host imap_port imap_socket smtp_host smtp_port smtp_socket username
if ! read -r imap_host imap_port imap_socket smtp_host smtp_port smtp_socket username < <(mail_settings) \
|| [ -z "$smtp_socket" ]; then
echo "The mail server didn't give its settings; using mail.$CORE_DOMAIN with the usual ports."
imap_host="mail.$CORE_DOMAIN" imap_port=993 imap_socket=SSL
smtp_host="mail.$CORE_DOMAIN" smtp_port=587 smtp_socket=STARTTLS
username="%EMAILADDRESS%"
fi
case "$username" in
%EMAILLOCALPART%) username="$CORE_USER" ;;
*) username="$CORE_EMAIL" ;;
esac
[ "$imap_socket" = SSL ] && imap_std=993 || imap_std=143
[ "$smtp_socket" = SSL ] && smtp_std=465 || smtp_std=587
"$GOA_ACCOUNT" add << EOF || return 1
$(jq -n --arg email "$CORE_EMAIL" --arg name "$CORE_NAME" --arg user "$username" \
--arg password "$PASSWORD" \
--arg imap "$(host_port "$imap_host" "$imap_port" "$imap_std")" --arg imap_socket "$imap_socket" \
--arg smtp "$(host_port "$smtp_host" "$smtp_port" "$smtp_std")" --arg smtp_socket "$smtp_socket" '{
provider: "imap_smtp",
identity: $email,
presentation_identity: $email,
credentials: {"imap-password": $password, "smtp-password": $password},
details: {
Enabled: "true",
EmailAddress: $email,
Name: $name,
ImapHost: $imap,
ImapUserName: $user,
ImapUseSsl: (if $imap_socket == "SSL" then "true" else "false" end),
ImapUseTls: (if $imap_socket == "STARTTLS" then "true" else "false" end),
ImapAcceptSslErrors: "false",
SmtpHost: $smtp,
SmtpUseAuth: "true",
SmtpUserName: $user,
SmtpAuthLogin: "false",
SmtpAuthPlain: "true",
SmtpUseSsl: (if $smtp_socket == "SSL" then "true" else "false" end),
SmtpUseTls: (if $smtp_socket == "STARTTLS" then "true" else "false" end),
SmtpAcceptSslErrors: "false"
}}')
EOF
echo "Added $CORE_EMAIL to Online Accounts."
}
# Mount your Nextcloud files at /mnt/<domain> with davfs2. You can mount it
# without sudo once it's in /etc/fstab with "user" and you're in Arch's davfs2
# group (network). It's mounted now and each time you log in.
setup_files() {
[ -n "$APP_PASSWORD" ] || { echo "This needs your Core password, and Nextcloud didn't accept it."; return 1; }
local secret
sudo install -d -o "$USER" -g "$(id -gn)" "$MOUNT" || return 1
if ! grep -qs " $MOUNT davfs " /etc/fstab; then
echo "$WEBDAV $MOUNT davfs rw,user,noauto,uid=$USER,_netdev 0 0" | sudo tee -a /etc/fstab > /dev/null || return 1
fi
if ! id -nG "$USER" | grep -qw network; then
sudo usermod -aG network "$USER" || return 1
fi
# davfs2 only reads this file if no one else can
mkdir -p -m 700 "$HOME/.davfs2"
touch "$HOME/.davfs2/secrets"
chmod 600 "$HOME/.davfs2/secrets"
secret="${APP_PASSWORD//\\/\\\\}"
secret="${secret//\"/\\\"}"
sed -i "\\#^$MOUNT #d" "$HOME/.davfs2/secrets"
printf '%s %s "%s"\n' "$MOUNT" "$CORE_USER" "$secret" >> "$HOME/.davfs2/secrets"
mkdir -p "$HOME/.config/autostart"
cat > "$HOME/.config/autostart/core-$CORE_DOMAIN-files.desktop" << EOF
[Desktop Entry]
Type=Application
Name=Nextcloud files ($CORE_DOMAIN)
Comment=Mount your Nextcloud files at $MOUNT
Exec=sh -c 'mountpoint -q $MOUNT || mount $MOUNT'
X-GNOME-Autostart-enabled=true
NoDisplay=true
EOF
# Shown in the Files sidebar as the domain
local bookmarks="$HOME/.config/gtk-3.0/bookmarks"
mkdir -p "$(dirname "$bookmarks")"
grep -qs "^file://$MOUNT " "$bookmarks" || echo "file://$MOUNT $CORE_DOMAIN" >> "$bookmarks"
# A new session through sudo picks up the network group now, without
# logging out and in again (Arch has no sg)
if ! mountpoint -q "$MOUNT"; then
sudo -u "$USER" mount "$MOUNT" || return 1
fi
echo "Your Nextcloud files are at $MOUNT (\"$CORE_DOMAIN\" in the Files sidebar)."
}
# Element Desktop reads ~/.config/Element/config.json over its built-in
# settings. Pointing it at the Core's Matrix server means you only choose
# "Sign in" (the Core offers single sign-on through Authelia). The Core's Matrix
# server is named after its address, matrix.<domain>, and says where its API is.
setup_element() {
local base_url config
base_url="$(curl -sf --max-time 15 "$MATRIX/.well-known/matrix/client" \
| jq -r '.["m.homeserver"].base_url // empty' 2> /dev/null)"
base_url="${base_url:-$MATRIX}"
mkdir -p "$(dirname "$ELEMENT_CONFIG")"
config="$(cat "$ELEMENT_CONFIG" 2> /dev/null)"
[ -n "$config" ] || config='{}'
jq --arg url "${base_url%/}" --arg name "matrix.$CORE_DOMAIN" \
'.default_server_config = {"m.homeserver": {base_url: $url, server_name: $name}}' \
<<< "$config" > "$ELEMENT_CONFIG.tmp" && mv -f "$ELEMENT_CONFIG.tmp" "$ELEMENT_CONFIG" || return 1
echo "Element will use $MATRIX the next time it starts."
}
# Run a step unless it's done; a failed step doesn't stop the ones after it
step() {
local name="$1" title="$2"
if "${name}_done"; then
echo " $title: already set up"
return 0
fi
if ! confirm_yes "Set up $title?"; then
return 0
fi
"setup_$name" || { echo -e "\e[31m$title was not set up.\e[0m"; STATUS=1; }
}
run_configure() {
local url
STATUS=0
read -erp "Your Superpanel's address (e.g. https://superpanel.example.com): " \
-i "${CORE_DOMAIN:+https://superpanel.$CORE_DOMAIN}" url
url="${url#*://}"
url="${url%%/*}"
CORE_DOMAIN="${url#superpanel.}"
if [ -z "$CORE_DOMAIN" ] || ! curl -s -o /dev/null --max-time 15 "https://superpanel.$CORE_DOMAIN/"; then
echo "Couldn't reach https://superpanel.$CORE_DOMAIN."
return 1
fi
read -erp "Your Core email: " -i "${CORE_EMAIL:-$USER@$CORE_DOMAIN}" CORE_EMAIL
read -erp "Your name, as shown on email you send: " \
-i "${CORE_NAME:-$(getent passwd "$USER" | cut -d: -f5 | cut -d, -f1)}" CORE_NAME
[[ "$CORE_EMAIL" == *@* ]] || { echo "That isn't an email address."; return 1; }
core_names
save_conf
# Nextcloud, email and files need your Core password; Nextcloud checks it
if ! nextcloud_done || ! email_done || ! files_done; then
local tries
for tries in 1 2 3; do
read -rsp "Your Core password: " PASSWORD
echo
nextcloud_app_password "CoreDesktop on $HOSTNAME"
case $? in
0) break ;;
2) echo "Nextcloud says that password is wrong."; PASSWORD="" ;;
*) echo "Couldn't reach Nextcloud at $NEXTCLOUD; trying the password for email anyway."; break ;;
esac
done
fi
echo
step superpanel "Superpanel in your applications"
step nextcloud "Nextcloud in Online Accounts"
step email "Email in Online Accounts"
step files "Nextcloud files at $MOUNT"
element_installed && step element "Element with your Core's Matrix server"
unset PASSWORD APP_PASSWORD
return $STATUS
}