#!/bin/bash

test "$1" = "--verbose" && { VERBOSE=true ; shift ; }
test "$1" = "--batchmode" && { BATCHMODE=true ; shift ; }
DIR_TO_CHECK=$1
DESTINATIONDIR=$2
OSC_MODE=""
test -n "$DIR_TO_CHECK" || DIR_TO_CHECK=`pwd`
HELPERS_DIR="/usr/lib/obs/service/source_validators/helpers"
$HELPERS_DIR/check_input_filename "$DIR_TO_CHECK" || exit 1
test -z "$DESTINATIONDIR" -a -d "$DIR_TO_CHECK/.osc" && {
	DESTINATIONDIR="$DIR_TO_CHECK/.osc"
	OSC_MODE="true"
}


RETURN=0
RPMBUILD=rpm
test -x /usr/bin/rpmbuild && RPMBUILD=rpmbuild

#
#  cleanup_and_exit
#
cleanup_and_exit () {
    if [ -n "$TMPDIR" ];then
    	rm -rf $TMPDIR
    fi
    exit $1
}

#
# display a warning if the file is not in the spec file sources
#
warn_on_unmentioned_files () {
  grep -a -x $1 $TMPDIR/sources > /dev/null || echo "(W) Attention, $1 is not mentioned in spec files as source or patch."
}

test "$VERBOSE" = true && echo -n "- checking if needed files are present and none stale "
#
# first make my TMPDIR
#
export TMPDIR=`mktemp -d -t check_if_valid_source_dir-XXXXXX 2>/dev/null || mktemp -d /var/tmp/check_if_valid_source_dir-XXXXXX` || cleanup_and_exit 1

#
# now create list of Sources.
#
MY_ARCH=`uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/`
case $MY_ARCH in
  i386)
    MY_ARCH="%ix86"
    ;;
  arm)
    MY_ARCH="%arm"
    ;;
esac

unique_sources() {
	local TMP="$1"
	rm -f "$TMP/unique.sed"
	for i in "source" "patch"; do
		grep -i -n "^$i[[:digit:]]*\s*:" "$2" | while IFS=" :" read N L; do
			# the "i" flag is a GNU extension
			echo "$N s/^$i/$i$N/i" >> "$TMP/unique.sed"
		done
	done
	sed -f "$TMP/unique.sed" -i "$2"
}

for i in $DIR_TO_CHECK/*.spec ; do
        test -f "$i" || continue
	sed -e 's/^\s*//' \
	    -e '/^%if/d' \
	    -e '/^%else/d' \
	    -e '/^%endif/d' "$i" > "$TMPDIR/tmp.spec"

	unique_sources "$TMPDIR" "$TMPDIR/tmp.spec"

	$HELPERS_DIR/spec_sources "$TMPDIR/tmp.spec" "$TMPDIR/sources" \
	    2>"$TMPDIR/sources.err" || cleanup_and_exit 1
	if [ -s "$TMPDIR/sources.err" ]; then
	    echo "Unable to extract sources from spec - spec_sources failed:"
	    cat "$TMPDIR/sources.err"
	    cleanup_and_exit 1
	fi
done
for i in $DIR_TO_CHECK/*.dsc ; do
	test -f "$i" || continue
	( sed -ne '/^Files:/,$p' < "$i" | sed -e 1d | sed -e '/^[^ ]/,$d' | while read debchk debsize debfile ; do echo "$debfile" ; done ) >> $TMPDIR/sources
done

# Add debian only patches to the allowed sources list.
# but strip tailing -p1 arguments
test -f $DIR_TO_CHECK/debian.series && \
  sed -e '/^$/d' -e '/^#/d' -e 's,[ \t]*-p[0123456789]*$,,' \
  $DIR_TO_CHECK/debian.series >> $TMPDIR/sources

test -f $TMPDIR/sources || cleanup_and_exit

#
# check if all Sources, patches and the icon are present
#
touch $TMPDIR/sources.t

for i in `cat $TMPDIR/sources` ; do
	echo "${i##*/}" >> $TMPDIR/sources.t
done
mv $TMPDIR/sources.t $TMPDIR/sources

# XML validate files starting with _..
if [ -x $(type -p xmllint) ]; then
    for i in $DIR_TO_CHECK/_service; do
        test -f $i || continue
        BASE=${i##*/}

        xmllint --format $i >/dev/null || {
            echo "(E) $(basename $i) is not valid XML"
            RETURN=2
            continue
        }

        # Check if _service is sane
        if [ "$BASE" = "_service" ]; then
            xmllint --format $i > $TMPDIR/_service

            if egrep -q "service .*mode=." $TMPDIR/_service \
                    && ! egrep -q "service .*mode=.(disabled|buildtime|explicit|localonly" \
                    $TMPDIR/_service; then
                echo "(W) openSUSE: projects only allow 'disabled', 'buildtime', 'explicit' or 'localonly' services."
            fi
        fi

    done
fi

check_tracked()
{
	local file=${1##*/}

	if test "$OSC_MODE" = "true" ; then
		if test -f "$DESTINATIONDIR/$file"; then
			return 0
		fi
	        if test -f "$DESTINATIONDIR/${file/\.tar\.*/}.obscpio"; then
			# assume it will generated on builtime based of the archive
			return 0
		fi
		if grep -qsFx "$file" "$DESTINATIONDIR/_to_be_added"; then
			return 0
		fi
		echo "(E) $file mentioned in spec file is not tracked."
		return 1
	fi
	if ! test -f "$DIR_TO_CHECK/$file"; then
	        if test -f "$DIR_TO_CHECK/${file/\.tar\.*/}.obscpio"; then
			# assume it will generated on builtime based of the archive
			return 0
		fi
		echo "(E) $file mentioned in spec file does not exist."
		return 1
	fi
}

if ! test -f "$DIR_TO_CHECK/_service"; then
    for HASTOBETHER in `cat $TMPDIR/sources` ; do
        check_tracked "$HASTOBETHER" || RETURN=2
    done
fi

#
# Verify GPG keys
#

if [ -f $DIR_TO_CHECK/*.keyring 2>/dev/null ]; then
    GPG_OPTIONS="-q --no-default-keyring --keyring $TMPDIR/.checkifvalidsourcedir-gpg-keyring --trust-model always"
    gpg $GPG_OPTIONS --import $DIR_TO_CHECK/*.keyring
    for i in $DIR_TO_CHECK/*.sig $DIR_TO_CHECK/*.sign $DIR_TO_CHECK/*.asc; do
        if [ -f "$i" ]; then
	    validatefn=${i%.asc}
	    validatefn=${validatefn%.sig}
	    validatefn=${validatefn%.sign}
	    if [ -f "$validatefn" ]; then
                gpg $GPG_OPTIONS --verify "$i" || {
                    echo "(E) signature $i does not validate"
                    RETURN=2
                }
            else
	        if [ -f "$validatefn.gz" ]; then
		    TMPFILE=`mktemp`
		    zcat "$validatefn.gz" > $TMPFILE
                    gpg $GPG_OPTIONS --verify "$i" "$TMPFILE" || {
                        echo "(E) signature $i does not validate"
                        RETURN=2
                    }
		    rm $TMPFILE
		fi
	        if [ -f "$validatefn.bz2" ]; then
		    TMPFILE=`mktemp`
		    bzcat "$validatefn.bz2" > $TMPFILE
                    gpg $GPG_OPTIONS --verify "$i" "$TMPFILE" || {
                        echo "(E) signature $i does not validate"
                        RETURN=2
                    }
		    rm $TMPFILE
		fi
	        if [ -f "$validatefn.xz" ]; then
		    TMPFILE=`mktemp`
		    xzcat "$validatefn.xz" > $TMPFILE
                    gpg $GPG_OPTIONS --verify "$i" "$TMPFILE" || {
                        echo "(E) signature $i does not validate"
                        RETURN=2
                    }
		    rm $TMPFILE
		fi
	    fi
        fi
    done
    rm $TMPDIR/.checkifvalidsourcedir-gpg-keyring
fi

#
# now check if everything is marked in spec files.
#
for i in $DIR_TO_CHECK/* $DIR_TO_CHECK/.* ; do
    BASE=${i##*/}
    case $BASE in
	\.|\.\.) continue ;;
    esac
    # files to display first
    case $BASE in 
	config-dist.sh | \
	get_version_number.sh | \
	get_release_number.sh | \
	check-build.sh | \
	baselibs.conf )
	    if test -n "$DESTINATIONDIR" -a -f "$DESTINATIONDIR/$BASE" && cmp -s "$DIR_TO_CHECK/$BASE" "$DESTINATIONDIR/$BASE" ; then
		echo "- package has $BASE: (unchanged)"
	    else
		echo "- package has $BASE: (new or modified)"
		echo "--------------------------------------------------------------"
		cat $DIR_TO_CHECK/$BASE
		echo "--------------------------------------------------------------"
		if test "$BATCHMODE" != true ; then
		    echo -n "Is this correct? [N/y/d] (y to ignore) "
		    read ANSWER
		    test "$ANSWER" = y -o "$ANSWER" = Y || {
			if test "$ANSWER" = d -o "$ANSWER" = D ; then
			    rm -v -- "$DIR_TO_CHECK/$BASE"
			else
			    echo ok, please fix it...
			    test "$RETURN" != "2" && RETURN=1
			fi
		    }
	    	else
		    echo "###ASK $DIR_TO_CHECK/$BASE"
	    	fi
	    fi
            # we want baselibs.conf in the src.rpm
            if test "$BASE" = baselibs.conf; then
              warn_on_unmentioned_files $BASE
            fi

	    ;;
	*rpmlintrc)
	    if test -n "$DESTINATIONDIR" -a -f "$DESTINATIONDIR/$BASE" && cmp -s "$DIR_TO_CHECK/$BASE" "$DESTINATIONDIR/$BASE" ; then
		echo "- package has $BASE: (unchanged)"
	    else
		echo "- package has $BASE: (new or modified)"
		echo "--------------------------------------------------------------"
		cat $DIR_TO_CHECK/$BASE
		echo "--------------------------------------------------------------"
		if test "$BATCHMODE" != true ; then
		    echo -n "Is this correct? [N/y/d] (y to ignore) "
		    read ANSWER
		    test "$ANSWER" = y -o "$ANSWER" = Y || {
			if test "$ANSWER" = d -o "$ANSWER" = D ; then
			    rm -v -- "$DIR_TO_CHECK/$BASE"
			else
			    echo ok, please fix it...
			    test "$RETURN" != "2" && RETURN=1
			fi
		    }
		else
			echo "###ASK $DIR_TO_CHECK/$BASE"
		fi
	    fi
            warn_on_unmentioned_files $BASE


	    LINE=$(egrep "^[^#]*setBadness" "$DIR_TO_CHECK/$BASE")
	    if [ "$LINE" != "" ]; then
	        if test "$BATCHMODE" != true ; then
		    echo "ERROR: Found possibly illegal rpmlintrc line:"
		    echo "       $LINE"
		    echo -n "Is this correct? [N/y] (y to ignore) "
		    read ANSWER
		    test "$ANSWER" = y -o "$ANSWER" = Y || {
			echo ok, please fix it...
			test "$RETURN" != "2" && RETURN=1
		    }
		else
		    echo "###ASK $DIR_TO_CHECK/$BASE"
		fi
	    fi
	    ;;
	.*.spec)
	    rm -v -- "$DIR_TO_CHECK/$BASE"
	    ;; 
	*.changes | \
	*.lsm | \
	*.spec | \
	*.spec.in | \
	*.changes.in | \
	*.test | \
	MD5SUMS | \
	MD5SUMS.meta | \
	Makefile | \
	README.autobuild | \
	bigpack | \
	prepare-build.sh | \
	minmem | \
	needed_space_in_mb | \
	pre_checkin.sh | \
	newestfile | \
	.osc | \
	.bsinfo | \
	.bsnote | \
	.check_if_valid_source_dir | \
	.setup | \
	*.dsc | \
	*.obscpio | \
	*.obsinfo | \
	ready | \
	_* | \
	*.orig | \
	*~ | \
	.git | \
	.gitignore | \
	.emacs.backup | \
	PKGBUILD | \
	appimage.yml | \
	debian.changelog | \
	debian.compat | \
	debian.control | \
	debian.copyright | \
	debian.manpages | \
	debian.postinst | \
	debian.postrm | \
	debian.preinst | \
	debian.prerm | \
	debian.rules | \
	debian.series | \
	debian.tar.gz | \
	debian.triggers | \
	debian.format | \
	debian.*.default | \
	debian.*.dirs | \
	debian.*.files | \
	debian.*.init | \
	debian.*.install | \
	debian.*.logrotate | \
	debian.*.manpages | \
	debian.*.postinst | \
	debian.*.postrm | \
	debian.*.preinst | \
	debian.*.prerm  | \
	debian.*.lintian-overrides )
	    ;;
	*)
            grep -a -x "$BASE" $TMPDIR/sources > /dev/null && continue
            test -f $DIR_TO_CHECK/_service && egrep -q 'mode=.remoterun' $DIR_TO_CHECK/_service && continue
            # be a bit more relaxed for osc, it won't upload directories anyway
            [ -d "$DIR_TO_CHECK/$BASE" ] && [ -d  $DIR_TO_CHECK/.osc ] && continue
            # and source services on server side
            [ -d "$DIR_TO_CHECK/$BASE" ] && [ -d $DIR_TO_CHECK/.old ] && continue

            warn_on_unmentioned_files $BASE

            if test "$RETURN" != "2" ; then
                if [ -d "$DIR_TO_CHECK/$BASE" ] ; then
                    # be a bit more relaxed for osc, it won't upload directories anyway
                    if [ ! -d $DIR_TO_CHECK/.osc ] ; then
                        echo "!! $BASE is a directory !!"
                        if test "$BATCHMODE" != true ; then
                            echo    "     remove subtree with 'r'"
                            echo    "ignore and continue with 'y'"
                            echo -n "Is this correct? [N/y/r] "
                            read ANSWER
                            test "$ANSWER" = y -o "$ANSWER" = Y || {
                            # r for remove is also accepted, to make it compatible with osc itself
                            if test "$ANSWER" = d -o "$ANSWER" = D -o "$ANSWER" = r -o "$ANSWER" = R; then
                                rm -Rfv -- "$DIR_TO_CHECK/$BASE"
                            else
                                echo ok, please fix it...
                                test "$RETURN" != "2" && RETURN=1
                            fi
                        }
                    else
                        echo "###ASK -r $DIR_TO_CHECK/$BASE"
                    fi
                fi
	      else
		if test "$BATCHMODE" != true ; then
		    echo -n "Is this correct? [N/y/d] (y to ignore) "
		    read ANSWER
		    test "$ANSWER" = y -o "$ANSWER" = Y || {
			if test "$ANSWER" = d -o "$ANSWER" = D ; then
			    rm -v "$DIR_TO_CHECK/$BASE"
			else
			    echo ok, please fix it...
			    test "$RETURN" != "2" && RETURN=1
			fi
		    }
		else
		    echo "###ASK $DIR_TO_CHECK/$BASE"
		fi
	      fi
	    fi
	    ;;
    esac
done

test "$VERBOSE" = true && echo done

cleanup_and_exit $RETURN
