#!/bin/bash

################################################################################
# Copyright 2023-2025 by NI SP Software GmbH, All rights reserved.
# Copyright 1999-2023 by Nice, srl., All rights reserved.
#
# This software includes confidential and proprietary information
# of NI SP Software GmbH ("Confidential Information").
# You shall not disclose such Confidential Information
# and shall use it only in accordance with the terms of
# the license agreement you entered into with NI SP Software.
################################################################################

#-------------------------------------------------------------------------------
# Avoid recursive loops
#-------------------------------------------------------------------------------
[ -n "$FM_DO_NOT_RECURSE" ] && exit 0

FM_DO_NOT_RECURSE=true
export FM_DO_NOT_RECURSE


EF_PLUGIN_NAME="EnginFrame File Manager Plug-in"
export EF_PLUGIN_NAME

EF_PLUGIN_FILE="FileManager"
export EF_PLUGIN_FILE

EF_PLUGIN_ROOT="${EF_ROOT}/plugins/fm"
export EF_PLUGIN_ROOT


#-------------------------------------------------------------------------------
# Config file
#-------------------------------------------------------------------------------

. "${EF_ROOT}/plugins/ef/lib/utils"
ef_source_conf fm "fm.conf"

#-------------------------------------------------------------------------------
# Plugin specific functions
#-------------------------------------------------------------------------------


# Report a fatal system error
fatal() {
    fm_ef_error -fatal -message "$1" -secondary "$2" -exit 1
}


# Report a user error
error() {
    fm_ef_error -error -message "$1" -secondary "$2" -exit 1
}


fm_ef_error() {
    _fm_ef_error_level='Error'
    _fm_ef_error_code='1'
    _fm_ef_error_message=''
    _fm_ef_error_secondary_message=''

    while [ -n "$1" ]; do
        case "$1" in
            -fatal)
                _fm_ef_error_level='Fatal Error'
                ;;
            -error)
                _fm_ef_error_level='Error'
                ;;
            -msg|-message)
                _fm_ef_error_message="$2"
                shift
                ;;
            -secondary)
                _fm_ef_error_secondary_message="$2"
                shift
                ;;
            -exit)
                _fm_ef_error_code="$2"
                shift
                ;;
            *)
                ;;
        esac
        shift
    done

    . "${EF_ROOT}/plugins/ef/lib/xmlfuncs"
    _fm_ef_error_message=$(ef_xml_escape -i "${_fm_ef_error_message}")
    _fm_ef_plugin_name=$(ef_xml_escape -i "${EF_PLUGIN_NAME}")
    _fm_ef_command=$(ef_xml_escape -i "${0//${EF_ROOT}/EF_ROOT}")

    echo "<ef:error xmlns:ef=\"${EF_XMLNS}\">"
    echo "  <ef:title>${_fm_ef_plugin_name} ${_fm_ef_error_level}</ef:title>"
    echo "  <ef:message>${_fm_ef_error_message}</ef:message>"
    if [ -n "${_fm_ef_error_secondary_message}" ]; then
        _fm_ef_error_secondary_message=$(ef_xml_escape -i "${_fm_ef_error_secondary_message}")
        echo "  <ef:secondary-message>${_fm_ef_error_secondary_message}</ef:secondary-message>"
    fi
    echo '  <ef:apply-acl select="admin-only">'
    echo "    <ef:command>${_fm_ef_command}</ef:command>"
    echo '  </ef:apply-acl>'
    echo '</ef:error>'

    exit "${_fm_ef_error_code}"
}


#-------------------------------------------------------------------------------
# Turn a spooler into the spooler with TTL "forever" used to hold the vroot map
#-------------------------------------------------------------------------------
fm_create_vroots_spooler() {
    if [ -z "${1}" ]; then
       fatal "Cannot perform the requested action" "Missing input option: spooler"
    fi

    # The fm spooler should never expire
    . "${EF_ROOT}/plugins/ef/lib/spoolers"
    ef_reset_spooler --uri "${1}" --ttl 'forever'

    # Set FM_BROWSE_SPOOLER_URI and as a persistent session variable
    . "${EF_ROOT}/plugins/ef/lib/sessions"
    ef_session -p -n 'FM_BROWSE_SPOOLER_URI' -t "${1}"
}

_fm_validate_subpath() {
    # Discard an absolute path
    [[ "$1" != /* ]]
}

# Validates sandbox'ed paths under a vroot
# Parameters:
#   $1: either the path(s) to be validated (separator \n in case of multiple paths)
#       or "-" for reading paths to be validated from the stdin
#
# NOTE: valid paths are those that, once resolved, point to a resource under the vroot
#      A more strict check could be something like [ "$1" = "$(basename "$1")" ]
#      since sandbox'ed paths should direct be under vroot + path
fm_validate_subpaths() {
    if [ -z "${path}" ]; then
        error "Cannot perform the requested action" "Missing input option: path"
    fi
    local _subpaths
    local _fullpath
    local _fullpaths
    local _errorpath
    if [ "$1" = "-" ]; then
        _subpaths="$(cat -)"
    else
        _subpaths="$1"
    fi
    if [ -z "${_subpaths}" ]; then
        return
    fi
    printf "%s" "${_subpaths}" | while read _subpath; do
        # _fm_validate_subpath "${_subpath}"
        if echo "$_subpath" | grep -q "^/" ; then
             fatal "Cannot perform the requested action" "Invalid target path: ${_subpath}"
        fi
        _fullpath="${path}/${_subpath}"
        [ -n "${_fullpaths}" ] && _fullpaths="${_fullpaths}"$'\n'"${_fullpath}" || _fullpaths="${_fullpath}"
    done # <<< "${_subpaths}"

    # In case of error 'realpath' returns the input error path
    # _errorpath=$("${EF_ROOT}/plugins/fm/lib/realpath" - <<< "${_fullpaths}" 2>&1)
    _errorpath=$(printf "%s" "${_fullpaths}" | "${EF_ROOT}/plugins/fm/lib/realpath" - 2>&1)
    if [ $? -ne 0 ]; then
        # Print the original input path so let's strip '${path}/'
        fatal "Cannot perform the requested action" "Invalid target path: "${_errorpath#${path}/}""
    fi
}

# Check if currently logged in user is allowed to download files.
# Check is performed against the allowlist configuration file
# defined in FM_DOWNLOAD_ALLOWED_CONF configuration parameter.
# Returns:
#  0 if user is not allowed
#  1 if user is allowed
fm_download_user_not_allowed() {
    local _allowlist_conf
    local _lines=0

    _allowlist_conf=$(ef_get_conf_path fm "$FM_DOWNLOAD_ALLOWED_CONF")
    if [ -r "$_allowlist_conf" ] ; then
        while read -r _line; do
            ((_lines++))
            [[ "$EF_USER" =~ ${_line// /} ]] && return 1
        done < <(grep -v '^[[:space:]]*$\|^[[:space:]]*#' "$_allowlist_conf")
    fi
    
    # If allowlist conf file is not readable or does not contain any expression, allow...
    [ $_lines -eq 0 ] && return 1
    # ...otherwise do not allow
    return 0
}

#-------------------------------------------------------------------------------
# Manage vroots and common options for FM services
#-------------------------------------------------------------------------------
manage_vroots() {
  # check params mandatory for all services
  if [ -z "${spooler}" ]; then
    fatal "Cannot perform the requested action" "Missing input option: spooler"
  fi

  . "${EF_ROOT}/plugins/ef/lib/utils"
  _spooler_dir=`spooler2file "${spooler}"`
  if [ ! -d "${_spooler_dir}" ]; then
    fatal "Cannot perform the requested action" "Invalid spooler option: ${_spooler_dir}"
  fi

  if [ -z "${vroot}" ]; then
    fatal "Cannot perform the requested action" "Missing input option: vroot"
  fi
  if [ -z "${path}" ]; then
    error "Cannot perform the requested action" "Missing input option: path"
  fi

  # Canonicalize path
  canonicpath=`"${EF_ROOT}/plugins/fm/lib/realpath" "${path}"`
  if [ $? -ne 0 ]; then
    fatal "Cannot perform the requested action" "Invalid path: $path"
  fi
  path="${canonicpath}"
  export path

  # create or read vroot cookie
  cookie="${_spooler_dir}/.vroot/"$(basename "${vroot}")

  if [ ! -r "${cookie}" ]; then
    fatal "Cannot perform the requested action" "Invalid vroot paramater: ${vroot}"
    # Log error should be:
    # fatal "Cannot perform the requested action" "Could not read vroot cookie: ${cookie}"
  fi

  . "${cookie}" >/dev/null 2>&1

  if [ $? -ne 0 ]; then
    fatal "Cannot perform the requested action" "Invalid vroot paramater: ${vroot}"
  fi

  # check mandatory vroot cookie uri components
  if [ -z "${FM_VROOT_PLUGIN}" ]; then
    fatal "Cannot perform the requested action" "Invalid vroot cookie contents: plugin not specified"
  fi

  if [ -z "${FM_VROOT_SCHEME}" -o -z "${FM_VROOT_PATH}" ]; then
    fatal "Cannot perform the requested action" "Invalid vroot cookie contents"
  fi

  # export cookie variables
  export FM_VROOT_SCHEME
  export FM_VROOT_LOGIN
  export FM_VROOT_CREDENTIALS
  export FM_VROOT_HOST
  export FM_VROOT_PORT
  export FM_VROOT_PATH
  export FM_VROOT_QUERY_STRING
  export FM_VROOT_ANCHOR
  export FM_VROOT_PLUGIN
}

if [ -z "$FM_DO_NOT_MANAGE_VROOTS" ] ; then
  manage_vroots
fi


# --------------------------------------------------------------------------- #
# vi: ts=4 sw=4 et syntax=sh :
# --------------------------------------------------------------------------- #

