#!/bin/sh
# Met a jour l'enregistrement DuckDNS avec l'adresse publique courante.
#
# Pourquoi : l'enregistrement a ete saisi a la main une seule fois. Une IP
# residentielle change (redemarrage du modem, expiration du bail) et le jour ou
# ca arrive plus personne ne joint le serveur de signalisation, sans le moindre
# message — puis le renouvellement Let's Encrypt echoue a son tour.
#
# Le parametre `ip` est laisse VIDE deliberement : DuckDNS utilise alors
# l'adresse source de la requete, qui est par definition l'adresse publique
# vue depuis Internet. C'est exactement ce qu'on veut publier, et ca evite de
# dependre d'un service tiers de decouverte d'IP.
#
# Le token est lu dans un fichier a part (mode 600), jamais passe en argument :
# /proc/<pid>/cmdline est lisible par tous les utilisateurs de la machine.

set -eu

CONF="${XDG_CONFIG_HOME:-$HOME/.config}/meshamoto/duckdns.env"

if [ ! -r "$CONF" ]; then
    echo "meshamoto-duckdns: fichier de configuration absent ou illisible : $CONF" >&2
    exit 78  # EX_CONFIG
fi

# Le fichier est LU, jamais source. Un fichier de configuration n'est pas du
# code : le sourcer transforme la moindre faute de frappe en commande executee.
# Concretement, un token colle seul sur sa ligne (sans `DUCKDNS_TOKEN=`) est
# interprete comme une commande, et le shell recrache alors le token dans le
# message d'erreur — donc dans le journal systemd, lisible sans privilege.
# C'est exactement ce que le mode 600 du fichier cherchait a empecher.
lire_cle() {
    awk -v cle="$1" '
        $0 ~ "^[ \t]*" cle "[ \t]*=" {
            sub("^[ \t]*" cle "[ \t]*=[ \t]*", "")
            sub("\r$", "")          # fichier edite sous Windows
            print
            exit
        }' "$CONF"
}

DUCKDNS_DOMAINS="$(lire_cle DUCKDNS_DOMAINS)"
DUCKDNS_TOKEN="$(lire_cle DUCKDNS_TOKEN)"

if [ -z "$DUCKDNS_TOKEN" ] || [ -z "$DUCKDNS_DOMAINS" ]; then
    echo "meshamoto-duckdns: DUCKDNS_TOKEN ou DUCKDNS_DOMAINS manquant dans $CONF" >&2
    echo "meshamoto-duckdns: format attendu, une cle par ligne : DUCKDNS_TOKEN=<valeur>" >&2
    exit 78
fi

# DuckDNS repond en texte brut : "OK" ou "KO", jamais un code HTTP d'erreur.
# Un `curl` qui reussit ne prouve donc rien — il faut lire le corps.
reponse="$(curl -fsS --max-time 30 \
    "https://www.duckdns.org/update?domains=${DUCKDNS_DOMAINS}&token=${DUCKDNS_TOKEN}&ip=")" || {
    echo "meshamoto-duckdns: la requete vers DuckDNS a echoue (reseau ou TLS)" >&2
    exit 1
}

case "$reponse" in
    OK*)
        echo "meshamoto-duckdns: ${DUCKDNS_DOMAINS} a jour"
        ;;
    *)
        # "KO" = domaine inconnu ou token invalide. Ne jamais journaliser la
        # reponse brute sans reflechir : elle ne contient pas le token, mais
        # l'URL complete, elle, le contiendrait.
        echo "meshamoto-duckdns: DuckDNS a refuse la mise a jour (reponse: ${reponse})" >&2
        exit 1
        ;;
esac
