#!/bin/sh
# CertifiedCopy agent installer.
#
#   curl -fsSL https://certified.sh/install.sh | sh
#
# The agent is optional and always will be. Everything CertifiedCopy does with a file you already
# have — reading a chat.db, finding sms.db inside a backup, searching, exporting a PDF — happens in
# the browser and needs none of this. The agent exists for the one thing a browser cannot do: talk
# to a plugged-in iPhone. On macOS, usbmuxd opens the Apple Mobile Device interface the moment a
# cable goes in and holds it, and WebUSB has to claim an interface to speak to a device.
#
# Two notes on how this is written, both deliberate.
#
# Everything is inside main(), which is called on the very last line. A `curl | sh` that is cut off
# mid-transfer — a dropped connection, a proxy timeout — otherwise executes whatever fraction
# arrived, which for an installer means a half-finished install with no error. Truncated here, the
# shell reaches the end of the file without ever calling main(), and does nothing at all.
#
# The download is checksummed. Not because the transport is untrusted — it is HTTPS — but because
# a truncated or corrupted binary is the failure that actually happens, and it fails later, in a
# place that looks like a bug in the program.

set -eu

BASE="${CERTIFIEDCOPY_BASE:-https://certified.sh}"
PREFIX="${CERTIFIEDCOPY_PREFIX:-$HOME/.certifiedcopy}"
BIN_DIR="${CERTIFIEDCOPY_BIN_DIR:-$HOME/.local/bin}"

red()  { printf '\033[31m%s\033[0m\n' "$*" >&2; }
bold() { printf '\033[1m%s\033[0m\n' "$*"; }
die()  { red "$*"; exit 1; }

main() {
    [ "$(uname -s)" = "Darwin" ] || die \
"The agent is macOS only for now.

It exists to talk to a plugged-in iPhone, and it does that through usbmuxd, which
ships with macOS. Everything else CertifiedCopy does works in your browser on any
system, with no install: https://certified.sh/app/"

    need curl
    need shasum
    need tar

    bold "Downloading the CertifiedCopy agent…"
    tmp=$(mktemp -d)
    # Cleared on any exit, including a failure part-way through: a half-extracted archive left in
    # /tmp is the kind of thing that gets found months later and puzzled over.
    trap 'rm -rf "$tmp"' EXIT INT TERM

    # One small file names the current build and carries its digest. The archive's own name
    # contains that digest, so the two can never be cached out of step with each other -- which is
    # the failure a fixed filename invites: a stale archive, a fresh checksum, and an installer
    # refusing a release that is perfectly good.
    fetch "${BASE}/download/latest.txt" "$tmp/latest"
    name=$(cut -d' ' -f1 < "$tmp/latest")
    expected=$(cut -d' ' -f2 < "$tmp/latest")
    case "$name" in
        certifiedcopy-macos-*.tar.gz) ;;
        *) die "The download index names something unexpected: ${name:-<empty>}" ;;
    esac

    archive="$tmp/agent.tar.gz"
    fetch "${BASE}/download/${name}" "$archive"
    actual=$(shasum -a 256 "$archive" | cut -d' ' -f1)
    [ "$expected" = "$actual" ] || die \
"The download does not match its checksum.

  expected  $expected
  got       $actual

This usually means the transfer was cut short. Try again; if it persists, say so
at https://certified.sh and do not run what was downloaded."

    bold "Installing to ${PREFIX}…"
    rm -rf "$PREFIX"
    mkdir -p "$PREFIX"
    tar -xzf "$archive" -C "$PREFIX"
    [ -x "$PREFIX/certifiedcopy" ] || die "The archive did not contain the agent."

    mkdir -p "$BIN_DIR"
    ln -sf "$PREFIX/certifiedcopy" "$BIN_DIR/certifiedcopy"

    # Fetched by curl, so it never receives com.apple.quarantine and Gatekeeper does not block it.
    # A binary downloaded through a browser would be quarantined and refuse to run, which is worth
    # knowing before anyone tries to be helpful by offering a download button.
    printf '\n'
    bold "Installed."
    printf '  agent   %s\n' "$PREFIX/certifiedcopy"
    printf '  link    %s\n\n' "$BIN_DIR/certifiedcopy"

    case ":${PATH}:" in
        *":${BIN_DIR}:"*) printf 'Run it with:\n\n  certifiedcopy\n\n' ;;
        *) printf '%s is not on your PATH. Either run it directly:\n\n  %s\n\nor add it:\n\n  echo '"'"'export PATH="%s:$PATH"'"'"' >> ~/.zshrc\n\n' \
               "$BIN_DIR" "$BIN_DIR/certifiedcopy" "$BIN_DIR" ;;
    esac

    printf 'It serves the app at http://127.0.0.1:4823 and reads nothing until you ask it to.\n'
    printf 'To remove it: rm -rf %s %s\n\n' "$PREFIX" "$BIN_DIR/certifiedcopy"
}

need() {
    command -v "$1" >/dev/null 2>&1 || die "This installer needs $1, which is not on your PATH."
}

fetch() {
    # --fail so an HTML error page is never written to disk and then run. -L because the download
    # may be served from a redirect.
    curl -fsSL --proto '=https' --tlsv1.2 -o "$2" "$1" || die "Could not download $1"
}

# Called last, on purpose. See the note at the top: a truncated download of this file runs nothing.
main "$@"
