#!/bin/bash
# *******************************************************************
# **  @file eisman_mirror.sh - synchronize a local package mirror
# **
# **  Creation:     2017-01-11 dv
# **  Last update:  $Id: eisman_mirror.sh 44606 2017-01-19 19:01:47Z dv $
# **
# **  Copyright (c) 2013-2017 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


wget=/usr/local/bin/wget.sh


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

show_help()
{
cat <<EOF

Create / synchronize a local package mirror

usage:
    eisman mirror [OPTIONS] /path/to/package/mirror

options:
    --help            show this help screen
    --url <url>       package index download url
    --relocate <url>  relocate package urls to 'url'
    --no-progress     don't show progress information and show
                      messages in [mecho --file] format
                      default is [mecho --tty]

EOF
}


# *******************************************************************
# **  show current progress in percent
# **  @param  $1 progress in percent
# **  @return n/a
# *******************************************************************

show_progress()
{
    echo -en "\033[7D"
    printf "[%3d%%] completed!" "${1}"
    echo -en "\033[10D"
}


# *******************************************************************
# **  parse wget output to display a simple progress bar. Receives
# **  wget output on stdin
# **  @param  n/a
# **  @return n/a
# *******************************************************************

wget_progress_bar()
{
    local flag=2
    local c
    local count=0

    while read -d '' -rn 1 c
    do
        if [ "${output}" = "--tty" ]
        then
            if [ "${flag}" -eq 2 ]
            then
                if [ "${c}" = '[' ]
                then
                    flag=1
                    if [ ${count} -gt 0 ]
                    then
                        echo -en "\033[${count}D"
                    fi
                    count=0
                fi
            elif [ "${flag}" -eq 1 ]
            then
                if [ "${c}" = ']' ]
                then
                    flag=2
                elif [ "${c}" = '=' ] || [ "${c}" = '>' ]
                then
                    echo -n "#"
                    count=$((${count} + 1))
                fi
            fi
        fi
    done
}


# *******************************************************************
# **  read index file line by line, check if package meets current system
# **  and fetch the info header file and package archive if it is't
# **  already in the local directory.
# **  Further a new index file is generatet, that references the local
# **  package files.
# **  @param  $1  package directory
# **  @param  $2  optional relocation URL for local files
# **  @return n/a
# *******************************************************************

update_local_mirror()
{
    local version
    local invalid_base='no'
    local linecount='0'
    local progress='0'
    local linetotal
    local idx2='0'
    local package_dir="${1}"
    local relocate_url="${2}"

    # show what we are doing
    mecho ${output} --info 'Synchronizing package mirror ...'

    # init progress display
    if [ "${output}" = "--tty" ]
    then
        echo -n "       "
        show_progress 0
    fi

    # get total number of lines in index
    linetotal=$(wc -l < /tmp/index.txt)

    # overwrite existing index file
    {
        echo "# This file was automatically generated by eisman"
        echo "# Last synchronization: $(date +'%Y-%m-%d %H:%M:%S')"
    } > "${package_dir}/index.txt"

    # read index line by line
    while read -r
    do
        local line=${REPLY}

        linecount=$((${linecount} + 1))

        if [ "${line:0:1}" != "#" ]
        then
            local idx=0
            local package=""
            local version=""
            local status=""
            local url=""
            local system=""
            local file=""
            local success=0

            for word in ${line}
            do
                case ${idx} in
                0)  package="${word}"
                    ;;
                1)  version="${word}"
                    ;;
                2)  status="${word}"
                    ;;
                3)  url="${word}"
                    file="${word:0:$((${#word} - 5))}"
                    ;;
                4)  system="${word}"
                    ;;
                esac

                idx=$((${idx} + 1))
            done

            # handle entries missing system tag
            if [ -z "${system}" ]
            then
                system='eisfair-1'
            fi

            if [ "${idx}" -ge 3 ]
            then
                if [ $(expr match "${system}" "[a-zA-Z0-9\;-]*${EISMAN_SYSTEM}") -gt 0 ]
                then
                    local packfile="${package_dir}/${package}/${version}/$(basename ${file})"

                    if [ ! -d "${package_dir}/${package}/${version}" -o \
                         ! -f "${packfile}" ]
                    then
                        if [ "${output}" = "--file" ]
                        then
                            nonewline='-n'
                        fi
                        # echo ${nonewline} "=> Fetching ${package} (${version}) ..."

                        # prepare progress bar
                        local printline="${package} (${version})..."
                        local length=$((${#printline} + 22))
                        if [ "${output}" = "--tty" ]
                        then
                            echo -en "[                    ] ${printline}"
                            echo -en "\033[${length}D"
                        else
                            echo -en "${printline}"
                        fi

                        # first download files into temporary locations
                        rm -f /tmp/tmpinfofile$$
                        rm -f /tmp/tmppackfile$$

                        success=1
                        ${wget} -q -O /tmp/tmpinfofile$$ "${file}.info"
                        if [ "$?" != "0" ]
                        then
                            success=0
                        fi
                        if [ "${success}" != "0" ]
                        then
                            # execute wget to download the package and pass stdout of wget over
                            # to wget_progress_bar, to show a simple progress bar.
                            # Do some pipe redirection to fetch exit code of wget.
                            # see: http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
                            (
                                (
                                    (
                                        (
                                            ${wget} -O /tmp/tmppackfile$$  \
                                                    --progress=bar:force "${file}" 2>&1; echo $? >&3
                                        ) |
                                        wget_progress_bar >&4
                                    ) 3>&1
                                ) |
                                (read xs; exit $xs)
                            ) 4>&1

                            if [ "$?" = "0" ]
                            then
                                # show 100% progress
                                echo "[===================>]" | wget_progress_bar
                                echo
                            else
                                success=0
                            fi
                        fi

                        # if successful, copy files to correct destination
                        if [ "${success}" = "1" ]
                        then
                            mkdir -p "${package_dir}/${package}/${version}"

                            mv "/tmp/tmpinfofile$$" "${packfile}.info"
                            mv "/tmp/tmppackfile$$" "${packfile}"
                        else
                            rm -f /tmp/tmpinfofile$$
                            rm -f /tmp/tmppackfile$$
                        fi

                        # show result ${output} --tty or --file
                        if [ "${success}" = "1" ]
                        then
                            mecho ${output} --ok
                        else
                            mecho ${output} --fail
                        fi

                        # show progress of last file fetched
                        if [ "${output}" = "--tty" ]
                        then
                            progress=$((${linecount} * 100 / ${linetotal}))
                            show_progress "${progress}"
                            idx2=0
                        fi
                    else
                        # update file date of recfile
                        touch "${packfile}"

                        # every 25th loop update progress
                        if [ "${output}" = "--tty" ] &&
                           [ ${idx2} -eq 25 ]
                        then
                            progress=$((${linecount} * 100 / ${linetotal}))
                            show_progress "${progress}"
                            idx2=0
                        fi
                        idx2=$((${idx2} + 1))
                    fi

                    # add file to the local index file
                    if [ -f "${packfile}" ]
                    then
                        local localurl="${packfile}.info"
                        if [ -n "${relocate_url}" ]
                        then
                            localurl=$(echo ${localurl} | \
                               sed -e "s#^${package_dir}#${relocate_url}#g")
                        fi

                        printf "%-20s %-10s %-12s %s %s\n" \
                            "${package}"  \
                            "${version}"  \
                            "${status}"   \
                            "${localurl}" \
                            "${system}" >> "${package_dir}/index.txt"
                    fi
                fi
            fi
        elif [ "${line:0:17}" = "# required_base: " ]
        then
            {
                echo "### BEGIN BASE INFO"
                echo "${line}"
                echo "### END BASE INFO"
            } >> "${package_dir}/index.txt"
        fi
    done < /tmp/index.txt

    # we are done !
    # ${output} --tty
    if [ "${output}" = "--tty" ]
    then
        show_progress 100
        echo
    fi
    mecho ${output} --info 'Done!'
}


# *******************************************************************
# **  clean up all packages older than 90 days. Since the mtime of
# **  all info files existing in the index is set to the current time
# **  upon update, only the mtime of old packages becomes more and
# **  more out of date.
# **  @param  $1  package directory
# **  @return n/a
# *******************************************************************

clean_up_old_packages()
{
    local package_dir="${1}"
    local files=$(find ${package_dir} -mtime +90 -name "*.info")
    local OLDIFS=${IFS}

    IFS="
"
    for file in ${files}
    do
        rm    -f "${file}"
        rm    -f "${file:0:$((${#file} - 5))}"

        rmdir -p "$(dirname ${file})" 2> /dev/tmp
    done

    IFS="${OLDIFS}"
}


# *******************************************************************
# **  main script
# **  @param  n/a
# **  @return n/a
# *******************************************************************

main()
{
    local package_dir
    local relocate_url
    local download_url

    # output shows messages in [--tty or--file] format
    # --file ist set with --quiet
    output='--tty'

    while [ "${1:0:2}" = "--" ]
    do
        case ${1} in
        --help)
            show_help
            exit 0
            ;;
        --no-progress|--quiet)
            output='--file'
            shift
            ;;
        --url=*)
            download_url="${1:6}.*"
            shift
            ;;
        --url)
            if [ "$#" -gt 0 ]
            then
                download_url="${1}"
            else
                echo "error: missing argument for \"--url\"!" >&2
                echo
                show_help
            fi
            shift
            ;;
        --relocate=*)
            relocate_url="${1:11}.*"
            shift
            ;;
        --relocate)
            shift
            if [ "$#" -gt 0 ]
            then
                relocate_url="${1}"
            else
                echo "error: missing argument for \"--relocate\"!" >&2
                echo
                show_help
            fi
            shift
            ;;
        *)
            echo "error: unknown option \"${1}\"!" >&2
            echo
            show_help
            exit 1
            ;;
        esac
    done

    # read package directory
    package_dir="${1}"
    if [ -z "${package_dir}" ]
    then
        echo 'error: missing local package directory!' >&2
        echo
        show_help
        exit 1
    fi
    shift

    # check if all arguments have been processed
    if [ "$#" -gt 0 ]
    then
        echo 'error: invalid number of arguments!' >&2
        echo
        show_help
        exit 1
    fi

    if [ -n "${download_url}" ]
    then
        # get index according to specified url from command line
        if ! ${wget} -t 1 -q --spider "${download_url}"
        then
            if ! ${wget} -t 1 -q --spider "${download_url}/index.txt"
            then
                if ! ${wget} -t 1 -q --spider "$(dirname ${download_url})/index.txt"
                then
                    echo 'error: failed to download package index!' >&2
                    exit 1
                fi
            fi
        fi
    else
        # get index according to configured package sources
        /var/install/bin/get-index --clear
    fi

    # create info file path
    if ! mkdir -p "${package_dir}"
    then
        echo 'error: unable to create package directory!' >&2
        exit 1
    fi

    # read index line by line and fetch new info files
    update_local_mirror "${package_dir}" "${relocate_url}"

    # remove info files older than 90 days
    clean_up_old_packages "${package_dir}"
}

main "${@}"
