#!/bin/bash

#Usage : ./sign_deb_package deb_path crt_path [keypath] [wosign_conf] [-ft feitian_conf] [-force-sign] [-no-tsa]
debug_crt_path="/usr/share/ca-certificates/deepin/private/priv.crt"
debug_key_path="/usr/share/ca-certificates/deepin/private/priv.key"
sign_info_file="sign"

usage() {
    echo "Usage      : $(basename $0) deb_path crt_path keypath wosign_conf -t trustlevel "
    echo "             -m machine_scope_conf -force-sign -no-tsa"
    echo "deb_path   : the deb file to sign"
    echo "crt_path | -c crt_path"   
    echo "           : the certificate for signature"
    echo "             this file name must end with .crt"
    echo "key_path | -k key_path"
    echo "           : the key for signature, it can be absent if you use U-key."
    echo "             this file name must end with .key"
    echo "-t trustlevel"
    echo "           : trustlevel is used to set elf file trust level"
    echo "-m machine_scope_conf "
    echo "           : specify which machines can be permitted to install package or run program."
    echo "wosign_conf | -w wosign_conf"
    echo "           : the wosign configure file if you specified,"
    echo "             this file name must end with .conf"
    echo "-ft feitian_conf"
    echo "           : the feitian configure file if you specified,"
    echo "             this file name must end with .conf"
    echo "-force-sign: forced to replace signature"
    echo "-no-tsa    : denote that signature without timestamp"
}

check_sign_args() 
{
        if [[ -z "$crt_path" && -z "$key_path" ]]; then
                crt_path=$debug_crt_path
                key_path=$debug_key_path
        fi
        if [[ -z $deb_path ]]; then
                echo "the deb package absent"
                exit 1
        fi
        if [[ ! -f "$deb_path" ]]; then
               echo "deb_path : $deb_path does not exist"
               exit 1
        fi
        if [[ -z $crt_path ]]; then
                echo "the certificate file absent"
                exit 1
        fi
        if [[ -z $key_path && -z $wosign_conf && -z $feitian_conf ]]; then
                echo "neither ukey configure file nor private key present"
                exit 1
        fi
        if [[ -n $key_path && -n $wosign_conf ]]; then
                echo "only one of private key path and wosign configure file is permited present"
                exit 1
        fi
        if [[ -n $key_path && -n $feitian_conf ]]; then
                echo "only one of private key path and feitian configure file is permited present"
                exit 1
        fi
}

if [[ $# -eq 0 ]]; then
	usage
	exit
fi

while [[ $# -gt 0 ]]; do
        case $1 in 
                -h)
                        usage
                        exit 
                        ;;
                -c)
                        crt_path=`realpath $2`
                        shift 2
                        ;;
                -k)
                        key_path=`realpath $2`
                        shift 2
                        ;;
                -m)
                        machine_scope_conf=`realpath $2`
                        shift 2
                        ;;
                -t)
                        trustlevel=$2
                        shift 2
                        ;;
                -w)  
                        wosign_conf=`realpath $2`
                        shift 2
                        ;;
                -ft)  
                        feitian_conf=`realpath $2`
                        shift 2
                        ;;
                *)
                        if [[ $1 = "-no-tsa" ]]
                        then
                                no_timestamp="--no-timestamp"
                        elif [[ $1 = "-force-sign" ]] ;then
                                force_sign="--force"
                        elif [[ ${1##*.} = crt ]];then
                                if [[ -n $crt_path ]] ; then
                                        echo "there should no more than one certificate file"
                                        exit 1
                                else
                                        crt_path=`realpath $1`
                                fi
                        elif [[ ${1##*.} = key ]]; then
                                if [[ -n $key_path ]]; then
                                        echo "there should one private key at most"
                                        exit 1
                                else 
                                        key_path=`realpath $1`
                                fi
                        elif [[ ${1##*.} = conf ]]; then
                                if [[ -n $wosign_conf || -n $feitian_conf ]]; then
                                        echo "there should one ukey config at most"
                                        exit 1
                                else 
                                        wosign_conf=`realpath $1`
                                fi
                        elif [[ ${1##*.} = deb ]] ;then
                                echo "deb_path=$1"
                                deb_path=`realpath $1`
                        else
                                echo "Unknow option or argument $1 !!!"
                                usage
                                exit 1
                        fi
                        shift
                        ;;
        esac
done

check_sign_args
if [[ $key_path"x" != x ]]; then
        key_arg="-k $key_path"
fi
if [[ $crt_path"x" != x ]]; then
        crt_arg="-c $crt_path"
fi
if [[ $wosign_conf"x" != x ]]; then
        wosign_arg="--wosign-conf=$wosign_conf"
fi
if [[ $feitian_conf"x" != x ]]; then
        feitian_arg="--feitian-conf=$feitian_conf"
fi
if [[ $machine_scope_conf"x" != x ]]; then
        machine_scope_arg="-machine_ids $machine_scope_conf"
fi 
if [[ $trustlevel"x" != x ]]; then
        trust_arg="-trustLevel $trustlevel"
fi


deb_arg="-f $deb_path"
elfsign_args="$crt_arg $key_arg $wosign_arg $feitian_arg $trust_arg $machine_scope_arg $force_sign $no_timestamp"


deb_file=$(basename $deb_path)
out_path=$(mktemp -d /tmp/SIGN_DEB_XXXXXXXXXX)

fakeroot dpkg-deb -R $deb_path $out_path

declare -a arr_sign_inode
#sign elfs
function sign_elfs() {
    for element in `ls -a $1`; do
        if [ $element == "." ] || [ $element == ".." ]; then
          continue
        fi
        signed_flag=FALSE
        dir_or_file=$1/$element
        if [ -d $dir_or_file ]; then
            if [ -L $dir_or_file ]; then
                continue
            fi
           sign_elfs $dir_or_file
        else
            if [ -L $1/$element ]; then
                continue
            fi
            magic=`xxd -l 4 -ps $dir_or_file`
            if [ "$magic" = "7f454c46" ]; then
                cur_inode=`ls -i $1/$element | cut -d ' ' -f 1`
                for inode in ${arr_sign_inode[*]}; do
                    if [ $cur_inode == $inode ]; then
                        signed_flag=TRUE
                        echo "$1/$element has signed because of another file with the same inode!"
                        break
                    fi
                done
                if [ $signed_flag = TRUE ]; then
                    continue
                fi
                elf_arg="-f $dir_or_file"
                if [ x"$CLEAR_EMBEDED_SIGNATURE" = xyes ]; then
                    objcopy --remove-section=.sig $dir_or_file || true
                fi
                echo "deepin-elf-sign $elf_arg $elfsign_args"
                deepin-elf-sign $elf_arg $elfsign_args
                if [ $? != 0 ]; then
                    echo "fatal error!"
                    rm -rf $out_path
                    exit -1
                else
                    arr_sign_inode[${#arr_sign_inode[*]}]=$cur_inode
                fi
            fi
        fi
    done
}

#OLD_ELFSIGN_WITH_OPTION=$ELFSIGN_WITH_OPTION
#export ELFSIGN_WITH_OPTION=Y
sign_elfs $out_path
#ELFSIGN_WITH_OPTION=$OLD_ELFSIGN_WITH_OPTION


#compress
echo "now sign deb file ...."
if [ -n "$DEB_OUT_DIR" ]; then
    out_deb_dir=$DEB_OUT_DIR
else
    out_deb_dir=./signed_deb
fi
if [ ! -d $out_deb_dir ];then
    mkdir $out_deb_dir
fi

out_deb=$out_deb_dir/$deb_file
echo "$out_deb"
fakeroot dpkg-deb -b $out_path $out_deb

# signinfo file can not extract by dpkg-deb -R
# so we should us ar x to extract it in the workspace directory
# then we can find it if we do pkcs7 sign for another certificate.
cp $deb_path $out_path
echo "cp $deb_path to $out_path"
cd $out_path
if [ x"$CLEAR_EMBEDED_SIGNATURE" = xyes ]; then
	ar d $out_path sign || true
else 
	ar x $deb_file $sign_info_file
fi
rm $deb_file
cd -

if [ -f $out_path/$sign_info_file ]
then
    ar r $out_deb $out_path/$sign_info_file
fi

OLD_DEB_SIGN_METHOD=$DEB_SIGN_METHOD
export DEB_SIGN_METHOD=file

rm -rf $out_path
deb_arg="-f $out_deb"
debsign_args="$deb_arg $crt_arg $key_arg $wosign_arg $feitian_arg $machine_scope_arg $force_sign $no_timestamp"
echo "deepin-deb-sign $debsign_args"
if deepin-deb-sign $debsign_args
then
    DEB_SIGN_METHOD=$OLD_DEB_SIGN_METHOD
else
    DEB_SIGN_METHOD=$OLD_DEB_SIGN_METHOD
    rm -rf $out_deb
    exit -1
fi


