1 if [ ! "$_MEDIA_WLAN_SUBR" ]; then _MEDIA_WLAN_SUBR=1
3 # Copyright (c) 2013-2016 Devin Teske
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 ############################################################ INCLUDES
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 . $BSDCFG_SHARE/common.subr || exit 1
33 f_dprintf "%s: loading includes..." media/wlan.subr
34 f_include $BSDCFG_SHARE/device.subr
35 f_include $BSDCFG_SHARE/dialog.subr
36 f_include $BSDCFG_SHARE/strings.subr
37 f_include $BSDCFG_SHARE/sysrc.subr
39 BSDCFG_LIBE="/usr/libexec/bsdconfig"
40 f_include_lang $BSDCFG_LIBE/include/messages.subr
42 ############################################################ GLOBALS
48 # Settings used while interacting with various dialog(1) menus
50 : ${DIALOG_MENU_WLAN_SCAN_DURATION:=5}
51 : ${DIALOG_MENU_WLAN_SHOW_ALL=}
52 : ${DIALOG_MENU_WLAN_SHOW_CONFIGURED=1}
53 : ${DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=1}
56 # Structure to contain the wpa_supplicant.conf(5) default overrides
58 f_struct_define WPA_DEFAULTS \
61 ctrl_interface_group \
66 # Structure of wpa_supplicant.conf(5) network={ ... } entry
68 f_struct_define WPA_NETWORK \
102 private_key2_passwd \
119 # The following properties are ``Lists'' and as such should not be quoted.
120 # Everything else should be quoted.
122 WPA_NETWORK_LIST_PROPERTIES="
132 # Structure of wpa_cli(8) `scan_results' entry
134 f_struct_define WPA_SCAN_RESULT \
142 # Structure of a menu item in the wireless editor
144 f_struct_define WLAN_MENU_ITEM \
151 ############################################################ FUNCTIONS
153 # f_wpa_supplicant_init $file
155 # Initialize $file with basic contents of new wpa_supplicant.conf(5).
157 f_wpa_supplicant_init()
159 local funcname=f_wpa_supplicant_init
160 local conf_file="$1" tmpfile
162 # Create a temporary file
163 f_eval_catch -k tmpfile $funcname mktemp 'mktemp -t "%s"' "$pgm" ||
166 # Make it unreadable by anyone but ourselves
167 f_eval_catch $funcname chmod \
168 'chmod 0600 "%s"' "$tmpfile" || return $FAILURE
170 # Make it owned by root/wheel
171 f_eval_catch $funcname chown \
172 'chown 0:0 "%s"' "$tmpfile" || return $FAILURE
175 cat <<-EOF >> "$tmpfile"
176 ctrl_interface=/var/run/wpa_supplicant
184 f_eval_catch $funcname mv 'mv "%s" "%s"' "$tmpfile" "$conf_file"
187 # f_wpa_supplicant_parse $file [struct_prefix [count_var]]
189 # Parse wpa_supplicant.conf(5) $file. Default overrides are stored in a struct
190 # (see struct.subr for additional details) named `{struct_prefix}defaults'. See
191 # WPA_DEFAULTS struct definition in the GLOBALS section above.
193 # In addition, for each one of the wireless networks we parse from $file,
194 # create a struct named `struct_prefixN' where `N' is a number starting from 1
195 # and ending in $count_var (zero means no networks). See WPA_NETWORK struct
196 # definition in the GLOBALS section above.
198 # If a `blob-base64-*={ ... }' entry appears, a struct named
199 # `{struct_prefix}blob_base64_*' is created and the `data' property holds the
200 # base64 encoded binary data without whitespace.
202 # Custom `*={ ... }' definitions are also supported, but should be unique
203 # (unlike the `network' definition). A struct named `{struct_prefix}*' is
204 # created if at least one property is defined in the block.
206 f_wpa_supplicant_parse()
208 local file="$1" struct_prefix="$2" count_var="$3"
210 [ "$count_var" ] && setvar "$count_var" 0
212 [ "$file" ] || file=$( f_sysrc_get wpa_supplicant_conf_file )
213 if [ ! -e "$file" ]; then
214 f_dprintf "%s: No such file or directory" "$file"
218 local list_properties
219 f_replaceall "$WPA_NETWORK_LIST_PROPERTIES" "$NL" "" list_properties
221 -v count_var="$count_var" \
222 -v struct_prefix="$struct_prefix" \
223 -v list_properties="$list_properties" '
225 if (!count_var && !struct_prefix) exit
226 blob = count = custom_struct = network = 0
227 split(list_properties, lists, FS)
229 function set_value(struct, prop, value)
231 quoted = substr(value, 0, 1) == "\""
232 for (l in lists) if (list = prop == lists[l]) break
233 # Remove data after whitespace if unquoted and not a list
234 if (!quoted && !list) sub("[[:space:]].*", "", value)
235 # Otherwise if quoted and not a list, remove the quotes
236 # NB: wep_keyN needs to retain quoting if/when present
237 else if (quoted && !list && prop !~ /^wep_key[[:digit:]]+/) {
238 sub("^\"", "", value)
239 sub("\".*", "", value)
241 gsub(/'\''/, "'\''\\'\'\''", value) # Sanitize the value
242 if (!created[struct]) {
243 print "debug= f_struct_free", struct
244 print "debug= f_struct_new WPA_NETWORK", struct
247 printf "debug= %s set %s '\'%s\''\n", struct, prop, value
250 if ($1 ~ /^network={/) {
251 empty = 1 # We do not increment count unless !empty
254 } else if (match($1, "^blob-base64-[[:alnum:]_./-]+={")) {
257 struct = struct_prefix "blob_bas64_"
258 struct = struct substr($1, 13, RLENGTH - 14)
260 } else if (match($1, "^[[:alnum:]_./-]+={")) {
263 struct = struct_prefix substr($1, 0, RLENGTH - 2)
264 gsub(/[^[:alnum:]_]/, "_", struct)
266 } else if ($1 ~ /^}/) {
268 gsub("[[:space:]]", "", blob_data)
269 set_value(struct, "data", blob_data)
271 blob = custom_struct = network = 0
273 } else if (!match($0, /^[[:space:]]*[[:alnum:]_]+=/))
277 blob_data = blob_data $0
279 } else if (network) {
280 if (empty) { count++; empty = 0 }
281 struct = struct_prefix count
282 } else if (!custom_struct)
283 struct = struct_prefix "defaults"
285 if (!struct_prefix) next
287 prop = substr($0, 0, RLENGTH - 1)
288 sub(/^[[:space:]]*/, "", prop)
289 value = substr($0, RSTART + RLENGTH)
291 set_value(struct, prop, value)
293 END { if (count_var) print count_var "=" count }' "$file" )"
296 # f_wpa_scan_results_parse [struct_prefix [count_var]]
298 # Parse the results of wpa_cli(8) `scan_results' into a series of structs (see
299 # struct.subr for additional details) named `struct_prefixN' where `N' is a
300 # number starting from 1 and ending in $count_var (zero means no results). See
301 # WPA_SCAN_RESULT struct definition in the GLOBALS section above.
303 f_wpa_scan_results_parse()
305 local struct_prefix="$1" count_var="$2"
307 [ "$count_var" ] && setvar "$count_var" 0
309 eval "$( wpa_cli scan_results 2> /dev/null | awk \
310 -v count_var="$count_var" \
311 -v struct_prefix="$struct_prefix" '
313 if (!count_var && !struct_prefix) exit
315 seg = "[[:xdigit:]][[:xdigit:]]"
316 bssid = seg":"seg":"seg":"seg":"seg":"seg
317 freq = siglev = flags = "[^[:space:]]+"
319 line = bssid S freq S siglev S flags
320 line = "^[[:space:]]*" line "[[:space:]]*"
322 function set_value(struct, prop, value)
324 gsub(/'\''/, "'\''\\'\'\''", value) # Sanitize the value
325 if (!created[struct]) {
326 print "debug= f_struct_free", struct
327 print "debug= f_struct_new WPA_SCAN_RESULT", struct
330 printf "debug= %s set %s '\'%s\''\n", struct, prop, value
333 if (!match($0, line)) next
334 ssid = substr($0, RLENGTH + 1)
337 if (!struct_prefix) next
339 struct = struct_prefix count
340 set_value(struct, "ssid", ssid)
341 set_value(struct, "bssid", $1)
342 set_value(struct, "freq", $2)
343 set_value(struct, "siglev", $3)
344 set_value(struct, "flags", $4)
346 END { if (count_var) print count_var "=" count }' )"
349 # f_wpa_scan_match_network WPA_SCAN_RESULT WPA_NETWORK
351 # Compares a WPA_SCAN_RESULT struct to a WPA_NETWORK struct. If they appear to
352 # be a match returns success, otherwise failure.
354 f_wpa_scan_match_network()
356 local scan_struct="$1" wireless_struct="$2"
359 f_struct "$scan_struct" || return $FAILURE
360 f_struct "$wireless_struct" || return $FAILURE
362 local scan_ssid scan_bssid
363 $scan_struct get ssid scan_ssid
364 $scan_struct get bssid scan_bssid
365 local wireless_ssid wireless_bssid
366 $wireless_struct get ssid wireless_ssid
367 $wireless_struct get bssid wireless_bssid
370 if [ "$wireless_ssid" -a "$wireless_bssid" ]; then
371 # Must match both SSID and BSSID
372 [ "$scan_ssid" = "$wireless_ssid" -a \
373 "$scan_bssid" = "$wireless_bssid" ] && id_matched=1
374 elif [ "$wireless_ssid" ]; then
375 # Must match SSID only
376 [ "$scan_ssid" = "$wireless_ssid" ] && id_matched=1
377 elif [ "$wireless_bssid" ]; then
378 # Must match BSSID only
379 [ "$scan_bssid" = "$wireless_bssid" ] && id_matched=1
381 [ "$id_matched" ] || return $FAILURE
385 # Get the scanned flags for the next few comparisons
388 $scan_struct get flags flags
391 # Compare configured key management against scanned network
393 if $wireless_struct get key_mgmt cp && [ "$cp" -a "$cp" != "NONE" ]
395 local mgmt mgmt_matched=
398 [ "$mgmt" != "${mgmt#WPA-}" ] &&
399 mgmt2="WPA2${mgmt#WPA}"
401 "$mgmt"|"$mgmt"-*|*-"$mgmt"|*-"$mgmt"-*)
402 mgmt_matched=1 break ;;
403 "$mgmt2"|"$mgmt2"-*|*-"$mgmt2"|*-"$mgmt2"-*)
404 mgmt_matched=1 break ;;
407 [ "$mgmt_matched" ] || return $FAILURE
413 # Compare configured encryption against scanned network
415 for enc in psk:PSK eap:EAP \
416 wep_key0:WEP wep_key1:WEP wep_key2:WEP wep_key3:WEP
420 { debug= $wireless_struct get $type cp && [ "$cp" ]; } ||
422 # Configured network requires encryption
423 case "$flags" in "[$flag]"|*"-$flag-"*)
424 break # Success; stop after first match
428 cp="" # sensitive info
431 # Compare scanned network encryption against configuration
432 # NB: Scanned network flags indicates _one_ of PSK EAP or WEP
433 # NB: Otherwise, no encryption (so encryption won't match)
436 for enc in -PSK-:psk -EAP-:eap; do
439 case "$flags" in *"$flag"*)
441 { debug= $wireless_struct get $type cp &&
442 [ "$cp" ]; } || return $FAILURE
446 case "$flags" in *"[WEP]"*)
449 for type in wep_key0 wep_key1 wep_key2 wep_key3; do
450 debug= $wireless_struct get $type cp && [ "$cp" ] &&
453 [ "$wep_found" ] || return $FAILURE
455 if [ ! "$enc_wanted" ]; then
456 # No match if the network specifies encryption
457 for type in psk eap wep_key0 wep_key1 wep_key2 wep_key3; do
458 debug= $wireless_struct get $type cp && [ "$cp" ] &&
462 cp="" # sensitive info
467 # f_wpa_scan_find_matches scans_prefix $scans_count \
468 # wireless_prefix $wireless_count
470 # For each struct from `{scans_prefix}1' up to `{scans_prefix}$scans_count'
471 # (see struct.subr for additional details) compare the wireless network info
472 # (defined as struct WPA_SCAN_RESULT) to that of each configured wireless
473 # stored in `{wireless_prefix}1' (defined as struct WPA_NETWORK) up to
474 # `{wireless_prefix}$wireless_count'.
476 # If a scanned network is deemed to be a match to a configured wireless
477 # network, a new `match' property is set on the WPA_NETWORK struct with a value
478 # of `{scans_prefix}N' (where N represents the scanned network that matched).
479 # At the same time, a new `matched' property is set on the WPA_SCAN_RESULT
480 # struct with a value of 1, indicating that this network has been matched to a
481 # stored [known] configuration and that it should not be displayed in menus.
483 # NB: If a matching entry is not correct, the user can optionally `Forget' the
484 # network and that will cause the WPA_SCAN_RESULT to no longer match anything,
485 # causing it to appear in the menus again.
487 # Return status should be ignored.
489 f_wpa_scan_find_matches()
491 local scans_prefix="$1" scans_count="$2"
492 local wireless_prefix="$3" wireless_count="$4"
495 [ "$scans_count" -a "$wireless_count" ] || return $SUCCESS
496 f_isinteger "$scans_count" || return $FAILURE
497 f_isinteger "$wireless_count" || return $FAILURE
500 # Go through and eradicate any flags we set in a prior run, as things
501 # might have changed on us (either from the config side or scan side)
504 while [ $w -le $wireless_count ]; do
505 f_struct "$wireless_prefix$w" set matches ""
510 # Find matches and set match data on structs
513 while [ $s -le $scans_count ]; do
514 f_struct "$scans_prefix$s" set matched ""
516 while [ $w -le $wireless_count ]; do
517 if f_wpa_scan_match_network \
518 "$scans_prefix$s" "$wireless_prefix$w"
520 f_struct "$scans_prefix$s" set matched 1
521 debug= f_struct "$wireless_prefix$w" \
523 matches="$matches${matches:+ }$scans_prefix$s"
524 f_struct "$wireless_prefix$w" \
525 set matches "$matches"
526 break # to next scan result
534 # f_dialog_menu_wlandev_edit $wlandev [$defaultitem]
536 # Display a list of wireless network devices (wlan*) associated with
537 # $wlandev (e.g., `iwn0'). Allow the user to create and destroy wlan interfaces
538 # while selecting ones to be cloned at startup (by setting `wlans_$wlandev').
540 f_dialog_menu_wlandev_edit()
542 local funcname=f_dialog_menu_wlandev_edit
543 local wlandev="$1" defaultitem="$2"
544 local title="$DIALOG_TITLE"
545 local btitle="$DIALOG_BACKTITLE"
546 local prompt # Calculated below
547 local hline="$hline_arrows_tab_enter"
549 [ "$wlandev" ] || return $FAILURE
551 f_sprintf prompt "$msg_select_wlan_interfaces_for" "wlandev"
554 # Initially mark wlan devices with a %parent of $wlandev
556 local dev devs if list_to_save=
557 f_device_find "" $DEVICE_TYPE_NETWORK devs
559 f_struct "$dev" get name if || continue
560 case "$if" in wlan[0-9]*)
561 parent=$( sysctl -n net.wlan.${if#wlan}.%parent \
563 if [ "$parent" = "$if" ]; then
564 local _wlanmark_$if="X"
565 list_to_save="$list_to_save $if"
569 list_to_save="${list_to_save# }"
572 # Operate in a loop so we can create/destroy interfaces from here
576 # Refresh list of wlan interfaces
579 f_device_rescan_network
580 f_device_find "" $DEVICE_TYPE_NETWORK devs
582 f_struct "$dev" get name if || continue
583 case "$if" in wlan[0-9]*)
584 wlanlist="$wlanlist $if"
589 # Build menu list of wlan devices
592 '> $msg_save_exit' '$msg_return_to_previous_menu'
593 '> $msg_create_new' 'wlan'
594 '> $msg_destroy' '...'
597 for if in $wlanlist; do
598 f_getvar _wlanmark_$if-" " X
599 menu_list="$menu_list '[$X] $if' '%parent: $parent'"
600 [ "$defaultitem" = "$if" ] && defaultitem="[$X] $if"
604 # Ask user to make a choice
606 local height width rows
607 eval f_dialog_menu_size height width rows \
608 \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" \
611 menu_choice=$( eval $DIALOG \
612 --title \"\$title\" \
613 --backtitle \"\$btitle\" \
614 --hline \"\$hline\" \
615 --ok-label \"\$msg_select\" \
616 --cancel-label \"\$msg_cancel\" \
617 --default-item \"\$defaultitem\" \
618 --menu \"\$prompt\" \
619 $height $width $rows \
621 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
623 f_dialog_data_sanitize menu_choice
625 case "$menu_choice" in
626 "> $msg_save_exit") # Save list to rc.conf(5) `wlans_$wlandev'
627 f_eval_catch $funcname f_sysrc_set \
628 'f_sysrc_set "wlans_%s" "%s"' \
629 "$wlandev" "$list_to_save" || continue
632 "> $msg_create_new") # Create new wlan interface for wlandev
634 f_eval_catch -k wlan $funcname ifconfig \
635 'ifconfig wlan create wlandev "%s"' \
636 "$wlandev" || continue
637 local _wlanmark_$wlan="X"
638 list_to_save="$list_to_save${list_to_save:+ }$wlan"
640 "> $msg_destroy") # Display a menu to pick one item to destroy
641 [ "$wlanlist" ] || continue # Nothing to destroy
644 for if in $wlanlist; do
645 menu_list="$menu_list '$if' ''"
647 local msg="$msg_pick_an_interface_to_destroy"
648 eval f_dialog_menu_size height width rows \
649 \"\$title\" \"$btitle\" \"\$msg\" \"\" $menu_list
650 menu_choice=$( eval $DIALOG \
651 --title \"\$title\" \
652 --backtitle \"\$btitle\" \
653 --ok-label \"\$msg_destroy\" \
654 --cancel-label \"\$msg_cancel\" \
656 $height $width $rows \
658 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
660 f_dialog_data_sanitize menu_choice
661 f_eval_catch $funcname ifconfig \
662 'ifconfig "%s" destroy' "$menu_choice"
664 "[ ] wlan"[0-9]*) # Unmarked; Mark
665 if="${menu_choice#??? }"
666 local _wlanmark_$if="X"
667 list_to_save="$list_to_save${list_to_save:+ }$if"
669 "[X] wlan"[0-9]*) # Marked; Unmark
670 menu_choice="${menu_choice#??? }"
671 local _wlanmark_$menu_choice=" "
672 local new_list_to_save=
673 for if in $list_to_save; do
674 [ "$if" = "$menu_choice" ] && continue
675 new_list_to_save="$new_list_to_save $if"
677 list_to_save="${new_list_to_save# }"
685 # f_dialog_scan_wireless
687 # Initiate a scan for wireless networks. If wpa_supplicant(8) is not running
688 # but a wlan interface has been created, start an instance of wpa_supplicant(8)
689 # with the first wlan(4) interface we find. After initiating the scan, displays
690 # a message for 5 seconds (with option to dismiss). Returns failure if an error
691 # occurs, otherwise success.
693 f_dialog_scan_wireless()
695 local funcname=f_dialog_scan_wireless
698 # Try to communicate with a running wpa_supplicant(8)
700 if ! f_eval_catch -d $funcname wpa_cli 'wpa_cli ping'; then
702 # If there is indeed one running, bail!
703 if ps axo ucomm= | grep -qw wpa_supplicant; then
704 f_show_msg "$msg_failed_to_reach_wpa_supplicant" \
705 "$msg_wpa_cli_ping_failed"
709 # Try and find a wlan device so we can start wpa_supplicant
710 local dev devs if wlan=
711 f_device_rescan_network
712 f_device_find "" $DEVICE_TYPE_NETWORK devs
714 f_struct "$dev" get name if || continue
715 case "$if" in wlan[0-9]*)
720 if [ ! "$wlan" ]; then
721 # We can't start wpa_supplicant without wlan interface
722 # Tell the user they have to create one by navigating
723 # to a Wireless device to create a wlan interface. But
724 # let's go one step further and find an interface that
725 # we can provide in the prompt text.
728 case "$if" in wlan[0-9]*) next; esac
729 if f_device_is_wireless $if; then
734 if [ "$wlandev" ]; then
735 f_show_msg "$msg_cant_start_wpa_supplicant" \
738 # Warn user, appears no wireless available
739 f_show_msg "$msg_warning_no_wireless_devices"
744 # NB: Before we can proceed to fire up wpa_supplicant(8), let's
745 # make sure there is a bare-bones wpa_supplicant.conf(5) for it
747 conf_file=$( f_sysrc_get wpa_supplicant_conf_file )
748 if [ ! -e "$conf_file" ]; then
749 f_wpa_supplicant_init "$conf_file" || return $FAILURE
750 f_eval_catch -d $funcname wpa_cli 'wpa_cli reconfigure'
753 # Try and start wpa_supplicant(8)
754 f_eval_catch $funcname wpa_supplicant \
755 '/etc/rc.d/wpa_supplicant start "%s"' "$wlan" ||
758 # Try to reach this new wpa_supplicant(8)
759 if ! f_eval_catch -d $funcname wpa_cli 'wpa_cli ping'; then
760 f_show_msg "$msg_failed_to_reach_wpa_supplicant" \
761 "$msg_wpa_cli_ping_failed"
765 fi # ! f_quietly wpa_cli ping
767 # If we reach hear, then it should be OK to scan the airwaves
768 f_eval_catch -d $funcname wpa_cli 'wpa_cli scan' || return $FAILURE
770 # Return immediately if a duration is: null or not a number >= 1
771 local duration="$DIALOG_MENU_WLAN_SCAN_DURATION"
772 f_isinteger "$duration" || return $SUCCESS
773 [ $duration -gt 0 ] || return $SUCCESS
775 # Display a message that times-out if not dismissed manually
777 f_sprintf prompt "$msg_scanning_wireless_pausing" "$duration"
778 f_dialog_pause "$prompt" "$duration"
781 # f_dialog_wireless_edit $ssid
783 # Display a menu to allow the user to either create a new entry for the
784 # wpa_supplicants.conf(5) file, or to edit values for an existing entry.
786 # If more than one wireless network is found to match $ssid, a sub-menu is
787 # presented, allowing the user to select the desired network.
789 f_dialog_wireless_edit()
791 local title="$DIALOG_TITLE"
792 local btitle="$DIALOG_BACKTITLE"
793 local prompt1="$msg_select_the_configuration_you_would_like"
794 local prompt2 # Calculated below
795 local hline="$hline_alnum_arrows_punc_tab_enter"
796 local ssid="$1" bssid="$2"
798 f_sprintf prompt2 "$msg_wireless_network_configuration_for" "$ssid"
801 # Find one or more configurations that match the SSID selection
803 local height1 width1 rows1 menu_list1=
804 local n=0 nmatches=0 tag wssid wbssid help matches=
805 while [ $n -lt $NWIRELESS_CONFIGS ]; do
808 debug= f_struct WIRELESS_$n get ssid wssid
809 [ "$ssid" = "$wssid" ] || continue
810 debug= f_struct WIRELESS_$n get bssid wbssid
811 [ "${bssid:-$wbssid}" = "$wbssid" ] || continue
813 nmatches=$(( $nmatches + 1 ))
814 [ $nmatches -le ${#DIALOG_MENU_TAGS} ] || break
815 f_substr -v tag "$DIALOG_MENU_TAGS" $nmatches 1
817 f_wireless_describe WIRELESS_$n help
818 menu_list1="$menu_list1
819 '$tag $wssid' '$wbssid' '$help'
822 matches="$matches WIRELESS_$n"
824 if [ $nmatches -eq 0 ]; then
825 f_show_msg "$msg_cannot_edit_wireless_ssid" "$ssid"
827 elif [ $nmatches -eq 1 ]; then
830 eval f_dialog_menu_with_help_size height1 width1 rows1 \
831 \"\$title\" \"\$btitle\" \"\$prompt1\" \"\$hline\" \
836 # Operate in a loop; for the case of $nmatches > 1, we can cycle back
837 # to allow the user to make another choice after inspecting each one.
839 local menu_choice index struct defaultitem1=
841 if [ $nmatches -gt 1 ]; then
842 menu_choice=$( eval $DIALOG \
843 --title \"\$title\" \
844 --backtitle \"\$btitle\" \
845 --hline \"\$hline\" \
846 --ok-label \"\$msg_select\" \
847 --cancel-label \"\$msg_cancel\" \
849 --default-item \"\$defaultitem1\" \
850 --menu \"\$prompt1\" \
851 $height1 $width1 $rows1 \
853 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
855 f_dialog_data_sanitize menu_choice
856 defaultitem1="$menu_choice"
857 index=$( eval f_dialog_menutag2index_with_help \
858 \"\$menu_choice\" $menu_list1 )
859 struct=$( set -- $matches; eval echo \${$index} )
863 # Operate within another loop to allow editing multiple values
865 local menu_list2 height2 width2 rows2 member
869 '$msg_return_to_previous_menu'
872 for member in $_struct_typedef_WPA_NETWORK; do
873 [ "$member" = "ssid" ] && continue
874 debug= $struct get $member value || continue
876 [ $n -le ${#DIALOG_MENU_TAGS} ] || break
877 f_substr -v tag "$DIALOG_MENU_TAGS" $n 1
878 if [ ${#value} -gt 32 ]; then
879 f_snprintf value 29 "%s" "$value"
883 password|pin|private_key_passwd|psk|wep_key*)
884 f_replaceall "$value" "?" "*" value ;;
886 f_shell_escape "$value" value
887 menu_list2="$menu_list2
888 '$tag $member' '$value'
891 eval f_dialog_menu_size height2 width2 rows2 \
892 \"\$title\" \"\$btitle\" \"\$prompt2\" \
893 \"\$hline\" $menu_list2
894 menu_choice=$( eval $DIALOG \
895 --title \"\$title\" \
896 --backtitle \"\$btitle\" \
897 --hline \"\$hline\" \
898 --ok-label \"\$msg_select\" \
899 --cancel-label \"\$msg_cancel\" \
900 --default-item \"\$defaultitem2\" \
901 --menu \"\$prompt2\" \
902 $height2 $width2 $rows2 \
904 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
906 f_dialog_data_sanitize menu_choice
907 defaultitem2="$menu_choice"
911 [ $nmatches -eq 1 ] && break
916 # This is where we display a menu that edits the entry
917 # And then we modify the wpa_supplicants.conf(5) config file
921 return $FAILURE # XXXDT Simulating DIALOG_CANCEL to mean ``no changes''
924 # f_wireless_describe WPA_NETWORK [$var_to_set]
926 # Provide a description of the WPA_NETWORK struct. If $var_to_set is missing or
927 # NULL, the description is provided on standard output (which is less preferred
928 # due to performance; e.g., if called in a loop).
930 f_wireless_describe()
932 local __struct="$1" __var_to_set="$2" debug=
934 [ "$__var_to_set" ] && setvar "$__var_to_set" ""
935 f_struct "$__struct" || return $FAILURE
938 # Basic description is `proto key_mgmt group eap'
940 local __member __cp __desc=
941 for __member in proto key_mgmt group eap; do
942 $__struct get $__member __cp && [ "$__cp" ] &&
943 __desc="$__desc${__desc:+ }$__cp"
949 # Make sure we add WEP40/WEP140 even if omitted from the key_mgmt
952 local __wep_keyN __f_wireless_describe_first_char __length
953 for __wep_keyN in wep_key0 wep_key1 wep_key2 wep_key3; do
954 $__struct get $__wep_keyN __kk
955 [ "$__kk" ] || continue
957 # What type is it? ASCII or HEX?
959 f_substr -v __f_wireless_describe_first_char "$__kk" 1 1
960 case "$__f_wireless_describe_first_char" in
962 __length=$(( ${#__kk} - 2 ))
963 if [ $__length -le 5 ]; then
965 elif [ $__length -le 13 ]; then
970 if [ $__length -eq 10 ]; then
972 elif [ $__length -le 26 ]; then
976 __kk="" # sensitive info
979 *"$__check"*) : already there ;;
980 *) __desc="$__desc${__desc:+ }$__check"
985 # Make sure we display PSK even if omitted
986 # from the key_mgmt section of the entry
988 $__struct get psk __kk
990 __kk="" # sensitive info
993 *"$__check"*) : already there ;;
994 *) __desc="$__desc${__desc:+ }$__check"
1001 if [ "$__var_to_set" ]; then
1002 setvar "$__var_to_set" "${__desc:-NONE}"
1008 # f_menu_wireless_configs
1010 # Generates the tag/item/help triplets for wireless network menu (`--item-help'
1011 # required) from wpa_supplicant.conf(5) [WPA_NETWORK] structs.
1013 f_menu_wireless_configs()
1015 [ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ] || return $SUCCESS
1017 echo "' - $msg_configured_ssids -' ' - $msg_details -' ''"
1019 local n=0 nunique=0 debug=
1020 local ssid ussid matches nmatches nconfigs nfound help desc w
1021 while [ $n -lt $NWIRELESS_CONFIGS ]; do
1024 f_struct WIRELESS_$n get ssid ssid
1025 [ ! "$DIALOG_MENU_WLAN_SHOW_ALL" -a ! "$ssid" ] && continue
1028 while [ $u -lt $nunique ]; do
1030 menuitem_$u get ssid ussid
1031 [ "$ssid" != "$ussid" ] || unique= break
1033 if [ "$unique" ]; then
1034 nunique=$(( $nunique + 1 ))
1037 # Set SSID and initialize number of configs found (1)
1038 f_struct_new WLAN_MENU_ITEM menuitem_$u
1039 menuitem_$u set ssid "$ssid"
1040 menuitem_$u set nconfigs 1
1042 # Set number of wireless networks that match config
1043 WIRELESS_$n get matches matches
1044 f_count nmatches $matches
1045 menuitem_$u set nfound $nmatches
1047 # Set help to description of the wireless config
1048 f_wireless_describe WIRELESS_$n desc
1049 menuitem_$u set help "$desc"
1051 # Increment number of configs found with this SSID
1052 menuitem_$u get nconfigs nconfigs
1053 nconfigs=$(( $nconfigs + 1 ))
1054 menuitem_$u set nconfigs $nconfigs
1056 # Add number of matched networks to existing count
1057 WIRELESS_$n get matches matches
1058 f_count nmatches $matches
1059 menuitem_$u get nfound nfound
1060 nfound=$(( $nfound + $nmatches ))
1061 menuitem_$u set nfound $nfound
1063 # Combine description with existing help
1064 menuitem_$u get help help
1065 f_wireless_describe WIRELESS_$n desc
1068 "$w"|"$w "*|*" $w"|*" $w "*) : already there ;;
1072 menuitem_$u set help "${help# }"
1077 while [ $n -lt $nunique ]; do
1079 menuitem_$n get ssid ssid
1081 menuitem_$n get nconfigs nconfigs
1082 desc="$nconfigs $msg_configured_lc"
1083 [ $nconfigs -lt 10 ] && desc=" $desc"
1084 menuitem_$n get nfound nfound
1085 [ $nfound -gt 0 ] && desc="$desc $nfound $msg_found"
1087 menuitem_$n get help help
1088 echo "'[X] $ssid' '$desc' '$help'"
1089 done | sort -bf | awk 'BEGIN { prefix = "" }
1091 cur_prefix = toupper(substr($0, 6, 1))
1092 if (cur_prefix != "'\''" && prefix != cur_prefix ) {
1094 printf "'\''%c%s\n", prefix, substr($0, 2)
1096 printf "'\'' %s\n", substr($0, 2)
1100 # f_menu_wpa_scan_results
1102 # Generates the tag/item/help triplets for wireless network menu (`--item-help'
1103 # required) from wpa_cli(8) `scan_results' [WPA_SCAN_RESULT] structs.
1105 f_menu_wpa_scan_results()
1107 [ "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] || return $SUCCESS
1109 if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then
1110 echo "' - $msg_discovered_ssids -' ' - $msg_details -' ''"
1112 echo "' - $msg_discovered_ssids -' '' ''"
1115 local n=0 nunique=0 debug=
1116 local ssid ussid matched nfound help flags f
1117 while [ $n -lt $NWSCAN_RESULTS ]; do
1120 WSCANS_$n get ssid ssid
1121 [ ! "$DIALOG_MENU_WLAN_SHOW_ALL" -a ! "$ssid" ] && continue
1123 WSCANS_$n get matched matched
1124 [ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" -a "$matched" ] &&
1128 while [ $u -lt $nunique ]; do
1130 menuitem_$u get ssid ussid
1131 [ "$ssid" != "$ussid" ] || unique= break
1133 if [ "$unique" ]; then
1134 nunique=$(( $nunique + 1 ))
1137 # Set SSID and initialize number of networks found (1)
1138 f_struct_new WLAN_MENU_ITEM menuitem_$u
1139 menuitem_$u set ssid "$ssid"
1140 menuitem_$u set nfound 1
1143 WSCANS_$n get flags flags
1144 f_replaceall "$flags" "[" " " flags
1145 f_replaceall "$flags" "]" "" flags
1149 ESS) flags="NONE ESS" ;;
1151 menuitem_$u set help "$flags"
1153 # Increment number of networks found with this SSID
1154 menuitem_$u get nfound nfound
1155 nfound=$(( $nfound + 1 ))
1156 menuitem_$u set nfound $nfound
1158 # Combine flags into existing help
1159 WSCANS_$n get flags flags
1160 f_replaceall "$flags" "[" " " flags
1161 f_replaceall "$flags" "]" "" flags
1163 case "$flags" in *" ESS")
1165 flags="${flags% ESS}"
1168 menuitem_$u get help help
1169 case "$help" in *" ESS")
1173 for f in ${flags:-NONE}; do
1175 "$f"|"$f "*|*" $f"|*" $f "*) : already there ;;
1179 [ "$flags_ess" -a ! "$help_ess" ] && help="$help ESS"
1180 menuitem_$u set help "${help# }"
1185 while [ $n -lt $nunique ]; do
1187 menuitem_$n get ssid ssid
1190 if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then
1191 menuitem_$n get nfound nfound
1192 desc="$nfound $msg_found"
1193 [ $nfound -lt 10 ] && desc=" $desc"
1196 menuitem_$n get help help
1197 echo "'[ ] $ssid' '$desc' '$help'"
1198 done | sort -bf | awk 'BEGIN { prefix = "" }
1200 cur_prefix = toupper(substr($0, 6, 1))
1201 if (cur_prefix != "'\''" && prefix != cur_prefix ) {
1203 printf "'\''%c%s\n", prefix, substr($0, 2)
1205 printf "'\'' %s\n", substr($0, 2)
1209 # f_dialog_menu_wireless_edit
1211 # Display a list of wireless networks configured in wpa_supplicants.conf(5) and
1212 # (if wpa_supplicant(8) is running) also displays scan results for unconfigured
1213 # wireless networks.
1215 f_dialog_menu_wireless_edit()
1217 local funcname=f_dialog_menu_wireless_edit
1218 local title="$DIALOG_TITLE"
1219 local btitle="$DIALOG_BACKTITLE"
1220 local prompt="$msg_wireless_networks_text"
1221 local menu_list # Calculated below
1222 local defaultitem= # Calculated below
1223 local hline="$hline_alnum_arrows_punc_tab_enter"
1225 f_show_info "$msg_loading_wireless_menu"
1228 conf_file=$( f_sysrc_get wpa_supplicant_conf_file )
1231 # Operate in a loop so we can edit wpa_supplicant.conf(5) and rescan
1232 # for new wireless networks from here.
1234 local do_parse=1 remake_menu=1 item
1237 # If this is the first time here, parse wpa_supplicant.conf(5),
1238 # scan the airwaves, and compare to find matches.
1240 if [ "$do_parse" -a "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ]
1242 f_dprintf "$funcname: Parsing wireless scan results"
1243 f_dialog_scan_wireless &&
1244 f_wpa_scan_results_parse WSCANS_ NWSCAN_RESULTS
1245 f_dprintf "$funcname: Parsed %i scanned networks" \
1248 if [ "$do_parse" -a "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ]
1250 f_dprintf "$funcname: Parsing wpa_supplicants.conf(5)"
1251 f_wpa_supplicant_parse "$conf_file" \
1252 WIRELESS_ NWIRELESS_CONFIGS
1253 f_dprintf "%s: Parsed %i wireless configurations" \
1254 $funcname $NWIRELESS_CONFIGS
1255 f_wpa_scan_find_matches WSCANS_ $NWSCAN_RESULTS \
1256 WIRELESS_ $NWIRELESS_CONFIGS
1260 if [ "$remake_menu" ]; then
1264 # Add both items scanned from the airwaves and networks
1265 # parsed from wpa_supplicants.conf(5). Latter items are
1266 # marked, sorted, and added to top of list above the
1267 # former (which are unmarked and sorted separately).
1269 f_dprintf "$funcname: Building menu list..."
1271 # Process wpa_supplicant.conf(5) structs
1272 f_menu_wireless_configs
1273 # Process wpa_cli(8) `scan_results' structs
1274 f_menu_wpa_scan_results
1276 f_dprintf "$funcname: menu list built."
1279 # Add static top-level menu items
1281 local XA=" " XC=" " XS=" "
1282 [ "$DIALOG_MENU_WLAN_SHOW_ALL" ] && XA="X"
1283 [ "$DIALOG_MENU_WLAN_SHOW_CONFIGURED" ] && XC="X"
1284 [ "$DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS" ] && XS="X"
1286 '> $msg_exit' '$msg_return_to_previous_menu'
1288 '> $msg_rescan_wireless' '*'
1289 '$msg_rescan_wireless_help'
1290 '> $msg_forget_all' '*'
1291 '$msg_forget_all_help'
1292 '> $msg_show_configured' '[$XC]'
1293 '$msg_show_configured_help'
1294 '> $msg_show_scan_results' '[$XS]'
1295 '$msg_show_scan_results_help'
1296 '> $msg_show_all' '[$XA]'
1297 '$msg_show_all_help'
1298 '> $msg_manually_connect' '...'
1299 '$msg_manually_connect_help'
1300 $menu_list" # END-QUOTE
1303 local height width rows
1304 eval f_dialog_menu_with_help_size height width rows \
1305 \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" \
1309 menu_choice=$( eval $DIALOG \
1310 --title \"\$title\" \
1311 --backtitle \"\$btitle\" \
1312 --hline \"\$hline\" \
1313 --ok-label \"\$msg_select\" \
1314 --cancel-label \"\$msg_cancel\" \
1316 --default-item \"\$defaultitem\" \
1317 --menu \"\$prompt\" \
1318 $height $width $rows \
1320 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1322 f_dialog_data_sanitize menu_choice
1323 defaultitem="$menu_choice"
1325 case "$menu_choice" in
1326 "> $msg_exit") break ;;
1327 "> $msg_rescan_wireless") do_parse=1 remake_menu=1 ;;
1328 "> $msg_forget_all")
1329 if f_noyes "$msg_forget_all_confirm"; then
1330 f_eval_catch $funcname rm \
1331 'rm -f "%s"' "$conf_file" || continue
1332 f_wpa_supplicant_init "$conf_file" || continue
1333 f_eval_catch -d $funcname wpa_cli \
1334 'wpa_cli reconfigure'
1335 f_wpa_supplicant_parse "$conf_file" \
1336 WIRELESS_ NWIRELESS_CONFIGS
1337 f_wpa_scan_find_matches \
1338 WSCANS_ $NWSCAN_RESULTS \
1339 WIRELESS_ $NWIRELESS_CONFIGS
1340 do_parse=1 remake_menu=1
1342 "> $msg_show_configured")
1343 item=$( eval f_dialog_menutag2item_with_help \
1344 \"\$menu_choice\" $menu_list )
1345 if [ "$item" = "[ ]" ]; then
1346 DIALOG_MENU_WLAN_SHOW_CONFIGURED=1
1348 DIALOG_MENU_WLAN_SHOW_CONFIGURED=
1351 "> $msg_show_scan_results")
1352 item=$( eval f_dialog_menutag2item_with_help \
1353 \"\$menu_choice\" $menu_list )
1354 if [ "$item" = "[ ]" ]; then
1355 DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=1
1357 DIALOG_MENU_WLAN_SHOW_SCAN_RESULTS=
1361 item=$( eval f_dialog_menutag2item_with_help \
1362 \"\$menu_choice\" $menu_list )
1363 if [ "$item" = "[ ]" ]; then
1364 DIALOG_MENU_WLAN_SHOW_ALL=1
1366 DIALOG_MENU_WLAN_SHOW_ALL=
1369 "> $msg_manually_connect")
1370 f_dialog_wireless_edit && remake_menu=1 ;;
1372 ssid="${menu_choice#??X? }"
1373 f_dialog_wireless_edit "$ssid" || continue
1374 do_parse=1 remake_menu=1 ;;
1388 ############################################################ MAIN
1390 f_dprintf "%s: Successfully loaded." media/wlan.subr
1392 fi # ! $_MEDIA_WLAN_SUBR