diff options
Diffstat (limited to 'src/wifi-menu')
-rwxr-xr-x | src/wifi-menu | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/wifi-menu b/src/wifi-menu new file mode 100755 index 0000000..f46db0d --- /dev/null +++ b/src/wifi-menu @@ -0,0 +1,263 @@ +#! /bin/bash + +. /usr/lib/network/network +. "$SUBR_DIR/8021x" +. /etc/conf.d/netcfg + +usage() +{ + cat << END +Usage: wifi-menu [-o | --obscure] [-h | --help] [interface] + +Interactively connect to a wireless network. + +Arguments: + -o, --obscure Show asterisks for the characters of the password + and store the password as a hexadecimal string. + -h, --help Show this help. + interface The wireless interface to use. + (default: WIRELESS_INTERFACE from /etc/conf.d/netcfg) + +For choosing from all available profiles, use netcfg-menu. +END +} + +# Fills PROFILES and ESSIDS with the profile names and essids of the profiles +# for interface $1. +init_profiles() +{ + local i=0 essid profile + while read profile; do + essid=$( + unset INTERFACE ESSID + . "$PROFILE_DIR/$profile" &> /dev/null + if [[ "$INTERFACE" = "$1" && -n "$ESSID" ]]; then + printf "%s" "$ESSID" + if [[ "$DESCRIPTION" =~ "Automatically generated" ]]; then + return 2 + else + return 1 + fi + fi + return 0 + ) + case $? in + 2) + GENERATED+=("$profile") + ;& + 1) + PROFILES[i]=$profile + ESSIDS[i]=$essid + (( ++i )) + ;; + esac + done < <(list_profiles) +} + +# Builds ENTRIES as an argument list for dialog based on scan results in $1. +init_entries() +{ + local i=0 flags signal ssid + while IFS=$'\t' read signal flags ssid; do + ENTRIES[i++]="--" # $ssid might look like an option to dialog. + ENTRIES[i++]=$ssid + if inarray "$ssid" "${ESSIDS[@]}"; then + if inarray "$(ssid_to_profile "$ssid")" "${GENERATED[@]}"; then + ENTRIES[i]="+" # Automatically generated + else + ENTRIES[i]="*" # Handmade + fi + else + ENTRIES[i]="-" # Not present + fi + if [[ "$ssid" = "$CONNECTION" ]]; then + ENTRIES[i]="!" # Currently connected + fi + if [[ "$flags" =~ WPA2|WPA|WEP ]]; then + ENTRIES[i]+=":${BASH_REMATCH[0],,}" + else + ENTRIES[i]+=":none" + fi + ENTRIES[i]+=" :$signal" + (( ++i )) + done < "$1" +} + +# Finds a profile name for ssid $1. +ssid_to_profile() +{ + local i + for i in $(seq 0 $((${#ESSIDS[@]}-1))); do + if [[ "$1" = "${ESSIDS[i]}" ]]; then + printf "%s" "${PROFILES[i]}" + return 0 + fi + done + return 1 +} + +# Creates a profile for ssid $1. +create_profile() +{ + local box flags key msg security + PROFILE="$INTERFACE-${1//\//_}" + [[ -e "$PROFILE_DIR/$PROFILE" ]] && PROFILE+=".wifi-menu" + flags=$(grep -m 1 $'\t'"$1\$" "$NETWORKS" | cut -f 2) + if [[ "$flags" =~ WPA|WEP ]]; then + security=${BASH_REMATCH[0],,} + else + security=none + fi + if [[ "$flags" =~ PSK|WEP ]]; then + [[ "$OBSCURE" ]] && box="--insecure --passwordbox" || box="--inputbox" + msg="Enter $security security key for\n'$1'" + key=$(dialog $box "$msg" 10 40 --stdout) || return $? + if [[ "${#key}" -ge 8 && "${#key}" -le 63 ]]; then + if [[ "$OBSCURE" ]]; then + key=$(wpa_passphrase "$1" "$key" | grep -m 1 "^[[:space:]]*psk=") + key=${key#*psk=} + else + key=$(printf "%q" "$key") + fi + elif ! [[ "${#key}" -eq 64 && "$key" = +([[:xdigit:]]) ]]; then + return 4 + fi + fi + cat << EOF > "$PROFILE_DIR/$PROFILE" +CONNECTION='wireless' +DESCRIPTION='Automatically generated profile by wifi-menu' +INTERFACE='$INTERFACE' +SECURITY='$security' +ESSID=$(printf "%q" "$1") +IP='dhcp' +${key+KEY=$key} +EOF + printf "%s" "$PROFILE" + return 0 +} + +# Connects to ssid $1 using an available profile or an automatically created +# one if none exists. +connect_to_ssid() +{ + local msg + PROFILE=$(ssid_to_profile "$1") + if [[ $? -eq 0 ]]; then + clear + check_profile "$PROFILE" && profile_down "$PROFILE" + else + PROFILE=$(create_profile "$1") + RETURN=$? + (( RETURN == 0 )) || return $RETURN + SPAWNED_PROFILE=1 + clear + fi + if ! profile_up "$PROFILE"; then + if (( SPAWNED_PROFILE )); then + msg=" CONNECTING FAILED + +Do you want to keep the generated profile ('$PROFILE')?" + dialog --yesno "$msg" 10 40 --stdout || rm "$PROFILE_DIR/$PROFILE" + clear + fi + return 2 + fi + return 0 +} + +while [[ "$1" = -* ]]; do + case "$1" in + -h|--help) + usage + exit + ;; + -o|--obscure) + OBSCURE=1 + shift + ;; + -*) + report_err "Invalid option: $1" + usage + exit 255 + ;; + esac +done +if [[ $# -gt 1 ]]; then + report_err "Too many arguments" + usage + exit 255 +fi + +if [[ $(id -u) -ne 0 ]]; then + exit_stderr "This script needs to be run with root privileges" +fi +if ! type dialog &> /dev/null; then + exit_stderr "Please install 'dialog' to use wifi-menu" +fi + +INTERFACE=${1-$WIRELESS_INTERFACE} +if [[ -z "$INTERFACE" ]]; then + report_err "Missing interface specification" + usage + exit 255 +fi + +cd / # We do not want to spawn anything that can block unmounting +is_interface "$INTERFACE" || exit_fail "No such interface: $INTERFACE" +if ! interface_is_up "$INTERFACE"; then + [[ -f "$IFACE_DIR/$INTERFACE" ]] && . "$IFACE_DIR/$INTERFACE" + bring_interface up "$INTERFACE" || exit_fail "Interface unavailable" + SPAWNED_INTERFACE=1 +fi + +report_try "Scanning for networks" +CONNECTION=$(wpa_call "$INTERFACE" status 2> /dev/null | grep -m 1 "^ssid=") +CONNECTION=${CONNECTION#ssid=} +NETWORKS=$(wpa_supplicant_scan "$INTERFACE" 3,4,5) +if [[ $? -eq 0 ]]; then + trap 'rm -f "$NETWORKS"' EXIT + report_success + init_profiles "$INTERFACE" + init_entries "$NETWORKS" + MSG="Select the network you wish to use +Flags description: + * - handmade profile present + + - automatically generated profile present + - - no profile present + ! - active connection present" + CHOICE=$(dialog --column-separator : --menu "$MSG" 24 50 12 \ + "${ENTRIES[@]}" --stdout) + RETURN=$? + if (( RETURN == 0 )); then + connect_to_ssid "$CHOICE" + RETURN=$? + fi +else + report_fail + RETURN=3 +fi + +case $RETURN in + 0|2) # Connected | Connecting failed + ;; + 1) # Canceled + clear + ;; + 3) # No networks found + report_err "No networks found" + ;; + 4) # Invalid passphrase length (WEP keys have tighter restrictions) + clear + report_err "Passphrase must be 8..63 characters" + ;; + 255) # ESC or error + clear + report_err "Aborted" + ;; + *) # Should not happen + report_err "Unexpected return code from dialog: $RETURN" + RETURN=7 + ;; +esac +(( RETURN && SPAWNED_INTERFACE )) && bring_interface down "$INTERFACE" +exit $RETURN |