#!/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
