#!/bin/bash

ROOT="/opt/share/dpkg"
ARCHIVES_CFG=".archives"
#KEYID=13487DA6
KEYID=9DF9F24A
ORIGIN=pestinger.net
FORCE=0
SCAN=0
UPDATE=
UPDATE_LINK=
VERBOSE=0

#
#	Distribution	Release		Component
#
#ARCHIVES=(\
#	debian		sarge		backports \
#	debian		lenny		export \
#	debian		squeeze		export \
#	debian		squeeze		zarafa \
#	debian		wheezy		zarafa \
#	debian		jessie		extra \
#	debian		stretch		extra \
#	debian		buster		extra \
#	ubuntu		lucid-lynx	zarafa \
#	ubuntu		precise		extra \
#	ubuntu		trusty		extra \
#	ubuntu		xenial		extra \
#	ubuntu		bionic		extra \
#	ubuntu		focal		extra \
#)
unset ARCHIVES

function Usage
{
	echo -e "\n$(basename $0) [-f][-r <root-path>][-s][-u <dpkg-path>]\n"
	echo -e "\t-f\t\tForce (re-)generation of archives"
	echo -e "\t-r <root-path>\tDefine archives root path [${ROOT}]"
	echo -e "\t-s\t\tScan for archives and generate archive configuration file '<root>/.archives'"
	echo -e "\t-u <dpkg-path>\tUpgrade existing instance of specified packet in all pools\n"
	exit ${1:-0}
}

function Abend 
{
	local msg=$1
	local ret=${2:-9}
	echo -e "\n$(basename $0) - ABEND: ${msg}\n" 1>&2
	exit ${ret}
}

function UpdateArchive
{
	local DIST=$1
	local REL=$2
	local COMP=$3

	echo "Processing Distribution: $DIST Release: $REL Component: $COMP"
	#
	#	Check whether the request repository is existent...
	#
	if [ ! -e "${ROOT}/${DIST}/pool/${REL}/${COMP}" ]; then
		echo "SKIP processing - non-existent component pool!"
		return
	fi
	pushd "$ROOT/$DIST" > /dev/null
	#
	#	In case of update option search for old packet and replace it...
	#
	if [ -n "$UPDATE" ]; then
		PKG_FULL_NAME=$(basename $UPDATE .deb)
		[[ $PKG_FULL_NAME =~ ([^_]+)_([^_]+)_([^_]+) ]]
		local NAME=${BASH_REMATCH[1]}
		local VERS=${BASH_REMATCH[2]}
		local ARCH=${BASH_REMATCH[3]}
		echo -e "NAME: $NAME\tVERS: $VERS\tARCH: $ARCH"
		local FLAG=
		for f in ./pool/${REL}/${COMP}/${NAME}_*.deb; do
			if [ -e $f ]; then
				[[ $(basename $f .deb) =~ ([^_]+)_([^_]+)_([^_]+) ]]
				local VREF=${BASH_REMATCH[2]}
				if $(dpkg --compare-versions $VREF lt $VERS); then
					echo -e "FOUND:\t$f"
					if [ -z "$FLAG" ]; then
						if [ -z "$UPDATE_LINK" ]; then
							cp $UPDATE ./pool/$REL/$COMP/
							UPDATE_LINK=$(realpath "./pool/$REL/$COMP/$PKG_FULL_NAME.deb")
						else
							ln "$UPDATE_LINK" "./pool/$REL/$COMP/$PKG_FULL_NAME.deb"
						fi
					fi
					rm $f
				fi
			fi
		done
	fi
	#
	# Test all pool's for updates
	#
	if [ $FORCE -gt 0 ] || [ ./pool/$REL/$COMP -nt ./dists/$REL/Release ] || [ ./pool/$REL/$COMP -nt ./dists/$REL/$COMP ]; then
		CONF=$ROOT/$DIST/.config
    local ARCH_CSV="i386,amd64"
    if [ -e "${CONF}/arch" ]; then
      ARCH_CSV="$(cat ${CONF}/arch)"
    else
      echo "${ARCH_CSV}" > ${CONF}/arch
    fi
    local ARCH_STR=${ARCH_CSV//,/ }

		echo "Making directories $ROOT/$DIST/{.cache,.config,dists/$REL/$COMP/{source,binary-{$ARCH_CSV}},pool/$REL/$COMP}"
		
#		Abend "DEBUG - END-OF-TEST - DEBUG" -1
#		
#		mkdir -p $ROOT/$DIST/{.cache,.config,dists/$REL/$COMP/{source,binary-{$ARCH_CSV}},pool/$REL/$COMP}
#
#   FixMe: the {,} expansion isn't working anymore => creating the architecture dependend binaries in extra loop ! 
#
		mkdir -p $ROOT/$DIST/{.cache,.config,dists/$REL/$COMP/source,pool/$REL/$COMP}

		for arch in ${ARCH_STR}; do
		  mkdir -p $ROOT/$DIST/dists/$REL/$COMP/binary-$arch
			PACK=$CONF/$REL.$COMP.$arch
			echo creating $PACK.packages
			apt-ftparchive --arch $arch packages pool/$REL/$COMP > $PACK.packages
			echo creating $PACK.list
			gawk '/^Filename:/ { print $2; }' $PACK.packages > $PACK.list
		done

		echo creating $CONF/$REL.ftparchive.conf
		cat <<- EOF > $CONF/$REL.ftparchive.conf
			Dir {
				ArchiveDir "$ROOT/$DIST";
				CacheDir "$ROOT/$DIST/.cache";
				FileListDir "$ROOT";
			};
			Default {
				Packages::Compress ". gzip bzip2";
				Sources::Compress ". gzip bzip2";
				Contents::Compress ". gzip bzip2";
			};
			TreeDefault {
				BinCacheDB "packages-\$(SECTION)-\$(ARCH).db";
				Directory "pool/$REL/$COMP";
				SrcDirectory "pool/$REL/$COMP";
				Contents "\$(DIST)/\$(SECTION)/Contents-\$(ARCH)";
				Packages "\$(DIST)/\$(SECTION)/binary-\$(ARCH)/Packages";
				SrcPackages "\$(DIST)/\$(SECTION)/source/Sources";
				FileList "$CONF/$REL.$COMP.\$(ARCH).list";
			};
			Tree "dists/$REL" {
				Sections "$COMP";
				Architectures "source $ARCH_STR";
			};
		EOF

#			SourceFileList "$ROOT/$DIST.$REL.$COMP.source.list";


#			BinDirectory "dists/$REL/$COMP/binary-i386" {
#				Packages "dists/$REL/$COMP/binary-i386/Packages";
#				Contents "dists/$REL/Contents-i386";
#				SrcPackages "dists/$REL/$COMP/source/Sources";
#			};
#			BinDirectory "dists/$REL/$COMP/binary-amd64" {
#				Packages "dists/$REL/$COMP/binary-amd64/Packages";
#				Contents "dists/$REL/Contents-amd64";
#				SrcPackages "dists/$REL/$COMP/source/Sources";
#			};

		echo generating archive
		apt-ftparchive clean $CONF/$REL.ftparchive.conf
		apt-ftparchive generate $CONF/$REL.ftparchive.conf
		
    COMPONENTS=
    for f in $ROOT/$DIST/dists/$REL/*; do
      [ -d $f ] && COMPONENTS="$COMPONENTS $(basename $f)"
    done
    COMPONENTS=${COMPONENTS## }

		echo creating $CONF/$REL.release.conf
		cat <<- EOF > $CONF/$REL.release.conf
			APT::FTPArchive::Release::Archive "$REL";
			APT::FTPArchive::Release::Origin "$ORIGIN";
			APT::FTPArchive::Release::Label "$DIST";
			APT::FTPArchive::Release::Suite "$REL";
			APT::FTPArchive::Release::Codename "$REL";
			APT::FTPArchive::Release::Architectures "source $ARCH_STR";
			APT::FTPArchive::Release::Components "$COMPONENTS";
			APT::FTPArchive::Release::Description "Custom Repository - Label: $DIST Archive: $REL from Origin: $ORIGIN - hostmaster@$ORIGIN";
		EOF

		echo generating Release file
		RELDIR=$ROOT/$DIST/dists/$REL
		RELFILE=$RELDIR/Release
		apt-ftparchive -c $CONF/$REL.release.conf release $RELDIR > $RELFILE
		[ -f $RELFILE.gpg ] && rm $RELFILE.gpg
		gpg --detach-sign --armor --output $RELFILE.gpg --local-user $KEYID --sign $RELFILE
	else
		echo "SKIP processing - target is up-to-date!"
	fi
	popd > /dev/null
}

#
# Scan Archives
#

function ScanArchives
{
	echo "Scan Archives: ${ROOT}"
	unset ARCHIVES
	for d in ${ROOT}/*; do
	  if [ -d "${d}/pool" ]; then
      [[ ${VERBOSE} -eq 0 ]] || echo "DIR: $d"
      local dist=${d##*/}
      [[ ${VERBOSE} -eq 0 ]] || echo "DIST: ${dist}"
	    for r in ${d}/pool/*; do
        if [ -d ${r} ]; then
          local rel=${r##*/}
          #
          # In case the REL found in 'pool' is defined in 'dists' as well, 
          # the repository is assumed as well-formed and will be added to the archive list
          #
          if [ -e "${d}/dists/${rel}" ]; then
  	        [[ ${VERBOSE} -eq 0 ]] || echo "REL: ${rel}"
            for c in ${d}/pool/${rel}/*; do
              if [ -d ${c} ]; then
                local comp=${c##*/}
                [[ ${VERBOSE} -eq 0 ]] || echo "COMP: ${comp}"
                [[ ${VERBOSE} -le 1 ]] || echo -e "${dist}\t${rel}\t${comp}"
                ARCHIVES[${#ARCHIVES[@]}]=${dist}
                ARCHIVES[${#ARCHIVES[@]}]=${rel}
                ARCHIVES[${#ARCHIVES[@]}]=${comp}
              fi
            done
  	      fi
  	    fi
	    done
	  fi 
	done
	echo "${ARCHIVES[@]}" > ${ROOT}/${ARCHIVES_CFG}
}

#
#	Process cmd-line-options
#
while getopts ":fhr:su:v" opt; do
	case ${opt} in
	  f )
	    FORCE=1
	    ;;
		h )
			Usage 0
			;;
		r )
		  ROOT=$(realpath ${OPTARG})
		  ;;
		s )
		  SCAN=1
		  ;;
		u )
			UPDATE=$(realpath ${OPTARG})
			;;
		v )
			((VERBOSE += 1))
			;;
		\? )
			Abend "Invalid Option: -${OPTARG}" 2
			;;
	esac
done
shift $((OPTIND - 1))
#
#	Verify and setup environment
#
[ -e ${ROOT} ] || Abend "No access to DPKG repository [${ROOT}]!" 3
if [ ${#ARCHIVES[@]} -lt 3 ]; then
  [ -e ${ROOT}/${ARCHIVES_CFG} ] && IFS=', ' read -r -a ARCHIVES < ${ROOT}/${ARCHIVES_CFG}
fi
[ ${#ARCHIVES[@]} -ge 3 ] || Abend "No archives defines - please use scan option!" 4
#
# Scan option
#
[ $SCAN -gt 0 ] && ScanArchives
#
#	Start processing
#
for (( i = 0 ; i < ${#ARCHIVES[@]}-1; i += 3 ))
do
	DIST=${ARCHIVES[$i]}
	REL=${ARCHIVES[$i+1]}
	COMP=${ARCHIVES[$i+2]}
	UpdateArchive $DIST $REL $COMP
done

