#!/bin/bash
# barium helper scripts
# author: rosalinux.ru: betcher_
# getmod is a fork of pfs-utils getpfs

DEVNULL=''
INSTALL_DIR="/.memory/layer-base/0/modules"
SORT_STR='-t = -bk2,2n -bk3,3n -bk4,4n'  # параметры сортировки

if [ -f $(dirname $0)/lib ] ;  then
    . $(dirname $0)/lib
else 
    . $(which lib) || exit 1
fi 

#$1 modname
get_requires() {
    local deplist
    deplist=$(barium modinfo  $1 |grep ^Dependenses: |sed -e 's/^Dependenses://' -e 's/,/ /g')
    [ -z "$deplist" ] && return
    echo "$deplist" |sed 's/-\{2,\}//g' >> "${WORK_DIR}"/requires.lst
    sed -i 's/^[ \t\n]*$//' "${WORK_DIR}"/requires.lst
}

check_net() {
    : > /dev/tcp/ya.ru/80 >/dev/null && return 0
    echo "Вероятно сеть не доступна, вы можете поднять сетевое соединение сейчас и продолжить"
    echo "Прервать $(basename $0)? - (Y/y)"
    read qqq 
    [ "$qqq" == 'y' -o "$qqq" == 'Y' ] &&  exit $LINENO
}

# Сделать реполист для папки $1
# Eсли указать имя с путем для файла реполиста ($2), то будет создан реполист без подписи,
# без сжатия и с прописанными путями к каждому файлу. Это нужно для указания папки с модулями в качестве репы.
# Если указан только $1 будет создан реполист со стандартным именем _REPOLIST.gz
# В случае настроенного gpg, также будет создан отдельный файл ключ _REPOLIST.gz.sig
# Каталог с этими файлами можно целиком синкать на сервер с любым протоколом, без правок реполиста.
# $1 папка
# $2 реполист вне репы
mkrepolist() {
    local reponame REPOLIST DATE SIZE VER REV MD5 LOCAL a NAME PACKNAME \
    MODVER MODREV ALIASES PACKALIASES AUTHOR MODAUTHOR
    reponame="$(realpath $1)"
    REPOLIST="${reponame}/_REPOLIST"
    if [ "$2" ] ; then
     REPOLIST="$2"
     LOCAL=yes
    fi
    : > $REPOLIST
    for a in $(find $reponame -maxdepth 2 -type f -name "*.$EXT") ; do
        if [ "$LOCAL" = yes ] ; then
            SUBPATH_a="file:/$a"
        else 
            SUBPATH_a="$(echo $a |sed "s:${reponame}/::")"
        fi
        unsquashfs -d /tmp/info "$a" -e ${METAINFODIR}/INFO >/dev/null 2>&1
        source /tmp/info/${METAINFODIR}/INFO 2>/dev/null
        DATE="$(ls -la $a --time-style=+%F |cut -d " " -f6)"
        SIZE="$(du -h $a |cut -f1)"
        VER="$MODVER" 
        [ -z "$VER" ] && VER=0
        REV=$MODREV 
        [ -z $REV ] && REV=0
        PACKNAME="$NAME" ; [ -z "$PACKNAME" ] && PACKNAME="$(basename $a)"
        PACKALIASES="$ALIAS" ; [ -z "$PACKALIASES" ] && PACKALIASES="$PACKNAME" 
        MODAUTHOR="$AUTHOR" ; [ -z "$MODAUTHOR" ] && MODAUTHOR='unknown'
        MD5="$(md5sum $a |awk '{print $1}')"
        echo "$SUBPATH_a VER=$VER REV=$REV DATE=$DATE NAME=$PACKNAME \
        ALIASES=$PACKALIASES AUTHOR=$MODAUTHOR SIZE=$SIZE MD5=$MD5 " >> "$REPOLIST"
        unset VER REV NAME PACKNAME MODVER MODREV DATE SIZE MD5 ALIASES PACKALIASES AUTHOR MODAUTHOR
        rm -rf /tmp/info 2>/dev/null
    done 
    if [ "$LOCAL" != yes ] ; then
        gzip $REPOLIST 2>/dev/null && REPOLIST=${REPOLIST}.gz
        gpg --detach-sign --output $REPOLIST.sig $REPOLIST 2>/dev/null
    fi
    [ -f $REPOLIST ] && echo $REPOLIST
}

# склеивает списки в форматах txt и txt.gz в один
make_full_repolist() {
    :> ${WORK_DIR}/sfs-full-repolist
    for a in $(find ${WORK_DIR}/lists -type f -name *.list ) ; do
        zcat $a  2>/dev/null || cat $a 
    done |sort |uniq |awk '$1=$1' >> ${WORK_DIR}/sfs-full-repolist
} 

# поиск модуля с самой свежей версией
# $1 шаблон поиска для grep (ищет только отдельное слово)
find_mods() {
    new=$(cat ${WORK_DIR}/sfs-full-repolist | grep -wi "$1" |sort $SORT_STR |head -n1)
if [ "$2" == new ] ; then
    echo "$new" 
    return
elif [ "$2" == all ] ; then
    cat ${WORK_DIR}/sfs-full-repolist | grep -wi "$1" |sort $SORT_STR  | grep -v "$new" | cut -d " " -f1 
fi
}

# поиск по синонимам
# $1 что ищем
# $2 файл с синонимами
find_alias() {
    FIND=$(grep -m1 -w "$1" $2 | awk '{print $1'} )
    # поиск с учетом ошибок написания если есть agrep
    [ -z $FIND -a  "$(command -v agrep 2>/dev/null)" ] && FIND=$( agrep -I 1 -D 1 -E 1 -w "$1" $2 | head -n 1 | awk '{print $1'})
    [ $FIND ] || return
    FIND=$(echo $FIND |sed 's/^_//')
    LIST=$(grep _${FIND} $2 |sed 's/^_[^\ ]*\ //')
echo ${FIND}:  "$LIST"
}

checksign() {
    local a ans
    for a in ${PUBKEYS}/*.gpg ;do
        if gpg --verify --keyring $a "$1" "$2" ;then
            echo "File $(basename $2) signed by $(basename $a)"
        return 0
        fi
    done
    echo "Incorrect signature in $(basename $1)"
    echo "Continue: (y/n)"
    read ans
    [ $ans != "y" -a $ans != "Y" ] && exit 1
}

update_repolists() {
local file sign_file ans a 
n=1
for a in $(cat $mirror_list) ; do
    echo $a |grep -q "^[[:space:]]*#" && continue
    file='' ; sign_file=''
    if [ -d $a ] ; then
        file="$(mkrepolist $a ${WORK_DIR}/lists/${n}.list)"
    elif [ -f $a ] ; then
        file="$(getfile $a ${WORK_DIR}/lists/${n}.list)"
    else 
        file="$(getfile $a ${WORK_DIR}/lists/${n}.list)"
        sign_file="$(getfile ${a}.sig ${WORK_DIR}/lists/${n}.list.sig)"
        if ! [ -f "$file" ] ; then
            echo -e "Cannot download repository list \n $a"
            continue
        fi
        if [ -f "$sign_file" ] ; then
            echo "check $sign_file <--> $file"
            checksign "$sign_file" "$file"
        else
            echo "check $sign_file <--> $file"
            echo "Repository list was not signed, or signature was not found"
            echo "Continue: (y/n)"
            read ans
            [ $ans != "y" -a $ans != "Y" ] && exit 1 
        fi
        add_repo_path "$file" "$a"
    fi 
    [ "$GUIMODE" != "on" ] && echo "$a --> $file" 
    n=$(( $n + 1 ))
done
}

#добавляем пути до репы в реполист
#$1 локальный реполист
#$2 реполист с путем до сервера
add_repo_path() {
    local path 
    path="$(dirname $2)"
    # если не нужны листы в формате gz можно sed -i (без временного файла)
    zcat "$1" >/tmp/tmpfilelist 2>/dev/null || cat "$1" >/tmp/tmpfilelist
    cat /tmp/tmpfilelist | sed -r '/^[a-z]{3,6}:\/\//!s#^#'$path'/#' >  "$1"
    rm -f /tmpfilelist
}
HLP() {
echo "
Usage:
    $(basename $0) mod_name     - find and download module
    $(basename $0) -m dir       - create repo lists for local dir
    $(basename $0) -u           - update repo lists from sever
    
Keys:
    -u | --update-media     - update repolists
    -f | --force            - do not ask something
    -s | --search           - search only
    -g | --guimode          - formatting the output to use in wrapers
    -m | --mkrepolist       - make repolist for directoty with modules
    -h | --help             - this help
    -b | --build            - build the module by chroot2mod, when downloading a script mod.c2m instead mod.$EXT
    -o | --outdir           - set the dirname to module
    -i | --instal           - same as -o -b
"
exit 1
}

#################### Начало ##############################
separ=''
sourcelist=''
while [ -n "$1" ] ; do
  case "${1}" in
    "-u" | "--update-media" ) UPDATE_M="on";;
    "-f" | "--force" ) FORCE="on";;
    "-s" | "--search" ) SEARCH="on";;
    "-g" | "--guimode" ) GUIMODE="on";;
    "-m" | "--mkrepolist" ) MKREPOLIST="on";;
    "-h" | "--help")  HLP ;;
    "-b" | "--build" ) BUILD="on";;
    "-i" | "--install") OUTDIR="$INSTALL_DIR"
                       BUILD="on" ;;
    "-o" | "--outdir" ) shift ; OUTDIR="${$1}";;
    "-"*[A-Za-z]*) echo "$(basename "$0"): invalid option -- '$(echo ${arg} | tr -d '-')'" >&2; HLP ;;
    *) sourcelist="${sourcelist} ${1}"
  esac
  shift
done

[ "$GUIMODE" = "on" ] && DEVNULL=">/dev/null 2>&1"

# если --mkrepolist создаем _REPOLIST для указанной папки в ней же
# и выходим. Для создания реполистов на сервере.
if [ "$MKREPOLIST" ] ; then
    mkrepolist $sourcelist
    exit
fi

[ "$SEARCH" ] || check_net

mkdir -p ${WORK_DIR}/lists

# если ключ -u или холодный старт создаем списки
if [ "$UPDATE_M" == "on" -o ! -f ${WORK_DIR}/sfs-full-repolist ] ; then
    [ "$GUIMODE" != "on" ] && echo '#########################################################################'
    rm -rf ${WORK_DIR}/lists/*
    update_repolists
    make_full_repolist
    [ "$GUIMODE" != "on" ] && echo '#########################################################################' 
fi
[ -z "$sourcelist" ] && exit
#Обнуляем списки загрузки
:> ${WORK_DIR}/download.lst
:> ${WORK_DIR}/requires.lst
for reg in $sourcelist ; do
    unset MODPATH MODULE  MODULES MAYBE MD5
    OVL_FOUND=$(barium ls  --raw '$bname_source' |grep -w "$reg")
    # если передано имя модуля, то достаем из него файл INFO и ищем NAME вместо поиска переданного
    # это для обновления 
    if [ -f "$reg" ] ; then 
        MODULE="$(basename $reg)"
        unsquashfs -d /tmp/info "$reg" -e ${METAINFODIR}/INFO >/dev/null 2>&1
        [ -f /tmp/info/${METAINFODIR}/INFO ] && reg=$(cat /tmp/info/${METAINFODIR}/INFO |grep NAME |sed 's/NAME=//')
        rm -rf /tmp/info
    fi
    [ ! -f "$reg" ] && MODULE=$(find_mods $reg new)
    [ ! -f "$reg" ] && MODULES=$(find_mods $reg all)
    [ -z "$MODULE" ] && MAYBE=$(find_alias $reg $ALIASCFG)
    MODPATH="$(echo $MODULE |cut -d " " -f1)"
    echo $MODULE |grep -q 'MD5=' && MD5=$(echo $MODULE |sed 's/.*MD5=//' |cut -f1)
    if [ "$FORCE" = "on" ] ; then
        if [ -z "$MODULE" ] ; then
            echo "Модуль не найден, измените шаблон,повторите поиск"
            exit
        fi
        echo "$MODPATH" "$MD5" >> ${WORK_DIR}/download.lst
    elif [ "$GUIMODE" = "on" ] ; then
        echo "maybe:>> $MAYBE"   
        echo "new:>> $MODPATH"
        for b in $MODULES ; do echo "old:>> $b" ; done
    else
        if [ -n "$OVL_FOUND" ] ; then
            # если подключен такой модуль предупреждаем
            echo "Модуль подходящий под шаблон $reg - $OVL_FOUND подключен в данный момент"
            read qqq 
            [ "$qqq" == 'y' -o "$qqq" == 'Y' ] &&  exit $LINENO
        fi
        if [ -z "$MODULE" ] ; then
            if [ -z "$MAYBE" ] ; then
                echo "$reg Не найден в репозитории. Измените шаблон,повторите поиск"
            else
                echo "Возможно вы искали:"
                echo "==> $MAYBE"
                echo "Измените шаблон,повторите поиск"
            fi
        else
            if [ $(echo $MODULES |wc -w) -ge 1 ] ; then
                echo -e "\nПодходят под шаблон - \"$reg\":"
                for b in $MODULES ; do echo "==> $b" ; done
            fi
            echo ''
            echo -e "\nВероятно вы искали это:" 
            echo -n "==> "
            for item in $(echo $MODULE) ; do
                echo $item
            done
            [ $SEARCH ] || echo "загружать?  (Y/N)"
            if [ "$SEARCH" == "on" ] ; then d=Y ; else  read d ; fi
            if [ "$d" != Y -a "$d" != y ] ; then
                echo "Уточните шаблон поиска и повторите"
                continue
            fi
        fi
    fi
    [ -z "$MODPATH" ] || echo "$MODPATH" "$MD5" >> ${WORK_DIR}/download.lst
done

[ "$SEARCH" = "on" ] && exit
[ "$OUTDIR" ] && TARGET_PATH=$OUTDIR
[ "$TARGET_PATH" ] || TARGET_PATH="./"
if ! [ -w $TARGET_PATH ] ; then
    echo "ERROR: '$TARGET_PATH' не доступен для записи"
    exit ${LINENO}
fi
cat ${WORK_DIR}/download.lst |while read a ;do
    MOD='' ; MD5='' ; CHECKMD5=no ; DMOD=''
    DMOD=$(echo $a |awk '{print $1}')
    MD5=$(echo $a |awk '{print $2}')
    MOD=$(getfile "$DMOD" $TARGET_PATH/$(basename $DMOD))
    if [ -n "$MD5" ] ; then
        CHECKMD5=checked
        md5sum "$MOD" |grep -q $MD5 || CHECKMD5=error
    fi
    if  [ -f "$MOD" -a "$CHECKMD5" != "error" ] ; then
        [ "$GUIMODE" != "on" ] && echo "==> $(basename $MOD) - загружен успешно. (проверка md5 - $CHECKMD5)"
        [ "$GUIMODE" = "on" ] && echo "download:>> $MOD"
    else
        [ "$GUIMODE" != "on" ] && echo "==> $(basename $MOD) - ошибка загрузки!!! (проверка md5 - $CHECKMD5, $MD5)"
    fi
    if [  "$BUILD" == on -a ${MOD: -4} == '.c2m' ] ; then
        SCRIPT="$MOD"
        MOD=$(echo $MOD |sed 's/\.c2m$/\.'$EXT'/')
        echo "barium chroot2mod -o "$MOD" --script $SCRIPT"
        read qqq
        barium chroot2mod -o "$MOD" --script $SCRIPT
        [ -f $MOD ] || exit 1
    fi
    get_requires $MOD

done
if ! [ -z "$(cat ${WORK_DIR}/requires.lst)" ] ;then
    reqlist=''
    allreqlist=$(cat ${WORK_DIR}/requires.lst)
    if ! [ -z "$allreqlist" ] ; then
    [ "$GUIMODE" != "on" ] && echo "Список зависимостей: $(echo $allreqlist)"
        for req in $(cat ${WORK_DIR}/requires.lst) ; do
            barium lsovl --raw '$bname_source' |grep -wq "$req" && eval echo "$req - уже подключен" $DEVNULL && continue
            find $TARGET_PATH -maxdepth 1 -type f -name "*${req}*" |grep -wq "$req" && eval echo "$req - уже загружен" $DEVNULL && continue
            reqlist="$reqlist $req"
        done
    fi
    M='-f'; [ "$GUIMODE" = "on" ] && M='-g'
    if [ -n "$reqlist" ] ; then
        [ "$GUIMODE" != "on" ] && echo "Из них отсутствуют: $reqlist, загружаем"
        $0 $L $M -o $TARGET_PATH $reqlist
    fi
fi
