#!/bin/bash
#
# WebHTTrack launcher script
# Initializes the htsserver GUI frontend and launch the default browser

BROWSEREXE=
SRCHBROWSEREXE=(x-www-browser www-browser iceape mozilla firefox-developer-edition firefox icecat iceweasel abrowser firebird galeon konqueror midori opera google-chrome chrome chromium chromium-browser netscape firefox-developer-edition)
# shellcheck disable=SC2153 # BROWSER is the standard freedesktop env var, not a typo
if test -n "${BROWSER}"; then
    # sensible-browser will f up if BROWSER is not set
    SRCHBROWSEREXE=(xdg-open sensible-browser "${SRCHBROWSEREXE[@]}")
fi
# Patch for Darwin/Mac by Ross Williams
if test "$(uname -s)" == "Darwin"; then
    # open hands the URL to Darwin's default browser. It goes last so a configured
    # $BROWSER still wins the search.
    SRCHBROWSEREXE+=(open)
fi
BINWD=$(dirname "$0")
SRCHPATH=("$BINWD" /usr/local/bin /usr/share/bin /usr/bin /usr/lib/httrack /usr/local/lib/httrack /usr/local/share/httrack /opt/local/bin /sw/bin "${HOME}/usr/bin" "${HOME}/bin")
IFS=':' read -ra pathdirs <<<"$PATH"
for d in "${pathdirs[@]}"; do
    # drop empty PATH fields, matching the old echo|tr word-split
    test -n "$d" && SRCHPATH+=("$d")
done
# The substituted datadir goes after the relative entries so a moved tree wins, and
# before the guesses so --datadir works (#887).
SRCHDISTPATH=("$BINWD/../share" "$BINWD/.." "/usr/share" /usr/share /usr/local /usr /local /usr/local/share "${HOME}/usr" "${HOME}/usr/share" /opt/local/share /sw "${HOME}/usr/local" "${HOME}/usr/share")

###
# And now some famous cuisine

function log {
    echo "$0($$): $*" >&2
    return 0
}

# Map a POSIX locale ("zh_TW.UTF-8@euro") to its lang.indexes number, English (1) if unknown
function lang_index {
    local locale=$1 indexes=$2 tag n t
    tag=$(echo "${locale}" | cut -f1 -d'.' | cut -f1 -d'@' | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z_')
    # a few languages carry a region-specific entry (zh_tw, pt_br); else use the bare language
    for t in "${tag}" "${tag%%_*}"; do
        n=$(grep -E "^${t}:" "${indexes}" | cut -f2 -d':')
        test -n "${n}" && break
    done
    test -n "${n}" || n=1
    echo "${n}"
}

function launch_browser {
    log "Launching $1"
    browser=$1
    url=$2
    log "Spawning browser.."
    ${browser} "${url}"
    # note: browser can hiddenly use the -remote feature of
    # mozilla and therefore return immediately
    log "Browser (or helper) exited"
}

# Wait until the server stops (the interface closed, so this process has nothing
# left to represent) or the browser exits, whichever comes first. An old browser
# only ever signals by exiting.
function wait_for_session {
    local browserpid=$1 sessionpid=$2
    while test -n "${sessionpid}${browserpid}"; do
        test -n "${sessionpid}" && ! kill -0 "${sessionpid}" 2>/dev/null && break
        test -n "${browserpid}" && ! kill -0 "${browserpid}" 2>/dev/null && break
        sleep 1
    done
}

# First ensure that we can launch the server
BINPATH=
for i in "${SRCHPATH[@]}"; do
    ! test -n "${BINPATH}" && test -x "${i}/htsserver" && BINPATH="${i}"
done
for i in "${SRCHDISTPATH[@]}"; do
    ! test -n "${DISTPATH}" && test -f "${i}/httrack/lang.def" && DISTPATH="${i}/httrack"
done
test -n "${BINPATH}" || ! log "Could not find htsserver" || exit 1
test -n "${DISTPATH}" || ! log "Could not find httrack directory" || exit 1
test -f "${DISTPATH}/lang.def" || ! log "Could not find ${DISTPATH}/lang.def" || exit 1
test -f "${DISTPATH}/lang.indexes" || ! log "Could not find ${DISTPATH}/lang.indexes" || exit 1
test -d "${DISTPATH}/lang" || ! log "Could not find ${DISTPATH}/lang" || exit 1
test -d "${DISTPATH}/html" || ! log "Could not find ${DISTPATH}/html" || exit 1

# Locale: POSIX precedence, LC_ALL overrides LC_MESSAGES overrides LANG
HTSLANG="${LC_ALL}"
! test -n "${HTSLANG}" && HTSLANG="${LC_MESSAGES}"
! test -n "${HTSLANG}" && HTSLANG="${LANG}"
LANGN=$(lang_index "${HTSLANG}" "${DISTPATH}/lang.indexes")

# Find the browser
# note: not all systems have sensible-browser or www-browser alternative
# thefeore, we have to find a bit more if sensible-browser could not be found

for i in "${SRCHBROWSEREXE[@]}"; do
    for j in "${SRCHPATH[@]}"; do
        if test -x "${j}/${i}"; then
            BROWSEREXE="${j}/${i}"
        fi
        test -n "$BROWSEREXE" && break
    done
    test -n "$BROWSEREXE" && break
done
test -n "$BROWSEREXE" || ! log "Could not find any suitable browser" || exit 1

# Without -W, open returns as soon as the browser is up and wait_for_session would end
# the session there and then. Only Darwin puts open in the list above.
BROWSERWAITS=
if test "$(basename "${BROWSEREXE}")" = open; then
    BROWSEREXE="${BROWSEREXE} -W"
    BROWSERWAITS=1
fi

# "browse" command
if test "$1" = "browse"; then
    if test -f "${HOME}/.httrack.ini"; then
        INDEXF=$(tr '\r' '\n' <"${HOME}/.httrack.ini" | grep -E "^path=" | cut -f2- -d'=')
        if test -n "${INDEXF}" -a -d "${INDEXF}" -a -f "${INDEXF}/index.html"; then
            INDEXF="${INDEXF}/index.html"
        else
            INDEXF=""
        fi
    fi
    if ! test -n "$INDEXF"; then
        INDEXF="${HOME}/websites/index.html"
    fi
    launch_browser "${BROWSEREXE}" "file://${INDEXF}"
    exit $?
fi

# Create a temporary filename
TMPSRVFILE="$(mktemp "${TMPDIR:-/tmp}/.webhttrack.XXXXXXXX")" || ! log "Could not create the temporary file ${TMPSRVFILE}" || exit 1
# Launch htsserver binary and setup the server
(
    "${BINPATH}/htsserver" "${DISTPATH}/" --ppid "$$" path "${HOME}/websites" lang "${LANGN}" "$@"
    echo SRVURL=error
) >"${TMPSRVFILE}" &
# Find the generated SRVURL
SRVURL=
MAXCOUNT=60
while ! test -n "$SRVURL"; do
    MAXCOUNT=$((MAXCOUNT - 1))
    test $MAXCOUNT -gt 0 || exit 1
    test $MAXCOUNT -lt 50 && echo "waiting for server to reply.."
    SRVURL=$(grep -E URL= "${TMPSRVFILE}" | cut -f2- -d=)
    test ! "$SRVURL" = "error" || ! log "Could not spawn htsserver" || exit 1
    test -n "$SRVURL" || sleep 1
done

# open -W blocks until the browser itself quits, so an unreaped one keeps the bundle
# registered with LaunchServices and its Dock icon up until the user force-quits it.
function reap_browser {
    test -n "${BROWSERWAITS}" || return 0
    # A dead pid may already have been reused, so signalling it would hit a stranger.
    kill -0 "${BROWSERPID}" 2>/dev/null || return 0
    # BROWSERPID names the launch_browser subshell, whose child is open.
    pkill -P "${BROWSERPID}" 2>/dev/null
    kill "${BROWSERPID}" 2>/dev/null
    return 0
}

# Cleanup function
# shellcheck disable=SC2120 # $1 is an optional "signal caught" marker; bare calls are intentional
function cleanup {
    test -n "$1" && log "Nasty signal caught, cleaning up.."
    # Do not kill if browser exited (chrome bug issue) ; server will die itself
    test -n "$1" && test -f "${TMPSRVFILE}" && SRVPID=$(grep -E PID= "${TMPSRVFILE}" | cut -f2- -d=)
    test -n "${SRVPID}" && kill -9 "${SRVPID}"
    test -f "${TMPSRVFILE}" && rm "${TMPSRVFILE}"
    reap_browser
    test -n "$1" && log "..Done"
    return 0
}

# Cleanup in case of emergency
trap "cleanup now; exit" HUP INT QUIT PIPE TERM

# Got SRVURL, launch browser. Backgrounded so the wait below can watch the
# server too, rather than only the browser.
launch_browser "${BROWSEREXE}" "${SRVURL}" &
BROWSERPID=$!
# Deliberately not SRVPID, which cleanup would then kill, taking a running mirror
# with it.
SESSIONPID=$(grep -E PID= "${TMPSRVFILE}" | cut -f2- -d=)
wait_for_session "${BROWSERPID}" "${SESSIONPID}"

# That's all, folks!
trap "" HUP INT QUIT PIPE TERM
cleanup
exit 0
