# -*- mode: shell-script -*-


ssl_get_plugin_fun() {
    local cfg="$1"

    type="$(echo "$cfg" | shyaml -y get-type 2>/dev/null)" || return 1
    case "$type" in
        bool)
            err "Option ${WHITE}ssl${NORMAL} value '$cfg' is not supported."
            return 1
            ;;
        str)
            keys=("$cfg")
            ;;
        struct)
            keys=( $(echo "$cfg" | shyaml keys 2>/dev/null) )
            ;;
        *)
            err "Invalid ${WHITE}ssl${NORMAL} value type '$type': please provide a string or a struct."
            return 1
            ;;
    esac

    for key in "${keys[@]}"; do
        target_relation="cert-provider"
        fun="ssl_plugin_${target_relation}"
        while read-0 relation_name target_service relation_config tech_dep; do
            [ "$relation_name" == "${target_relation}" ] || continue
            [ "$target_service" == "$key" ] || continue
            verb "Corresponding plugin ${DARKGREEN}found${NORMAL}" \
                 "in ${DARKBLUE}$relation_name${NORMAL}/${DARKYELLOW}$key${NORMAL}"
            ssl_cfg=$(printf "%s" "$cfg" | shyaml get-value "$key" 2>/dev/null) || true
            merged_config=$(merge_yaml_str "$relation_config" "$ssl_cfg") || return 1
            printf "%s\0" "$fun" "$key" "$merged_config"
            return 0
        done < <(get_service_relations "$SERVICE_NAME") || return 1
        case "$key" in
            cert|ca-cert|key)
                :
                ;;
            *)
                err "Invalid key '$key' in ${WHITE}ssl${NORMAL}:" \
                    "no corresponding services declared in ${DARKBLUE}${target_relation}$NORMAL"
                return 1
                ;;
        esac
    done
}


ssl_fallback_vars() {
    local cfg="$1" DOMAIN="$2" cert key ca_cert

    base_dir=/etc/ssl
    print "%s\0" \
          "$base_dir"/certs/"${DOMAIN}".pem \
          "$base_dir"/private/"${DOMAIN}".key \
          "$base_dir"/certs/"${DOMAIN}"-ca.pem

    print "%s\0" "$base_dir"/cert.pem "$base_dir"/privkey.pem "$base_dir"/chain.pem
}


ssl_fallback_prepare() {
    local cfg="$1" service="$2" l_cert l_key l_ca_cert

    shift 2
    read-0 l_cert l_key l_ca_cert < <(ssl_fallback_vars "$cfg" "$@")

    dst="$CONFIGSTORE/$BASE_SERVICE_NAME"
    volumes=""
    for label in cert key ca-cert; do
        if content=$(echo "$cfg" | shyaml get-value "$label" 2>/dev/null); then
            location="$(eval echo "\$l_${label//-/_}")"
            echo "$content" | file_put "$dst$location"
            config_hash=$(printf "%s\0" "$config_hash" "$label" "$content" | md5_compat)
            volumes="$volumes
    - $dst$location:$location:ro"
        fi
    done

    if [ "$volumes" ]; then
        init-config-add "\
$MASTER_TARGET_SERVICE_NAME:
  volumes:
$volumes
"
    fi

}

ssl_plugin_cert-provider_vars() {
    local cfg="$1" DOMAIN="$2"

    base_dir="/etc/letsencrypt/live/$DOMAIN"

    print "%s\0" "$base_dir"/cert.pem "$base_dir"/privkey.pem "$base_dir"/chain.pem
}

ssl_plugin_cert-provider_prepare() {
    local cfg="$1" service="$2"  options
    shift 2

    options=$(yaml_key_val_str "options" "$cfg") || return 1
    service_config=$(yaml_key_val_str "$service" "$options")
    compose --debug --add-compose-content "$service_config" run --rm --service-ports "$service" \
            crt create "$@" || {
        err "Failed to launch letsencrypt for certificate creation."
        return 1
    }
    init-config-add "\
$MASTER_TARGET_SERVICE_NAME:
  volumes:
    - $DATASTORE/$service/etc/letsencrypt:/etc/letsencrypt:ro
" || return 1
}


make_pasv_options() {
    local pasv="$1"
    if [[ "${pasv,,}" =~ ^(false|no|n|0|disable|disabled)$ ]]; then
        echo "pasv_enable=no"
        return 0
    fi

    ports="$(printf "%s" "$pasv" | shyaml get-value -q "ports")"
    if [ "$ports" ]; then
        if [[ "$ports" =~ ^[0-9]+-[0-9]+$ ]]; then
            port_min=${ports##*-}
            port_max=${ports%%-*}
            if [ "$port_min" -gt "$port_max" ]; then
                die "Invalid value for ${WHITE}ports${NORMAL}: first port in range has to be lesser than second."
            fi
        else
            die "Invalid value for ${WHITE}ports${NORMAL}: please specify a port range (ie: '123-345')."
        fi
    fi

    port_min=${port_min:-21100}
    port_max=${port_max:-21110}

    host_address="$(printf "%s" "$pasv" | shyaml get-value -q "address")"
    if [ -z "$host_address" ]; then
        if [[ "$SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then
            host_address="$SERVICE_NAME"
        fi
    fi

    if [ -z "$host_address" ]; then
        echo "## Could not find appropriate host return address"
        echo "pasv_enable=no"
        return 0
    fi

    if ! str_is_ipv4 "$host_address"; then
        host_address="$(get_ip_from_hostname "$host_address")" || {
            err "Can't resolve given hostname '$host_address'"
            return 1
        }
    else
        err "You should NOT provide a direct IP for ${WHITE}pasv.address${NORMAL}," \
            "try a resolvable address."
        return 1
    fi
    cat <<EOF
pasv_address=$host_address
pasv_enable=yes
pasv_min_port=$port_min
pasv_max_port=$port_max
EOF

    init-config-add "
  $SERVICE_NAME:
    ports:
    - \"$port_min-$port_max:$port_min-$port_max\"
"

}


make_ssl_options() {
    local ssl="$1"
    if [ -z "$ssl" ]; then
        target_relation="cert-provider"
        ssl=no
        while read-0 rn ts rc td; do
            [ "$rn" == "${target_relation}" ] || continue
            info "A ${DARKBLUE}$target_relation${NORMAL} ${DARKYELLOW}$ts${NORMAL} declared as 'ssl' value"
            ssl="$ts"
            break
        done < <(get_service_relations "$SERVICE_NAME")
    fi
    if [[ "${ssl,,}" =~ false|no|n|0|disable|disabled ]]; then
        echo "ssl_enable=no"
        return 0
    fi

    cat <<EOF
##
## SSL conf
##

ssl_enable=yes
ssl_tlsv1=yes
ssl_sslv2=no
ssl_sslv3=no
ssl_ciphers=HIGH
implicit_ssl=no

EOF
    read-0 SSL_PLUGIN_FUN SSL_CFG_VALUE SSL_CFG_OPTIONS < <(ssl_get_plugin_fun "$ssl") || return 1
    read-0 cert key ca_cert < <("$SSL_PLUGIN_FUN"_vars "$SSL_CFG_OPTIONS" "$SSL_CFG_VALUE") || return 1
    "$SSL_PLUGIN_FUN"_prepare "$SSL_CFG_OPTIONS" "$SSL_CFG_VALUE"  || return 1

    cat <<EOF

rsa_cert_file=$cert
rsa_private_key_file=$key
ca_certs_file=$ca_cert

EOF

}


make_build_script() {
    local users_def="$1" allow_writeable_chroot="$2"

    if [ -z "$users_def" ]; then
        return 0
    fi

    fixed_groups_code=""
    groups_code=""

    declare -A created_groups
    while read-0 user user_def; do
        first_group=
        groups=()
        first=1
        while read-0 group; do
            [ "${created_groups[$group]}" ] && continue
            if [[ "$group" == *":"* ]]; then
                gid=${group##*:}
                group=${group%%:*}
                fixed_groups_code+="addgroup -g \"$gid\" \"$group\""$'\n'
            else
                groups_code+="addgroup \"$group\""$'\n'
            fi
            created_groups[$group]=1
            echo "X" >&2
            if [ "$first" ]; then
                first_group="$group"
                first=
            else
                remaining_groups+=("$group")
            fi
            groups+=("$group")
        done < <(echo "$user_def" | shyaml get-values-0 groups 2>/dev/null)
        password=$(echo "$user_def" | shyaml get-value password 2>/dev/null) ||
            password=$(gen_password 14)
        uid=$(echo "$user_def" | shyaml get-value uid 2>/dev/null)

        useradd_options=(
            "-D"                ## don't assign a password
            "-s" "/bin/false"   ## default shell
        )
        if [ "$uid" ]; then
            useradd_options+=("-u" "$uid")    ## force uid
        fi
        if [ "$first_group" ]; then
            useradd_options+=("-G" "$first_group")  ## force main group
        fi

        code+="adduser ${useradd_options[*]} \"$user\""$'\n'
        code+="mkdir -p \"/home/$user\""$'\n'
        if [ "$allow_writeable_chroot" ]; then
            code+="chown $user \"/home/$user\""$'\n'  ## sanitize
        else
            code+="chown root:root \"/home/$user\""$'\n'  ## sanitize
        fi
        code+="chmod 755 \"/home/$user\""$'\n'        ## sanitize
        code+="echo '$user:$password' | chpasswd"$'\n'
        for group in "${remaining_groups[@]}"; do
            code+="adduser \"$user\" \"$group\""$'\n'
        done
    done < <(echo "$users_def" | shyaml key-values-0)
    echo -n "$fixed_groups_code"
    echo -n "$groups_code"
    echo -n "$code"
}