#!/bin/bash

#
# Name:         ftpmgr
# Version:      1.1
# Description:  Manage various Pure-FTPd settings
#
# (c) 2019, Rapidseedbox, alexey@rapidseedbox.com
#

APP_NAME="FTP Service Manager"

DEBUG="yes"
UI="yes"

export LANG=en_US

# Fix "bad" terminals
case "${TERM}" in
  "xterm" )
      export TERM="linux"
      ;;
esac

SC_VER=$( grep -m 1 -e '^#\s*Version:' "${0}" | awk '{print $NF}' )
SC_DIR=$( dirname "${0}" )
SC_NAME=$( basename "${0}" )


LOGFILE="/var/log/${SC_NAME}.log"

function usage() {
    cat <<EOF
Usage: ${0}
EOF
}

# Quick checks before we allow bad things to happen
if [ -z "${BASH_VERSINFO}" ]; then
    echo "ERROR: You must execute this script with BASH"
    exit 1
fi

_uid=$( id -u )
if [ ${_uid} -ne 0 ] ; then
    echo "Sorry."
    echo "'root' credentials required to run this script !"
    exit 1
fi

if ! which dialog > /dev/null 2>&1 ; then
    echo "'dialog': command not found!"
    echo "Can not continue..."
    exit 1
fi

# Main return code for the script
retcode=0

# Define the dialog exit status codes
DIALOG_OK=0
DIALOG_CANCEL=1
DIALOG_HELP=2
DIALOG_EXTRA=3
DIALOG_ITEM_HELP=4
DIALOG_ESC=255


DIALOG_OPTS="--ascii-lines"

if [ "${1}" = "-h" -o "${1}" = "--help" ] ; then
    usage
    exit 0
fi


# Store menu options selected by the user
INPUT="/tmp/${SC_NAME}.in.$$"

# Storage file for displaying commands output
OUTPUT="/tmp/${SC_NAME}.out.$$"

# Storage file for misc data
DATA="/tmp/${SC_NAME}.data.$$"

# trap and delete temp files
trap "exit_func" SIGHUP SIGINT SIGTERM

# TERM dimensions
max_lines=$( tput lines )
max_lines=$((max_lines-3))
max_columns=$( tput cols )


# -------------------------------------------
# Logging functions
# -------------------------------------------
function ENTRY() {
    local cfn="${FUNCNAME[1]}"
    local tstamp=$( date )
    local msg="> ${cfn} ${FUNCNAME}"
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
}

function RETURN() {
    local cfn="${FUNCNAME[1]}"
    local tstamp=$( date )
    local msg="< ${cfn} ${FUNCNAME}"
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
}

function INFO() {
    local msg="${1}"
    local tstamp=$( date )
    echo -e "[${tstamp}] [INFO]\t${msg}" >> "${LOGFILE}"
}

function ERROR() {
    local msg="${1}"
    local tstamp=$( date )
    echo -e "[${tstamp}] [ERROR]\t${msg}" >> "${LOGFILE}"
}

function DEBUG() {
    local msg="${1}"
    local tstamp=$( date )
    echo -e "[${tstamp}] [DEBUG]\t${msg}" >> "${LOGFILE}"
}


#
# Purpose - echo the backtitle string in form 'Backtitle ... spaces ... [_VER]'
_backtitle(){
    local v_len
    local t_len
    local title

    title="${1}"
    if [ "${DEBUG}" = "yes" ] ; then
        title="[DEBUG] ${title}"
    fi

    t_len=$( echo "${title}" | wc -c )
    v_len=$( echo "${_VER}" | wc -c )

    printf "%s%$((${max_columns}-${t_len}-${v_len}))s%s" "${1}" " " "${SC_VER}"
}



#
# Purpose - display output using msgbox 
#  $1 -> set msgbox height
#  $2 -> set msgbox width
#  $3 -> set msgbox title
#
function display_output() {
    ENTRY
    local h=${1-10}                 # box height default 10
    local w=${2-41}                 # box width default 41
    local t=${3-Output}             # box title 

    if [ "${UI}" = "yes" ] ; then
        dialog ${DIALOG_OPTS} \
               --backtitle "${__backtitle}" \
               --title " ${t} " \
               --clear \
               --msgbox "$(<$OUTPUT)" ${h} ${w}
    fi
    RETURN
}


#
# Perform cleanup and exit
#
function _exit() {
    local _ret
    ENTRY
    [ -z "${1}" ] && _ret="${retcode}" || _ret="${1}"
    # if temp files found, delete them
    [ -f "${OUTPUT}" ] && rm -fv "${OUTPUT}" >> "${LOGFILE}" 2>&1
    [ -f "${INPUT}" ] && rm -fv "${INPUT}" >> "${LOGFILE}" 2>&1
    [ -f "${DATA}" ] && rm -fv "${DATA}" >> "${LOGFILE}" 2>&1
    [ "${UI}" = "yes" ] && clear
    RETURN
    exit ${_ret}
}


#
# Stub for not implemented stuff
#
not_implemented() {
    echo "Not implemented yet!" > "${OUTPUT}"
    display_output
}



#
# Change PureFTPd security mode
# 'echo 1 > /etc/pure-ftpd/conf/TLS && service pure-ftpd restart'
#
# 0 - disable SSL/TLS encryption layer
# 1 - accept both traditional and encrypted sessions
# 2 (default on our boxes) - refuse connections that don't use SSL/TLS security mechanisms
#                            including anonymous sessions.
#
svc_sec_mode() {
    local _ret
    local i
    local q0
    local q1
    local q2
    local n
    local _rc

    for i in 0 1 2 ; do
        eval q${i}="OFF"
    done
    n="$( cat "/etc/pure-ftpd/conf/TLS")"
    if [ -z "${n}" ] ; then
        echo "Error reading Pure-FTPd TLS configuration file!" > "${OUTPUT}"
        display_output
        return 1
    fi
    eval q${n}="ON"
    _ret="$( dialog ${DIALOG_OPTS} \
                  --title "Please choose FTP server security level" \
                  --backtitle "${__backtitle}" \
                  --default-item "${n}" \
                  --radiolist \
                      "Use UP/DOWN keys to navigate and SPACE to select an option.\n\
Press OK to confirm or Cancel to exit without saving the changes." \
                      14 60 3 \
                      "0" "Disable SSL/TLS" "${q0}" \
                      "1" "Accept both normal sessions and TLS ones" "${q1}" \
                      "2" "Force SSL/TLS only" "${q2}" \
                          3>&1 1>&2 2>&3 )"
    _rc="$?"
    if [ ${_rc} -eq 0 ]; then
        echo "${_ret}" > "/etc/pure-ftpd/conf/TLS"
        _rc="$?"
        if [ ${_rc} -ne 0 ] ; then
            echo "Error writing to Pure-FTPd TLS configuration file! RC: [${_rc}]" > "${OUTPUT}"
            display_output
            return ${_rc}
        fi
        service pure-ftpd restart
        _rc="$?"
        if [ ${_rc} -ne 0 ] ; then
            echo "Error restarting Pure-FTPd! RC: [${_rc}]" > "${OUTPUT}"
            display_output
            return ${_rc}
        else
            echo "FTP server reconfigured successfully! RC: [${_rc}]" > "${OUTPUT}"
            display_output
        fi
    else
        return 1
    fi

    return 0
}


#
# Add Virtual FTP users accounts
#
add_ftp_users() {
    local ret
    local users_number

    FTP_USERS_LIMIT=30
    N_EXISTING_USERS="$( pure-pw list 2> /dev/null | wc -l )"

    while true ; do
        rm -vf "${DATA}" >> "${LOGFILE}" 2>&1
        dialog ${DIALOG_OPTS} \
               --title " [ Add Virtual FTP users ] " \
               --backtitle "${__backtitle} -> Add users" \
               --clear \
               --inputbox "Enter the number of new virtual FTP user accounts ($((FTP_USERS_LIMIT-N_EXISTING_USERS)) max)" \
               9 45 1 \
            2> "${DATA}"
        ret=$?
        if [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} -o ${ret} -eq ${DIALOG_EXTRA} ] ; then
            break
        fi
        users_number=$(<"${DATA}")
        if [ -n "$( printf '%s\n' "${users_number}" | sed 's/[0-9]//g' )" ]; then
            echo "${users_number}: is not numeric!" > "${OUTPUT}"
            display_output
            continue
        fi
        if [ ${users_number} -gt $((FTP_USERS_LIMIT-N_EXISTING_USERS)) ]; then
            echo "The maximum number of additional Virtual FTP user accounts is: $((FTP_USERS_LIMIT-N_EXISTING_USERS))" > "${OUTPUT}"
            display_output
            continue
        fi
        dialog ${DIALOG_OPTS} --title " Adding ${users_number} FTP user account(s) " \
             --infobox "Processing...\nPlease wait" \
             6 50
        addmultipleftpusers "${users_number}" > "${OUTPUT}" 2>&1
        display_output 20 60
        break
    done

    return
}


#
# Delete Virtual FTP users accounts
#
del_ftp_users() {
    local users_tbd
    local all_users
    local ret
    local u
    local n

    all_users=()
    for u in $( pure-pw list | awk '{print $1}' | sort -n -k 1.4 ) ; do
        all_users+=("${u}" "" "OFF")
    done
    users_tbd=$( dialog ${DIALOG_OPTS} \
                    --title " [ Add Virtual FTP users ] " \
                    --checklist  "Please mark user accounts to be deleted" \
                    25 30 20 \
                    "${all_users[@]}" \
                        3>&1 1>&2 2>&3 )
    ret=$?
    [ ${ret} -ne 0 ] && return
    n=0
    for u in ${users_tbd} ; do n=$((n+1)) ; done
    if [ ${n} -ne 0 ] ; then
        rm -f "${OUTPUT}"
        for u in ${users_tbd} ; do
            dialog ${DIALOG_OPTS} --title " Deleting ${n} FTP user account(s) " \
                 --infobox "Processing...\nPlease wait" \
                 6 50
            delftpuser "${u}" >> "${OUTPUT}" 2>&1
        done
        display_output 20 60
    fi

    return
}


#
# Manage Virtual FTP users
#
virt_ftp_users() {
    local ret
    local operation
    local default_op

    while true ; do
        dialog ${DIALOG_OPTS} --clear  \
               --backtitle "${__backtitle}" \
               --title " [ Virtual FTP users ] " \
               --default-item "${default_op}" \
               --menu "" \
                10 45 3 \
                "Add FTP user accounts" "" \
                "Change FTP user password" "" \
                "Delete FTP user accounts" "" \
            2>"${INPUT}"
        ret=$?
        if [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} -o ${ret} -eq ${DIALOG_EXTRA} ] ; then
            break
        fi
        operation=$(<"${INPUT}")
        default_op="${operation}"
        case "${operation}" in
          "Add FTP user accounts")
              add_ftp_users
              break
              ;;
          "Change FTP user password")
              changeftppass
              continue
              ;;
          "Delete FTP user accounts")
              del_ftp_users
              continue
              ;;
        esac
    done

    return
}





# --------------------------------------------------------------------- #
#   MAIN SECTION                                                        #
# --------------------------------------------------------------------- #

if ! which pure-pw > /dev/null 2>&1 ; then
    echo "'pure-pw': command not found!"
    echo "Can not continue..."
    _exit 1
fi

while true ; do

    dialog ${DIALOG_OPTS} --clear  --backtitle "$( _backtitle "${APP_NAME}")" \
           --title " [ M A I N - M E N U ] " \
           --extra-button \
           --extra-label "Exit" \
           --default-item "${default_op}" \
           --menu "You can use the UP/DOWN arrow keys, the first \n\
letter of the choice as a hot key, or the \n\
number keys 1-9 to choose an option.\n\
Choose the TASK" \
            0 0 0 \
            "FTP service security mode" "" \
            "Virtual FTP users" "" \
            "Exit" "" \
        2>"${INPUT}"
    ret=$?
    if [ ${ret} -eq ${DIALOG_CANCEL} -o ${ret} -eq ${DIALOG_ESC} -o ${ret} -eq ${DIALOG_EXTRA} ] ; then
        break
    fi

    operation=$(<"${INPUT}")
    default_op="${operation}"
    __backtitle="$( _backtitle "${APP_NAME} -> ${operation}")"

    case "${operation}" in
      "FTP service security mode")
          svc_sec_mode
          ;;
      "Virtual FTP users")
          virt_ftp_users
          ;;
      "Exit")
          break
          ;;
    esac

done

_exit 0
