#!/bin/bash

## Init is run on host
## For now it is run every time the script is launched, but
## it should be launched only once after build.

## Accessible variables are:
## - SERVICE_NAME        Name of current service
## - DOCKER_BASE_IMAGE   Base image from which this service might be built if any
## - SERVICE_DATASTORE           Location on host of the DATASTORE of this service
## - SERVICE_CONFIGSTORE         Location on host of the CONFIGSTORE of this service


. lib/common || exit 1


CONFIGFILE="$SERVICE_CONFIGSTORE/etc/vsftpd/vsftpd.conf"


VSFTP_OPTIONS=(

    ##
    ## Feature not yet supported (and removed for simplicity's sake, and
    ##   because implication were not though through yet), or in dire need
    ##   of YAML simplification in the options.
    ##

    ## No support for anon user for now (for simplification purpose)
    #"allow_anon_ssl:bool" "anon_mkdir_write_enable:bool" "anon_other_write_enable:bool"
    #"anon_upload_enable:bool" "anon_world_readable_only:bool" "anonymous_enable:bool"
    #"force_anon_data_ssl:bool" "force_anon_logins_ssl:bool"
    #"no_anon_password:bool"
    #"anon_root:string" "chown_username:string" "ftp_username:string"
    #"anon_max_rate:numeric" "anon_umask:numeric"

    ## No support for chroot list
    #"chroot_list_enable:bool"

    #"connect_from_port_20:bool"  ## port not propagated yet to host

    ## No support for that (requires more YAML love to list emails)
    #"secure_email_list_enable:bool"
    #"user_config_dir:string"

    ## To simplify usage
    #"userlist_deny:bool"
    #"userlist_enable:bool"


    ##
    ## Not sure if we need that
    ##

    #"deny_email_enable:bool"
    #"guest_enable:bool" "setproctitle_enable:bool"
    #"session_support:bool" "tcp_wrappers:bool"
    #"banned_email_file:string" "guest_username:string"
    #"pam_service_name:string" "ftp_data_port:numeric"


    ##
    ## Set by design
    ##

    ## Alway background by design (as a dockerized process)
    # "background:bool" "listen:bool"

    ## Using local user functionality
    #"chroot_local_user:bool"
    #"local_enable:bool"
    #"passwd_chroot_enable:bool"
    #"virtual_use_local_privs:bool"
    #"chroot_list_file:string"
    #"email_password_file:string"
    #"user_sub_token:string"
    #"userlist_file:string"

    ## Logging options are simplified
    #"dual_log_enable:bool" "log_ftp_protocol:bool" "no_log_lock:bool"
    #"syslog_enable:bool"
    #"xferlog_enable:bool" "xferlog_std_format:bool"
    #"vsftpd_log_file:string" "xferlog_file:string"

    ## Managed by ``ssl`` options
    #"implicit_ssl:bool"
    #"ssl_enable:bool" "ssl_request_cert:bool" "ssl_sslv2:bool" "ssl_sslv3:bool"
    #"ssl_tlsv1:bool"
    #"ca_certs_file:string"
    #"dsa_cert_file:string"
    #"dsa_private_key_file:string"
    #"rsa_cert_file:string"
    #"rsa_private_key_file:string"
    #"ssl_ciphers:string"

    ## Obscure option not really needed
    #"one_process_model:bool"

    ## Managed by ``pasv`` options
    #"pasv_addr_resolve:bool" "pasv_enable:bool"
    #"pasv_promiscuous:bool"
    #"pasv_max_port:numeric" "pasv_min_port:numeric"
    #"pasv_address:string"

    ## We use root user in docker to launch vsftp
    #"run_as_launching_user:bool"

    ## We don't need to change this in docker
    #"listen_port:numeric"
    #"listen_address:string" "listen_address6:string"

    ## Hum, should not be of charm users concern
    #"nopriv_user:string"
    #"secure_chroot_dir:string"


    ##
    ## Permitted
    ##

    ## commands
    "chmod_enable:bool" "dirlist_enable:bool" "download_enable:bool"
    "port_enable:bool" "ls_recurse_enable:bool" "write_enable:bool"

    ## others
    "dirmessage_enable:bool" "ascii_download_enable:bool"
    "ascii_upload_enable:bool" "async_abor_enable:bool"
    "check_shell:bool" "chown_uploads:bool" "debug_ssl:bool"
    "delete_failed_uploads:bool" "force_dot_files:bool"
    "hide_ids:bool" "listen_ipv6:bool" "lock_upload_files:bool"
    "mdtm_write:bool" "port_promiscuous:bool" "require_cert:bool"
    "require_ssl_reuse:bool" "strict_ssl_read_eof:bool"
    "strict_ssl_write_shutdown:bool" "text_userdb_names:bool"
    "tilde_user_enable:bool" "use_localtime:bool" "use_sendfile:bool"
    "validate_cert:bool"

    ## timeouts
    "accept_timeout:numeric" "connect_timeout:numeric" "data_connection_timeout:numeric"
    "idle_session_timeout:numeric"

    ## delays
    "delay_failed_login:numeric" "delay_successful_login:numeric"

    ## modes
    "chown_upload_mode:numeric" "file_open_mode:numeric"

    "local_max_rate:numeric" "local_umask:numeric"

    "max_clients:numeric" "max_login_fails:numeric" "max_per_ip:numeric"
    "trans_chunk_size:numeric"

    "banner_file:string"

    "cmds_allowed:string" "cmds_denied:string"

    "deny_file:string" "ftpd_banner:string" "hide_file:string"
    "local_root:string" "message_file:string"

    "force_local_data_ssl:bool" "force_local_logins_ssl:bool"

    "allow_writeable_chroot:bool"
)

VSFTP_OPTIONS_CONCAT=" ${VSFTP_OPTIONS[*]} "

get_ips_from_hostname() {
    local hostname="$1"
    getent hosts "$hostname" | awk '{ print $1 }'
}


get_ip_from_hostname() {
    local hostname="$1"
    get_ips_from_hostname "$hostname" | head -n 1
}


mkdir -p "$(dirname "$CONFIGFILE")"

service_def=$(get_compose_service_def "$SERVICE_NAME") || return 1
domain=
users=
pasv=
ssl=
while read-0 key val; do
    key_option=${key//-/_}
    case "$VSFTP_OPTIONS_CONCAT" in
        *" ${key_option}:bool "*)
            case "${val,,}" in
                true|ok|yes|y)
                    val=yes
                    ;;
                false|ko|nok|no|n)
                    val=no
                    ;;
                *)
                    die "Invalid value for ${WHITE}$key$NORMAL, please use a boolean value."
                    ;;
            esac
            ;;
        *" ${key_option}:numeric "*)
            if ! is_int "$val"; then
                die "Invalid value for ${WHITE}$key$NORMAL, please use numeric value."
            fi
            ;;
        *" ${key_option}:string "*)
            :
            ;;
        *)
            case "${key//_/-}" in
                users) users="$val";;
                pasv)  pasv="$val";;
                ssl) ssl="$val";;
		domain) domain="$val";;
                *) die "Unknown option ${WHITE}$key$NORMAL.";;
            esac
            continue
            ;;
    esac
    case "$key_option" in
        allow_writeable_chroot)
            allow_writeable_chroot=1
            ;;
    esac
    printf "%s=%s\n" "$key_option" "$val"
done < <(printf "%s" "$service_def" | shyaml key-values-0 options) > "$CONFIGFILE"

if [ -z "$domain" ]; then
    if [[ "$SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then
        domain="$SERVICE_NAME"
    fi
fi

make_pasv_options "$pasv" "$domain" >> "$CONFIGFILE" || exit 1
make_ssl_options "$ssl" "$domain" >> "$CONFIGFILE" || exit 1


## Logs

cat <<EOF >> "$CONFIGFILE"
##
## Logs
##

syslog_enable=no
dual_log_enable=no
vsftpd_log_file=/var/log/vsftp/vsftp.log
xferlog_enable=yes
xferlog_std_format=no
EOF

## Dockerisation means those

cat <<EOF >> "$CONFIGFILE"

##
## Dockerisation
##

background=no
## we don't use that feature
passwd_chroot_enable=no

##
## Use local system passwd account
##

local_enable=yes
chroot_local_user=yes

## Seem to be required to avoid 500 OOOps child died
seccomp_sandbox=no

EOF



##
## Users
##

## Note: this creates a file that will be interpreted in the
## entrypoint of the docker image. It also creates group with same gid
## on host to be able to easily share files.

build_script="$(make_build_script "$users" "$allow_writeable_chroot")" || exit 1
echo "build-script:" >&2
echo "$build_script" | prefix "  $GRAY|$NORMAL " >&2
docker_update "$SERVICE_NAME" "$build_script" -v "$SERVICE_DATASTORE/home":"/home" || exit 1

config_hash=$(
    {
        printf "%s\0" "$build_script" "$(cat "$CONFIGFILE")"
    } | md5_compat) || exit 1

init-config-add "
$MASTER_BASE_SERVICE_NAME:
  labels:
    - compose.config_hash=$config_hash
"