#!/bin/bash
# *******************************************************************
# **  @file eisman_install.sh - install package
# **
# **  Creation:     2013-01-13 dv
# **  Last update:  $Id: eisman_install.sh 42806 2016-08-09 23:00:57Z dv $
# **
# **  Copyright (c) 2013-2016 the eisfair team, team(at)eisfair(dot)org
# **
# **  This program is free software; you can redistribute it and/or modify
# **  it under the terms of the GNU General Public License as published by
# **  the Free Software Foundation; either version 2 of the License, or
# **  (at your option) any later version.
# *******************************************************************

. /var/install/include/eislib
. /var/install/include/eismanlib

FLAGS=''
AUTO='no'
INFO='no'
REQUIRED_SPACE='0'    # is the space required for installation


if tar --help | grep -q 'no-overwrite-dir'
then
    taroption1='--no-overwrite-dir'
else
    taroption1=''
fi

if tar --help | grep -q 'keep-directory-symlink'
then
    taroption2='--keep-directory-symlink'
else
    taroption2=''
fi

# *******************************************************************
# **  show help on arguments and possible options
# **  @param  n/a
# **  @return n/a
# *******************************************************************

show_help()
{
cat <<EOF

Install one or more eisfair packages

usage: eisman install [OPTIONS] {PACKAGE[=VERSION]}*

options:
   --help          show this help screen
   --unstable      allow unstable packages to be included
   --auto          non interactive mode
   --info          do not install, only show packages
   --no-wait-anim  do not show wait animation

EOF
}


# *******************************************************************
# **  remove package from database of installed files
# **  @param  $1  package name
# **  @return n/a
# *******************************************************************

remove_from_database()
{
    local package="${1}"

    # grep all packages other then "$package" into temporary file
    grep -v -e "^\[.*\]\[${package}\]\[.*\]\[.*\]\[.*\]" \
       "${EISMAN_PATH}/installed.db" > "${EISMAN_PATH}/installed.db.tmp"

    if [ "$?" -eq 0 ]
    then
        # remove database file
        rm -f "${EISMAN_PATH}/installed.db"

        # move temporary file to new database
        mv "${EISMAN_PATH}/installed.db.tmp" "${EISMAN_PATH}/installed.db"
        return 0
    else
        echo "error: unable to delete packate from database!" >&2
        return 1
    fi
}


# *******************************************************************
# **  list the packages returned by eisman_tree to show all
# **  requirements prior to installation
# **  @param  $1  list of packages
# **  @return n/a
# *******************************************************************

list_packages()
{
    local packages="$1"
    local count
    local line

    # show table header if not in auto mode
    if [ "${AUTO}" = "no" ]
    then
        echo " "
        echo "The following packages will be installed:"
        echo " "
        echo "version  status   name              source"
        echo "---------------------------------------------------------"
    fi

    # redefine separator
    IFS_BAK=$IFS
    IFS="
"

    # split package list into lines
    count=0
    for line in $packages
    do
        # if this is the space entry remember it's value
        # otherwise print the line onto the screen
        if [ "${line:0:8}" = "space = " ]
        then
            space=${line:8}
        elif [ "${line:0:8}" = "warning:" ]
        then
            mecho --warn "$line"
        else
            if [ "${AUTO}" = "no" ]
            then
                if [ "${#line}" -gt 72 ]
                then
                    echo "${line:0:72}..."
                else
                    echo "${line}"
                fi
            fi
            count=$(($count + 1))
        fi
    done

    # restore separator
    IFS=${IFS_BAK}

    # show statistics (number of packages and disk space)
    if [ "${AUTO}" = "no" ]
    then
        echo " "
        echo "${count} package(s) using approx. ${space} MB of disk space."
        echo " "
    fi

    # store required space in global var
    REQUIRED_SPACE="${space}"
}


# *******************************************************************
# **  download required packages to the package cache. Only if all
# **  packages have been successfully downloaded installation can
# **  begin
# **  @param  $1  list of packages
# **  @return n/a
# *******************************************************************

download_packages()
{
    local packages="$1"

    mecho --info 'Downloading required packages ...'

    IFS_BAK=$IFS
    IFS="
"

    for line in $packages
    do
        if [ "${line:0:8}" != "space = " ]
        then
            local version=""
            local package=""
            local status=""
            local url=""
            local token
            local idx=0

            IFS=" "
            for token in $line
            do
                case $idx in
                0)
                    version="${token}"
                    ;;
                1)
                    status="${token}"
                    ;;
                2)
                    package="${token}"
                    ;;
                3)
                    url="${token}"
                esac
                idx=$(($idx + 1))
            done
            IFS="
"

            ${EISMAN_BIN_PATH}/eisman_download.sh \
                "${package}"                      \
                "${version}"                      \
                "${status}"                       \
                "${url}"
            if [ "$?" != 0 ]
            then
                echo 'Installation aborted!' >&2
                IFS=${IFS_BAK}
                exit 1
            fi
        fi
    done

    IFS=${IFS_BAK}

    mecho --info 'Done!'
}


# *******************************************************************
# **  restore the permissions of the temp folder since these seem to
# **  get lost some times
# **  @param  n/a
# **  @return n/a
# *******************************************************************

restore_tmp_folder()
{
    chown root.root /tmp
    chmod 1777 /tmp
}


# *******************************************************************
# **  install one single package by first executing /tmp/preinstall
# **  then extracting the whole archive and then executing /tmp/install
# **  @param  $1  name of package
# **  @param  $2  version of package
# **  @param  $3  archive file name (and path)
# **  @return n/a
# *******************************************************************

install_single_package()
{
    local package="${1}"
    local version="${2}"
    local url="${3}"
    local filename="${4}"
    local instdate=$(grep -E "^\[.*\]\[$package\]\[.*\]\[.*\]\[.*\](date)" \
                    "${EISMAN_PATH}/info/${package}/${version}/$(basename ${filename}).rec" |
                    sed 's#^.*date##; s# ##g')

    # change into root directory
    cd /

    # get preinstall script first
    rm -f /tmp/preinstall.sh
    tar $taroption1 $taroption2 -x -f $filename tmp/preinstall.sh 2>/dev/null

    # some packages modify /tmp folder  permissions and owner -> restore
    restore_tmp_folder

    # now execute preinstall script if present
    if [ -f /tmp/preinstall.sh ]
    then
        sh /tmp/preinstall.sh "${url}"
        res="${?}"
        rm -f  /tmp/preinstall.sh

        # evaluate result from preinstall script
        if [ "${res}" -ne 0 ]
        then
            echo "error: installation of ${package} aborted by /tmp/preinstall.sh!"
            exit 1
        fi
    else
        ${EISMAN_BIN_PATH}/eisman_uninstall.sh --quiet --update --auto $package
        res="${?}"

        # evaluate result from uninstall script
        if [ "${res}" -ne 0 ]
        then
            echo "error: installation of ${package} aborted by uninstall!" >&2
            exit 1
        fi
    fi

    # now extract the complete package
    rm -f /tmp/install.sh
    if ! tar $taroption1 $taroption2 -x -f $filename 2>/tmp/install.log
    then
        echo "error: failed to extract archive!" >&2
        exit 1
    fi

    # remove again, included in the tarball
    rm -f /tmp/preinstall.sh

    if [ ! -s /tmp/install.log ]
    then
        rm -f /tmp/install.log
    fi

    # some packages modify /tmp folder  permissions and owner -> restore
    restore_tmp_folder

    # now execute install script
    if [ -f /tmp/install.sh ]
    then
        sh /tmp/install.sh "${url}"
        rm -f /tmp/install.sh
    fi

    # log installation via syslog
    {
        echo "package ${package} (${version}|${instdate}|$(basename ${filename}))"
        echo "(${url})"
    } | /usr/bin/logger -t 'eis-install' -p 'local5.info'

    # restore original directory
    cd - > /dev/null
}


# *******************************************************************
# **  install the list of packages one by one following the precise
# **  order returned by eisman_tree.sh
# **  @param  $1  list of packages
# **  @return n/a
# *******************************************************************

install_packages()
{
    local packages="$1"

    IFS_BAK=$IFS
    IFS="
"

    for line in $packages
    do
        if [ "${line:0:8}" != "space = " ]
        then
            local version=""
            local package=""
            local token
            local filename
            local idx=0

            IFS=" "
            for token in $line
            do
                case $idx in
                0)
                    version="${token}"
                    ;;
                2)
                    package="${token}"
                    ;;
                3)
                    url="${token}"
                    ;;
                esac
                idx=$(($idx + 1))
            done
            IFS="
"

            filename=$(ls "${EISMAN_PATH}/cache/${package}/${version}")

            # prompt action to perform
            mecho --info -n "Installation of: "
            mecho        -n "${package} (${version}) "
            mecho --info    "..."

            # check if file name is valid
            if [ -z "${filename}" ]
            then
                echo "error: unable to find package file for \"${package}\"!" >&2
                exit 1
            fi

            # check if record file in info path is present
            if [ ! -f "${EISMAN_PATH}/info/${package}/${version}/${filename}.rec" ]
            then
                echo "error: unable to find record file for \"${package}\"!" >&2
                exit 1
            fi

            # call subroutine to perform the installation task
            install_single_package \
               "${package}" \
               "${version}" \
               "${url}/${filename}" \
               "${EISMAN_PATH}/cache/${package}/${version}/${filename}"
            if [ "$?" = 0 ]
            then
                # just in case there is still a database entry for this
                # package left, remove it first
                if remove_from_database "${package}"
                then
                    local base_url="${url}/dummy"

                    base_url="$(echo "${base_url}" | cut -d '?' -f 1)"
                    base_url="$(dirname "${base_url}" | sed 's|/[0-9]*$||')"

                    # copy record file to installed db and replace URL we
                    # really installed the package from
                    sed -e "s|url        .*|url        ${url}/${filename}|g" \
                        -e "s|\[[^][]*\]|\[${base_url}]|5"    \
                        < "${EISMAN_PATH}/info/${package}/${version}/${filename}.rec" \
                        >> "${EISMAN_PATH}/installed.db"

                    # remove file from cache
                    rm -rf "${EISMAN_PATH}/cache/${package}"

                    # show result
                    mecho --info -n 'Successfully installed: '
                    mecho        -n "${package} (${version})"
                    mecho --info    '!'
                else
                    mecho --warn -n 'Failed to install: '
                    mecho           "${package} (${version})!"
                    exit 1
                fi
            else
                mecho --warn -n 'Failed to install: '
                mecho           "${package} (${version})!"
                exit 1
            fi
        fi
    done

    IFS=${IFS_BAK}
}


# *******************************************************************
# **  main program
# *******************************************************************

main()
{
    local wait_animation="yes"

    while [ "${1:0:2}" = "--" ]
    do
        case $1 in
        --help)
            show_help
            exit 0
            ;;
        --unstable)
            shift
            FLAGS="${FLAGS} --unstable" # use unstable packages too
            ;;
        --auto)
            shift
            AUTO='yes'        # non interactive installation
            ;;
        --info)
            shift
            INFO='yes'        # not a real installation only show what would be done
            ;;
        --url)
            shift
            FLAGS="${FLAGS} --url=${1}" # set filter url
            shift
            ;;
        --url=*)
            FLAGS="${FLAGS} --url=${1:6}" # set filter url
            shift
            ;;
        --no-wait-anim)
            wait_animation="no"
            shift
            ;;
        *)
            echo "error: unknown option \"$1\"!" >&2
            echo ""
            show_help
            exit 1
            ;;
        esac
    done

    # read parameters
    while [ "$#" -ge 1 ]
    do
        PACKAGELIST="${PACKAGELIST} ${1}"
        shift
    done

    # exit upon empty package list
    if [ -z "${PACKAGELIST}" ]
    then
        exit 0
    fi

    # echo "Resolving package requirements..."
    ${EISMAN_BIN_PATH}/eisman_tree.sh ${FLAGS} ${PACKAGELIST} \
        > "/tmp/tmpresult$$" &

    # show wait animation and fetch exit code
    if [ "${wait_animation}" = "yes" ]
    then
        show_wait "searching..."
    fi
    wait $!

    if [ "$?" != 0 ]
    then
        echo 'Installation aborted!' >&2
        rm -f "/tmp/tmpresult$$"
        exit 1
    fi

    packages="$(cat /tmp/tmpresult$$)"
    rm -f "/tmp/tmpresult$$"

    # list packages found in query
    list_packages "$packages"

    # ask user to continue
    if [ "${AUTO}" = "no" -a "${INFO}" = "no" ]
    then
        res=$(/var/install/bin/ask "Continue" "yes")

        # if ask break, ask returned 255
        if [ $? = 255 ]
        then
            res="no"
        fi

        if [ "${res}" != "yes" ]
        then
            echo 'Installation aborted by user!' >&2
            exit 1
        fi
    fi

    if [ "${INFO}" != "yes" ]
    then
        # download listed packages
        download_packages "$packages"

        # check disk space
        if [ -n "${REQUIRED_SPACE}" ]
        then
            space_avail=$(df -m / | awk '/\/$/ { print $4 }')
            if [ "${space_avail}" -lt "${REQUIRED_SPACE}" ]
            then
                echo "Space required : ${REQUIRED_SPACE} MB"
                echo "Space available: ${space_avail} MB"
                echo
                echo 'Installation aborted!' >&2
                exit 1
            fi
        fi

        # install packages listed in $packages
        install_packages "${packages}"
    fi

    exit 0
}

main "${@}"
