#!/bin/sh
# w3bs installer: the W3BS command line. https://w3bs.org/packages
#
#   curl --proto '=https' --proto-redir '=https' -fsSL <this url> | sh
#
# Inspect first if you like:  curl -fsSL <this url> -o install.sh && less install.sh && sh install.sh
#
# What this does:
#   1. If the w3bs CLI is not installed, download the prebuilt binary for
#      this machine, check its SHA-256, and let it install itself from its signed repository.
#   2. If this installer is for an application, register the publisher's signed repository
#      (first-use trust: you are shown its fingerprint) and run `w3bs install <package>`.
#   3. Afterwards, `w3bs update` keeps every installed application current.
#
# Trust boundary: this script runs as you before W3BS can verify anything, and the
# checksums and fingerprints below arrive in the same response as the script. They catch
# corruption and mix-ups, not a malicious server. Everything after the bootstrap is
# verified by W3BS against the signed repository metadata (TUF).
#
# Options are passed straight to `w3bs install`, e.g.  ... | sh -s -- --version 1.2.3 --pin
# Environment: W3BS_YES=1 approves without prompting (requires the fingerprints below);
#              W3BS_HOME, W3BS_BIN_DIR relocate the install.
#              W3BS_LIBC=gnu|musl overrides the C library detected on Linux (musl on Alpine
#              and postmarketOS gets the static musl build).
#
# Nothing runs until the last line, so a truncated download does nothing.

w3bs_main() {
  set -u
  PACKAGE=''
  REPO_ALIAS='w3bs'
  REPO_URL='https://w3bs.org/repo/'
  REPO_FINGERPRINT='sha256:48d521f7610117c95cc782982e5c05b641b18bdfa8e48b36a634186133bc766f'
  MANAGER_REPO_URL='https://w3bs.org/repo/'
  MANAGER_FINGERPRINT='sha256:48d521f7610117c95cc782982e5c05b641b18bdfa8e48b36a634186133bc766f'
  MANAGER_DOWNLOAD_BASE='https://github.com/profullstack/w3bs.org/releases/download'
  MANAGER_VERSION='0.1.16'
  MANAGER_SHA256S='x86_64-unknown-linux-gnu=8a1ffd7e0a39730fe6bef224fada665a90d2e3e573d5a751c92972868d6e0109 aarch64-unknown-linux-gnu=f0aad4b98e4b1dbecc974f567204fce1a4310c328a90aefb26b230b3831b8388 x86_64-unknown-linux-musl=50fd98ae73a43825652cb0d5ecf147bdb4c9a2d73604f1168aef073c8555ff52 aarch64-unknown-linux-musl=50ece32b9dd6033c113271bbb77e5994e44c6d9287f9f684633e43fa3aa24dfb x86_64-apple-darwin=26878f4c07e1e74ca1f6cfff24fe0529e77e7a5b1e7324f2fcfa9896691cee4f aarch64-apple-darwin=a5fd90903c223e12d44dc424014367d32ca135b3832883bb224c359514433d3b'

  say() { printf '%s\n' "$*" >&2; }
  die() { say "w3bs installer: $*"; exit "${2:-4}"; }

  # musl (Alpine, postmarketOS) or glibc. ldd names its C library; without ldd, a glibc libc.so.6
  # wins over a musl loader (Debian's `musl` package adds one; Alpine's gcompat fakes the glibc
  # loader, not libc.so.6). W3BS_LIBC=gnu|musl overrides.
  linux_libc() {
    case "${W3BS_LIBC:-}" in gnu | musl) echo "$W3BS_LIBC"; return ;; esac
    v="$(ldd --version 2>&1 || true)"
    case "$v" in
      *musl*) echo musl; return ;;
      *GNU* | *GLIBC* | *glibc*) echo gnu; return ;;
    esac
    for f in /lib/*-linux-gnu/libc.so.6 /lib64/libc.so.6 /usr/lib64/libc.so.6 /usr/lib/libc.so.6; do
      [ -e "$f" ] && { echo gnu; return; }
    done
    for f in /lib/ld-musl-*.so.1; do
      [ -e "$f" ] && { echo musl; return; }
    done
    echo gnu
  }

  case "$(uname -s)" in
    Linux) os="unknown-linux-$(linux_libc)" ;;
    Darwin) os=apple-darwin ;;
    *) die "unsupported operating system $(uname -s); W3BS supports Linux and macOS" ;;
  esac
  case "$(uname -m)" in
    x86_64 | amd64) arch=x86_64 ;;
    aarch64 | arm64) arch=aarch64 ;;
    *) die "unsupported CPU $(uname -m); W3BS supports x86_64 and aarch64" ;;
  esac
  triple="$arch-$os"

  if [ -n "${W3BS_HOME:-}" ]; then
    data="$W3BS_HOME/data"
    bin="${W3BS_BIN_DIR:-$W3BS_HOME/bin}"
  else
    [ -n "${HOME:-}" ] || die "HOME is not set"
    data="${XDG_DATA_HOME:-$HOME/.local/share}/w3bs"
    bin="${W3BS_BIN_DIR:-$HOME/.local/bin}"
  fi
  # The managed manager lives at a fixed path. A random `w3bs` on PATH is never trusted.
  manager="$data/packages/w3bs-manager/current/bin/w3bs"

  yes=""
  if [ "${W3BS_YES:-}" = 1 ]; then
    yes="--yes"
  elif ! (: </dev/tty) 2>/dev/null; then
    die "no terminal to confirm with; re-run with W3BS_YES=1 to approve the fingerprints shown in this script" 3
  fi

  fetch() { # url dest
    if command -v curl >/dev/null 2>&1; then
      curl --proto '=https' --proto-redir '=https' --tlsv1.2 -fsSL "$1" -o "$2"
    elif command -v wget >/dev/null 2>&1; then
      wget --https-only -q -O "$2" "$1"
    else
      die "need curl or wget"
    fi
  }
  sha256() {
    if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
    elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1
    else die "need sha256sum or shasum"; fi
  }

  if [ ! -x "$manager" ]; then
    expected=""
    for pair in $MANAGER_SHA256S; do
      [ "${pair%%=*}" = "$triple" ] && expected="${pair#*=}"
    done
    [ -n "$expected" ] || die "no w3bs build for $triple yet"
    say "The w3bs CLI $MANAGER_VERSION will be installed. It manages this and future packages, and much more."
    say "  It keeps everything it installs updatable with one command: w3bs update"
    tmp="$(mktemp -d 2>/dev/null || mktemp -d -t w3bs)" || die "cannot create a temporary directory"
    trap 'rm -rf "$tmp"' EXIT INT TERM
    archive="$tmp/w3bs.tar.gz"
    fetch "$MANAGER_DOWNLOAD_BASE/w3bs-v$MANAGER_VERSION/w3bs-$MANAGER_VERSION-$triple.tar.gz" "$archive" || die "download failed"
    actual="$(sha256 "$archive")"
    [ "$actual" = "$expected" ] || die "checksum mismatch for the downloaded manager (got $actual, expected $expected)"
    mkdir "$tmp/x" && tar -xzf "$archive" -C "$tmp/x" || die "cannot unpack the manager"
    [ -x "$tmp/x/bin/w3bs" ] || die "the manager archive has no bin/w3bs"
    # The temporary binary installs the real manager through the verified engine.
    "$tmp/x/bin/w3bs" self install --repository-url "$MANAGER_REPO_URL" --root-fingerprint "$MANAGER_FINGERPRINT" $yes || exit $?
  fi

  if [ -n "$PACKAGE" ]; then
    if ! "$manager" repo info "$REPO_ALIAS" >/dev/null 2>&1; then
      "$manager" repo add "$REPO_ALIAS" "$REPO_URL" --root-fingerprint "$REPO_FINGERPRINT" $yes || exit $?
    fi
    "$manager" install "$PACKAGE" --repository "$REPO_ALIAS" --origin curl $yes "$@" || exit $?
  fi

  say ""
  say "w3bs: $manager   (try: w3bs packages, w3bs --help)"
  case ":${PATH:-}:" in
    *":$bin:"*) say "Update everything later with:  w3bs update" ;;
    *)
      say "$bin is not on your PATH. Add it with:"
      case "${SHELL:-}" in
        */fish) say "  fish_add_path $bin" ;;
        */zsh) say "  echo 'export PATH=\"$bin:\$PATH\"' >> ~/.zshrc" ;;
        *) say "  echo 'export PATH=\"$bin:\$PATH\"' >> ~/.profile" ;;
      esac
      say "Until then, run it as:  $bin/w3bs update"
      ;;
  esac
  # On a desktop, offer (never register silently) the handler for w3bs:// install links.
  if [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] || { [ "$(uname -s)" = Darwin ] && [ -z "${SSH_CONNECTION:-}" ]; }; then
    say "Open w3bs:// install links from your browser (asks before every install):  w3bs handler install"
  fi
}

w3bs_main "$@"
