#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

function display_help() {
    uecho "$scriptname: se connecter par ssh à un ou plusieurs hôtes

USAGE
    $scriptname host [options]
    $scriptname @hostsfile [options]
    $scriptname -R hosts...

OPTIONS
    host
    @hostsfile
        Spécifier un hôte distant sur lequel faire la connexion. Plusieurs
        hôtes peuvent être spécifiées, en les séparant par ':', e.g. host1:host2
        La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
        hostsfile, à raison d'un hôte par ligne.
    [options]
        Les arguments et options de ssh doivent se trouver *APRES* host, pour
        simplifier leur analyse.
Les options suivantes sont reconnues même quand elle sont spécifiées avant le
premier argument (les options longues ne sont reconnues qu'avant le premier
argument):
    -q, --quiet
        Mode non verbeux
    -t, --tty
        Forcer l'allocation d'un TTY
    -l, --login USER
        Spécifier le user avec lequel se connecter
    -o OPTION
        Ajouter une option de SSH
Les options suivantes *doivent* se trouver *AVANT* le premier argument:
    -d, --domain DOMAIN
        Spécifier un domaine par défaut pour les hôtes qui sont spécifiés sans
        domaine.
    -S, --ssh SSH
        Spécifier l'exécutable à utiliser pour lancer ssh.
    -R, --remove
        Lancer 'ssh-keygen -R' pour chacun des hôtes spécifiés. Par exemple:
            $scriptname -R host.tld
        est équivalent à:
            ssh-keygen -R host.tld
            ssh-keygen -R host
            ssh-keygen -R 10.10.1.5
        si l'adresse ip de host.tld est 10.10.1.5
        Quand cette option est spécifié, l'option -c est reconnue et permet de
        se reconnecter à l'hôte juste après avoir nettoyé les clés."
}

source "$(dirname "$0")/ulib/ulib" || exit 1
urequire DEFAULTS

set_defaults nutools

__PARSED_HOSTS=()
__PARSED_FILES=()
function parse_hostsfile() {
    # Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste
    # des hôtes mentionnés dans les fichiers.
    local inputfile basedir inputs input
    for inputfile in "$@"; do
        inputfile="$(abspath "$inputfile")"
        array_contains __PARSED_FILES "$inputfile" && {
            ewarn "$(ppath "$inputfile"): inclusion récursive"
            continue
        }
        array_add __PARSED_FILES "$inputfile"
        basedir="$(dirname "$inputfile")"
        
        array_from_lines inputs "$(<"$inputfile" filter_conf)" || {
            ewarn "$inputfile: fichier ingnoré"
            continue
        }
        for input in "${inputs[@]}"; do
            if [ "${input#@}" != "$input" ]; then
                # fichier inclus
                parse_hostsfile "$(abspath "${input#@}" "$basedir")"
            else
                array_addu __PARSED_HOSTS "$input"
            fi
        done
    done
}
function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; }
function fix_hosts() {
    # Si hosts contient des éléments multiple, comme a:b, séparer ces
    # éléments. i.e (a b:c) --> (a b c)
    # Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes
    # depuis un fichier.
    # Remplacer aussi les '.' par 'localhost'
    array_fix_paths hosts
    local -a _hosts _tmphosts host
    for host in "${hosts[@]}"; do
        host="${host%/}"
        if [ "${host#@}" != "$host" ]; then
            __PARSED_HOSTS=()
            parse_hostsfile "${host#@}"
            array_fix_paths __PARSED_HOSTS
            array_extendu _hosts __PARSED_HOSTS
        else
            array_addu _hosts "$host"
        fi
    done
    array_copy hosts _hosts
    array_map hosts __dot_is_localhost
}

function ussh() {
    args=(${quiet:+-q} ${tty:+-t} ${login:+-l "$login"})
    for option in "${options[@]}"; do
        args=("${args[@]}" -o "$option")
    done
    args=("${args[@]}" "$@")

    [ -n "$ssh" ] || ssh=ssh
    [ "${#hosts[*]}" -gt 1 ] && showtitle=1 || showtitle=
    for host in "${hosts[@]}"; do
        splituserhost "$host" user host
        [[ "$host" == *.* ]] || host="$host${domain:+.$domain}"
        [ -n "$showtitle" ] && etitle "${user:+$user@}$host"
        "$ssh" "${user:+$user@}$host" "${args[@]}"
        [ -n "$showtitle" ] && eend
    done
}

function remove_key() {
    estep "$1"
    ssh-keygen -R "$1" >&/dev/null
}
function remove_keys() {
    local -a __hosts; array_copy __hosts hosts
    local -a allhosts hosts ips; local host user ip
    array_copy allhosts __hosts

    etitle "Suppression des entrées dans ~/.ssh/known_hosts"
    for host in "${allhosts[@]}"; do
        splituserhost "$host" user host
        [[ "$host" == *.* ]] || host="$host${domain:+.$domain}"

        if ipcalc_checkip "$host" >/dev/null; then
            ip="$host"
            remove_key "$ip"

            resolv_hosts hosts "$ip"
            for host in "${hosts[@]}"; do
                remove_key "$host"
                hostname="${host%%.*}"
                [ "$hostname" != "$host" ] && remove_key "$hostname"
            done
        else
            remove_key "$host"
            hostname="${host%%.*}"
            [ "$hostname" != "$host" ] && remove_key "$hostname"

            resolv_ips ips "$host"
            for ip in "${ips[@]}"; do
                remove_key "$ip"
            done
        fi
    done
    eend
}

action=ssh
quiet=
tty=
login="$USSH_USER"
options=()
domain="$USSH_DOMAIN"
ssh=
connect_after_remove=
parse_opts + "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    -q,--quiet quiet=1 \
    -t,--tty tty=1 \
    -l:,--login: login= \
    -o options \
    -d:,--domain: domain= \
    -S:,--ssh ssh= \
    -R,--remove action=remove \
    -c,--connect connect_after_remove=1 \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

if [ "$action" == "ssh" ]; then
    [ -n "$1" ] || die "Vous devez spécifier l'hôte sur lequel faire la connexion"
    hosts=("$1"); shift
    fix_hosts
    ussh "$@"

elif [ "$action" == "remove" ]; then
    urequire ipcalc

    [ -n "$1" ] || die "Vous devez spécifier les hôtes à supprimer de ~/.ssh/known_hosts"
    hosts=("$@")
    fix_hosts
    remove_keys
    [ -n "$connect_after_remove" ] && ussh
fi