#!/bin/sh

# DSM 5 -> 6 upgrade path:
# - Not supported anymore

# installer log is not writable, use for reference only.
INST_LOG="/var/log/packages/${SYNOPKG_PKGNAME}.log"

# Optional FWPORTS file
FWPORTS_FILE="/var/packages/${SYNOPKG_PKGNAME}/target/app/${SYNOPKG_PKGNAME}.sc"

# Only DSM > 5 have this temp upgrade folder variable
TMP_DIR="${SYNOPKG_TEMP_UPGRADE_FOLDER}/var"


install_log ()
{
    local _msg_="$@"
    if [ -z "${_msg_}" ]; then
        # read multiline from stdin
        while IFS=$'\n' read -r line; do
            install_log "${line}"
        done
    else
        # stderr goes to /var/log/packages/{package}.log
        # stdout goes to dialog in package manager ui.
        echo -e "$(date +'%Y/%m/%d %H:%M:%S')\t${_msg_}" 1>&2
    fi
}

# Invoke shell function if available
call_func ()
{
    FUNC=$1
    if type "${FUNC}" 2>/dev/null | grep -q 'function' 2>/dev/null; then
        install_log "Begin ${FUNC}"
        LOG=$2
        ARG=$3
        if [ -z "${LOG}" ]; then
            if [ -z "${ARG}" ]; then
                eval ${FUNC}
            else
                eval ${FUNC} ${ARG}
            fi
        else
            if [ -z "${ARG}" ]; then
                eval ${FUNC} 2>&1 | ${LOG}
            else
                eval ${FUNC} ${ARG} 2>&1 | ${LOG}
            fi
        fi
        install_log "End ${FUNC}"
    fi
}

# Source installer variables and functions
INST_FUNCTIONS=$(dirname $0)"/functions"
if [ -r "${INST_FUNCTIONS}" ]; then
    . "${INST_FUNCTIONS}"
fi


# Source package specific variables and functions
SVC_SETUP=$(dirname $0)"/service-setup"
if [ -r "${SVC_SETUP}" ]; then
    . "${SVC_SETUP}"
fi


# Load (wizard) variables stored by postinst
load_variables_from_file ${INST_VARIABLES}

# init variables either from ${INST_VARIABLES}, from package or from wizard
call_func "initialize_variables"


### Functions library

# Remove user from system and from groups it is member of
syno_remove_user ()
{
    RM_USER=$1
    if [ -n "${RM_USER}" ]; then
        # Check if user exists
        if synouser --get "${RM_USER}" &> /dev/null; then
            echo "Removing user ${RM_USER}"
            synouser --del "${RM_USER}"
            synouser --rebuild all
            # Also rebuild groups so users are removed
            synogroup --rebuild all
        fi
    fi
}

# Create syno group $GROUP with parameter user as member
syno_group_create ()
{
    EFF_USER=$1
    if [ -n "${EFF_USER}" ]; then
        echo "Creating group ${GROUP}"
        # Create syno group
        synogroup --add "${GROUP}" "${EFF_USER}"
        # Set description of the syno group
        synogroup --descset "${GROUP}" "${GROUP_DESC}"
    fi
}

# Delete syno group if empty
syno_group_remove ()
{
    RM_GROUP=$1
    if [ -n "${RM_GROUP}" ]; then
        # Check if syno group is empty
        if ! synogroup --get "${RM_GROUP}" | grep -q "0:\["; then
            echo "Removing group ${RM_GROUP}"
            # Remove syno group
            synogroup --del "${RM_GROUP}"
            synogroup --rebuild all
        fi
    fi
}

# Add user to existing group
syno_user_add_to_group ()
{
    ADD_USER=$1
    ADD_GROUP=$2
    # Check user already in group
    if ! synogroup --get "$ADD_GROUP" | grep "^[0-9]:\[${ADD_USER}\]" &> /dev/null; then
        # Add user, not in group yet
        echo "Adding '${ADD_USER}' to '${ADD_GROUP}'"
        MEMBERS="$(synogroup --get $ADD_GROUP | grep '^[0-9]' | sed 's/.*\[\([^]]*\)].*/\1/' | tr '\n' ' ')"
        # The "synogroup --member" command clears all users before adding new ones
        # so all the users must be listed on the command line
        synogroup --member "$ADD_GROUP" $MEMBERS "${ADD_USER}"
    fi
}

# Sets recursive permissions for ${GROUP} on specified directory
# Usage: set_syno_permissions "${SHARE_FOLDER}" "${GROUP}"
set_syno_permissions ()
{
    DIRNAME=$(realpath "${1}")
    GROUP="${2}"

    VOLUME=$(echo "${DIRNAME}" | awk -F/ '{print "/"$2}')

    # Ensure directory resides in /volumeX before setting GROUP permissions
    if [ "$(echo ${VOLUME} | cut -c2-7)" = "volume" ]; then
        # Set read/write permissions for GROUP for folder and subfolders
        if [ ! "$(synoacltool -get ${DIRNAME} | grep ""group:${GROUP}:allow:rwxpdDaARWcC-:fd--"")" ]; then
            # First Unix permissions, but only if it's in Linux mode
            if [ "$(synoacltool -get ${DIRNAME} | grep -i 'Linux mode')" ]; then
                set_unix_permissions "${DIRNAME}"
                # If it is linux mode (due to old package) we need to add "administrators"-group,
                # otherwise the folder is not accessible from File Station anymore!
                synoacltool -add "${DIRNAME}" "group:administrators:allow:rwxpdDaARWc--:fd--"
            fi

            # Then fix the Synology permissions
            echo "Granting '${GROUP}' group permissions on ${DIRNAME}"
            synoacltool -add "${DIRNAME}" "group:${GROUP}:allow:rwxpdDaARWcC-:fd--"
            find "${DIRNAME}" -type d -exec synoacltool -enforce-inherit "{}" \;
        fi

        # Walk up the tree and set traverse execute permissions for GROUP up to VOLUME
        while [ "${DIRNAME}" != "${VOLUME}" ]; do
            if [ ! "$(synoacltool -get ""${DIRNAME}"" | grep ""group:${GROUP}:allow:r.x"")" ]; then
                # Here we also need to make sure the admin can access data via File Station
                if [ "$(synoacltool -get ""${DIRNAME}"" | grep -i 'Linux mode')" ]; then
                    synoacltool -add "${DIRNAME}" "group:administrators:allow:rwxpdDaARWc--:fd--"
                fi
                # Add the new group permissions
                echo "Granting '${GROUP}' group basic permissions on ${DIRNAME}"
                synoacltool -add "${DIRNAME}" "group:${GROUP}:allow:r-x---a-R----:---n"
            fi
            DIRNAME="$(dirname "${DIRNAME}")"
        done
    else
        echo "Skip granting '${GROUP}' group permissions on ${DIRNAME} as the directory does not reside in '/volumeX'. Set manually if needed."
    fi
}

# Set recursive permissions using chown
set_unix_permissions ()
{
    DIRNAME=$1
    if [ -n "${EFF_USER}" ]; then
        echo "Granting '${EFF_USER}' unix ownership on ${DIRNAME}"
        if [ -n "${GROUP}" ]; then
            chown -R ${EFF_USER}:${GROUP} "${DIRNAME}"
        else
            chown -R ${EFF_USER}:${USER} "${DIRNAME}"
        fi
    fi
}

# If package was moved to new group, we need to add the new package user
# also to the old group. Only if the legacy user was in the old group.
# Usage: syno_user_add_to_legacy_group "${NEW_USER}" "${LEGACY_USER}" "${LEGACY_GROUP}"
syno_user_add_to_legacy_group () {
    NEW_USER=$1
    LEGACY_USER=$2
    LEGACY_GROUP=$3

    # Check if user in old group
    if synogroup --get "$LEGACY_GROUP" | grep "^[0-9]:\[${LEGACY_USER}\]" &> /dev/null; then
        # Add new user and remove old one
        echo "Adding '${NEW_USER}' to '${LEGACY_GROUP}' for backwards compatibility"
        MEMBERS="$(synogroup --get $LEGACY_GROUP | grep '^[0-9]' | sed 's/.*\[\([^]]*\)].*/\1/' | tr '\n' ' ')"
        MEMBERS=${MEMBERS//$LEGACY_USER}
        # The "synogroup --member" command clears all users before adding new ones
        # so all the users must be listed on the command line
        synogroup --member "$LEGACY_GROUP" $MEMBERS "${NEW_USER}"
    fi
}


### Generic package behaviors

preinst ()
{
    log_step "preinst"
    call_func "validate_preinst"
    call_func "service_preinst" install_log

    exit 0
}

postinst ()
{
    log_step "postinst"

    # Link for backward compatibility of binaries location
    $LN "${SYNOPKG_PKGDEST}" "/usr/local/${SYNOPKG_PKGNAME}" 2>&1 | install_log

    # Link for DSM7 forward compatibility of var location
    $LN "${SYNOPKG_PKGDEST}/var" "/var/packages/${SYNOPKG_PKGNAME}/var" 2>&1 | install_log

    # Service user management
    if [ -n "${EFF_USER}" ]; then
        # Register service in "users" group to access any content
        if [ "$ADD_USER_IN_USERS" = "yes" ]; then
            syno_user_add_to_group "${EFF_USER}" "users" 2>&1 | install_log
        fi
    fi

    # Only if a group is provided via UI or set by script
    if [ -n "$GROUP" ]; then
        # Check if group already exists
        if ! synogroup --get "$GROUP" &> /dev/null; then
            # Group does not exist yet: create with user as member
            syno_group_create "${EFF_USER}" 2>&1 | install_log
        fi
        if synogroup --get "$GROUP" &> /dev/null; then
            syno_user_add_to_group "${EFF_USER}" "${GROUP}" 2>&1 | install_log
        fi
        # Not sure but invoked with hope DSM is updated
        synogroup --rebuild all 2>&1 | install_log
    fi

    # Share management (can be removed when SERVICE_WIZARD_SHARE is not used anymore)
    if [ -n "${SHARE_PATH}" ]; then
        if [ "${SHARE_WORKER}" != "0" ]; then
            install_log "Shared folder [${SHARE_PATH}] will be created by DSM."
        else
            # Create share if it does not exist
            # invalid characters: !"#$%&’()*+,/:;<=>?@[]nˆ`{} |
            if ! synoshare --get "${SHARE_NAME}" > /dev/null 2>&1; then
                SHARE_PATH=${SYNOPKG_PKGDEST_VOL}/${SHARE_NAME}
                install_log "Create share SHARE_NAME=${SHARE_NAME}, SHARE_PATH=${SHARE_PATH}"
                # mandatory arguments: 
                # name desc path na rw ro browseable adv_privilege 
                # na, rw and ro are list of user(s) and/or group(s), separated by comma
                synoshare --add "${SHARE_NAME}" "Share created for package ${SYNOPKG_PKGNAME}" "${SHARE_PATH}" "" "" "" 1 0 2>&1 | install_log
            else
                install_log "Share already exists SHARE_NAME=${SHARE_NAME}, SHARE_PATH=${SHARE_PATH}"
            fi

            # Add user permission if no GROUP is set in service-setup
            # GROUP permission will be added in set_syno_permissions
            if [ -z "$GROUP" ] && [ -n "${EFF_USER}" ]; then
                # check whether user is already added to RW users
                _synoshare_parameter="--getmap"
                if synoshare --getmap "${SHARE_NAME}" | grep "ACL.*\[yes\]" > /dev/null 2>&1; then
                    _synoshare_parameter="--list_acl"
                fi
                if synoshare ${_synoshare_parameter} "${SHARE_NAME}" | grep "RW list" | grep -o "\[.*\]"  | sed  's/[\[,]/ /g' | sed 's/\]/ /g' | grep -q " ${EFF_USER} " > /dev/null 2>&1; then
                    install_log "User has already RW access to share: SHARE_NAME=${SHARE_NAME}, EFF_USER=${EFF_USER}"
                else
                    install_log "Set user for share: SHARE_NAME=${SHARE_NAME}, EFF_USER=${EFF_USER}"
                    synoshare --setuser "${SHARE_NAME}" RW + "${EFF_USER}" 2>&1 | install_log
                fi
            fi
            synoshare --build 2>&1 | install_log

            # Permissions for folder, up to volume
            if [ -n "$GROUP" ]; then
                install_log "Set group for share: SHARE_PATH=${SHARE_PATH}, GROUP=${GROUP}"
                set_syno_permissions "${SHARE_PATH}" "${GROUP}" 2>&1 | install_log
            fi
        fi
    fi

    $MKDIR "${SYNOPKG_PKGVAR}" 2>&1 | install_log

    call_func "save_wizard_variables" install_log
    # Restrict permissions to protect sensitive options
    if [ -e "${INST_VARIABLES}" ]; then
        chmod go-rwx ${INST_VARIABLES}
        chown ${EFF_USER} ${INST_VARIABLES}
    fi

    call_func "service_postinst" install_log

    call_func "service_clean_tmpdir" install_log

    if [ -n "${LOG_FILE}" ]; then
        echo "Installation log: ${INST_LOG}" >> ${LOG_FILE}
    fi

    # On DSM 6 only var is concerned
    set_unix_permissions "${SYNOPKG_PKGVAR}" 2>&1 | install_log

    exit 0
}

preuninst ()
{
    log_step "preuninst"
    call_func "validate_preuninst"
    call_func "service_preuninst" install_log

    exit 0
}

postuninst ()
{
    log_step "postuninst"

    if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
        # Remove link
        $RM "/usr/local/${SYNOPKG_PKGNAME}" 2>&1 | install_log

        # Remove syno group if empty
        syno_group_remove "${GROUP}" 2>&1 | install_log

        # Remove user
        syno_remove_user "${EFF_USER}" 2>&1 | install_log
    fi

    call_func "service_postuninst" install_log

    if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
        $RM "${INST_VARIABLES}" 2>&1 | install_log
    fi

    exit 0
}

preupgrade ()
{
    log_step "preupgrade"
    call_func "validate_preupgrade"
    call_func "service_preupgrade" install_log

    # Save some stuff
    $RM "$TMP_DIR" 2>&1 | install_log
    $MKDIR "$TMP_DIR" 2>&1 | install_log

    call_func "service_save" install_log

    # Beware of /. outside the quotes
    # Needed to copy all files including hidden ones
    $CP "${SYNOPKG_PKGVAR}"/. "$TMP_DIR" 2>&1 | install_log

    exit 0
}

postupgrade ()
{
    log_step "postupgrade"

    call_func "service_restore" install_log

    # Restore some stuff, has to be cp otherwise fails on directories
    $CP "${TMP_DIR}"/. "${SYNOPKG_PKGVAR}" 2>&1 | install_log

    # Correct permissions of var folder
    set_unix_permissions "${SYNOPKG_PKGVAR}" 2>&1 | install_log

    $RM "$TMP_DIR" 2>&1 | install_log

    call_func "service_postupgrade" install_log

    exit 0
}
