#!/bin/sh

################################################################################
# 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 "$(echo "$0" | sed "s|${EF_ROOT}|EF_ROOT|g")")

    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
    case "$1" in
        /*) return 1 ;;
    esac
    return 0
}

# 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
    if [ "$1" = "-" ]; then
        _subpaths="$(cat -)"
    else
        _subpaths="$1"
    fi
    if [ -z "${_subpaths}" ]; then
        return 0
    fi

    # Temporary file to accumulate full paths
    _temp_file=$(mktemp 2>/dev/null || printf '%s\n' "${TMPDIR:-/tmp}/fm_validate_$$")
    # Ensure cleanup on any exit path
    trap 'rm -f "${_temp_file}"' EXIT INT TERM

    # Iterate over each subpath in the current shell
    printf '%s\n' "${_subpaths}" | while IFS= read -r _subpath; do
        # Reject absolute paths
        case "${_subpath}" in
            /*)
                fatal "Cannot perform the requested action" "Invalid target path: ${_subpath}"
                ;;
        esac

        # Build full path under vroot and append to list
        printf '%s\n' "${path}/${_subpath}" >> "${_temp_file}"
    done

    # Resolve full paths using the realpath helper
    # On error, the helper prints the failing path
    _errorpath=$("${EF_ROOT}/plugins/fm/lib/realpath" - < "${_temp_file}" 2>&1)
    if [ $? -ne 0 ]; then
        # If realpath failed, strip the vroot prefix and report the original subpath
        fatal "Cannot perform the requested action" "Invalid target path: ${_errorpath#${path}/}"
    fi

    return 0
}


# 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() {
    _lines=0

    _allowlist_conf=$(ef_get_conf_path fm "$FM_DOWNLOAD_ALLOWED_CONF") || return 0

    if [ -r "$_allowlist_conf" ]; then
        # read non‑blank, non‑comment lines
        while IFS= read -r _line; do
            _lines=$(( _lines + 1 ))

            # strip spaces from pattern
            _pat=$(printf '%s\n' "$_line" | tr -d ' ')

            # regex match: if EF_USER matches pattern, allow (1)
            printf '%s\n' "$EF_USER" | grep -Eq "$_pat" && return 1
        done <<EOF
$(grep -Ev '^[[:space:]]*$|^[[:space:]]*#' "$_allowlist_conf")
EOF
    fi

    # If allowlist conf file is not readable or has no expressions, allow (1)
    [ "$_lines" -eq 0 ] && return 1

    # Otherwise deny (0)
    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 :
# --------------------------------------------------------------------------- #
