#!/bin/sh

self='hetzner_ddns'

# Read variabels from configuration file
if test -G "/usr/local/etc/$self.conf"; then
    . "/usr/local/etc/$self.conf"
else
    >&2 echo 'unable to read configuration file'
    exit 78
fi

# Check dependencies
if ! command -v curl > /dev/null || \
   ! command -v awk > /dev/null || \
   ! command -v jq > /dev/null
then
    >&2 echo 'missing dependency'
    exit 1
fi

# Check logging support
if ! touch "/var/log/$self.log";
then
    >&2 echo 'unable to open logfile'
    exit 2
fi

get_zone() {
    # Get zone ID
    zone="$(
        curl "https://dns.hetzner.com/api/v1/zones" \
            -H "Auth-API-Token: $key" 2>/dev/null | \
        jq -r '.zones[] | .name + " " + .id' | \
        awk "\$1==\"$domain\" {print \$2}"
    )"
    if [ -z "$zone" ]; then
        return 1
    else
        printf '[%s] Zone for %s: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" \
            "$domain" "$zone" >> "/var/log/$self.log"
    fi
}

get_record() {
    # Get record IDs
    if [ -n "$zone" ]; then
        record_ipv4="$(
            curl "https://dns.hetzner.com/api/v1/records?zone_id=$zone" \
                -H "Auth-API-Token: $key" 2>/dev/null | \
            jq -r '.records[] | .name + " " + .type + " " + .id' | \
            awk "\$1==\"$1\" && \$2==\"A\" {print \$3}"
        )"
        record_ipv6="$(
            curl "https://dns.hetzner.com/api/v1/records?zone_id=$zone" \
                -H "Auth-API-Token: $key" 2>/dev/null | \
            jq -r '.records[] | .name + " " + .type + " " + .id' | \
            awk "\$1==\"$1\" && \$2==\"AAAA\" {print \$3}"
        )"
    fi
    if [ -z "$record_ipv4" ] && [ -z "$record_ipv6" ]; then
        return 1
    else
        printf '[%s] IPv4 record for %s: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1.$domain" \
            "${record_ipv4:-(missing)}" >> "/var/log/$self.log"
        printf '[%s] IPv6 record for %s: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1.$domain" \
            "${record_ipv6:-(missing)}" >> "/var/log/$self.log"
    fi
}

get_records() {
    # Get all record IDs
    for n in $records; do
        if get_record "$n"; then
            records_ipv4="$records_ipv4$n=$record_ipv4 "
            records_ipv6="$records_ipv6$n=$record_ipv6 "
        else
            printf '[%s] Missing both records for %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" \
                "$n.$domain" >> "/var/log/$self.log"
        fi
    done
}

get_record_ip_addr() {
    # Get record's IP address
    if [ -n "$record_ipv4" ]; then
        ipv4_rec="$(
            curl "https://dns.hetzner.com/api/v1/records/$record_ipv4" \
                -H "Auth-API-Token: $key" 2>/dev/null | \
            jq -r '.record.value'
        )"
    fi
    if [ -n "$record_ipv6" ]; then
        ipv6_rec="$(
            curl "https://dns.hetzner.com/api/v1/records/$record_ipv6" \
                -H "Auth-API-Token: $key" 2>/dev/null | \
            jq -r '.record.value'
        )"
    fi
    if [ -z "$ipv4_rec" ] && [ -z "$ipv6_rec" ]; then
        return 1
    fi
}

get_my_ip_addr() {
    # Get current public IP address
    ipv4_cur="$(
        curl 'http://ipv4.whatismyip.akamai.com/' 2>/dev/null
    )"
    ipv6_cur="$(
        curl 'http://ipv6.whatismyip.akamai.com/' 2>/dev/null
    )"
    if [ -z "$ipv4_cur" ] && [ -z "$ipv6_cur" ]; then
        return 1
    fi
}

set_record() {
    # Update record if IP address has changed
    if [ -n "$record_ipv4" ] && [ -n "$ipv4_cur" ] && [ "$ipv4_cur" != "$ipv4_rec" ]; then
        curl -X "PUT" "https://dns.hetzner.com/api/v1/records/$record_ipv4" \
            -H 'Content-Type: application/json' \
            -H "Auth-API-Token: $key" \
            -d "{
            \"value\": \"$ipv4_cur\",
            \"ttl\": $interval,
            \"type\": \"A\",
            \"name\": \"$n\",
            \"zone_id\": \"$zone\"
            }" 1>/dev/null 2>/dev/null &&
        printf "[%s] Update IPv4 for %s: %s => %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" \
            "$n.$domain" "$ipv4_rec" "$ipv4_cur" >> "/var/log/$self.log"
    fi
    if [ -n "$record_ipv6" ] && [ -n "$ipv6_cur" ] && [ "$ipv6_cur" != "$ipv6_rec" ]; then
        curl -X "PUT" "https://dns.hetzner.com/api/v1/records/$record_ipv6" \
            -H 'Content-Type: application/json' \
            -H "Auth-API-Token: $key" \
            -d "{
            \"value\": \"$ipv6_cur\",
            \"ttl\": $interval,
            \"type\": \"AAAA\",
            \"name\": \"$n\",
            \"zone_id\": \"$zone\"
            }" 1>/dev/null 2>/dev/null &&
        printf "[%s] Update IPv6 for %s: %s => %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" \
            "$n.$domain" "$ipv6_rec" "$ipv6_cur" >> "/var/log/$self.log"
    fi
}

pick_record() {
    # Get record ID from array
    echo "$2" | \
    awk "{
        for(i=1;i<=NF;i++){
            n=\$i;gsub(/=.*/,\"\",n);
            r=\$i;gsub(/.*=/,\"\",r);
            if(n==\"$1\"){
                print r;break
            }
        }}"
}

set_records() {
    # Get my public IP address
    if get_my_ip_addr; then
        # Update all records if possible
        for n in $records; do
            record_ipv4="$(pick_record "$n" "$records_ipv4")"
            record_ipv6="$(pick_record "$n" "$records_ipv6")"
            if [ -n "$record_ipv4" ] || [ -n "$record_ipv6" ]; then
                get_record_ip_addr && set_record
            fi
        done
    fi
}

run_ddns() {
    printf '[%s] Started Hetzner DDNS daemon\n' "$(date '+%Y-%m-%d %H:%M:%S')" \
                >> "/var/log/$self.log"

    while ! get_zone || ! get_records; do
        sleep $((interval/2+1))
    done

    while true; do
        set_records
        sleep "$interval"
    done
}

if [ "$1" = '--daemon' ]; then
    # Deamonize and write PID to file
    if touch "/var/run/$self.pid";
    then
        run_ddns &
        echo $! > "/var/run/$self.pid"
    else
        >&2 echo 'unable to daemonize'
        exit 2
    fi
else
    # Run in foreground
    run_ddns
fi
