#!/bin/sh -e
#
# 2018 Ander Punnar (ander-at-kvlt-dot-ee)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#

file_is="$( cat "$__object/explorer/file_is" )"

if [ "$file_is" = 'missing' ] \
    && [ -z "$__cdist_dry_run" ] \
    && [ ! -f "$__object/parameter/file" ] \
    && [ ! -f "$__object/parameter/directory" ]
then
    exit 0
fi

os="$( cat "$__global/explorer/os" )"

acl_path="/$__object_id"

acl_is="$( cat "$__object/explorer/acl_is" )"

if [ -f "$__object/parameter/source" ]
then
    acl_source="$( cat "$__object/parameter/source" )"

    if [ "$acl_source" = '-' ]
    then
        acl_should="$( cat "$__object/stdin" )"
    else
        acl_should="$( grep -Ev '^#|^$' "$acl_source" )"
    fi
elif [ -f "$__object/parameter/entry" ]
then
    acl_should="$( cat "$__object/parameter/entry" )"
else
    echo 'no parameters set' >&2
    exit 1
fi

# instead of setfacl's non-helpful message "Option -m: Invalid argument near character X"
# let's check if target has necessary users and groups, since mistyped or missing
# users/groups in target is most common reason.
echo "$acl_should" \
    | grep -Po '(user|group):[^:]+' \
    | sort -u \
    | while read -r l
    do
        if ! grep "$l" -Fxq "$__object/explorer/getent"
        then
            echo "no $l' in target" | sed "s/:/ '/" >&2
            exit 1
        fi
    done

if [ -f "$__object/parameter/default" ]
then
    acl_should="$( echo "$acl_should" \
        | sed 's/^default://' \
        | sort -u \
        | sed 's/\(.*\)/default:\1\n\1/' )"
fi

if [ "$file_is" = 'regular' ] \
    && echo "$acl_should" | grep -Eq '^default:'
then
    # only directories can have default ACLs,
    # but instead of error,
    # let's just remove default entries
    acl_should="$( echo "$acl_should" | grep -Ev '^default:' )"
fi

if echo "$acl_should" | awk -F: '{ print $NF }' | grep -Fq 'X'
then
    [ "$file_is" = 'directory' ] && rep=x || rep=-

    acl_should="$( echo "$acl_should" | sed "s/\\(.*\\)X/\\1$rep/" )"
fi

setfacl_exec='setfacl'

if [ -f "$__object/parameter/recursive" ]
then
    if echo "$os" | grep -Fq 'freebsd'
    then
        echo "$os setfacl do not support recursive operations" >&2
    else
        setfacl_exec="$setfacl_exec -R"
    fi
fi

if [ -f "$__object/parameter/remove" ]
then
    echo "$acl_is" | while read -r acl
    do
        # skip wanted ACL entries which already exist
        # and skip mask and other entries, because we
        # can't actually remove them, but only change.
        if echo "$acl_should" | grep -Eq "^$acl" \
            || echo "$acl" | grep -Eq '^(default:)?(mask|other)'
        then continue
        fi

        if echo "$os" | grep -Fq 'freebsd'
        then
            remove="$acl"
        else
            remove="$( echo "$acl" | sed 's/:...$//' )"
        fi

        echo "$setfacl_exec -x \"$remove\" \"$acl_path\""
        echo "removed '$remove'" >> "$__messages_out"
    done
fi

for acl in $acl_should
do
    if ! echo "$acl_is" | grep -Eq "^$acl"
    then
        if echo "$os" | grep -Fq 'freebsd' \
            && echo "$acl" | grep -Eq '^default:'
        then
            echo "setting default ACL in $os is currently not supported" >&2
        else
            echo "$setfacl_exec -m \"$acl\" \"$acl_path\""
            echo "added '$acl'" >> "$__messages_out"
        fi
    fi
done
