%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /usr/share/bsdconfig/media/
Upload File :
Create Path :
Current File : //usr/share/bsdconfig/media/wlan.subr

if [ ! "$_MEDIA_WLAN_SUBR" ]; then _MEDIA_WLAN_SUBR=1
#
# Copyright (c) 2013-2016 Devin Teske
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
############################################################ INCLUDES

BSDCFG_SHARE="/usr/share/bsdconfig"
. $BSDCFG_SHARE/common.subr || exit 1
f_dprintf "%s: loading includes..." media/wlan.subr
f_include $BSDCFG_SHARE/device.subr
f_include $BSDCFG_SHARE/dialog.subr
f_include $BSDCFG_SHARE/strings.subr
f_include $BSDCFG_SHARE/sysrc.subr

BSDCFG_LIBE="/usr/libexec/bsdconfig"
f_include_lang $BSDCFG_LIBE/include/messages.subr

############################################################ GLOBALS

NWIRELESS_CONFIGS=0
NWSCAN_RESULTS=0

#
# Settings used while interacting with various dialog(1) menus
#
: ${DIALOG_MENU_WLAN_SCAN_DURATION:=5}
: ${DIALOG_MENU_WLAN_SHOW_ALL=}
: ${DIALOG_MENU_WLAN_SHOW_CONFIGURED=1}
: ${DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=1}

# 
# Structure to contain the wpa_supplicant.conf(5) default overrides
#
f_struct_define WPA_DEFAULTS \
	ap_scan			\
	ctrl_interface		\
	ctrl_interface_group	\
	eapol_version		\
	fast_reauth

#
# Structure of wpa_supplicant.conf(5) network={ ... } entry
#
f_struct_define WPA_NETWORK \
	anonymous_identity	\
	auth_alg		\
	bssid			\
	ca_cert			\
	ca_cert2		\
	client_cert		\
	client_cert2		\
	dh_file			\
	dh_file2		\
	eap			\
	eap_workaround		\
	eapol_flags		\
	eappsk			\
	engine			\
	engine_id		\
	frequency		\
	group			\
	identity		\
	key_id			\
	key_mgmt		\
	mixed_cell		\
	mode			\
	nai			\
	pac_file		\
	pairwise		\
	password		\
	pcsc			\
	phase1			\
	phase2			\
	pin			\
	priority		\
	private_key		\
	private_key2		\
	private_key2_passwd	\
	private_key_passwd	\
	proto			\
	psk			\
	scan_ssid		\
	server_nai		\
	ssid			\
	subject_match		\
	subject_match2		\
	wep_key0		\
	wep_key1		\
	wep_key2		\
	wep_key3		\
	wpa_ptk_rekey		\
	wep_tx_keyidx

#
# The following properties are ``Lists'' and as such should not be quoted.
# Everything else should be quoted.
#
WPA_NETWORK_LIST_PROPERTIES="
	auth_algo
	eap
	group
	key_mgmt
	pairwise
	proto
" # END-QUOTE

#
# Structure of wpa_cli(8) `scan_results' entry
#
f_struct_define WPA_SCAN_RESULT \
	bssid	\
	flags	\
	freq	\
	siglev	\
	ssid

#
# Structure of a menu item in the wireless editor
#
f_struct_define WLAN_MENU_ITEM \
	letter		\
	ssid		\
	nconfigs	\
	nfound		\
	help

############################################################ FUNCTIONS

# f_wpa_supplicant_init $file
#
# Initialize $file with basic contents of new wpa_supplicant.conf(5).
#
f_wpa_supplicant_init()
{
	local funcname=f_wpa_supplicant_init
	local conf_file="$1" tmpfile

	# Create a temporary file
	f_eval_catch -k tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm" ||
		return $FAILURE

	# Make it unreadable by anyone but ourselves
	f_eval_catch $funcname chmod \
		'chmod 0600 "%s"' "$tmpfile" || return $FAILURE

	# Make it owned by root/wheel
	f_eval_catch $funcname chown \
		'chown 0:0 "%s"' "$tmpfile" || return $FAILURE

	# Populate it
	cat <<-EOF >> "$tmpfile"
	ctrl_interface=/var/run/wpa_supplicant
	eapol_version=2
	ap_scan=1
	fast_reauth=1
	EOF
	echo >> "$tmpfile"

	# Move it into place
	f_eval_catch $funcname mv 'mv "%s" "%s"' "$tmpfile" "$conf_file"
}

# f_wpa_supplicant_parse $file [struct_prefix [count_var]]
#
# Parse wpa_supplicant.conf(5) $file. Default overrides are stored in a struct
# (see struct.subr for additional details) named `{struct_prefix}defaults'. See
# WPA_DEFAULTS struct definition in the GLOBALS section above.
#
# In addition, for each one of the wireless networks we parse from $file,
# create a struct named `struct_prefixN' where `N' is a number starting from 1
# and ending in $count_var (zero means no networks). See WPA_NETWORK struct
# definition in the GLOBALS section above.
#
# If a `blob-base64-*={ ... }' entry appears, a struct named
# `{struct_prefix}blob_base64_*' is created and the `data' property holds the
# base64 encoded binary data without whitespace.
#
# Custom `*={ ... }' definitions are also supported, but should be unique
# (unlike the `network' definition). A struct named `{struct_prefix}*' is
# created if at least one property is defined in the block.
#
f_wpa_supplicant_parse()
{
	local file="$1" struct_prefix="$2" count_var="$3"

	[ "$count_var" ] && setvar "$count_var" 0

	[ "$file" ] || file=$( f_sysrc_get wpa_supplicant_conf_file )
	if [ ! -e "$file" ]; then
		f_dprintf "%s: No such file or directory" "$file"
		return $FAILURE
	fi

	local list_properties
	f_replaceall "$WPA_NETWORK_LIST_PROPERTIES" "$NL" "" list_properties
	eval "$( awk \
		-v count_var="$count_var"         \
		-v struct_prefix="$struct_prefix" \
		-v list_properties="$list_properties" '
	BEGIN {
		if (!count_var && !struct_prefix) exit
		blob = count = custom_struct = network = 0
		split(list_properties, lists, FS)
	}
	function set_value(struct, prop, value)
	{
		quoted = substr(value, 0, 1) == "\""
		for (l in lists) if (list = prop == lists[l]) break
		# Remove data after whitespace if unquoted and not a list
		if (!quoted && !list) sub("[[:space:]].*", "", value)
		# Otherwise if quoted and not a list, remove the quotes
		# NB: wep_keyN needs to retain quoting if/when present
		else if (quoted && !list && prop !~ /^wep_key[[:digit:]]+/) {
			sub("^\"", "", value)
			sub("\".*", "", value)
		}
		gsub(/'\''/, "'\''\\'\'\''", value) # Sanitize the value
		if (!created[struct]) {
			print "debug= f_struct_free", struct
			print "debug= f_struct_new WPA_NETWORK", struct
			created[struct] = 1
		}
		printf "debug= %s set %s '\'%s\''\n", struct, prop, value
	}
	{
		if ($1 ~ /^network={/) {
			empty = 1 # We do not increment count unless !empty
			network = 1
			next
		} else if (match($1, "^blob-base64-[[:alnum:]_./-]+={")) {
			blob = 1
			blob_data = ""
			struct = struct_prefix "blob_bas64_"
			struct = struct substr($1, 13, RLENGTH - 14)
			next
		} else if (match($1, "^[[:alnum:]_./-]+={")) {
			empty = 1
			custom_struct = 1
			struct = struct_prefix substr($1, 0, RLENGTH - 2)
			gsub(/[^[:alnum:]_]/, "_", struct)
			next
		} else if ($1 ~ /^}/) {
			if (blob) {
				gsub("[[:space:]]", "", blob_data)
				set_value(struct, "data", blob_data)
			}
			blob = custom_struct = network = 0
			next
		} else if (!match($0, /^[[:space:]]*[[:alnum:]_]+=/))
			next

		if (blob) {
			blob_data = blob_data $0
			next
		} else if (network) {
			if (empty) { count++; empty = 0 }
			struct = struct_prefix count
		} else if (!custom_struct)
			struct = struct_prefix "defaults"

		if (!struct_prefix) next

		prop = substr($0, 0, RLENGTH - 1)
		sub(/^[[:space:]]*/, "", prop)
		value = substr($0, RSTART + RLENGTH)

		set_value(struct, prop, value)
	}
	END { if (count_var) print count_var "=" count }' "$file" )"
}

# f_wpa_scan_results_parse [struct_prefix [count_var]]
#
# Parse the results of wpa_cli(8) `scan_results' into a series of structs (see
# struct.subr for additional details) named `struct_prefixN' where `N' is a
# number starting from 1 and ending in $count_var (zero means no results). See
# WPA_SCAN_RESULT struct definition in the GLOBALS section above.
#
f_wpa_scan_results_parse()
{
	local struct_prefix="$1" count_var="$2"

	[ "$count_var" ] && setvar "$count_var" 0

	eval "$( wpa_cli scan_results 2> /dev/null | awk \
		-v count_var="$count_var" \
		-v struct_prefix="$struct_prefix" '
	BEGIN {
		if (!count_var && !struct_prefix) exit
		count = 0
		seg = "[[:xdigit:]][[:xdigit:]]"
		bssid = seg":"seg":"seg":"seg":"seg":"seg
		freq = siglev = flags = "[^[:space:]]+"
		S = "[[:space:]]+"
		line = bssid S freq S siglev S flags
		line = "^[[:space:]]*" line "[[:space:]]*"
	}
	function set_value(struct, prop, value)
	{
		gsub(/'\''/, "'\''\\'\'\''", value) # Sanitize the value
		if (!created[struct]) {
			print "debug= f_struct_free", struct
			print "debug= f_struct_new WPA_SCAN_RESULT", struct
			created[struct] = 1
		}
		printf "debug= %s set %s '\'%s\''\n", struct, prop, value
	}
	{
		if (!match($0, line)) next
		ssid = substr($0, RLENGTH + 1)

		count++
		if (!struct_prefix) next

		struct = struct_prefix count
		set_value(struct, "ssid", ssid)
		set_value(struct, "bssid",  $1)
		set_value(struct, "freq",   $2)
		set_value(struct, "siglev", $3)
		set_value(struct, "flags",  $4)
	}
	END { if (count_var) print count_var "=" count }' )"
}

# f_wpa_scan_match_network WPA_SCAN_RESULT WPA_NETWORK
#
# Compares a WPA_SCAN_RESULT struct to a WPA_NETWORK struct. If they appear to
# be a match returns success, otherwise failure.
#
f_wpa_scan_match_network()
{
	local scan_struct="$1" wireless_struct="$2"
	local cp debug=

	f_struct "$scan_struct" || return $FAILURE
	f_struct "$wireless_struct" || return $FAILURE

	local scan_ssid scan_bssid
	$scan_struct get ssid scan_ssid
	$scan_struct get bssid scan_bssid
	local wireless_ssid wireless_bssid
	$wireless_struct get ssid wireless_ssid
	$wireless_struct get bssid wireless_bssid

	local id_matched=
	if [ "$wireless_ssid" -a "$wireless_bssid" ]; then
		# Must match both SSID and BSSID
		[ "$scan_ssid" = "$wireless_ssid" -a \
		  "$scan_bssid" = "$wireless_bssid" ] && id_matched=1
	elif [ "$wireless_ssid" ]; then
		# Must match SSID only
		[ "$scan_ssid" = "$wireless_ssid" ] && id_matched=1
	elif [ "$wireless_bssid" ]; then
		# Must match BSSID only
		[ "$scan_bssid" = "$wireless_bssid" ] && id_matched=1
	fi
	[ "$id_matched" ] || return $FAILURE


	#
	# Get the scanned flags for the next few comparisons
	#
	local flags
	$scan_struct get flags flags

	#
	# Compare configured key management against scanned network
	#
	if $wireless_struct get key_mgmt cp && [ "$cp" -a "$cp" != "NONE" ]
	then
		local mgmt mgmt_matched=
		for mgmt in $cp; do
			local mgmt2="$mgmt"
			[ "$mgmt" != "${mgmt#WPA-}" ] &&
				mgmt2="WPA2${mgmt#WPA}"
			case "$flags" in
			"$mgmt"|"$mgmt"-*|*-"$mgmt"|*-"$mgmt"-*)
				mgmt_matched=1 break ;;
			"$mgmt2"|"$mgmt2"-*|*-"$mgmt2"|*-"$mgmt2"-*)
				mgmt_matched=1 break ;;
			esac
		done
		[ "$mgmt_matched" ] || return $FAILURE
	fi

	local enc type flag

	#
	# Compare configured encryption against scanned network
	#
	for enc in psk:PSK eap:EAP \
		wep_key0:WEP wep_key1:WEP wep_key2:WEP wep_key3:WEP
	do
		type=${enc%%:*}
		flag=${enc#*:}
		{ debug= $wireless_struct get $type cp && [ "$cp" ]; } ||
			continue
		# Configured network requires encryption
		case "$flags" in "[$flag]"|*"-$flag-"*)
			break # Success; stop after first match
		esac
		return $FAILURE
	done
	cp="" # sensitive info

	#
	# Compare scanned network encryption against configuration
	# NB: Scanned network flags indicates _one_ of PSK EAP or WEP
	# NB: Otherwise, no encryption (so encryption won't match)
	#
	local enc_wanted=
	for enc in -PSK-:psk -EAP-:eap; do
		flag=${enc%%:*}
		type=${enc#*:}
		case "$flags" in *"$flag"*)
			enc_wanted=1
			{ debug= $wireless_struct get $type cp &&
				[ "$cp" ]; } || return $FAILURE
			break # success
		esac
	done
	case "$flags" in *"[WEP]"*)
		enc_wanted=1
		local wep_found=
		for type in wep_key0 wep_key1 wep_key2 wep_key3; do
			debug= $wireless_struct get $type cp && [ "$cp" ] &&
				wep_found=1 break
		done
		[ "$wep_found" ] || return $FAILURE
	esac
	if [ ! "$enc_wanted" ]; then
		# No match if the network specifies encryption
		for type in psk eap wep_key0 wep_key1 wep_key2 wep_key3; do
			debug= $wireless_struct get $type cp && [ "$cp" ] &&
				return $FAILURE
		done
	fi
	cp="" # sensitive info

	return $SUCCESS
}

# f_wpa_scan_find_matches scans_prefix $scans_count \
#                         wireless_prefix $wireless_count
#
# For each struct from `{scans_prefix}1' up to `{scans_prefix}$scans_count'
# (see struct.subr for additional details) compare the wireless network info
# (defined as struct WPA_SCAN_RESULT) to that of each configured wireless 
# stored in `{wireless_prefix}1' (defined as struct WPA_NETWORK) up to
# `{wireless_prefix}$wireless_count'.
#
# If a scanned network is deemed to be a match to a configured wireless
# network, a new `match' property is set on the WPA_NETWORK struct with a value
# of `{scans_prefix}N' (where N represents the scanned network that matched).
# At the same time, a new `matched' property is set on the WPA_SCAN_RESULT
# struct with a value of 1, indicating that this network has been matched to a
# stored [known] configuration and that it should not be displayed in menus.
#
# NB: If a matching entry is not correct, the user can optionally `Forget' the
# network and that will cause the WPA_SCAN_RESULT to no longer match anything,
# causing it to appear in the menus again.
#
# Return status should be ignored.
#
f_wpa_scan_find_matches()
{
	local scans_prefix="$1" scans_count="$2"
	local wireless_prefix="$3" wireless_count="$4"
	local matches

	[ "$scans_count" -a "$wireless_count" ] || return $SUCCESS
	f_isinteger "$scans_count" || return $FAILURE
	f_isinteger "$wireless_count" || return $FAILURE

	#
	# Go through and eradicate any flags we set in a prior run, as things
	# might have changed on us (either from the config side or scan side)
	#
	local w=1
	while [ $w -le $wireless_count ]; do
		f_struct "$wireless_prefix$w" set matches ""
		w=$(( $w + 1 ))
	done

	#
	# Find matches and set match data on structs
	#
	local s=1
	while [ $s -le $scans_count ]; do
		f_struct "$scans_prefix$s" set matched ""
		w=1
		while [ $w -le $wireless_count ]; do
			if f_wpa_scan_match_network \
				"$scans_prefix$s" "$wireless_prefix$w"
			then
				f_struct "$scans_prefix$s" set matched 1
				debug= f_struct "$wireless_prefix$w" \
				         get matches matches
				matches="$matches${matches:+ }$scans_prefix$s"
				f_struct "$wireless_prefix$w" \
				         set matches "$matches"
				break # to next scan result
			fi
			w=$(( $w + 1 ))
		done
		s=$(( $s + 1 ))
	done
}

# f_dialog_menu_wlandev_edit $wlandev [$defaultitem]
#
# Display a list of wireless network devices (wlan*) associated with
# $wlandev (e.g., `iwn0'). Allow the user to create and destroy wlan interfaces
# while selecting ones to be cloned at startup (by setting `wlans_$wlandev').
#
f_dialog_menu_wlandev_edit()
{
	local funcname=f_dialog_menu_wlandev_edit
	local wlandev="$1" defaultitem="$2"
	local title="$DIALOG_TITLE"
	local btitle="$DIALOG_BACKTITLE"
	local prompt # Calculated below
	local hline="$hline_arrows_tab_enter"

	[ "$wlandev" ] || return $FAILURE

	f_sprintf prompt "$msg_select_wlan_interfaces_for" "wlandev"

	#
	# Initially mark wlan devices with a %parent of $wlandev
	#
	local dev devs if list_to_save=
	f_device_find "" $DEVICE_TYPE_NETWORK devs
	for dev in $devs; do
		f_struct "$dev" get name if || continue
		case "$if" in wlan[0-9]*)
			parent=$( sysctl -n net.wlan.${if#wlan}.%parent \
				2> /dev/null )
			if [ "$parent" = "$if" ]; then
				local _wlanmark_$if="X"
				list_to_save="$list_to_save $if"
			fi
		esac
	done
	list_to_save="${list_to_save# }"

	#
	# Operate in a loop so we can create/destroy interfaces from here
	#
	while :; do
		#
		# Refresh list of wlan interfaces
		#
		local wlanlist=
		f_device_rescan_network
		f_device_find "" $DEVICE_TYPE_NETWORK devs
		for dev in $devs; do
			f_struct "$dev" get name if || continue
			case "$if" in wlan[0-9]*)
				wlanlist="$wlanlist $if"
			esac
		done

		#
		# Build menu list of wlan devices
		#
		local menu_list="
			'> $msg_save_exit'  '$msg_return_to_previous_menu'
			'> $msg_create_new' 'wlan'
			'> $msg_destroy'    '...'
		" # END-QUOTE
		local parent X
		for if in $wlanlist; do
			f_getvar _wlanmark_$if-" " X
			menu_list="$menu_list '[$X] $if' '%parent: $parent'"
			[ "$defaultitem" = "$if" ] && defaultitem="[$X] $if"
		done 

		#
		# Ask user to make a choice
		#
		local height width rows
		eval f_dialog_menu_size height width rows \
			\"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" \
			$menu_list
		local menu_choice
		menu_choice=$( eval $DIALOG \
			--title \"\$title\"              \
			--backtitle \"\$btitle\"         \
			--hline \"\$hline\"              \
			--ok-label \"\$msg_select\"      \
			--cancel-label \"\$msg_cancel\"  \
			--default-item \"\$defaultitem\" \
			--menu \"\$prompt\"              \
			$height $width $rows             \
			$menu_list                       \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		) || return $FAILURE
		f_dialog_data_sanitize menu_choice

		case "$menu_choice" in
		"> $msg_save_exit") # Save list to rc.conf(5) `wlans_$wlandev'
			f_eval_catch $funcname f_sysrc_set \
				'f_sysrc_set "wlans_%s" "%s"' \
				"$wlandev" "$list_to_save" || continue
			break # to success
			;;
		"> $msg_create_new") # Create new wlan interface for wlandev
			local wlan
			f_eval_catch -k wlan $funcname ifconfig \
				'ifconfig wlan create wlandev "%s"' \
				"$wlandev" || continue
			local _wlanmark_$wlan="X"
			list_to_save="$list_to_save${list_to_save:+ }$wlan"
			;;
		"> $msg_destroy") # Display a menu to pick one item to destroy
			[ "$wlanlist" ] || continue # Nothing to destroy

			menu_list=
			for if in $wlanlist; do
				menu_list="$menu_list '$if' ''"
			done
			local msg="$msg_pick_an_interface_to_destroy"
			eval f_dialog_menu_size height width rows \
			    \"\$title\" \"$btitle\" \"\$msg\" \"\" $menu_list
			menu_choice=$( eval $DIALOG \
				--title \"\$title\"             \
				--backtitle \"\$btitle\"        \
				--ok-label \"\$msg_destroy\"    \
				--cancel-label \"\$msg_cancel\" \
				--menu \"\$msg\"                \
				$height $width $rows            \
				$menu_list                      \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			) || continue
			f_dialog_data_sanitize menu_choice
			f_eval_catch $funcname ifconfig \
				'ifconfig "%s" destroy' "$menu_choice"
			;;
		"[ ] wlan"[0-9]*) # Unmarked; Mark
			if="${menu_choice#??? }"
			local _wlanmark_$if="X"
			list_to_save="$list_to_save${list_to_save:+ }$if"
			;;
		"[X] wlan"[0-9]*) # Marked; Unmark
			menu_choice="${menu_choice#??? }"
			local _wlanmark_$menu_choice=" "
			local new_list_to_save=
			for if in $list_to_save; do
				[ "$if" = "$menu_choice" ] && continue
				new_list_to_save="$new_list_to_save $if"
			done
			list_to_save="${new_list_to_save# }"
			;;
		esac
	done

	return $SUCCESS
}

# f_dialog_scan_wireless
#
# Initiate a scan for wireless networks. If wpa_supplicant(8) is not running
# but a wlan interface has been created, start an instance of wpa_supplicant(8)
# with the first wlan(4) interface we find. After initiating the scan, displays
# a message for 5 seconds (with option to dismiss). Returns failure if an error
# occurs, otherwise success.
#
f_dialog_scan_wireless()
{
	local funcname=f_dialog_scan_wireless

	#
	# Try to communicate with a running wpa_supplicant(8)
	#
	if ! f_eval_catch -d $funcname wpa_cli 'wpa_cli ping'; then

		# If there is indeed one running, bail!
		if ps axo ucomm= | grep -qw wpa_supplicant; then
			f_show_msg "$msg_failed_to_reach_wpa_supplicant" \
			           "$msg_wpa_cli_ping_failed"
			return $FAILURE
		fi

		# Try and find a wlan device so we can start wpa_supplicant
		local dev devs if wlan=
		f_device_rescan_network
		f_device_find "" $DEVICE_TYPE_NETWORK devs
		for dev in $devs; do
			f_struct "$dev" get name if || continue
			case "$if" in wlan[0-9]*)
				wlan=$if
				break
			esac
		done
		if [ ! "$wlan" ]; then
			# We can't start wpa_supplicant without wlan interface
			# Tell the user they have to create one by navigating
			# to a Wireless device to create a wlan interface. But
			# let's go one step further and find an interface that
			# we can provide in the prompt text.
			local wlandev=
			for if in $devs; do
				case "$if" in wlan[0-9]*) next; esac
				if f_device_is_wireless $if; then
					wlandev=$if
					break
				fi
			done
			if [ "$wlandev" ]; then
				f_show_msg "$msg_cant_start_wpa_supplicant" \
				           "$wlandev"
			else
				# Warn user, appears no wireless available
				f_show_msg "$msg_warning_no_wireless_devices"
			fi
			return $FAILURE
		fi

		# NB: Before we can proceed to fire up wpa_supplicant(8), let's
		# make sure there is a bare-bones wpa_supplicant.conf(5) for it
		local conf_file
		conf_file=$( f_sysrc_get wpa_supplicant_conf_file )
		if [ ! -e "$conf_file" ]; then
			f_wpa_supplicant_init "$conf_file" || return $FAILURE
			f_eval_catch -d $funcname wpa_cli 'wpa_cli reconfigure'
		fi

		# Try and start wpa_supplicant(8)
		f_eval_catch $funcname wpa_supplicant \
		        '/etc/rc.d/wpa_supplicant start "%s"' "$wlan" ||
			return $FAILURE

		# Try to reach this new wpa_supplicant(8)
		if ! f_eval_catch -d $funcname wpa_cli 'wpa_cli ping'; then
			f_show_msg "$msg_failed_to_reach_wpa_supplicant" \
			           "$msg_wpa_cli_ping_failed"
			return $FAILURE
		fi

	fi # ! f_quietly wpa_cli ping

	# If we reach hear, then it should be OK to scan the airwaves
	f_eval_catch -d $funcname wpa_cli 'wpa_cli scan' || return $FAILURE

	# Return immediately if a duration is: null or not a number >= 1
	local duration="$DIALOG_MENU_WLAN_SCAN_DURATION"
	f_isinteger "$duration" || return $SUCCESS
	[ $duration -gt 0 ] || return $SUCCESS

	# Display a message that times-out if not dismissed manually
	local prompt
	f_sprintf prompt "$msg_scanning_wireless_pausing" "$duration"
	f_dialog_pause "$prompt" "$duration"
}

# f_dialog_wireless_edit $ssid
#
# Display a menu to allow the user to either create a new entry for the
# wpa_supplicants.conf(5) file, or to edit values for an existing entry.
#
# If more than one wireless network is found to match $ssid, a sub-menu is
# presented, allowing the user to select the desired network.
#
f_dialog_wireless_edit()
{
	local title="$DIALOG_TITLE"
	local btitle="$DIALOG_BACKTITLE"
	local prompt1="$msg_select_the_configuration_you_would_like"
	local prompt2 # Calculated below
	local hline="$hline_alnum_arrows_punc_tab_enter"
	local ssid="$1" bssid="$2"

	f_sprintf prompt2 "$msg_wireless_network_configuration_for" "$ssid"

	#
	# Find one or more configurations that match the SSID selection
	#
	local height1 width1 rows1 menu_list1=
	local n=0 nmatches=0 tag wssid wbssid help matches=
	while [ $n -lt $NWIRELESS_CONFIGS ]; do
		n=$(( $n + 1 ))

		debug= f_struct WIRELESS_$n get ssid wssid
		[ "$ssid" = "$wssid" ] || continue
		debug= f_struct WIRELESS_$n get bssid wbssid
		[ "${bssid:-$wbssid}" = "$wbssid" ] || continue

		nmatches=$(( $nmatches + 1 ))
		[ $nmatches -le ${#DIALOG_MENU_TAGS} ] || break
		f_substr -v tag "$DIALOG_MENU_TAGS" $nmatches 1

		f_wireless_describe WIRELESS_$n help
		menu_list1="$menu_list1
			'$tag $wssid' '$wbssid' '$help'
		" # END-QUOTE

		matches="$matches WIRELESS_$n"
	done
	if [ $nmatches -eq 0 ]; then
		f_show_msg "$msg_cannot_edit_wireless_ssid" "$ssid"
		return $FAILURE
	elif [ $nmatches -eq 1 ]; then
		struct=${matches# }
	else
		eval f_dialog_menu_with_help_size height1 width1 rows1 \
			\"\$title\" \"\$btitle\" \"\$prompt1\" \"\$hline\" \
			$menu_list1
	fi

	#
	# Operate in a loop; for the case of $nmatches > 1, we can cycle back
	# to allow the user to make another choice after inspecting each one.
	#
	local menu_choice index struct defaultitem1=
	while :; do
		if [ $nmatches -gt 1 ]; then
			menu_choice=$( eval $DIALOG \
				--title \"\$title\"               \
				--backtitle \"\$btitle\"          \
				--hline \"\$hline\"               \
				--ok-label \"\$msg_select\"       \
				--cancel-label \"\$msg_cancel\"   \
				--item-help                       \
				--default-item \"\$defaultitem1\" \
				--menu \"\$prompt1\"              \
				$height1 $width1 $rows1           \
				$menu_list1                       \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			) || return $FAILURE
			f_dialog_data_sanitize menu_choice
			defaultitem1="$menu_choice"
			index=$( eval f_dialog_menutag2index_with_help \
				\"\$menu_choice\" $menu_list1 )
			struct=$( set -- $matches; eval echo \${$index} )
		fi

		#
		# Operate within another loop to allow editing multiple values
		#
		local menu_list2 height2 width2 rows2 member
		while :; do
			menu_list2="
				'> $msg_save_exit'
					'$msg_return_to_previous_menu'
			" # END-QUOTE
			n=0
			for member in $_struct_typedef_WPA_NETWORK; do
				[ "$member" = "ssid" ] && continue
				debug= $struct get $member value || continue
				n=$(( $n + 1 ))
				[ $n -le ${#DIALOG_MENU_TAGS} ] || break
				f_substr -v tag "$DIALOG_MENU_TAGS" $n 1
				if [ ${#value} -gt 32 ]; then
					f_snprintf value 29 "%s" "$value"
					value="$value..."
				fi
				case "$member" in
				password|pin|private_key_passwd|psk|wep_key*)
					f_replaceall "$value" "?" "*" value ;;
				esac
				f_shell_escape "$value" value
				menu_list2="$menu_list2
					'$tag $member' '$value'
				" # END-QUOTE
			done
			eval f_dialog_menu_size height2 width2 rows2 \
				\"\$title\" \"\$btitle\" \"\$prompt2\" \
				\"\$hline\" $menu_list2
			menu_choice=$( eval $DIALOG \
				--title \"\$title\"               \
				--backtitle \"\$btitle\"          \
				--hline \"\$hline\"               \
				--ok-label \"\$msg_select\"       \
				--cancel-label \"\$msg_cancel\"   \
				--default-item \"\$defaultitem2\" \
				--menu \"\$prompt2\"              \
				$height2 $width2 $rows2           \
				$menu_list2                       \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			) || break
			f_dialog_data_sanitize menu_choice
			defaultitem2="$menu_choice"

			# XXXDT Unfinished
		done
		[ $nmatches -eq 1 ] && break
	done

	#
	# XXXDT Unfinished
	# This is where we display a menu that edits the entry
	# And then we modify the wpa_supplicants.conf(5) config file
	# XXXDT Unfinished
	#

	return $FAILURE # XXXDT Simulating DIALOG_CANCEL to mean ``no changes''
}

# f_wireless_describe WPA_NETWORK [$var_to_set]
#
# Provide a description of the WPA_NETWORK struct. If $var_to_set is missing or
# NULL, the description is provided on standard output (which is less preferred
# due to performance; e.g., if called in a loop).
#
f_wireless_describe()
{
	local __struct="$1" __var_to_set="$2" debug=

	[ "$__var_to_set" ] && setvar "$__var_to_set" ""
	f_struct "$__struct" || return $FAILURE

	#
	# Basic description is `proto key_mgmt group eap'
	#
	local __member __cp __desc=
	for __member in proto key_mgmt group eap; do
		$__struct get $__member __cp && [ "$__cp" ] &&
			__desc="$__desc${__desc:+ }$__cp"
	done

	local __check __kk

	#
	# Make sure we add WEP40/WEP140 even if omitted from the key_mgmt
	# section of entry
	#
	local __wep_keyN __f_wireless_describe_first_char __length
	for __wep_keyN in wep_key0 wep_key1 wep_key2 wep_key3; do
		$__struct get $__wep_keyN __kk
		[ "$__kk" ] || continue

		# What type is it? ASCII or HEX?
		__check=WEP
		f_substr -v __f_wireless_describe_first_char "$__kk" 1 1
		case "$__f_wireless_describe_first_char" in
		\") # ASCII
			__length=$(( ${#__kk} - 2 ))
			if [ $__length -le 5 ]; then
				__check=WEP40
			elif [ $__length -le 13 ]; then
				__check=WEP104
			fi ;;
		*) # HEX
			__length=${#__kk}
			if [ $__length -eq 10 ]; then
				__check=WEP40
			elif [ $__length -le 26 ]; then
				__check=WEP104
			fi
		esac
		__kk="" # sensitive info

		case "$__desc" in
		*"$__check"*) : already there ;;
		*) __desc="$__desc${__desc:+ }$__check"
		esac
	done

	#
	# Make sure we display PSK even if omitted
	# from the key_mgmt section of the entry
	#
	$__struct get psk __kk
	if [ "$__kk" ]; then
		__kk="" # sensitive info
		__check=PSK
		case "$__desc" in
		*"$__check"*) : already there ;;
		*) __desc="$__desc${__desc:+ }$__check"
		esac
	fi

	#
	# Produce results
	#
	if [ "$__var_to_set" ]; then
		setvar "$__var_to_set" "${__desc:-NONE}"
	else
		echo "$__desc"
	fi
}

# f_menu_wireless_configs
#
# Generates the tag/item/help triplets for wireless network menu (`--item-help'
# required) from wpa_supplicant.conf(5) [WPA_NETWORK] structs.
#
f_menu_wireless_configs()
{
	[ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ] || return $SUCCESS

	echo "' - $msg_configured_ssids -' ' - $msg_details -' ''"

	local n=0 nunique=0 debug=
	local ssid ussid matches nmatches nconfigs nfound help desc w
	while [ $n -lt $NWIRELESS_CONFIGS ]; do
		n=$(( $n + 1 ))

		f_struct WIRELESS_$n get ssid ssid
		[ ! "$DIALOG_MENU_WLAN_SHOW_ALL" -a ! "$ssid" ] && continue

		local u=0 unique=1
		while [ $u -lt $nunique ]; do
			u=$(( $u + 1 ))
			menuitem_$u get ssid ussid
			[ "$ssid" != "$ussid" ] || unique= break
		done
		if [ "$unique" ]; then
			nunique=$(( $nunique + 1 ))
			u=$nunique

			# Set SSID and initialize number of configs found (1)
			f_struct_new WLAN_MENU_ITEM menuitem_$u
			menuitem_$u set ssid "$ssid"
			menuitem_$u set nconfigs 1

			# Set number of wireless networks that match config
			WIRELESS_$n get matches matches
			f_count nmatches $matches
			menuitem_$u set nfound $nmatches

			# Set help to description of the wireless config
			f_wireless_describe WIRELESS_$n desc
			menuitem_$u set help "$desc"
		else
			# Increment number of configs found with this SSID
			menuitem_$u get nconfigs nconfigs
			nconfigs=$(( $nconfigs + 1 ))
			menuitem_$u set nconfigs $nconfigs

			# Add number of matched networks to existing count
			WIRELESS_$n get matches matches
			f_count nmatches $matches
			menuitem_$u get nfound nfound
			nfound=$(( $nfound + $nmatches ))
			menuitem_$u set nfound $nfound

			# Combine description with existing help
			menuitem_$u get help help
			f_wireless_describe WIRELESS_$n desc
			for w in $desc; do
				case "$help" in
				"$w"|"$w "*|*" $w"|*" $w "*) : already there ;;
				*) help="$help $w"
				esac
			done
			menuitem_$u set help "${help# }"
		fi
	done

	n=0
	while [ $n -lt $nunique ]; do
		n=$(( $n + 1 ))
		menuitem_$n get ssid ssid

		menuitem_$n get nconfigs nconfigs
		desc="$nconfigs $msg_configured_lc"
		[ $nconfigs -lt 10 ] && desc=" $desc"
		menuitem_$n get nfound nfound
		[ $nfound -gt 0 ] && desc="$desc $nfound $msg_found"

		menuitem_$n get help help
		echo "'[X] $ssid' '$desc' '$help'"
	done | sort -bf | awk 'BEGIN { prefix = "" }
	{
		cur_prefix = toupper(substr($0, 6, 1))
		if (cur_prefix != "'\''" && prefix != cur_prefix ) {
			prefix = cur_prefix
			printf "'\''%c%s\n", prefix, substr($0, 2)
		} else
			printf "'\'' %s\n", substr($0, 2)
	}'
}

# f_menu_wpa_scan_results
#
# Generates the tag/item/help triplets for wireless network menu (`--item-help'
# required) from wpa_cli(8) `scan_results' [WPA_SCAN_RESULT] structs.
#
f_menu_wpa_scan_results()
{
	[ "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] || return $SUCCESS

	if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then
		echo "' - $msg_discovered_ssids -' ' - $msg_details -' ''"
	else
		echo "' - $msg_discovered_ssids -' '' ''"
	fi

	local n=0 nunique=0 debug=
	local ssid ussid matched nfound help flags f
	while [ $n -lt $NWSCAN_RESULTS ]; do
		n=$(( $n + 1 ))

		WSCANS_$n get ssid ssid
		[ ! "$DIALOG_MENU_WLAN_SHOW_ALL" -a ! "$ssid" ] && continue

		WSCANS_$n get matched matched
		[ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" -a "$matched" ] &&
			continue

		local u=0 unique=1
		while [ $u -lt $nunique ]; do
			u=$(( $u + 1 ))
			menuitem_$u get ssid ussid
			[ "$ssid" != "$ussid" ] || unique= break
		done
		if [ "$unique" ]; then
			nunique=$(( $nunique + 1 ))
			u=$nunique

			# Set SSID and initialize number of networks found (1)
			f_struct_new WLAN_MENU_ITEM menuitem_$u
			menuitem_$u set ssid "$ssid"
			menuitem_$u set nfound 1

			# Set help to flags
			WSCANS_$n get flags flags
			f_replaceall "$flags" "[" " " flags
			f_replaceall "$flags" "]" "" flags
			flags="${flags# }"
			case "$flags" in
			"") flags="NONE" ;;
			ESS) flags="NONE ESS" ;;
			esac
			menuitem_$u set help "$flags"
		else
			# Increment number of networks found with this SSID
			menuitem_$u get nfound nfound
			nfound=$(( $nfound + 1 ))
			menuitem_$u set nfound $nfound

			# Combine flags into existing help
			WSCANS_$n get flags flags
			f_replaceall "$flags" "[" " " flags
			f_replaceall "$flags" "]" "" flags
			local flags_ess=
			case "$flags" in *" ESS")
				flags_ess=1
				flags="${flags% ESS}"
			esac
			local help_ess=
			menuitem_$u get help help
			case "$help" in *" ESS")
				help_ess=1
				help="${help% ESS}"
			esac
			for f in ${flags:-NONE}; do 
				case "$help" in
				"$f"|"$f "*|*" $f"|*" $f "*) : already there ;;
				*) help="$help $f"
				esac
			done
			[ "$flags_ess" -a ! "$help_ess" ] && help="$help ESS"
			menuitem_$u set help "${help# }"
		fi
	done

	local desc n=0
	while [ $n -lt $nunique ]; do
		n=$(( $n + 1 ))
		menuitem_$n get ssid ssid

		desc=
		if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then
			menuitem_$n get nfound nfound
			desc="$nfound $msg_found"
			[ $nfound -lt 10 ] && desc=" $desc"
		fi

		menuitem_$n get help help
		echo "'[ ] $ssid' '$desc' '$help'"
	done | sort -bf | awk 'BEGIN { prefix = "" }
	{
		cur_prefix = toupper(substr($0, 6, 1))
		if (cur_prefix != "'\''" && prefix != cur_prefix ) {
			prefix = cur_prefix
			printf "'\''%c%s\n", prefix, substr($0, 2)
		} else
			printf "'\'' %s\n", substr($0, 2)
	}'
}

# f_dialog_menu_wireless_edit
#
# Display a list of wireless networks configured in wpa_supplicants.conf(5) and
# (if wpa_supplicant(8) is running) also displays scan results for unconfigured
# wireless networks.
#
f_dialog_menu_wireless_edit()
{
	local funcname=f_dialog_menu_wireless_edit
	local title="$DIALOG_TITLE"
	local btitle="$DIALOG_BACKTITLE"
	local prompt="$msg_wireless_networks_text"
	local menu_list # Calculated below
	local defaultitem= # Calculated below
	local hline="$hline_alnum_arrows_punc_tab_enter"

	f_show_info "$msg_loading_wireless_menu"

	local conf_file
	conf_file=$( f_sysrc_get wpa_supplicant_conf_file )

	#
	# Operate in a loop so we can edit wpa_supplicant.conf(5) and rescan
	# for new wireless networks from here.
	#
	local do_parse=1 remake_menu=1 item
	while :; do
		#
		# If this is the first time here, parse wpa_supplicant.conf(5),
		# scan the airwaves, and compare to find matches.
		#
		if [ "$do_parse" -a "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ]
		then
			f_dprintf "$funcname: Parsing wireless scan results"
			f_dialog_scan_wireless &&
				f_wpa_scan_results_parse WSCANS_ NWSCAN_RESULTS
			f_dprintf "$funcname: Parsed %i scanned networks" \
			          $NWSCAN_RESULTS
		fi
		if [ "$do_parse" -a "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ]
		then
			f_dprintf "$funcname: Parsing wpa_supplicants.conf(5)"
			f_wpa_supplicant_parse "$conf_file" \
			                       WIRELESS_ NWIRELESS_CONFIGS
			f_dprintf "%s: Parsed %i wireless configurations" \
				  $funcname $NWIRELESS_CONFIGS
			f_wpa_scan_find_matches WSCANS_ $NWSCAN_RESULTS \
			                        WIRELESS_ $NWIRELESS_CONFIGS
		fi
		do_parse=

		if [ "$remake_menu" ]; then
			remake_menu=

			#
			# Add both items scanned from the airwaves and networks
			# parsed from wpa_supplicants.conf(5). Latter items are
			# marked, sorted, and added to top of list above the
			# former (which are unmarked and sorted separately).
			#
			f_dprintf "$funcname: Building menu list..."
			menu_list=$( 
				# Process wpa_supplicant.conf(5) structs
				f_menu_wireless_configs
				# Process wpa_cli(8) `scan_results' structs
				f_menu_wpa_scan_results
			)
			f_dprintf "$funcname: menu list built."

			#
			# Add static top-level menu items
			#
			local XA=" " XC=" " XS=" "
			[ "$DIALOG_MENU_WLAN_SHOW_ALL"          ] && XA="X"
			[ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED"   ] && XC="X"
			[ "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] && XS="X"
			menu_list="
				'> $msg_exit' '$msg_return_to_previous_menu'
					''
				'> $msg_rescan_wireless'   '*'
					'$msg_rescan_wireless_help'
				'> $msg_forget_all'        '*'
					'$msg_forget_all_help'
				'> $msg_show_configured'   '[$XC]'
					'$msg_show_configured_help'
				'> $msg_show_scan_results' '[$XS]'
					'$msg_show_scan_results_help'
				'> $msg_show_all'          '[$XA]'
					'$msg_show_all_help'
				'> $msg_manually_connect'  '...'
					'$msg_manually_connect_help'
			$menu_list" # END-QUOTE
		fi

		local height width rows
		eval f_dialog_menu_with_help_size height width rows \
			\"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" \
			$menu_list

		local menu_choice
		menu_choice=$( eval $DIALOG \
			--title \"\$title\"              \
			--backtitle \"\$btitle\"         \
			--hline \"\$hline\"              \
			--ok-label \"\$msg_select\"      \
			--cancel-label \"\$msg_cancel\"  \
			--item-help                      \
			--default-item \"\$defaultitem\" \
			--menu \"\$prompt\"              \
			$height $width $rows             \
			$menu_list                       \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		) || break
		f_dialog_data_sanitize menu_choice
		defaultitem="$menu_choice"

		case "$menu_choice" in
		"> $msg_exit") break ;;
		"> $msg_rescan_wireless") do_parse=1 remake_menu=1 ;;
		"> $msg_forget_all")
			if f_noyes "$msg_forget_all_confirm"; then
				f_eval_catch $funcname rm \
					'rm -f "%s"' "$conf_file" || continue
				f_wpa_supplicant_init "$conf_file" || continue
				f_eval_catch -d $funcname wpa_cli \
					'wpa_cli reconfigure'
				f_wpa_supplicant_parse "$conf_file" \
					WIRELESS_ NWIRELESS_CONFIGS
				f_wpa_scan_find_matches \
					WSCANS_ $NWSCAN_RESULTS \
					WIRELESS_ $NWIRELESS_CONFIGS
				do_parse=1 remake_menu=1
			fi ;;
		"> $msg_show_configured")
			item=$( eval f_dialog_menutag2item_with_help \
			        		\"\$menu_choice\" $menu_list )
			if [ "$item" = "[ ]" ]; then
				DIALOG_MENU_WLAN_SHOW_CONFIGURED=1
			else
				DIALOG_MENU_WLAN_SHOW_CONFIGURED=
			fi
			remake_menu=1 ;;
		"> $msg_show_scan_results")
			item=$( eval f_dialog_menutag2item_with_help \
			        		\"\$menu_choice\" $menu_list )
			if [ "$item" = "[ ]" ]; then
				DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=1
			else
				DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=
			fi
			remake_menu=1 ;;
		"> $msg_show_all")
			item=$( eval f_dialog_menutag2item_with_help \
			        		\"\$menu_choice\" $menu_list )
			if [ "$item" = "[ ]" ]; then
				DIALOG_MENU_WLAN_SHOW_ALL=1
			else
				DIALOG_MENU_WLAN_SHOW_ALL=
			fi
			remake_menu=1 ;;
		"> $msg_manually_connect")
			f_dialog_wireless_edit && remake_menu=1 ;;
		?"[X] "*)
			ssid="${menu_choice#??X? }"
			f_dialog_wireless_edit "$ssid" || continue
			do_parse=1 remake_menu=1 ;;
		"[ ] "*)
			:
			: XXXDT Unfinished
			:
			;;
		esac
	done

	#
	# XXXDT Unfinished
	#
}

############################################################ MAIN

f_dprintf "%s: Successfully loaded." media/wlan.subr

fi # ! $_MEDIA_WLAN_SUBR

Zerion Mini Shell 1.0