| 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 |
| 11 | |
| 12 | set -u |
| 13 | |
| 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 |
| 87 | fi |
| 88 | |
| 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 |