From 6c9e8e0b76d19ada3cb41e309b66a477c01caab0 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Mon, 25 Aug 2025 17:54:46 +0200 Subject: [PATCH] feat: install leechblock origin --- scripts/install_leechblock_origin.sh | 364 +++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100755 scripts/install_leechblock_origin.sh diff --git a/scripts/install_leechblock_origin.sh b/scripts/install_leechblock_origin.sh new file mode 100755 index 0000000..2f980b7 --- /dev/null +++ b/scripts/install_leechblock_origin.sh @@ -0,0 +1,364 @@ +#!/usr/bin/env bash +# Installs LeechBlock NG extension for Chrome/Chromium and/or Firefox +# and fetches/imports LeechBlockOptions.txt from a provided gist. +# +# Chrome/Chromium: enforced install via policy (ExtensionInstallForcelist) +# Firefox: system-wide side-load by placing XPI in distribution/extensions/ +# Options: the script downloads LeechBlockOptions.txt to ~/.config/leechblock/ +# and opens the extension options page to let you import the file. + +set -Eeuo pipefail + +# Globals & flags +VERBOSE=false + +GIST_ID="fb2417de59e5b6f295079364a9fb0ae0" +GIST_USER="kuhyx" +OPTIONS_FILENAME="LeechBlockOptions.json" +RAW_GIST_URL="https://gist.githubusercontent.com/${GIST_USER}/${GIST_ID}/raw/${OPTIONS_FILENAME}" + +# Chrome Web Store (LeechBlock NG) extension ID +CHROME_EXT_ID="blaaajhemilngeeffpbfkdjjoefldkok" +CHROME_UPDATE_URL="https://clients2.google.com/service/update2/crx" + +# Firefox AMO latest XPI (stable pattern) +FF_ADDON_SLUG="leechblock-ng" +FF_LATEST_XPI_URL="https://addons.mozilla.org/firefox/downloads/latest/${FF_ADDON_SLUG}/latest.xpi" +FF_EXTENSION_ID="leechblockng@proginosko.com" + +OPTIONS_DIR="$HOME/.config/leechblock" +OPTIONS_PATH="${OPTIONS_DIR}/${OPTIONS_FILENAME}" + +bold() { echo -e "\033[1m$*\033[0m"; } +note() { echo -e "[+] $*"; } +warn() { echo -e "[!] $*"; } +err() { echo -e "[x] $*" 1>&2; } +dbg() { if [[ "$VERBOSE" == true ]]; then echo -e "[.] $*"; fi } + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + err "Required command not found: $1" + exit 1 + fi +} + +need_root() { + if [[ $EUID -ne 0 ]]; then + warn "Some steps require root (policies and system install). Requesting sudo..." + exec sudo --preserve-env=HOME "$0" "$@" + fi +} + +detect_browsers() { + BROWSERS=() + # Chromium family + for bin in google-chrome chromium chromium-browser brave-browser brave vivaldi thorium-browser thorium microsoft-edge microsoft-edge-stable; do + if command -v "$bin" >/dev/null 2>&1; then BROWSERS+=("$bin"); fi + done + # Firefox + if command -v firefox >/dev/null 2>&1; then BROWSERS+=(firefox); fi +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + -v|--verbose) + VERBOSE=true + shift + ;; + -h|--help) + cat </dev/null 2>&1; then + dbg "Fetching gist page to discover raw link: ${page}" + local html + if html=$(curl -fsSL "$page" 2>/dev/null); then + local link + # Prefer a link that contains the filename + link=$(printf '%s' "$html" | grep -oE 'https://gist\.githubusercontent\.com/[^" ]+/raw/[^" ]+' | grep -i "/${OPTIONS_FILENAME}" | head -n1 || true) + if [[ -z "$link" ]]; then + # Fallback to any raw link on the page + link=$(printf '%s' "$html" | grep -oE 'https://gist\.githubusercontent\.com/[^" ]+/raw/[^" ]+' | head -n1 || true) + fi + if [[ -n "$link" ]]; then + candidates=("$link" "${candidates[@]}") + fi + else + dbg "Failed to fetch gist page for discovery; will use default candidates" + fi + fi + + printf '%s\n' "${candidates[@]}" +} + +download_options() { + require_cmd curl + mkdir -p "${OPTIONS_DIR}" + note "Downloading LeechBlock options to ${OPTIONS_PATH}" + + local urls + IFS=$'\n' read -r -d '' -a urls < <(resolve_gist_raw_url && printf '\0') + + local ok=false + for u in "${urls[@]}"; do + [[ -z "$u" ]] && continue + note "Attempting: $u" + local tmp="${OPTIONS_PATH}.tmp" + # Capture HTTP code while following redirects + local code + code=$(curl -sS -L -w '%{http_code}' -o "$tmp" "$u" || true) + dbg "HTTP code for $u => $code" + if [[ "$code" == "200" || "$code" == "302" || "$code" == "301" ]]; then + # If file seems HTML (gist page), skip; otherwise accept + if file --mime-type "$tmp" 2>/dev/null | grep -q 'text/html'; then + dbg "Downloaded HTML, not the raw options; trying next candidate" + continue + fi + mv "$tmp" "$OPTIONS_PATH" + ok=true + break + else + warn "Failed ($code): $u" + fi + done + + if [[ "$ok" != true ]]; then + err "Could not download options from gist. Last tried: ${urls[-1]}" + exit 1 + fi + + note "Options saved. Size: $(wc -c < "${OPTIONS_PATH}") bytes" +} + +install_chrome_policy() { + local entry + entry="${CHROME_EXT_ID};${CHROME_UPDATE_URL}" + local policy_json + policy_json='{ "ExtensionInstallForcelist": [ '"\"${entry}\""' ] }' + + if command -v google-chrome >/dev/null 2>&1; then + local dir="/etc/opt/chrome/policies/managed" + mkdir -p "$dir" + echo "$policy_json" > "${dir}/leechblock.json" + note "Chrome policy installed at ${dir}/leechblock.json" + fi + + # Chromium (Arch uses /etc/chromium) + if command -v chromium >/dev/null 2>&1 || command -v chromium-browser >/dev/null 2>&1; then + local dir="/etc/chromium/policies/managed" + mkdir -p "$dir" + echo "$policy_json" > "${dir}/leechblock.json" + note "Chromium policy installed at ${dir}/leechblock.json" + fi + + note "Chrome/Chromium will fetch LeechBlock NG on next start (or after a minute if running)." +} + + # Generic installer for Chromium-based browsers (per-user, no root): + # Creates External Extensions JSON in the browser's user config dir. + install_chromium_external_user() { + local config_dir="$1"; shift || true + mkdir -p "${config_dir}/External Extensions" + local json_path="${config_dir}/External Extensions/${CHROME_EXT_ID}.json" + echo "{ \"external_update_url\": \"${CHROME_UPDATE_URL}\" }" > "$json_path" + note "Per-user external extension registered: $json_path" + } + + # Policy writer for Chromium-based browsers; takes one or more policy directories + install_chromium_policy_dirs() { + local entry + entry="${CHROME_EXT_ID};${CHROME_UPDATE_URL}" + local policy_json + policy_json='{ "ExtensionInstallForcelist": [ '"\"${entry}\""' ] }' + for dir in "$@"; do + mkdir -p "$dir" + echo "$policy_json" > "${dir}/leechblock.json" + note "Policy installed at ${dir}/leechblock.json" + done + } + +install_firefox_distribution() { + # Target typical Firefox system distribution directory + local distro_dir="/usr/lib/firefox/distribution" + local ext_dir="${distro_dir}/extensions" + if ! command -v firefox >/dev/null 2>&1; then + warn "Firefox not detected; skipping Firefox install." + return 0 + fi + + require_cmd curl + mkdir -p "$ext_dir" + local xpi_path="${ext_dir}/${FF_EXTENSION_ID}.xpi" + note "Downloading Firefox XPI to ${xpi_path}" + curl -fL --retry 3 --retry-delay 2 "${FF_LATEST_XPI_URL}" -o "${xpi_path}" + chmod 644 "${xpi_path}" + note "Firefox will side-load LeechBlock NG from ${xpi_path} on next start." +} + +open_import_pages() { + # Best-effort: open the extension options/import pages to quickly import the downloaded file. + # Chrome-based: try chrome-extension:///options.html (may vary per version) + # Fallback: open the store page with guidance. + if command -v google-chrome >/dev/null 2>&1; then + (google-chrome "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v chromium >/dev/null 2>&1; then + (chromium "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v chromium-browser >/dev/null 2>&1; then + (chromium-browser "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v brave-browser >/dev/null 2>&1; then + (brave-browser "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v brave >/dev/null 2>&1; then + (brave "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v vivaldi >/dev/null 2>&1; then + (vivaldi "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v thorium-browser >/dev/null 2>&1; then + (thorium-browser "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v thorium >/dev/null 2>&1; then + (thorium "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v microsoft-edge >/dev/null 2>&1; then + (microsoft-edge "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + if command -v microsoft-edge-stable >/dev/null 2>&1; then + (microsoft-edge-stable "chrome-extension://${CHROME_EXT_ID}/options.html" >/dev/null 2>&1 || true) & + fi + + # Firefox: cannot know the moz-extension UUID beforehand; open about:addons + if command -v firefox >/dev/null 2>&1; then + (firefox about:addons >/dev/null 2>&1 || true) & + fi + + cat < LeechBlock NG -> Preferences +EOF +} + +main() { + bold "LeechBlock NG installer" + parse_args "$@" + detect_browsers + if [[ ${#BROWSERS[@]} -eq 0 ]]; then + warn "No supported browsers detected (Chrome/Chromium/Firefox). Proceeding to download options only." + else + note "Detected browsers: ${BROWSERS[*]}" + fi + + download_options + + # Per-user installs for Chromium-based browsers (works without root) + if command -v google-chrome >/dev/null 2>&1; then + install_chromium_external_user "$HOME/.config/google-chrome" + fi + if command -v chromium >/dev/null 2>&1 || command -v chromium-browser >/dev/null 2>&1; then + install_chromium_external_user "$HOME/.config/chromium" + fi + if command -v brave-browser >/dev/null 2>&1 || command -v brave >/dev/null 2>&1; then + install_chromium_external_user "$HOME/.config/BraveSoftware/Brave-Browser" + fi + if command -v vivaldi >/dev/null 2>&1; then + install_chromium_external_user "$HOME/.config/vivaldi" + fi + if command -v thorium-browser >/dev/null 2>&1 || command -v thorium >/dev/null 2>&1; then + install_chromium_external_user "$HOME/.config/thorium-browser" + fi + if command -v microsoft-edge >/dev/null 2>&1 || command -v microsoft-edge-stable >/dev/null 2>&1; then + install_chromium_external_user "$HOME/.config/microsoft-edge" + fi + + # Elevate for system policy installs if possible + if [[ $EUID -ne 0 ]]; then + sudo --preserve-env=HOME "$0" --as-root || warn "Skipping system policy installs (no sudo)." + # After root part returns, open import pages as user + open_import_pages + exit 0 + fi + + # Root section: install policies / side-loads + # Chrome + if command -v google-chrome >/dev/null 2>&1; then + install_chromium_policy_dirs /etc/opt/chrome/policies/managed + fi + # Chromium + if command -v chromium >/dev/null 2>&1 || command -v chromium-browser >/dev/null 2>&1; then + install_chromium_policy_dirs /etc/chromium/policies/managed + fi + # Brave (try common paths) + if command -v brave-browser >/dev/null 2>&1 || command -v brave >/dev/null 2>&1; then + install_chromium_policy_dirs /etc/opt/brave/policies/managed /etc/brave/policies/managed + fi + # Vivaldi + if command -v vivaldi >/dev/null 2>&1; then + install_chromium_policy_dirs /etc/opt/vivaldi/policies/managed /etc/vivaldi/policies/managed + fi + # Thorium + if command -v thorium-browser >/dev/null 2>&1 || command -v thorium >/dev/null 2>&1; then + install_chromium_policy_dirs /etc/opt/thorium/policies/managed /etc/thorium/policies/managed + fi + # Edge (optional; best effort) + if command -v microsoft-edge >/dev/null 2>&1 || command -v microsoft-edge-stable >/dev/null 2>&1; then + install_chromium_policy_dirs /etc/opt/edge/policies/managed /etc/edge/policies/managed + fi + if command -v firefox >/dev/null 2>&1; then + install_firefox_distribution + fi + + note "Root tasks done. Returning to user session to open import pages..." +} + +if [[ "${1:-}" == "--as-root" ]]; then + shift || true + # Now running as root (from re-exec) + if command -v google-chrome >/dev/null 2>&1 || command -v chromium >/dev/null 2>&1 || command -v chromium-browser >/dev/null 2>&1; then + # Handled per browser in --as-root main block; keep for backward compat + install_chromium_policy_dirs /etc/opt/chrome/policies/managed /etc/chromium/policies/managed + fi + if command -v firefox >/dev/null 2>&1; then + install_firefox_distribution + fi + exit 0 +fi + +main "$@" +