#!/bin/sh

# DSM 5 -> 6 upgrade path:
#  Cannot remove user as still in use or may be DSM service itself
# Require warning in ChangeLog and manual user/group cleanup

INST_LOG="/tmp/${SYNOPKG_PKGNAME}_install.log"
INST_ETC="/var/packages/${SYNOPKG_PKGNAME}/etc"
INST_VARIABLES="${INST_ETC}/installer-variables"
INST_VAR="${SYNOPKG_PKGDEST}/var"

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

# Versions lower then DSM 6 don't support an upgrade folder
if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
    TMP_DIR="/var/tmp/${SYNOPKG_PKGNAME}/var"
else
    TMP_DIR="${SYNOPKG_TEMP_UPGRADE_FOLDER}/var"
fi

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

# Reload wizard variables stored by postinst
if [ -r "${INST_VARIABLES}" ]; then
    . "${INST_VARIABLES}"
fi

# Expect user to be set from package specific variables
if [ -n "${USER}" -a -z "${USER_DESC}" ]; then
    USER_DESC="User running $SYNOPKG_PKGNAME"
fi

# Default description if group name provided by UI
if [ -n "${GROUP}" -a -z "${GROUP_DESC}" ]; then
    GROUP_DESC="SynoCommunity Package Group"
fi

# Extract share volume and share name from download location if provided
if [ -n "${SHARE_PATH}" ]; then
    SHARE_VOLUME=$(echo "${SHARE_PATH}" | awk -F/ '{print "/"$2}')
    SHARE_NAME=$(echo "${SHARE_PATH}" | awk -F/ '{print $3}')
fi

# Tools shortcuts
MV="/bin/mv -f"
RM="/bin/rm -rf"
CP="/bin/cp -rfp"
MKDIR="/bin/mkdir -p"
LN="/bin/ln -nsf"
TEE="/usr/bin/tee -a"

### Functions library

log_step ()
{
    date >> ${INST_LOG}
    echo "===> Step $1. USER=$USER GROUP=$GROUP SHARE_PATH=${SHARE_PATH}" >> ${INST_LOG}
}

save_wizard_variables ()
{
    $RM "${INST_VARIABLES}"
    if [ -n "${GROUP}" ]; then
        echo "GROUP=${GROUP}" >> ${INST_VARIABLES}
    fi
    if [ -n "${SHARE_PATH}" ]; then
        echo "SHARE_PATH=${SHARE_PATH}" >> ${INST_VARIABLES}
    fi
}

# 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}" >> ${INST_LOG}
            synouser --del "${RM_USER}" >> ${INST_LOG} 2>&1
            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}" >> ${INST_LOG}
        # Create syno group
        synogroup --add "${GROUP}" "${EFF_USER}" >> ${INST_LOG}
        # Set description of the syno group
        synogroup --descset "${GROUP}" "${GROUP_DESC}" >> ${INST_LOG}
    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}" >> ${INST_LOG}
            # Remove syno group
            synogroup --del "${RM_GROUP}" >> ${INST_LOG} 2>&1
            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}'" >> ${INST_LOG}
        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}" >> ${INST_LOG}
    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 \"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--" >> ${INST_LOG} 2>&1
            fi

            # Then fix the Synology permissions
            echo "Granting '${GROUP}' group permissions on ${DIRNAME}" >> ${INST_LOG}
            synoacltool -add "${DIRNAME}" "group:${GROUP}:allow:rwxpdDaARWcC-:fd--" >> ${INST_LOG} 2>&1
            find "${DIRNAME}" -type d -exec synoacltool -enforce-inherit "{}" \; >> ${INST_LOG} 2>&1
        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 \"Linux mode\"`" ]; then
                    synoacltool -add "${DIRNAME}" "group:administrators:allow:rwxpdDaARWc--:fd--" >> ${INST_LOG} 2>&1
                fi
                # Add the new group permissions
                echo "Granting '${GROUP}' group basic permissions on ${DIRNAME}" >> ${INST_LOG}
                synoacltool -add "${DIRNAME}" "group:${GROUP}:allow:r-x---a-R----:---n" >> ${INST_LOG} 2>&1
            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." >> ${INST_LOG}
    fi
}

# Set recursive permissions using chown
set_unix_permissions ()
{
    DIRNAME=$1
    if [ -n "${EFF_USER}" ]; then
        echo "Granting '${EFF_USER}' unix ownership on ${DIRNAME}" >> ${INST_LOG}
        if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
            chown -R ${EFF_USER}:root "${DIRNAME}" >> $INST_LOG 2>&1
        else
            chown -R ${EFF_USER}:${USER} "${DIRNAME}" >> $INST_LOG 2>&1
        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" >> ${INST_LOG}
        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}" >> ${INST_LOG}
    fi
}


### Generic package behaviors

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

    # Check volume exists
    if [ -n "${SHARE_PATH}" ]; then
        if [ ! -d "${SHARE_VOLUME}" ]; then
            echo "Volume ${SHARE_VOLUME} does not exist." | $TEE ${INST_LOG}
            exit 1
        fi
    fi

    exit 0
}

postinst ()
{
    log_step "postinst"
    save_wizard_variables

    # Link for backward compatibility of binaries location
    $LN "${SYNOPKG_PKGDEST}" "/usr/local/${SYNOPKG_PKGNAME}" >> ${INST_LOG}

    # Add firewall config
    if [ -r "${FWPORTS_FILE}" ]; then
        echo "Installing service configuration ${FWPORTS_FILE}" >> ${INST_LOG}
        servicetool --install-configure-file --package "${FWPORTS_FILE}" >> ${INST_LOG} 2>&1
    fi

    # Service user management
    if [ -n "${EFF_USER}" ]; then
        if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
            # DSM 5 specific operations
            # Create prefixed synouser
            if ! cat /etc/passwd | grep "${EFF_USER}:x:" &> /dev/null; then
                synouser --add "${EFF_USER}" "" "$USER_DESC" 0 "" 0 >> ${INST_LOG} 2>&1
                # Set HOME for consistency with DSM 6, location available even if homes not enabled
                BACKUP_PASSWD="/tmp/install_${SYNOPKG_PKGNAME}_passwd"
                cp /etc/passwd ${BACKUP_PASSWD}
                sed -i "s#/var/services/homes/${EFF_USER}#/var/packages/${SYNOPKG_PKGNAME}/target#" /etc/passwd
            fi
        fi
        # Register service in "users" group to access any content
        if [ "$ADD_USER_IN_USERS" = "yes" ]; then
            syno_user_add_to_group "${EFF_USER}" "users"
        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}"
        fi
        if synogroup --get "$GROUP" &> /dev/null; then
            syno_user_add_to_group "${EFF_USER}" "${GROUP}"
        fi
        # Not sure but invoked with hope DSM is updated
        synogroup --rebuild all
    fi

    # Share management
    if [ -n "${SHARE_PATH}" ]; then
        echo "Configuring ${SHARE_PATH}" >> ${INST_LOG}
        # Create share if does not exist
        #  !"#$%&’()*+,/:;<=>?@[]nˆ`{} |
        if ! synoshare --get "${SHARE_NAME}" &> /dev/null ; then
            synoshare --add "${SHARE_NAME}" "${SHARE_DESC}" "${SHARE_PATH}" "" "rw" "" 1 0 >> ${INST_LOG}
        fi

        # Add user permission if no GROUP is set in UI
        # GROUP permission will be added in set_syno_permissions
        if [ ! -n "$GROUP" ] && [ -n "${EFF_USER}" ]; then
            synoshare --setuser "${SHARE_NAME}" RW + "${EFF_USER}" >> ${INST_LOG} 2>&1
        fi
        synoshare --build

        $MKDIR "${SHARE_PATH}"

        # Permissions for folder, up to volume
        if [ -n "$GROUP" ]; then
            set_syno_permissions "${SHARE_PATH}" "${GROUP}"
        fi
    fi

    $MKDIR "${INST_VAR}"

    call_func "service_postinst"

    $CP "${INST_LOG}" "${INST_VAR}"
    if [ -n "${LOG_FILE}" ]; then
        echo "Installation log: ${INST_VAR}/${SYNOPKG_PKGNAME}_install.log" >> ${LOG_FILE}
    fi
    if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
        # On DSM 5 set package files permissions for consistency with DSM 6
        set_unix_permissions "${SYNOPKG_PKGDEST}"
    else
        # On DSM 6 only var is concerned
        set_unix_permissions "${INST_VAR}"
    fi
    exit 0
}

preuninst ()
{
    log_step "preuninst"

    if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
        # Remove firewall config
        if [ -r "${FWPORTS_FILE}" ]; then
            echo "Removing service configuration ${SYNOPKG_PKGNAME}.sc" >> ${INST_LOG}
            servicetool --remove-configure-file --package "${SYNOPKG_PKGNAME}.sc" >> ${INST_LOG} 2>&1
        fi
    fi

    call_func "service_preuninst"
    exit 0
}

postuninst ()
{
    log_step "postuninst"

    if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
        # Remove link
        $RM "/usr/local/${SYNOPKG_PKGNAME}" >> ${INST_LOG}

        # Remove syno group if empty
        syno_group_remove "${GROUP}"

        # Remove user
        syno_remove_user "${EFF_USER}"
    fi

    call_func "service_postuninst"
    if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
        $RM "${INST_VARIABLES}"
    fi
    exit 0
}

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

    # Save some stuff
    $RM "$TMP_DIR" >> ${INST_LOG}
    $MKDIR "$TMP_DIR" >> ${INST_LOG}

    call_func "service_save"

    # Beware of /. outside the quotes
    # Needed to copy all files including hidden ones
    $CP "${INST_VAR}"/. "$TMP_DIR" >> ${INST_LOG}
    exit 0
}

postupgrade ()
{
    log_step "postupgrade"

    call_func "service_restore"

    # Restore some stuff, has to be cp otherwise fails on directories
    $CP "${TMP_DIR}"/. "${INST_VAR}" >> ${INST_LOG}

    # Correct permissions of var folder
    set_unix_permissions "${INST_VAR}"

    $RM "$TMP_DIR" >> ${INST_LOG}

    call_func "service_postupgrade"

    # Make sure we also have the logging for this step
    $CP "${INST_LOG}" "${INST_VAR}"
    exit 0
}
