netctl wifi-menu

#! /bin/bash

. /usr/lib/netctl/globals
. "$SUBR_DIR/interface"
. "$SUBR_DIR/rfkill"
. "$SUBR_DIR/wpa"


usage() {
    cat << END
Usage: wifi-menu [-h | --help] [-o | --obscure] [INTERFACE]

Interactively connect to a wireless network on INTERFACE using netctl.
If only one wireless interface is available, INTERFACE can be omitted.

Arguments:
  -h, --help     Show this help
  -o, --obscure  Show asterisks for the characters of the password
                 and store the password as a hexadecimal string
END
}

# Undo printf escaping in $1
printf_decode() {
    printf -- "${1//%/%%}"
}

# Prepare $1 for use in a special quoting context
quote_safe() {
    if [[ "$1" = \"* ]]; then
        printf '""%s"' "$1"
    else
        printf "%s" "$1"
    fi
}

# Fill PROFILES and ESSIDS with the profile names and essids and fill GENERATED
# with the names of automatically generated profiles for interface $1
init_profiles() {
    local i=0 essid profile
    while IFS= read -r profile; do
        # Sandbox the sourcing of profiles
        essid=$(
            source "$PROFILE_DIR/$profile" > /dev/null
            if [[ "$Interface" = "$1" && -n "$ESSID" ]]; then
                if [[ "$ESSID" = \"\"*\" ]]; then
                    ESSID=${ESSID:2:-1}
                fi
                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)
}

# Build ENTRIES as an argument list for dialog based on scan results in $1
init_entries() {
    local i=0 sep=$'\t' decoded flags signal ssid
    while IFS=$'\t' read -r signal flags ssid; do
        decoded=$(printf_decode "$ssid")
        ENTRIES[i++]="--"  # the SSID might look like an option to dialog
        if [[ "$CHARMAP" = "UTF-8" ]]; then
            ENTRIES[i++]=$decoded
        else
            ENTRIES[i++]=$ssid
        fi
        if is_yes "${CONNECTED:-no}" && [[ "$decoded" = "$CONNECTION" ]]; then
            ENTRIES[i]="*"  # Currently connected
        elif in_array "$decoded" "${ESSIDS[@]}"; then
            if in_array "$(ssid_to_profile "$decoded")" "${GENERATED[@]}"; then
                ENTRIES[i]="."  # Automatically generated
            else
                ENTRIES[i]=":"  # Handmade
            fi
        else
            ENTRIES[i]=" "  # Not present
        fi
        if [[ "$flags" =~ WPA2|WPA|WEP ]]; then
            ENTRIES[i]+="${sep}${BASH_REMATCH[0],,}"
        else
            ENTRIES[i]+="${sep}none"
        fi
        ENTRIES[i]+="   ${sep}${signal}"
        (( ++i ))
    done < "$1"
}

# Find 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
}

# Ask the user for the name of the new profile
confirm_profile() {
    local msg="Enter a name for the new profile\n"
    PROFILE=$(dialog --inputbox "$msg" 10 50 "$PROFILE" --stdout) || return $?
    if [[ "$PROFILE" = */* ]]; then
        PROFILE=${PROFILE//\//_}
        confirm_profile
    elif [[ -e "$PROFILE_DIR/$PROFILE" ]]; then
        msg="A profile by the name '$PROFILE' already exists.
Do you want to overwrite it?"
        dialog --yesno "$msg" 10 50 --stdout || confirm_profile
    fi
}

# Create a profile for ssid $1
create_profile() {
    local box flags key msg security signal ssid
    PROFILE=$(iconv -c -f UTF-8 -t //TRANSLIT <<< "$1")
    PROFILE="$INTERFACE-${PROFILE//[?\/]/_}"
    [[ -e "$PROFILE_DIR/$PROFILE" ]] && PROFILE+=".wifi-menu"
    confirm_profile || return $?
    while IFS=$'\t' read -r signal flags ssid; do
        [[ "$(printf_decode "$ssid")" != "$1" ]] || break
    done < "$NETWORKS"
    if [[ "$flags" =~ WPA|WEP ]]; then
        security=${BASH_REMATCH[0],,}
    else
        security=none
    fi
    if [[ "$flags" =~ PSK|WEP ]]; then
        if is_yes "${OBSCURE:-no}"; then
            box="--insecure --passwordbox"
        else
            box="--inputbox"
        fi
        msg="Enter $security security key for\n'$1'"
        key=$(dialog $box "$msg" 10 40 --stdout) || return $?
        if [[ "${BASH_REMATCH[0]}" = "WEP" ]]; then
            if [[ "$key" = +([[:xdigit:]][[:xdigit:]]) ]]; then
                key="\"$key"
            else
                key=$(quote_safe "$key")
            fi
        elif [[ ${#key} -ge 8 && ${#key} -le 63 ]]; then
            if is_yes "${OBSCURE:-no}"; then
                key="\"$(wpa_passphrase "$1" "$key" | sed -n "s/^[[:space:]]*psk=//p")"
            else
                key=$(quote_safe "$key")
            fi
        elif [[ ${#key} -eq 64 && "$key" = +([[:xdigit:]]) ]]; then
            key="\"$key"
        else
            return 4
        fi
    fi
    cat << EOF > "$PROFILE_DIR/$PROFILE"
Description='Automatically generated profile by wifi-menu'
Interface=$INTERFACE
Connection=wireless
Security=$security
ESSID=$(printf "%q" "$(quote_safe "$1")")
IP=dhcp
${key+Key=$(printf "%q" "$key")}
EOF
    printf "%s" "$PROFILE"
    return 0
}

# Connect 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 [[ $? -ne 0 ]]; then
        PROFILE=$(create_profile "$1") || return $?
        NEW_PROFILE=yes
    fi
    clear
    if systemctl is-active --quiet "[email protected]$INTERFACE.service"; then
        report_notice "Interface '$INTERFACE' is controlled by netctl-auto"
        if is_yes "${NEW_PROFILE:-no}"; then
            do_debug systemctl restart "[email protected]$INTERFACE.service"
        fi
        do_debug netctl-auto switch-to "$PROFILE"
    elif ! netctl switch-to "$PROFILE"; then
        if is_yes "${NEW_PROFILE:-no}"; 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=yes
            shift
            ;;
        -*)
            report_error "Invalid option: $1"
            usage
            exit 255
            ;;
    esac
done
if [[ $# -gt 1 ]]; then
    report_error "Too many arguments"
    usage
    exit 255
fi

ensure_root "$(basename "$0")"
if ! type dialog &> /dev/null; then
    exit_error "Please install 'dialog' to use wifi-menu"
fi
CHARMAP=$(locale charmap)
cd /  # We do not want to spawn anything that can block unmounting

INTERFACE=$1
if [[ -z "$INTERFACE" ]]; then
    INTERFACE=(/sys/class/net/*/wireless/)
    if [[ ${#INTERFACE[@]} -ne 1 || ! -d "$INTERFACE" ]]; then
        report_error "Invalid interface specification"
        usage
        exit 255
    fi
    INTERFACE=${INTERFACE:15:-10}
    report_debug "Using interface '$INTERFACE'"
elif ! is_interface "$INTERFACE"; then
    exit_error "No such interface: $INTERFACE"
fi
load_interface_config "$INTERFACE"

if [[ "$RFKill" && "$(rf_status "$INTERFACE" "$RFKill")" ]]; then
    if ! rf_enable "$INTERFACE" "$RFKill"; then
        exit_error "Could not unblock transmission on interface '$INTERFACE'"
    fi
    RF_UNBLOCKED=yes
fi

echo -n "Scanning for networks... "
if CONNECTION=$(wpa_call "$INTERFACE" status 2> /dev/null | grep -m 1 "^ssid="); then
    CONNECTION=$(printf_decode "${CONNECTION#ssid=}")
    CONNECTED=yes
fi
NETWORKS=$(wpa_supplicant_scan "$INTERFACE" 3,4,5)
RETURN=$?

if is_yes "${RF_UNBLOCKED:-no}"; then
    rf_disable "$INTERFACE" "$RFKill"
fi

if (( RETURN == 0 )); then
    trap 'rm -f "$NETWORKS"' EXIT
    echo "done"
    init_profiles "$INTERFACE"
    init_entries "$NETWORKS"
    MSG="Select the network you wish to use
Flags description:
 * - active connection present
 : - handmade profile present
 . - automatically generated profile present"
    CHOICE=$(dialog --menu "$MSG" 24 50 12 "${ENTRIES[@]}" --stdout)
    RETURN=$?
    if (( RETURN == 0 )); then
        if [[ "$CHARMAP" != "UTF-8" ]]; then
            CHOICE=$(printf_decode "$CHOICE")
        fi
        connect_to_ssid "$CHOICE"
        RETURN=$?
    fi
else
    echo "failed"
    RETURN=3
fi

case $RETURN in
    0|2)  # Connected | Connecting failed
        ;;
    1)  # Canceled
        clear
        ;;
    3)  # No networks found
        report_error "No networks found"
        ;;
    4)  # Invalid passphrase length (WEP keys have tighter restrictions)
        clear
        report_error "Passphrase must be 8..63 characters"
        ;;
    255)  # ESC or error
        clear
        report_error "Aborted"
        ;;
    *)  # Should not happen
        report_error "Unexpected return code from dialog: $RETURN"
        RETURN=7
        ;;
esac
exit $RETURN


# vim: ft=sh ts=4 et sw=4:

Popular posts from this blog

Hidden Wiki

[SOLVED] IDM WAS REGISTERED WITH A FAKE SERIAL NUMBER