#!/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/ 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., 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 }