##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Outils de haut niveau pour gérer apache et sa configuration
##@cooked nocomments
##@require base
##@require sysinfos
##@require apache
uprovide apache.tools
urequire base sysinfos apache

function __apache_resolvcert() {
    [ -n "$__rc_dir" ] || __rc_dir="$(dirname "$__rc_conf")"
    eval "$(
        source "$__rc_conf"
        set_var_cmd __rc_cert "$cert"
        set_var_cmd __rc_key "$key"
        set_var_cmd __rc_ca "$ca"
    )"
    [ -n "$__rc_cert" ] && __rc_cert="$(abspath "$__rc_cert" "$__rc_dir")"
    [ -n "$__rc_key" ] && __rc_key="$(abspath "$__rc_key" "$__rc_dir")"
    [ -n "$__rc_ca" ] && __rc_ca="$(abspath "$__rc_ca" "$__rc_dir")"
}

function __apache_checkvars() {
    if [ -n "$__rc_cert" -a -z "$__rc_key" ]; then
        local __rc_name __rc_ext
        splitname "$__rc_cert" __rc_name __rc_ext
        if [ "$__rc_ext" == "crt" -o "$__rc_ext" == "pem" ]; then
            __rc_key="$__rc_name.key"
            enote "La clé privée n'a pas été spécifiée. La valeur $(ppath "$__rc_key") sera utilisée"
        else
            eerror "Impossible de trouver la clé privée correspondant au certificat $(ppath "$__rc_cert")"
            return 1
        fi
    fi
    if [ -z "$__rc_cert" -a -z "$__rc_ca" ]; then
        eerror "Vous devez spécifier le certificat à installer"
        return 1
    elif [ -z "$__rc_cert" ]; then
        eattention "Seul le certificat autorité a été spécifié."
    elif [ -z "$__rc_ca" ]; then
        ewarn "Aucun certificat autorité n'a pas été spécifié. Cela ne peut marcher que si le certificat est autosigné"
    fi

    local i
    for i in "$__rc_cert" "$__rc_key" "$__rc_ca"; do
        [ -n "$i" ] || continue
        [ -f "$i" ] || {
            eerror "$i: Fichier introuvable"
            return 1
        }
    done
}

function apache_resolvecert() {
    # Calculer l'emplacement des certificats correspondant aux arguments $1 et
    # $2 (qui correspondent aux options --conf et --dir de apache_addcert()),
    # puis initialiser les variables $3(=cert), $4(=key) et $5(=ca)
    local __rc_conf="$1" __rc_dir="$2"
    local __rc_cert __rc_key __rc_ca

    __apache_resolvcert
    __apache_checkvars || return 1
    set_var "${3:-cert}" "$__rc_cert"
    set_var "${4:-key}" "$__rc_key"
    set_var "${5:-ca}" "$__rc_ca"
}

function apache_addcert() {
    function __apache_addcert_display_help() {
        uecho "apache_addcert: Installer un certificat sur le serveur

USAGE
    apache_addcert [options] [cert.pem [cert.key [ca.pem]]]

OPTIONS
    --conf certsconf
    --dir certsdir
        Spécifier un fichier de configuration et un répertoire depuis lesquels
        prendre les informations sur les certificats à utiliser.
        Le fichier de configuration doit définir les variables cert, key et ca
        avec les noms des fichiers contenant respectivement le certificat, la
        clé privée, et les certificats autorités, exprimés relativement au
        répertoire certsdir.
        Si ces options ne sont pas spécifiées, les fichiers doivent être donnés
        sur la ligne de commande.

    --out-cert cert
    --out-key key
    --out-ca ca
        Au lieu d'installer les certificats, placer les chemins vers les
        fichiers correspondant dans les variables spécifiées"
    }

    eval "$(utools_local)"
    local action=install
    local certsconf certsdir cert key ca
    local __out_cert __out_key __out_ca
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with __apache_addcert_display_help' \
        -C:,--conf: certsconf= \
        -d:,--dir: certsdir= \
        --out-cert: '$set@ __out_cert; action=dump' \
        --out-key: '$set@ __out_key; action=dump' \
        --out-ca: '$set@ __out_ca; action=dump' \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    local __rc_conf __rc_dir
    local __rc_cert __rc_key __rc_ca
    if [ -n "$certsconf" ]; then
        __rc_conf="$certsconf"
        __rc_dir="$certsdir"
        __apache_resolvconf
        __apache_checkvars || return 1
    else
        __rc_cert="$1"
        __rc_key="$2"
        __rc_ca="$3"
        __apache_checkvars || return 1
    fi
    cert="$__rc_cert"
    key="$__rc_key"
    ca="$__rc_ca"

    if [ -n "$cert" ]; then
        estepi "Certificat: $(ppath "$cert")"
        estepi "Clé privée: $(ppath "$key")"
    fi
    [ -n "$ca" ] && estepi "CAutorités: $(ppath "$ca")"
    ask_yesno "Voulez-vous continuer?" O || return 1
    urequire install

    etitle "Installation des certificats"
    certsdir="$(get_APACHESSLCERTSDIR_prefix)"
    keysdir="$(get_APACHESSLKEYSDIR_prefix)"
    if [ ! -d "$certsdir" ]; then
        mkdir -p "$certsdir" || return 1
        chmod 755 "$certsdir" || return 1
    fi
    if [ ! -d "$keysdir" ]; then
        mkdir -p "$keysdir" || return 1
        chmod 710 "$keysdir" || return 1
    fi
    if [ -n "$cert" ]; then
        copy_replace "$cert" "$certsdir" || return 1
        chmod 644 "$certsdir/$(basename "$cert")" || return 1
        copy_replace "$key" "$keysdir" || return 1
        chmod 640 "$keysdir/$(basename "$key")" || return 1
    fi
    if [ -n "$ca" ]; then
        copy_replace "$ca" "$certsdir" || return 1
        chmod 644 "$certsdir/$(basename "$ca")" || return 1
    fi
    eend

    return 0
}

function __apache_autoconf_fillcopy() {
    # copier le fichier $1 vers le fichier $2. Si le fichier $1 contient l'une
    # des variables du tableau $FILLVARS, corriger d'abord le fichier avec le
    # script sed $FILLSCRIPT. Le fichier temporaire $FILLTEMP est utilisé pour
    # le remplacement des valeurs. $3 contient le cas échéant des commandes sed
    # supplémentaires
    local src="$1" dest="$2" sedscript="$3" perms="${4:-go+rX}"

    local var found
    for var in "${FILLVARS[@]}"; do
        if quietgrep "@@${var}@@" "$1"; then
            found=1
            break
        fi
    done

    if [ "$found" ]; then
        sed "$FILLSCRIPT
$sedscript" <"$src" >"$FILLTEMP"
        src="$FILLTEMP"
    fi

    copy_update "$src" "$dest" "$perms"
}

function apache_autoconf() {
    eval "$(utools_local)"
    local autoconfdir certsdir confdir modulesdir sitesdir cgibindir wwwdir certsconfdir rrdir
    local restart=1
    parse_opts "${PRETTYOPTS[@]}" \
        --confdir: confdir= \
        --modulesdir: modulesdir= \
        --sitesdir: sitesdir= \
        --cgibindir: cgibindir= \
        --wwwdir: wwwdir= \
        --certsconfdir: certsconfdir= \
        --rrdir: rrdir= \
        --no-restart restart= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    if ! check_sysinfos -s linux -d debian; then
        eerror "apache_autoconf n'est supporté que sur Debian linux"
        return 1
    fi
    urequire install
    compute_apache_prefixes

    # Configuration
    autoconfdir="$1"; shift
    [ -n "$autoconfdir" ] || {
        eerror "Vous devez spécifier le répertoire de base de la configuration apache"
        return 1
    }
    certsdir="$1"; shift
    if [ -z "$confdir" -o -z "$modulesdir" -o -z "$sitesdir" \
        -o -z "$cgibindir" -o -z "$wwwdir" -o -z "$certsconfdir" \
        -o -z "$rrdir" ]; then
        [ -d "$autoconfdir" ] || {
            eerror "$autoconfdir: répertoire invalide"
            return 1
        }
    fi
    [ -n "$confdir" ] || confdir="$autoconfdir"
    [ -n "$modulesdir" ] || modulesdir="$autoconfdir/modules"
    [ -n "$sitesdir" ] || sitesdir="$autoconfdir/sites"
    [ -n "$cgibindir" ] || cgibindir="$autoconfdir/cgi-bin"
    [ -n "$wwwdir" ] || wwwdir="$autoconfdir/www"
    [ -n "$certsconfdir" ] || certsconfdir="$autoconfdir/certsconf"
    [ -n "$rrdir" ] || rrdir="$autoconfdir/RewriteRules"
    if [ -d "$certsconfdir" ]; then
        if [ -z "$certsdir" ]; then
            eerror "CERTSDIR est requis si --certsconfdir est spécifié"
            return 1
        elif [ ! -d "$certsdir" ]; then
            eerror "$certsdir: répertoire invalide"
            return 1
        fi
    fi

    # Faire un script sed pour remplacer les variables spécifiées par leur
    # valeur dans les fichiers
    local -a FILLVARS; local FILLSCRIPT FILLTEMP
    local var name value first=1
    for var in "$@"; do
        splitvar "$var" name value
        array_addu FILLVARS "$name"
        [ -n "$first" ] || FILLSCRIPT="$FILLSCRIPT"$'\n'
        FILLSCRIPT="${FILLSCRIPT}s/@@${name}@@/$(qseds "${value}")/g"
        first=
    done
    # Il faut un fichier temporaire pour les remplacement de fichiers
    ac_set_tmpfile FILLTEMP

    # Copie des certificats
    local modified conf
    if [ -d "$certsconfdir" ]; then
        local -a certsconfs
        local certsconf cert key ca
        array_addu FILLVARS cert
        array_addu FILLVARS key
        array_addu FILLVARS ca

        etitle "Installation des certificats"
        array_lsfiles certsconfs "$certsconfdir" "*.conf"
        for certsconf in "${certsconfs[@]}"; do
            apache_resolvecert "$certsconf" "$certsdir" cert key ca || return 1
            apache_addcert -y "$cert" "$key" "$ca"
            modified=1
        done
        eend
    fi

    # Configuration des modules
    if [ -d "$modulesdir" ]; then
        local -a confs
        local conf
        etitle "Installation des configurations des modules"
        array_from_lines confs "$(list_files "$modulesdir" "*.conf")"
        for conf in "${confs[@]}"; do
            estep "$conf"
            __apache_autoconf_fillcopy \
                "$modulesdir/$conf" \
                "$APACHECONFDIR/mods-available/$conf" && modified=1
        done
        eend
    fi

    # Règles de réécriture
    if [ -d "$rrdir" ]; then
        local -a confs
        local conf
        etitle "Installation des règles de réécriture"
        array_from_lines confs "$(list_files "$rrdir" "RewriteRules*.conf")"
        for conf in "${confs[@]}"; do
            estep "$conf"
            __apache_autoconf_fillcopy \
                "$rrdir/$conf" \
                "$APACHECONFDIR/$conf" && modified=1
        done
        eend
    fi

    # Sites
    local -a enablesites disablesites
    if [ -d "$sitesdir" ]; then
        local -a confs
        local conf confname destconf certsconf
        etitle "Installation des sites"
        array_lsfiles confs "$sitesdir" "*.conf"
        for conf in "${confs[@]}"; do
            confname="$(basename "$conf")"
            destconf="$confname"
            certsconf=
            if [ "${destconf%.ssl.conf}" != "$destconf" ]; then
                if [ -d "$certsconfdir" ]; then
                    certsconf="${destconf%.ssl.conf}-certs.conf"
                else
                    ewarn "$conf: fichier ignoré parce que --certsconfdir n'a pas été spécifié"
                fi
            fi
            case "$destconf" in
            default.conf) destconf=default;;
            default.ssl.conf) destconf=default-ssl;;
            esac

            if [ -n "$certsconf" ]; then
                certsconf="$certsconfdir/$certsconf"
                if [ -f "$certsconf" ]; then
                    apache_resolvecert "$certsconf" "$certsdir" cert key ca || return 1
                    __apache_autoconf_fillcopy \
                        "$conf" \
                        "$APACHEAVSITESDIR/$destconf" "\
s#@@cert@@#$APACHESSLCERTSDIR/$(basename "$cert")#g
s#@@key@@#$APACHESSLKEYSDIR/$(basename "$key")#g
s#@@ca@@#$APACHESSLCERTSDIR/$(basename "$ca")#g
"
                else
                    eerror "$(ppath "$certsconf"): fichier introuvable. Il a été ignoré"
                fi
            else
                __apache_autoconf_fillcopy \
                    "$conf" \
                    "$APACHEAVSITESDIR/$destconf"
            fi
            enablesites=("${enablesites[@]}" "$destconf")
            modified=1
        done
        eend
    fi

    # Fichiers de configuration
    if [ -d "$confdir" ]; then
        local -a confs
        local conf
        etitle "Configuration de base"
        array_from_lines confs "$(list_files "$confdir")"
        for conf in "${confs[@]}"; do
            case "$conf" in
            modules.conf|sites.conf) continue;;
            esac
            estep "$conf"
            __apache_autoconf_fillcopy \
                "$confdir/$conf" \
                "$APACHECONFDIR/$conf" && modified=1
        done
        if [ -f "$confdir/modules.conf" ]; then
            local -a modules
            local module
            array_from_lines modules "$(<"$confdir/modules.conf" filter_conf)"
            for module in "${modules[@]}"; do
                if [ "${module#-}" != "$module" ]; then
                    module="${module#-}"
                    estep "Désactivation du module $module"
                    a2dismod "$module" && modified=1
                else
                    module="${module#+}"
                    estep "Activation du module $module"
                    a2enmod "$module" && modified=1
                fi
            done
        fi
        if [ -f "$confdir/sites.conf" ]; then
            local -a sitesconfs; local sitesconf
            array_from_lines sitesconfs "$(<"$confdir/sites.conf" filter_conf)"
            if [ ${#sitesconfs[*]} -gt 0 ]; then
                # si une configuration existe, ignorer la configuration
                # automatique
                enablesites=()
                disablesites=()
                for sitesconf in "${sitesconfs[@]}"; do
                    if [ "${sitesconf#+}" != "$sitesconf" ]; then
                        array_del disablesites "${sitesconf#+}"
                        array_add enablesites "${sitesconf#+}"
                    elif [ "${sitesconf#-}" != "$sitesconf" ]; then
                        array_del enablesites "${sitesconf#-}"
                        array_add disablesites "${sitesconf#-}"
                    else
                        array_del disablesites "$sitesconf"
                        array_add enablesites "$sitesconf"
                    fi
                done
            fi
        fi
        eend
    fi

    # Scripts CGI
    if [ -d "$cgibindir" ]; then
        etitle "Installation des scripts CGI"
        cpdirnovcs "$cgibindir" "$CGIBINDIR"
        eend
    fi

    # Contenu web
    if [ -d "$wwwdir" ]; then
        etitle "Installation des fichiers du serveur web"
        cpdirnovcs "$wwwdir" "$HTDOCSDIR"
        eend
    fi

    # Nettoyer le fichier temporaire
    ac_clean "$FILLTEMP"

    if [ -n "${enablesites[*]}" -o -n "${disablesites[*]}" ]; then
        etitle "(dés)Activation des sites"
        local site
        for site in "${enablesites[@]}"; do
            estep "Activation du site $site"
            a2ensite "$site"
        done
        for site in "${disablesites[@]}"; do
            estep "Désactivation du site $site"
            a2dissite "$site"
        done
        eend
    fi

    if [ -n "$modified" -a -n "$restart" ]; then
        estep "Redémarrage d'apache"
        "$APACHECTL" restart
    fi
}