#!/bin/bash
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#

# verify that ELF NEEDED entries are known-good so hopefully builds run on
# lots of different GNU/Linux distributions

set -euo pipefail

PARA=1
check_path="${INSTDIR:-.}"

while [ "${1:-}" != "" ]; do
    parm=${1%%=*}
    arg=${1#*=}
    has_arg=
    if [ "${1}" != "${parm?}" ] ; then
        has_arg=1
    else
        arg=""
    fi

    case "${parm}" in
        --dir|-d)
	    if [ "$has_arg" ] ; then
		check_path=$arg
	    else
		shift
		check_path=$1
	    fi
            ;;
        -p)
	    # this sound counter intuitive. but the idea
            # is to possibly support -p <n>
            # in the mean time 0 = nolimit and -p 1 would mean
            # the current default: serialize
            PARA=0
            ;;
        -*)
            die "Invalid option $1"
            ;;
        *)
            if [ "$DO_NEW" = 1 ] ; then
                REPO="$1"
            else
                die "Invalid argument $1"
            fi
            ;;
    esac
    shift
done


files=$(find "${check_path}/program" "${check_path}/sdk/bin" -type f)
# all RPATHs should point to ${INSTDIR}/program so that's the files they find
programfiles=$(basename -a $(echo ${files} | grep -o '/program/[^/]* '))

# whitelists should contain only system libraries that have a good reputation
# of maintaining ABI stability
globalwhitelist="ld-linux-x86-64.so.2 libc.so.6 libm.so.6 libdl.so.2 libpthread.so.0 librt.so.1 libutil.so.1 libnsl.so.1 libcrypt.so.1 libgcc_s.so.1 libstdc++.so.6 libz.so.1 libfontconfig.so.1 libfreetype.so.6 libxml2.so.2 libxslt.so.1 libexslt.so.0"
x11whitelist="libX11.so.6 libXext.so.6 libSM.so.6 libICE.so.6 libXinerama.so.1 libXrender.so.1 libXrandr.so.2 libcairo.so.2"
openglwhitelist="libGL.so.1"
giowhitelist="libgio-2.0.so.0 libgobject-2.0.so.0 libglib-2.0.so.0 libdbus-glib-1.so.2 libdbus-1.so.3"
gstreamerwhitelist="libgstpbutils-1.0.so.0 libgstvideo-1.0.so.0 libgstbase-1.0.so.0 libgstreamer-1.0.so.0"
gtk2whitelist="libgtk-x11-2.0.so.0 libgdk-x11-2.0.so.0 libpangocairo-1.0.so.0 libatk-1.0.so.0 libcairo.so.2 libgio-2.0.so.0 libpangoft2-1.0.so.0 libpango-1.0.so.0 libfontconfig.so.1 libfreetype.so.6 libgdk_pixbuf-2.0.so.0 libgobject-2.0.so.0 libglib-2.0.so.0 libgmodule-2.0.so.0 libgthread-2.0.so.0 libdbus-glib-1.so.2 libdbus-1.so.3"
gtk3whitelist="libgtk-3.so.0 libgdk-3.so.0 libcairo-gobject.so.2 libpangocairo-1.0.so.0 libatk-1.0.so.0 libcairo.so.2 libgio-2.0.so.0 libpangoft2-1.0.so.0 libpango-1.0.so.0 libfontconfig.so.1 libfreetype.so.6 libgdk_pixbuf-2.0.so.0 libgobject-2.0.so.0 libglib-2.0.so.0 libgmodule-2.0.so.0 libgthread-2.0.so.0 libdbus-glib-1.so.2 libdbus-1.so.3"
kde4whitelist="libkio.so.5 libkfile.so.4 libkdeui.so.5 libkdecore.so.5 libQtNetwork.so.4 libQtGui.so.4 libQtCore.so.4 libglib-2.0.so.0"
avahiwhitelist="libdbus-glib-1.so.2 libdbus-1.so.3 libgobject-2.0.so.0 libglib-2.0.so.0 libavahi-common.so.3 libavahi-client.so.3"
kerberoswhitelist="libgssapi_krb5.so.2 libcom_err.so.2 libkrb5.so.3"

check_one_file()
{
local file="$1"

    skip=0
    whitelist="${globalwhitelist}"
    case "${file}" in
        */sdk/docs/*)
            # skip the majority of files, no ELF binaries here
            skip=1
        ;;
        */libcairocanvaslo.so)
            whitelist="${whitelist} libcairo.so.2"
        ;;
        */libucpgio1lo.so|*/liblosessioninstalllo.so|*/libevoablo.so)
            whitelist="${whitelist} ${giowhitelist}"
        ;;
        */libavmediagst.so)
            whitelist="${whitelist} ${gtk3whitelist} ${gstreamerwhitelist}"
        ;;
        */libvclplug_kde4lo.so|*/libkde4be1lo.so)
            whitelist="${whitelist} ${x11whitelist} ${kde4whitelist}"
        ;;
        */libvclplug_gtklo.so|*/libqstart_gtklo.so|*/updater)
            whitelist="${whitelist} ${x11whitelist} ${gtk2whitelist}"
        ;;
        */libvclplug_gtk3lo.so)
            whitelist="${whitelist} ${x11whitelist} ${gtk3whitelist}"
        ;;
        */libdesktop_detectorlo.so|*/ui-previewer|*/oosplash|*/gengal.bin)
            whitelist="${whitelist} ${x11whitelist}"
        ;;
        */libvclplug_genlo.so|*/libchartcorelo.so|*/libavmediaogl.so|*/libOGLTranslo.so|*/liboglcanvaslo.so|*/libchartopengllo.so)
            whitelist="${whitelist} ${x11whitelist} ${openglwhitelist}"
        ;;
        */libvcllo.so|*/libsofficeapp.so)
            whitelist="${whitelist} ${x11whitelist} ${openglwhitelist} ${giowhitelist} libcups.so.2"
        ;;
        */liblibreofficekitgtk.so)
            whitelist="${whitelist} ${gtk3whitelist}"
        ;;
        */libsdlo.so)
            whitelist="${whitelist} ${avahiwhitelist}"
        ;;
        */libofficebean.so)
            whitelist="${whitelist} libjawt.so"
        ;;
        */libpostgresql-sdbc-impllo.so)
            whitelist="${whitelist} ${kerberoswhitelist}"
        ;;
    esac
    if test "${skip}" = 0 && readelf -d "${file}" &> /dev/null ; then
        rpath=$(readelf -d "${file}" | grep '(\(RPATH\|RUNPATH\))' || true)
        neededs=$(readelf -d "${file}" | grep '(NEEDED)' | sed -e 's/.*\[\(.*\)\]$/\1/')
        neededsinternal=
        for needed in ${neededs}
        do
            if ! echo ${whitelist} | grep -q -w "${needed}" ; then
                neededsinternal="${neededsinternal} ${needed}"
                if ! echo ${programfiles} | grep -q -w "${needed}" ; then
                    echo "${file}" has suspicious NEEDED: "${needed}"
                    status=1
                fi
            fi
        done
        if test -z "${rpath}" ; then
            case "${file}" in
                */python-core-*/lib/lib-dynload/*)
                    # python modules don't have RPATH
                ;;
                */share/extensions/*)
                    # extension libraries don't have RPATH
                ;;
                *)
                    # no NEEDED from instdir, no RPATH needed
                    if test -n "${neededsinternal}" ; then
                        echo "${file}" has no RPATH
                        status=1
                    fi
                ;;
            esac
        else
            case "$file" in
                */sdk/bin/*)
                    if echo "${rpath}" | grep -q -v '\[\$ORIGIN/../../program\]$' ; then
                        echo "${file}" has unexpected RPATH "${rpath}"
                        status=1
                    fi
                ;;
                *)
                    if echo "${rpath}" | grep -q -v '\[\$ORIGIN\]$' ; then
                        echo "${file}" has unexpected RPATH "${rpath}"
                        status=1
                    fi
                ;;
            esac
        fi
    fi
}
status=0

if [ "$PARA" = "1" ] ; then
    for file in ${files}
    do
	check_one_file $file
    done
else
    rm -f check_elf.out
    for file in ${files}
    do
	(
	check_one_file $file
	)>> check_elf.out &
    done

    if [ -s check_elf.out ] ; then
	cat check_elf.out
	status=1
    fi
    rm check_elf.out
fi
exit ${status}