#!/bin/sh

# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide.
# This software is distributed without any warranty.

# You should have received a copy of the CC0 Public Domain Dedication
# along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

. /etc/repoctl.conf

function isingroup()
{
    local grp="$1"
    for group in `groups`
    do if [ "$grp" = "$group" ]
    then 
	return 0
    fi
    done
    return 1
}

function inlist()
{
    local k="$1"
    local list="$2"
    for e in $list
    do
	if [ a"$k" = a"$e" ]
	then
	    return 0
	fi
    done
    return 1
}

function get_media_path()
{
    local distrorelease="$1"; shift
    local section="$1"; shift
    local sectionrepo="$1"; shift
    local arch="$1"; shift
    echo "$distribdir/$distrorelease/$arch/media/$section/$sectionrepo"
}

function get_media_path_debug()
{
    local distrorelease="$1"; shift
    local section="$1"; shift
    local sectionrepo="$1"; shift
    local arch="$1"; shift
    echo "$distribdir/$distrorelease/$arch/media/debug/$section/$sectionrepo"
}

function get_media_path_src()
{
    local distrorelease="$1"; shift
    local section="$1"; shift
    local sectionrepo="$1"; shift
    echo "$distribdir/$distrorelease/SRPMS/$section/$sectionrepo"
}

function set_hdlists_needupdate()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"
    touch "$hdlistsdir/$distrorelease-$section-$sectionrepo"
}

function get_hdlists_needupdate()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"
    test -f "$hdlistsdir/$distrorelease-$section-$sectionrepo"
}

function rm_hdlists_needupdate()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"
    rm -f "$hdlistsdir/$distrorelease-$section-$sectionrepo"
}

function update_hdlists_if_needed()
{
    for distrorelease in $distroreleases
    do
	for section in $distrosections
	do
	    for sectionrepo in $sectionsrepos
	    do
		if get_hdlists_needupdate "$distrorelease" "$section" "$sectionrepo"
		then
		    update_hdlists "$distrorelease" "$section" "$sectionrepo"
		fi
	    done
	done
    done
}

function update_hdlist()
{
    local repository="$1"
    local fdeps="$repository/../../media_info/file-deps"
    test -f "$fdeps" && ofdeps="--file-deps $fdeps"
    $dryrun /usr/bin/genhdlist2 -v --versioned --allow-empty-media $ofdeps "$repository"
}

function update_hdlists()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"

    check_distro_section "$distrorelease" "$section" "$sectionrepo"
    get_repo_lock "$distrorelease" "$section" "$sectionrepo"
    rm_hdlists_needupdate "$distrorelease" "$section" "$sectionrepo"
    rm -f "$hdlistsdir/$distrorelease-$section-$sectionrepo"
    for arch in $arches
    do
	update_hdlist "$distribdir/$distrorelease/$arch/media/$section/$sectionrepo"
	update_hdlist "$distribdir/$distrorelease/$arch/media/debug/$section/$sectionrepo"
    done
    update_hdlist "$distribdir/$distrorelease/SRPMS/$section/$sectionrepo"
    rm_repo_lock "$distrorelease" "$section" "$sectionrepo"
}

function get_lock()
{
    local lockdir="$1"
    local waitdir="$lockdir/wait.$$"

    if mkdir "$lockdir"
    then
	return
    else
	if ! mkdir "$waitdir"
	then
	    get_lock "$lockdir"
	    return
	fi
	inotifywait -e delete_self "$waitdir"
    fi
}

function rm_lock()
{
    local lockdir="$1"
    if rmdir "$lockdir"
    then
	return
    fi
    local l=$(ls -tr "$lockdir" | head -1)
    rmdir "$lockdir/$l"
}

function get_repo_lock()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"
    repolock="$lockdir/$distrorelease-$section-$sectionrepo.lock"
    get_lock "$repolock"
}

function rm_repo_lock()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"
    repolock="$lockdir/$distrorelease-$section-$sectionrepo.lock"
    rm_lock "$repolock"
}

function get_distro_lock()
{
    local distrorelease="$1"
    local distrolock="$lockdir/$distrorelease.lock"
    get_lock "$distrolock"
    for distrosection in $distrosections
    do
	for sectionrepo in $sectionsrepos
	do
	    get_repo_lock $distrorelease $distrosection $sectionrepo
	done
    done
}

function rm_distro_lock()
{
    local distrorelease="$1"
    local distrolock="$lockdir/$distrorelease.lock"
    for distrosection in $distrosections
    do
	for sectionrepo in $sectionsrepos
	do
	    rm_repo_lock $distrorelease $distrosection $sectionrepo
	done
    done
    rm_lock "$distrolock"
}

function get_mirror_lock()
{
    local mirrorlock="$lockdir/mirror.lock"
    get_lock "$mirrorlock"
    for release in $distroreleases
    do
	get_distro_lock $release
    done
}

function rm_mirror_lock()
{
    local mirrorlock="$lockdir/mirror.lock"
    for release in $distroreleases
    do
	rm_distro_lock $release
    done
    rm_lock "$mirrorlock"
}

function update_common_MD5SUM()
{
    local distrorelease="$1"
    for arch in $arches
    do
	pushd "$distribdir/$distrorelease/$arch/media/media_info"
	if [ -z "$dryrun" ]
	then
	    /usr/bin/md5sum hdlist_* synthesis.* > MD5SUM
	fi
	popd
    done
}

function update_mirror_timestamp()
{
    date +%s%n%c > "$rootdir/$timestampfile"
}

function update_mirror_sha1sum()
{
    pushd "$rootdir"
    sha1sum "$timestampfile" > "$sha1sumfile"
    for distrorelease in $distroreleases
    do
	for arch in $arches
	do
	    for file in media.cfg hdlists compssUsers.pl rpmsrate
	    do
		sha1sum "distrib/$distrorelease/$arch/media/media_info/$file" >> "$sha1sumfile"
	    done
	done
	for section in $distrosections
	do
	    for sectionrepo in $sectionsrepos
	    do
		for arch in $arches
		do
		    sha1sum "distrib/$distrorelease/$arch/media/$section/$sectionrepo/media_info/MD5SUM" >> "$sha1sumfile"
		    sha1sum "distrib/$distrorelease/$arch/media/$section/$sectionrepo/media_info/pubkey" >> "$sha1sumfile"
		    sha1sum "distrib/$distrorelease/$arch/media/debug/$section/$sectionrepo/media_info/MD5SUM" >> "$sha1sumfile"
		    sha1sum "distrib/$distrorelease/$arch/media/debug/$section/$sectionrepo/media_info/pubkey" >> "$sha1sumfile"
		done
		sha1sum "distrib/$distrorelease/SRPMS/$section/$sectionrepo/media_info/MD5SUM" >> "$sha1sumfile"
		sha1sum "distrib/$distrorelease/SRPMS/$section/$sectionrepo/media_info/pubkey" >> "$sha1sumfile"
	    done
	done
    done
    $dryrun $sign_mirror_sha1sum > "$sha1sumsigfile"
    popd
}

function mirror_repository()
{
    local distrorelease="$1"
    if ! inlist "$distrorelease" "$distroreleases"
    then
	echo "Incorrect distrorelease $distrorelease" >&2
	exit 1
    fi
    get_distro_lock "$distrorelease"
    update_common_MD5SUM "$distrorelease"
    $dryrun /usr/bin/rsync $mirror_rsync_options "$distribdir/$distrorelease/" "$finaldistribdir/$distrorelease/"
    rm_distro_lock "$distrorelease"
    get_mirror_lock
    if [ -z $dryrun ]
    then
	update_mirror_timestamp
	update_mirror_sha1sum
    fi
    for file in "$timestampfile" "$sha1sumfile" "$sha1sumsigfile"
    do
	$dryrun /usr/bin/rsync $mirror_rsync_options "$rootdir/$file" "$finalrootdir/$file"
    done
    rm_mirror_lock
}

function check_distro_section()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"

    if ! inlist "$distrorelease" "$distroreleases"
    then
	echo "Incorrect distrorelease $distrorelease" >&2
	exit 1
    fi

    if ! inlist "$section" "$distrosections"
    then
	echo "Incorrect section $section" >&2
	exit 1
    fi

    if ! inlist "$sectionrepo" "$sectionsrepos"
    then
	echo "Incorrect repository $sectionrepo" >&2
	exit 1
    fi
}

# This function can be used to move/remove/link a package file
# To remove the file, set dst_* variables to ""
# To link the file, set src_* variables to ""
function move_pkg_file()
{
    local src_distrorelease="$1"; shift
    local src_section="$1"; shift
    local src_sectionrepo="$1"; shift

    local dst_distrorelease="$1"; shift
    local dst_section="$1"; shift
    local dst_sectionrepo="$1"; shift

    local srcfile="$1"; shift
    local destdir="$1"; shift
    local output="$1"; shift

    if [ -n "$dst_distrorelease$dst_section$dst_sectionrepo" ]
    then
	local bn=$(basename "$srcfile")
	get_repo_lock "$dst_distrorelease" "$dst_section" "$dst_sectionrepo"
	set_hdlists_needupdate "$dst_distrorelease" "$dst_section" "$dst_sectionrepo"
	$dryrun ln -v "$srcfile" "$destdir/$bn" >> "$output"
	rm_repo_lock "$dst_distrorelease" "$dst_section" "$dst_sectionrepo"
    fi
    if [ -n "$src_distrorelease$src_section$src_sectionrepo" ]
    then
	get_repo_lock "$src_distrorelease" "$src_section" "$src_sectionrepo"
	set_hdlists_needupdate "$src_distrorelease" "$src_section" "$src_sectionrepo"
	$dryrun rm -f -v "$srcfile" >> "$output"
	rm_repo_lock "$src_distrorelease" "$src_section" "$src_sectionrepo"
    fi
    if [ -z "$dst_distrorelease$dst_section$dst_sectionrepo$src_distrorelease$src_section$src_sectionrepo" ]
    then
        echo "$srcfile" >> "$output"
    fi
}

# This function can be used to move/remove/link package files
# To remove the files, set dst_* variables to ""
# To link the files, set src_* variables to ""
function move_pkg_files()
{
    local src_distrorelease="$1"; shift
    local src_section="$1"; shift
    local src_sectionrepo="$1"; shift

    local dst_distrorelease="$1"; shift
    local dst_section="$1"; shift
    local dst_sectionrepo="$1"; shift

    local srcdir="$1"; shift
    local destdir="$1"; shift
    local srcpkg="$1"; shift
    local output="$1"; shift

    for file in "$srcdir/"*.rpm
    do
        local fname=$(rpm -qp --qf '%{SOURCERPM}' "$file")
        if [ a"$fname" = a"$srcpkg" ]
        then
	    move_pkg_file \
		"$src_distrorelease" "$src_section" "$src_sectionrepo" \
		"$dst_distrorelease" "$dst_section" "$dst_sectionrepo" \
		"$file" "$destdir" "$output"
        fi
    done
}

function find_src_pkg()
{
    local distrorelease="$1"
    local section="$2"
    local sectionrepo="$3"
    local pkgname="$4"

    check_distro_section "$distrorelease" "$section" "$sectionrepo"
    srcdir=$(get_media_path_src "$distrorelease" "$section" "$sectionrepo")
    for file in "$srcdir/"*.rpm
    do
	local fname=$(rpm -qp --qf '%{NAME}' "$file")
	if [ a"$fname" = a"$pkgname" ]
	then
	    basename "$file"
	    return 0
	fi
    done
}

# This function can be used to move or remove a package
# To remove a package, set dst_* variables to ""
function move_pkg()
{
    local src_distrorelease="$1"
    local src_section="$2"
    local src_sectionrepo="$3"
    local dst_distrorelease="$4"
    local dst_section="$5"
    local dst_sectionrepo="$6"
    local srcname="$7"
    local output="$8"

    check_distro_section "$src_distrorelease" "$src_section" "$src_sectionrepo"
    [ -z "$dst_distrorelease$dst_section$dst_sectionrepo" ] \
	|| check_distro_section "$dst_distrorelease" "$dst_section" "$dst_sectionrepo"

    local srcpkg=$(find_src_pkg "$src_distrorelease" "$src_section" "$src_sectionrepo" "$srcname")

    if [ -z $srcpkg ]
    then
	echo "The package $srcname could not be found in $src_distrorelease/$src_section/$src_sectionrepo repository." >&2
	exit 2
    fi

    for arch in $arches
    do
	move_pkg_files \
	    "$src_distrorelease" "$src_section" "$src_sectionrepo" \
	    "$dst_distrorelease" "$dst_section" "$dst_sectionrepo" \
	    $(get_media_path "$src_distrorelease" "$src_section" "$src_sectionrepo" "$arch") \
	    $(get_media_path "$dst_distrorelease" "$dst_section" "$dst_sectionrepo" "$arch") \
	    "$srcpkg" "$output"
	move_pkg_files \
	    "$src_distrorelease" "$src_section" "$src_sectionrepo" \
	    "$dst_distrorelease" "$dst_section" "$dst_sectionrepo" \
	    $(get_media_path_debug "$src_distrorelease" "$src_section" "$src_sectionrepo" "$arch") \
	    $(get_media_path_debug "$dst_distrorelease" "$dst_section" "$dst_sectionrepo" "$arch") \
	    "$srcpkg" "$output"
    done
    move_pkg_file \
	"$src_distrorelease" "$src_section" "$src_sectionrepo" \
	"$dst_distrorelease" "$dst_section" "$dst_sectionrepo" \
	$(get_media_path_src "$src_distrorelease" "$src_section" "$src_sectionrepo")/"$srcpkg" \
	$(get_media_path_src "$dst_distrorelease" "$dst_section" "$dst_sectionrepo") \
	"$output"
}

function ls_pkg()
{
    local src_distrorelease="$1"; shift
    local src_section="$1"; shift
    local src_sectionrepo="$1"; shift
    local srcname="$1"; shift
    local output="$1"; shift

    check_distro_section "$src_distrorelease" "$src_section" "$src_sectionrepo"

    local srcpkg=$(find_src_pkg "$src_distrorelease" "$src_section" "$src_sectionrepo" "$srcname")

    if [ -z $srcpkg ]
    then
	return 0
    fi

    for arch in $arches
    do
	move_pkg_files \
	    "" "" "" \
	    "" "" "" \
	    $(get_media_path "$src_distrorelease" "$src_section" "$src_sectionrepo" "$arch") \
	    "" \
	    "$srcpkg" "$output"
	move_pkg_files \
	    "" "" "" \
	    "" "" "" \
	    $(get_media_path_debug "$src_distrorelease" "$src_section" "$src_sectionrepo" "$arch") \
	    "" \
	    "$srcpkg" "$output"
    done
    move_pkg_file \
	"" "" "" \
	"" "" "" \
	$(get_media_path_src "$src_distrorelease" "$src_section" "$src_sectionrepo")/"$srcpkg" \
	"" \
	"$output"
}

