#! /bin/sh
. /usr/lib/innshellvars

# actsyncd - actsync daemon
#
# Usage:
#       actsyncd config_file [debug_level [debug_outfmt]]
#
#       config_file     name of file used to determine how to run actsync
#       debug_level     force no action and use -v debug_level
#       debug_outfmt    change -o a1 output to -o debug_outfmt for debug

# By: Landon Curt Noll  chongo@toad.com  (chongo was here /\../\)
#
# Copyright (c) Landon Curt Noll, 1993.
# All rights reserved.
#
# Various bug fixes, code and documentation improvements since then
# in 1999-2003, 2007-2009, 2021-2024.
#
# Permission to use and modify is hereby granted so long as this
# notice remains.  Use at your own risk.  No warranty is implied.

# preset vars
#

# Our lock file.
LOCK="${LOCKS}/LOCK.actsyncd"
# Where actsync is located.
ACTSYNC="${PATHBIN}/actsync"
# Exit value if unable to get an active file.
NOSYNC=127
# Date format.
DATEFMT="+%Y-%m-%d %T %z"

# parse args
#
if [ $# -gt 1 ]; then
    case $1 in
    -x | -r) shift ;; # no longer relevant
    esac
fi
case $# in
1)
    cfg="$1"
    DEBUG=
    DEBUG_FMT=
    ;;
2)
    cfg="$1"
    DEBUG="$2"
    DEBUG_FMT=
    ;;
3)
    cfg="$1"
    DEBUG="$2"
    DEBUG_FMT="$3"
    ;;
*)
    echo "Usage: $0 config_file [debug_level [debug_outfmt]]" 1>&2
    exit 1
    ;;
esac
if [ ! -s "$cfg" ]; then
    echo "$0: config_file not found or empty: $cfg" 1>&2
    exit 2
fi

# parse config_file
#
host="$(sed -n -e 's/^host=[ 	]*//p' $cfg | tail -n 1)"
if [ -z "$host" ]; then
    echo "$0: no host specified in $cfg" 1>&2
    exit 3
fi

flags="$(sed -n -e 's/^flags=[ 	]*//p' $cfg | tail -n 1)"
if [ -z "$flags" ]; then
    echo "$0: no flags specified in $cfg" 1>&2
    exit 4
fi

ign="$(sed -n -e 's/^ignore_file=[ 	]*//p' $cfg | tail -n 1)"
if [ -z "$ign" ]; then
    echo "$0: no ignore file specified in $cfg" 1>&2
    exit 5
fi

# Keep ftppath for backwards-compatibility.
ftp="$(sed -n -e 's/^ftppath=[ 	]*\/*//p' $cfg | tail -n 1)"
path="$(sed -n -e 's/^path=[ 	]*\/*//p' $cfg | tail -n 1)"
protocol="$(sed -n -e 's/^protocol=[ 	]*//p' $cfg \
    | tr '[:upper:]' '[:lower:]' | tail -n 1)"

if [ -z "$ftp" ] && [ -z "$path" ]; then
    if [ -z "$protocol" ]; then
        protocol="nntp"
    else
        if [ "$protocol" != "nntp" ]; then
            echo "$0: path expected with protocol $protocol" 1>&2
            exit 6
        fi
    fi
fi

# If not NNTP, assume FTP by default for backwards-compatibility.
if [ -z "$protocol" ]; then
    protocol="ftp"
fi

if [ -n "$ftp" ] && [ -n "$path" ]; then
    echo "$0: ftppath and path cannot both be set" 1>&2
    exit 6
fi

if [ -n "$ftp" ] || [ -n "$path" ]; then
    if [ "$protocol" = "nntp" ]; then
        echo "$0: cannot set a path with protocol $protocol" 1>&2
        exit 6
    fi
fi

if [ -n "$ftp" ] && [ -z "$path" ]; then
    path=$ftp
    if [ "$protocol" != "ftp" ]; then
        echo "$0: cannot set ftppath with protocol $protocol" 1>&2
        exit 6
    fi
fi

# Try to find the ignore file in pathetc.
if [ ! -f "$ign" ]; then
    ign="${PATHETC}/$ign"
fi
if [ ! -s "$ign" ]; then
    echo "$0: ignore_file not found or empty: $ign" 1>&2
    exit 7
fi

# DEBUG processing, if debug_level was given.
#
if [ -z "$DEBUG" ]; then
    # Force "-o c" mode (overrides any -o argument in the command line).
    # Standard actsyncd output mode (commands to ctlinnd).
    flags="$flags -o c"
else
    if [ "$protocol" != "nntp" ]; then
        echo "$0: DEBUG mode (still) only available with nntp" >&2
        exit 88
    fi

    # force -v level as needed
    flags="$flags -v $DEBUG"

    # force -o level but reject -o x modes
    if [ -n "$DEBUG_FMT" ]; then
        case "$DEBUG_FMT" in
        x*)
            echo "$0: do not use any of the -o x debug_outfmt modes!" 1>&2
            exit 8
            ;;
        *) flags="$flags -o $DEBUG_FMT" ;;
        esac
    fi

    # execute actsync directly
    echo "$(date "$DATEFMT") will execute $ACTSYNC -i $ign $flags $host" 1>&2
    eval "$ACTSYNC -i $ign $flags $host"
    status="$?"
    echo "$(date "$DATEFMT") DEBUG: exit status $status" 1>&2
    exit "$status"
fi

# Lock out others
#
shlock -p $$ -f "${LOCK}" || {
    echo "$(date "$DATEFMT") Locked by $(cat "${LOCK}")" 1>&2
    exit 9
}

# setup
#
origdir=$(pwd)
workdir="${TMPDIR}/actsyncd"
ctlinndcmds="cc_commands"
out="sync.msg"
cleanup="$SED -e 's/^/    /' < $out; cd ${origdir}; rm -rf '$workdir' '$LOCK'"
#shellcheck disable=SC2064
trap "eval $cleanup; exit 123" 1 2 3 15

set -e
rm -rf "$workdir"
mkdir "$workdir"
cd "$workdir"
set +e

rm -f "$out"
touch "$out"
chmod 0644 "$out"

# try to sync
#
# Try to sync off of the host.  If unable to connect/sync then retry
# up to 9 more times waiting 6 minutes between each try.
#
echo "$(date "$DATEFMT") for $host: start" >>$out 2>&1
for loop in 1 2 3 4 5 6 7 8 9 10; do
    # get the active file to compare against
    status=0
    case $host in
    /*)
        cp $host active
        status=$?
        ;;
    .*)
        cp $origdir/$host active
        status=$?
        ;;
    *)
        if [ "$protocol" = "nntp" ]; then
            port=$(expr "$host" : '.*:\(.*\)')
            if [ -n "$port" ]; then
                port="-p $port"
                host=$(expr "$host" : '\(.*\):.*')
            fi
            echo "$(date "$DATEFMT") getlist -h $host $port" >>$out
            if getlist -h $host $port >active 2>>$out; then
                :
            else
                status=$NOSYNC
            fi
        else
            if [ "$protocol" = "ftp" ]; then
                program=$GETFTP
            else
                program=$GETHTTP
            fi
            echo "$(date "$DATEFMT") $program $protocol://$host/$path" >>$out
            if $program $protocol://$host/$path >>$out 2>&1; then
                case "$path" in
                *.bz2)
                    echo "$(date "$DATEFMT") $BZIP2 -d active.bz2" >>$out
                    if $BZIP2 -d active.bz2 >>$out 2>&1; then
                        :
                    else
                        status=1
                    fi
                    ;;
                *.gz)
                    echo "$(date "$DATEFMT") $GZIP -d active.gz" >>$out
                    if $GZIP -d active.gz >>$out 2>&1; then
                        :
                    else
                        status=1
                    fi
                    ;;
                *.Z)
                    echo "$(date "$DATEFMT") $UNCOMPRESS active.Z" >>$out
                    if $UNCOMPRESS active.Z >>$out 2>&1; then
                        :
                    else
                        status=1
                    fi
                    ;;
                esac
            else
                status=$NOSYNC
            fi
        fi

        if [ ! -s "active" ]; then
            echo "$(date "$DATEFMT") active file not retrieved or empty" >>$out
            status=1
        fi
        ;;
    esac

    if [ "$status" -ne "$NOSYNC" ]; then
        # detect bad status
        #
        if [ "$status" -ne 0 ]; then
            echo "$(date "$DATEFMT") FATAL for $host: exit $status" >>$out
            eval $cleanup
            exit "$status"
        fi

        echo "$(date "$DATEFMT") $ACTSYNC -i $ign $flags ./active" >>$out
        eval "$ACTSYNC -i $ign $flags ./active >$ctlinndcmds 2>>$out"
        status=$?

        if [ "$status" -ne 0 ]; then
            echo "$(date "$DATEFMT") FATAL for $host: actsync balked" >>$out
            eval $cleanup
            exit "$status"
        fi

        if [ ! -s $ctlinndcmds ]; then
            echo "$(date "$DATEFMT") No changes need to be made" >>$out
        else
            echo "$(date "$DATEFMT") for $host: updating active" >>$out
            echo "$(date "$DATEFMT") mod-active $ctlinndcmds" >>$out
            mod-active $ctlinndcmds >>$out 2>&1
            status=$?

            if [ "$status" -ne 0 ]; then
                echo "$(date "$DATEFMT") FATAL for $host: mod-active FAILED" \
                    >>$out
                eval $cleanup
                exit "$status"
            fi
        fi

        # normal exit - all done
        #
        echo "$(date "$DATEFMT") for $host: end" >>$out
        eval $cleanup
        exit 0
    fi

    # failed to get the remote active file
    echo "$(date "$DATEFMT") for $host: failed to connect/sync, retrying" \
        >>$out

    # wait 6 minutes
    #
    sleep 360
done

# give up
#
echo "$(date "$DATEFMT") FATAL for $host: failed to connect/sync $loop times" \
    >>$out 2>&1
eval $cleanup
exit 1
