#!/bin/sh
# Imprion PoS - the Back to PoS extension's native messaging host (README
# decision 283).
#
# The PoS page's openMaintenance() reaches the Service button's own launcher
# through this: Back to PoS calls chrome.runtime.sendNativeMessage(
# "cloud.imprion.maintenance", {op: "openMaintenance"}), Chromium starts this
# program as the manifest /etc/chromium/native-messaging-hosts/
# cloud.imprion.maintenance.json names it (allowed_origins: that extension
# only), and this calls cloud.imprion.Shell.OpenMaintenance, which the
# imprion-shell GNOME Shell extension exports on the session bus. It never
# starts a browser itself.
#
# The protocol is Chromium's: on stdin one message, a 32-bit length in native
# byte order and that many bytes of UTF-8 JSON; on stdout one reply, framed the
# same way. Chromium passes the caller's origin as the first argument.
#
# Replies: {"ok":true}, or {"ok":false,"error":E} with E one of
#   bad_request              not the one message this host takes
#   maintenance_unavailable  the shell said so (the live/diagnostics boot)
#   shell_unavailable        the shell did not answer (no session bus, the
#                            extension not loaded, the screen locked)
#   failed                   the shell's launcher failed
#
# POSIX sh, dd, od and busctl (systemd): nothing that is not already on the
# image. It runs as the till's user, in pos-frontend.service, with that unit's
# session bus.

set -u

ORIGIN='chrome-extension://jaalolkpfgfjkoficgcdjlaiappkghfd/'
REQUEST='{"op":"openMaintenance"}'

# reply JSON: the 4-byte little-endian length, then the text (ASCII only).
reply() {
	n=${#1}
	# shellcheck disable=SC2059 # the octal escapes are the format
	printf "$(printf '\\%03o\\%03o\\%03o\\%03o' \
		$((n & 255)) $((n >> 8 & 255)) $((n >> 16 & 255)) $((n >> 24 & 255)))"
	printf '%s' "$1"
	exit 0
}

fail() {
	reply "{\"ok\":false,\"error\":\"$1\"}"
}

[ "${1:-}" = "$ORIGIN" ] || fail bad_request

len=$(dd bs=4 count=1 iflag=fullblock 2>/dev/null | od -An -tu4 | tr -d ' ')
[ "$len" = "${#REQUEST}" ] || fail bad_request

message=$(dd bs="$len" count=1 iflag=fullblock 2>/dev/null)
[ "$message" = "$REQUEST" ] || fail bad_request

answer=$(busctl --user --timeout=3 call org.gnome.Shell /cloud/imprion/Shell \
	cloud.imprion.Shell OpenMaintenance 2>/dev/null) || fail shell_unavailable

case "$answer" in
's "ok"') reply '{"ok":true}' ;;
's "unavailable"') fail maintenance_unavailable ;;
*) fail failed ;;
esac
