aurora2aa63d2
scripts: improvise campus login, claude is king
+104 −11
1 file changed
- M .local/bin/campus-login +104 −11
| @@ -1,15 +1,108 @@ | |||
| 1 | − | #!/bin/bash | |
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | # campus-login: detect a captive portal and open it in your browser, | |
| 3 | + | # reporting every step through the notification daemon. | |
| 4 | + | # | |
| 5 | + | # Usage: campus-login [-n|--no-wait] [-h|--help] | |
| 6 | + | # -n, --no-wait don't wait around to confirm you're online afterwards | |
| 7 | + | # | |
| 8 | + | # Env overrides: | |
| 9 | + | # PROBE_URL plain-HTTP URL that returns 204 when there is no portal | |
| 10 | + | # WAIT_TIMEOUT seconds to wait for connectivity after opening the portal | |
| 2 | 11 | ||
| 3 | − | URL=$(curl -s -D - -o /dev/null http://neverssl.com 2>/dev/null \ | |
| 4 | − | | grep -i '^location:' \ | |
| 5 | − | | head -n1 \ | |
| 6 | − | | sed 's/^[Ll]ocation:[[:space:]]*//' \ | |
| 7 | − | | tr -d '\r') | |
| 12 | + | set -u | |
| 8 | 13 | ||
| 9 | − | if [[ -z "$URL" ]]; then | |
| 10 | − | echo "Already authenticated or no captive portal detected." | |
| 11 | − | exit 0 | |
| 14 | + | PROBE_URL=${PROBE_URL:-http://connectivitycheck.gstatic.com/generate_204} | |
| 15 | + | WAIT_TIMEOUT=${WAIT_TIMEOUT:-180} | |
| 16 | + | WAIT=1 | |
| 17 | + | ||
| 18 | + | case "${1:-}" in | |
| 19 | + | -n|--no-wait) WAIT=0 ;; | |
| 20 | + | -h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; | |
| 21 | + | esac | |
| 22 | + | ||
| 23 | + | # --- notifications ---------------------------------------------------------- | |
| 24 | + | # Every notification shares one "synchronous" tag so they replace each other | |
| 25 | + | # instead of stacking (works with dunst, mako, swaync). | |
| 26 | + | notify() { # notify <urgency> <icon> <title> <body> | |
| 27 | + | local urgency=$1 icon=$2 title=$3 body=$4 | |
| 28 | + | echo "$title: $body" | |
| 29 | + | command -v notify-send >/dev/null 2>&1 || return 0 | |
| 30 | + | notify-send -a campus-login -u "$urgency" -i "$icon" \ | |
| 31 | + | -h string:x-canonical-private-synchronous:campus-login \ | |
| 32 | + | -h string:x-dunst-stack-tag:campus-login \ | |
| 33 | + | "$title" "$body" | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | # --- probe ------------------------------------------------------------------ | |
| 37 | + | # Prints "<http_code> <redirect_url>". curl resolves relative redirects for us, | |
| 38 | + | # so no more grepping/sed-ing the Location header. | |
| 39 | + | probe() { | |
| 40 | + | curl -s -o /dev/null --max-time 5 \ | |
| 41 | + | -w '%{http_code} %{redirect_url}' "$PROBE_URL" 2>/dev/null | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | read -r CODE URL < <(probe) | |
| 45 | + | CODE=${CODE:-000} | |
| 46 | + | ||
| 47 | + | case "$CODE" in | |
| 48 | + | 000) | |
| 49 | + | notify critical network-error "No connection" \ | |
| 50 | + | "Couldn't reach the network. Are you connected to the Wi-Fi?" | |
| 51 | + | exit 2 | |
| 52 | + | ;; | |
| 53 | + | 204) | |
| 54 | + | notify low network-wireless "Already online" \ | |
| 55 | + | "No captive portal detected." | |
| 56 | + | exit 0 | |
| 57 | + | ;; | |
| 58 | + | esac | |
| 59 | + | ||
| 60 | + | if [[ -n "${URL:-}" ]]; then | |
| 61 | + | notify normal network-wireless-acquiring "Captive portal detected" "$URL" | |
| 62 | + | else | |
| 63 | + | # Some portals hijack the request and answer 200 without redirecting. | |
| 64 | + | # Opening a plain-HTTP page lets the portal intercept it in the browser. | |
| 65 | + | URL=$PROBE_URL | |
| 66 | + | notify normal network-wireless-acquiring "Captive portal likely" \ | |
| 67 | + | "Got HTTP $CODE with no redirect. Opening a test page to trigger it." | |
| 68 | + | fi | |
| 69 | + | ||
| 70 | + | # --- pick a browser --------------------------------------------------------- | |
| 71 | + | # $BROWSER is often unset when launched from fuzzel/rofi, so fall back. | |
| 72 | + | launch=() | |
| 73 | + | if [[ -n "${BROWSER:-}" ]]; then | |
| 74 | + | read -ra launch <<< "$BROWSER" | |
| 75 | + | fi | |
| 76 | + | if ! (( ${#launch[@]} )) || ! command -v "${launch[0]}" >/dev/null 2>&1; then | |
| 77 | + | launch=() | |
| 78 | + | for b in xdg-open firefox chromium google-chrome-stable brave; do | |
| 79 | + | if command -v "$b" >/dev/null 2>&1; then launch=("$b"); break; fi | |
| 80 | + | done | |
| 81 | + | fi | |
| 82 | + | ||
| 83 | + | if ! (( ${#launch[@]} )); then | |
| 84 | + | notify critical dialog-error "No browser found" \ | |
| 85 | + | "Set \$BROWSER or install one. Portal URL: $URL" | |
| 86 | + | exit 3 | |
| 12 | 87 | fi | |
| 13 | 88 | ||
| 14 | − | echo "Opening captive portal: $URL" | |
| 15 | − | "$BROWSER" "$URL" | |
| 89 | + | # Detach so the browser outlives this script (and the launcher). | |
| 90 | + | setsid -f "${launch[@]}" "$URL" >/dev/null 2>&1 | |
| 91 | + | notify normal web-browser "Opened portal" "${launch[0]} -> $URL" | |
| 92 | + | ||
| 93 | + | # --- wait for confirmation -------------------------------------------------- | |
| 94 | + | (( WAIT )) || exit 0 | |
| 95 | + | ||
| 96 | + | end=$((SECONDS + WAIT_TIMEOUT)) | |
| 97 | + | while (( SECONDS < end )); do | |
| 98 | + | sleep 3 | |
| 99 | + | read -r CODE _ < <(probe) | |
| 100 | + | if [[ "${CODE:-000}" == 204 ]]; then | |
| 101 | + | notify normal network-wireless "Connected" "You're online." | |
| 102 | + | exit 0 | |
| 103 | + | fi | |
| 104 | + | done | |
| 105 | + | ||
| 106 | + | notify normal network-wireless-offline "Still not online" \ | |
| 107 | + | "No connectivity after ${WAIT_TIMEOUT}s. The login may not have completed." | |
| 108 | + | exit 4 | |