diff --git a/appliance_setup/appliance_setup.py b/appliance_setup/appliance_setup.py new file mode 100755 index 0000000000000000000000000000000000000000..78e724b8e41ec9841691793859e57d46c494e113 --- /dev/null +++ b/appliance_setup/appliance_setup.py @@ -0,0 +1,711 @@ +#!/usr/bin/python +## appliance_setup.py - version 0.01 +## +## Linux configuration utility for several small network appliances. +## +## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> +## License: GPL v2 or later + +## This script (appliance_setup.pl) provides a relatively easy way to configure +## several small (wireless or wired) network appliances like print servers, +## USB network servers and wireless bridges. +## +## The devices need to use the config-string based protocol described in: +## http://wiki.kainhofer.com/hardware/hp2101nw_wlan_printserver +## ftp://94.145.20.132/Public/P/print-serv100/091001/FPS-5P-M/Manual/PDF/ +## Supported devices: +## -) HP 2101nw wireless G USB print server (USB-id 03f0:cd02), via USB +## -) VAP11G wireless bridge, via Ethernet II proto 0x8888 +## +## Other devices that use the same config format, but are not yet supported: +## -) Linksys WPS54G-DE Wireless-G printserver 802.11g USB +## -) NetHawk RF-200A +## -) SMC2621W-U EZ Connect Wireless Print Server +## +## A note to the HP2101nw wireless USB print server: +## The device is not a real print server, but rather a USB port forwarder +## over TCP/UDP. On Windows it creates a virtual USB port, so for Linux +## support, we'll need a proper kernel module doing the USB port forwarding... + +import device_types +import user_interface +import helpers + +#use Data::Dumper; + +our $debug=0; + +our $VERSION=0.01; +print "appliance_setup.py - version $VERSION\n"; +print "Linux configuration utility for the HP 2101nw wireless G USB print server.\n\n"; +print "(C) 2011 Reinhold Kainhofer <reinhold\@kainhofer.com>\n"; +print "License: GPL v2 or later\n\n"; + + + +# The PC always identifies itself as XXXXXXXX. +our $thisboxname = "XXXXXXXX"; + + + +############################################################################### +## USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS +############################################################################### + + +sub device_detect () { + my $usb = Device::USB->new(); + my @devices; + my @devicedescriptions; + + foreach (@supported_devices) { + foreach my $dev ($usb->list_devices (@$_)) { + push @devices, $dev; + push @devicedescriptions, sprintf ("%s / ID %04x:%04x (%s: %s)", $dev->filename(), + $dev->idVendor(), $dev->idProduct(), + $dev->manufacturer(), $dev->product()); + } + } + my $dev; + if (scalar (@devices) > 1) { + $dev = ask_choice ("Multiple supported devices detected:", \@devicedescriptions, \@devices, $devices[0]); + } elsif (scalar (@devices) == 1) { + $dev = @devices[0]; + } + if ($dev) { + progress ("Using device: %04x:%04x (%s: %s)\n\n", + $dev->idVendor(), $dev->idProduct(), + $dev->manufacturer(), $dev->product() ); + } else { + progress "\tNo supported devices found\n\n"; + print "ERROR: No supported device was found...\n\n"; + print "Supported devices are (USB IDs): \n"; + foreach (@supported_devices) { + printf "\t%04x:%04x (%s)\n", @$_; + } + print "\n"; + print "Please connect your HP wireless USB print server to \n"; + print "the computer with the black USB cable (micro USB plug) \n"; + print "and run this script again.\n\n"; + } + return $dev; +} + +sub device_open ($) { + my $dev = shift; + my $res = $dev->open; + if ($res<0) { + printf "ERROR opening device (%d): %s\n", $res, $!; + } + $res = $dev->detach_kernel_driver_np(0); + # No error check, Linux always gives an error! + $res = $dev->set_configuration(1); + if ($res<0) { + printf "ERROR setting configuration 1 (%d): %s\n", $res, $!; + } + $res = $dev->claim_interface(0); + if ($res<0) { + printf "ERROR claiming interface 0 (%d): %s\n", $res, $!; + } +} + +sub device_close ($) { + my $dev = shift; + $dev->release_interface(0); + $dev->reset; +} + + + +sub bulk_data_write ($$$) { + my $dev = shift; + my $data = shift; + my $len = shift; + # Add the header (12 bytes): '@\0', packet length, boxname (="XXXXXXXX") + my $request = sprintf ("@\0%s%s%s", pack ("v", $len), $thisboxname, $data); + debug "Request: $request\n"; + my $res = $dev->bulk_write (1, $request, length($request), 500); + if ($res<0) { + printf "ERROR write bulk data (%d): %s\n", $res, $!; + } + return $res; +} + +# Read USB bulk data into the second argument (output argument!), return +# size of data +sub bulk_data_read ($$$) { + my $dev = shift; + # TODO: Get rid of the length argument, simply read all that is sent + # FIXME: Read bulk data in loops until there is nothing left to read? + my $len = $_[1]; + my $readdata = ""; + my $res = $dev->bulk_read (2, $readdata, $len+13, 500); + if ($res<0) { + printf "ERROR reading bulk data (%d): %s\n", $res, $!; + return; + } + if ($res == 0) { + debug "\tEmpty response received\n"; + return 0; + } + debug ("read %d bytes: \n%s\n\n", $res, $readdata); + # Check and cut off the header after some sanity checks: + if (substr ($readdata, 0, 3) ne "\@\0\0") { + printf "ERROR reading data: Wrong header %s\n", substr ($readdata, 0, 3); + } + my $datalen = unpack ("v", substr ($readdata, 3, 2)); + $res = $res-13; + if ($datalen != $res) { + printf "ERROR reading data: Expected %d bytes of data, got %d\n", $datalen, $res; + } + my $boxname = substr ($readdata, 5, 8); + # FIXME: Check the name of the box... + + # cut off the header: + $_[0]=substr ($readdata, 13); + return $res; +} + +sub device_reset ($) { + my $dev = shift; + progress "Resetting the device, please wait"; + my $res = bulk_data_write ($dev, "9002:\n", 6); + debug "\tSent 9002 reset command: %d\n", $res; + my $reset_done = 0; + # Poll the device until it doesn't respond. Then sleep for ~15 seconds + do { + my $readdata = ""; + $res = $dev->bulk_read (2, $readdata, 13, 100); + debug "\tData received: $readdata (len: $res)\n"; + sleep (1); + } while ($res > 0); + sleep_progress (20); + device_close ($dev); + progress "Device was reset. Please press Return."; + <STDIN>; +} + + +############################################################################### +## DEVICE CONFIGURATION HANDLING +############################################################################### + + +sub print_current_configuration ($$) { + my $config = shift; + my $text = shift; + sub val ($$) { + my $config = shift; + my $var = shift; + foreach my $e (@$config) { + return $e->[2] if ($e->[0] eq $var); + } + return undef; + } + progress $text; + printf "\tDevice name: %s\n", val($config, '0001'); + if (val($config,'0012') ne "Enable") { + printf "\tTCP/IP not yet configured and/or enabled.\n\n"; + return; + } + my $adhoc = val($config,'7024') == '1'; + if ($adhoc) { + # ad-hoc network: + my $sec = val($config,'7103'); + printf "\tWireless mode: Ad-hoc network\n"; + printf "\tSSID: %s\n", val($config,'7100'); + printf "\tChannel: %s\n", val($config,'7102'); + printf "\tSecurity: %s\n", security2string ($sec); + if ($sec == 0) { + # None => nothing to display + } elsif ($sec == 1) { + # WEP => Print passphrase + printf "\tWEP Key: %s\n", val($config,'7106'); + } else { + # WPA 1/2 or mixed + printf "\tEncryption: %s\n", encryption2string(val($config,'7118')); + printf "\tPassphrase: %s\n", val($config,'7119'); + } + } else { + # infrastructure network + my $sec = val($config,'7003'); + printf "\tSSID: %s\n", val($config,'7000'); + printf "\tChannel: %s\n", val($config,'7002'); + printf "\tSecurity: %s\n", security2string ($sec); + if ($sec == 0) { + # None => nothing to display + } elsif ($sec == 1) { + # WEP => Print passphrase + printf "\tAuthentication: %s\n", authentication2string(val($config,'7012')); + printf "\tWEP Key: %s\n", val($config,'7006'); + } else { + # WPA 1/2 or mixed + printf "\tEncryption: %s\n", encryption2string(val($config,'7018')); + printf "\tPassphrase: %s\n", val($config,'7019'); + } + } + + my $dhcp = (val($config,'4020') eq "Enable"); + printf "\tIPv4 method: %s\n", $dhcp?"DHCP":"manual"; + my $associated = val($config,'7014') =~ m/STATE:Associated/; + if ($associated || !$dhcp) { + printf "\tIP address: %s\n", val($config,'4000'); + printf "\tGateway: %s\n", val($config,'4001'); + printf "\tNetmask: %s\n", val($config,'4002'); + } + if ($associated) { + my $assinfo = val($config,'7014'); + printf "\tLink state: %s\n", $assinfo; + } else { + # Not connected + printf "\tWireless not connected\n"; + } + print "\n\n"; +} + + +sub device_config_request ($) { + my $dev = shift; + my $cfg = ""; + debug "Reading current configuration...\n"; + my $res = bulk_data_write ($dev, "9100:\n", 6); + $res = bulk_data_read ($dev, $cfg, 25600); + return $cfg; +} + +sub device_config_send ($$) { + my $dev = shift; + my $cfg = shift; + my $tmp = ""; + progress "Sending new configuation to the device. Please wait"; + debug "\tNew device config: %s\n", $cfg; + my $res = bulk_data_write ($dev, $cfg, length($cfg)); + sleep_progress (10); + # Now reset the device: + return device_reset ($dev); +} + +sub config_parse ($) { + my $cfg = shift; + my @entries = split ('\n', $cfg); + my @config = (); + foreach (@entries) { + my $key = substr ($_, 0, 4); + (my $name, my $value) = split (/:/, substr ($_, 5), 2); + push (@config, [$key, $name, $value]); + } + return @config; +} + +# Convert a list of the form [ ["OPTIONID", "OPTIONNAME", VALUE], ...] +# into a string to be sent to the device +sub config_create ($) { + my $cfg = shift; + my $result = ""; + foreach my $o (@$cfg) { + $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]); + } + return $result; +} + +sub set_hostname ($) { + my $dev = shift; + my $hostname = ask_input ("New hostname: ", \&is_hostname, + "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters\n");; + my $config = config_create ([["0001", "BOX_NAME", $hostname]]); + debug "\tChanging hostname using config\n\t%s\n", $config; + return device_config_send ($dev, $config); +} + +sub device_config_save ($$) { + my $filename = shift; + my $config = shift; + my $status = open FILE, ">$filename"; + if (!$status) { + printf "ERROR: %s\n", $!; + return; + } + print FILE $config; + progress "Current print server configuration saved to file '%s'\n", $filename; +} + +sub device_config_restore ($$) { + my $dev = shift; + my $filename = shift; + my $status = open FILE, "<$filename"; + if (!$status) { + printf "ERROR: %s\n", $!; + return; + } + my $config = do { local $/; <FILE> }; + debug "Configuration file '%s' contains the data:\n", $filename, $config; + progress "Loaded configuration data from file '%s', sending it to the device.\n"; + return device_config_send ($dev, $config); +} + + + + + + +############################################################################### +## WIRELESS NETWORK DETECTION +############################################################################### + + +sub parse_survey_data ($) { + my $survey = shift; + my $networks = {}; + my @entries = split ("\x0b", $survey); + foreach (@entries) { + (my $ssid, my $settings) = split ("\x0c", $_); + my @settings = split (",", $settings); + $networks->{$ssid} = { + 'mac' => $settings[0], + 'channel' => $settings[1], + 'wifi_ver' => $settings[2], + 'secmode' => $settings[3], + 'signal' => $settings[4], + 'adhoc' => $settings[5] + }; + } + debug Dumper ($networks); + return $networks; +} + +sub scan_for_aps ($) { + my $dev = shift; + progress "Scanning for access points: "; + my $loop = 0; + my $found = 0; + my $survey; + while (!$found && $loop <= 4) { + ++$loop; + debug ("loop=", $loop, "\n"); + my $res = bulk_data_write ($dev, "9107:\n", 6); + $res = bulk_data_read ($dev, my $d="", 0); + sleep_progress (6); + my $cfg = ""; + $res = bulk_data_write ($dev, "9100:\n", 6); + $res = bulk_data_read ($dev, $cfg, 25600); + my @config = config_parse ($cfg); + $survey = val(\@config, '7021'); + $found = (defined ($survey) && length ($survey) > 0); + } + progress "\n"; + if ($found) { + debug "Found wireless networks: \n", $survey; + } else { + print "ERROR: No wireless networks detected.\n"; + } + return parse_survey_data ($survey); +} + +sub print_wireless_networks ($) { + my $aps = shift; + print "Detected wireless networks:\n"; + my $format = " %3s %-25s%-9s%-13s%-17s%-8s\n"; + printf $format, " ", "SSID", "Signal", "Security", "Type", "channel"; + print "\t------------------------------------------------------------------------\n"; + my $i = 0; + foreach my $ssid (sort {lc $a cmp lc $b} keys (%$aps)) { + ++$i; + my $network = $aps->{$ssid}; + printf $format, $i.")", $ssid, $network->{'signal'}, + security2string ($network->{'secmode'}), + $network->{'adhoc'}?"Ad-Hoc":"Infrastructure", + $network->{'channel'}; + } + print "\n"; +} + + + +############################################################################### +## INFRASTRUCTURE WIRELESS SETUP (i.e. wlan through access point / router) +############################################################################### + + +sub wifi_ask_ssid ($) { + my $dev = shift; + my $aps = scan_for_aps ($dev); + print_wireless_networks ($aps); + my @ssids = (sort {lc $a cmp lc $b} keys (%$aps)); + my $newssid; + print "Please enter the number or the SSID of the desired wireless network.\n"; + print "\tEnter '0', 'hidden' or 'h' to connect to a hidden network.\n"; + print "\tEnter 'r' or 'rescan' to rescan for wireless networks.\n"; + do { + $newssid = input ("Desired wireless network: "); + return $newssid if (exists $aps->{$newssid}); + if ($newssid =~ /^[1-9]\d*$/ && ($newssid <= scalar(@ssids))) { + return ($aps,$ssids[$newssid-1]); + } + if ($newssid eq "0" || $newssid eq "h" || $newssid eq "hidden") { + $newssid = ""; + while (length($newssid) < 1) { + $newssid = input ("Please enter the SSID: "); + } + return ($aps,$newssid); + } + if ($newssid eq "r" || $newssid eq "rescan") { + return wifi_ask_ssid ($dev); + } + } while 1; # We'll jump out of the loop via return! +} + +sub wifi_ask_address ($$) { + my $ap = shift; + my $aps = shift; + my $dhcp = ask_choice ("IP-address assignment:", ["DHCP", "Manual"], ["Enable", "Disable"], "Enable"); + my %result = ("dhcp" => $dhcp); + if ($dhcp ne "Enable") { + $result{'ip_addr'} = ask_ip_address ("IP-address of the device:\t"); + $result{'mask'} = ask_ip_address ("Netmask:\t"); + $result{'gateway'} = ask_ip_address ("IP-address of the gateway:\t"); + } else { + $result{'ip_addr'} = "0.0.0.0"; + $result{'mask'} = "0.0.0.0"; + $result{'gateway'} = "0.0.0.0"; + } + return %result; +} + +sub wifi_ask_security ($$) { + my $ap = shift; + my $aps = shift; + my %result = (); + my $sec = 0; + if (defined $aps->{$ap}) { + $sec = $aps->{$ap}->{'secmode'}; + $result{'channel'} = $aps->{$ap}->{'channel'}; + } + $sec = ask_choice ("Security: ", ["None", -1, "WEP", "WPA-PSK", "WPA2-PSK", "WPA/WPA2-PSK"], ['0', '1', '2', '4', '6'], $sec); + $result{'secmode'} = $sec; + + if ($sec == 0) { + # No encryption, nothing to ask the user + } elsif ($sec == 1) { + # WEP + progress "Using WEP security\n"; + $result{'authen'} = ask_choice ("Type of authentication:", ["Open System","Shared Key"], ['1','2'], '1'); + $result{'key'} = ask_wep_key ($ap); + $result{'key_len'} = length ($result{'key'})/2; + } elsif ($sec == 2 || $sec == 4 || $sec == 6 || $sec == 7) { + # WPA1 / WPA2 + progress "Using %s security\n", security2string($sec); + $result{'pskalset'} = ask_choice ("Type of encryption:", ["TKIP", "AES"], ['0','1'], '0'); + $result{'pskkey'} = ask_input ("Please enter the WPA passphrase for network '$ap': ", + \&is_wpa_passphrase, "\tEnter a passprase with 1-63 characters!\n"); + } else { + printf "ERROR: security type %s not supported\n", security2string($sec); + return wifi_ask_security ($ap, $aps); + } + return %result; +} + +sub wifi_config_create ($) { + my $newconfig = shift; + my $config = [ + ["4000", "IP_ADDR", $newconfig->{'ip_addr'}], # IP-address + ["4001", "GATEWAY", $newconfig->{'gateway'}], + ["4002", "MASK ", $newconfig->{'mask'}], + ["4020", "DHCP_MODE", $newconfig->{'dhcp'}], + ["4021", "", $newconfig->{'dhcp'}], # Unknown, == 4020 + ["4022", "", $newconfig->{'dhcp'}], # Unknown, == 4020 + ["5000", "", ""], # Unknown, always empty + ["5003", "", ""], # Unknown, always empty + ["5101", "", ""], # Unknown, always empty + ["5102", "", ""], # Unknown, always empty + ["5104", "", ""], # Unknown, always empty + ["5103", "", ""], # Unknown, always empty + ["7000", "SSID", $newconfig->{'SSID'}], + ["7001", "DOMAIN", ""], + ["7002", "CHANNEL", $newconfig->{'channel'}], + ["7003", "SECMODE", $newconfig->{'secmode'}], + ["7004", "KEY_LEN", $newconfig->{'key_len'}], + ["7005", "DEFAULTKEY","0"], + ["7006", "KEY0", $newconfig->{'key'}], + ["7007", "KEY1", ""], + ["7008", "KEY2", ""], + ["7009", "KEY3", ""], + ["7012", "AUTHEN", $newconfig->{'authen'}], + ["7013", "MODE", "0"], + ["7018", "PSKALSET", $newconfig->{'pskalset'}], + ["7019", "PSKKEY", $newconfig->{'pskkey'}], + ["7024", "STAMODE", "0"], + ["7025", "APCHANNEL", "5"], + ["7026", "CHMODE", "1"], + ["7030", "WLMODE", "0"], + ]; + return config_create ($config); +} + + + +############################################################################### +## AD-HOC WIRELESS SETUP (i.e. direct connection with print server and PC) +############################################################################### + + +sub adhoc_send_unknown_command ($) { + my $dev = shift; + bulk_data_write ($dev, "9971:\n", 6); + my $val = ""; + bulk_data_read ($dev, $val, 1); + debug "\tResponse to 9971 Ad-hoc setting command (unknown purpose): %s\n", $val; + return $val; +} + +sub adhoc_ask_ssid ($) { + my $dev = shift; + my $ssid = ""; + print "\n"; + do { + $ssid = input ("SSID of the ad-hoc wireless connection:"); + if (length ($ssid) > 0) { + # scan for wireless networks to check for name clashes + my $aps = scan_for_aps ($dev); + if (defined ($aps->{$ssid})) { + print "\tERROR: wireless network '$ssid' already exists, please choose a different name.\n\n"; + } else { + return $ssid; + } + } + } while 1; # We'll jump out of the loop via return! +} + +def adhoc_ask_channel (ap): + channel = False + while (!channel): + c = input ("Wireless channel for network '%s' [auto or 0-13]: " % ap); + if (c == "auto"): + channel = "6" # FIXME + elif (re.match ('^([0-9]|1[0-3])$', c)): + channel = c + debug ("\tNew channel: %s\n" % channel) + return channel + +sub adhoc_config_create ($) { + my $newconfig = shift; + my $config = [ + ["7024", "STAMODE", "1"], + ["7025", "APCHANNEL", $newconfig->{'channel'}], + ["7026", "CHMODE", "1"], # FIXME: Shall we change this? + ["7030", "WLMODE", "1"], # FIXME: Shall we change this? + ["7100", "APSSID", $newconfig->{'SSID'}], + ["7103", "APSECMODE", $newconfig->{'secmode'}], + ["7104", "APKEY_LEN", $newconfig->{'key_len'}], + ["7105", "APDEFAULTKE","0"], + ["7106", "APKEY0", $newconfig->{'key'}], + ["7107", "APKEY1", ""], + ["7108", "APKEY2", ""], + ["7109", "APKEY3", ""], + ["7112", "APAUTHEN", $newconfig->{'authen'}], + ["7118", "APPSKALSET", $newconfig->{'pskalset'}], + ["7119", "APPSKKEY", $newconfig->{'pskkey'}] + ]; + return config_create ($config); +} + + + +############################################################################### +## MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT +############################################################################### + + +sub ask_operation () { + return ask_choice ( + "What do you want to do?", + ["Setup a wireless connection through a wifi router or access point ('infrastructure')", + "Setup a direct wireless connection with the printer ('ad-hoc')", + -1, + "Change the name of the device", + -1, + "Save the device configuration to a file", + "Restore the device configuration from a file", + -1, + "Reload the configuration", + "Exit" + ], + ["wifi_infrastructure", + "wifi_adhoc", + "change_hostname", + "config_save", + "config_restore", + "reload", + "exit" + ], + "exit"); +} + +sub main () { + my $reconfigured = 0; + my $dev = device_detect () or exit; + device_open ($dev); + my $cfg = device_config_request ($dev); + my @config = config_parse ($cfg); + print_current_configuration (\@config, "Current configuration of the device:\n"); + # Ask what to do (set wifi AP, ad-hoc, save, restore) + my $operation = ask_operation (); + debug ("\tSelected operation: %s\n", $operation); + if ($operation eq "wifi_infrastructure") { + # Infrastructure, scan for APs + my ($aps,$newap) = wifi_ask_ssid ($dev); + my %newconfig = ('SSID' => $newap); + debug ("\tSelected wireless network: %s\n", $newap); + my %security = wifi_ask_security ($newap, $aps); + my %ip = wifi_ask_address ($newap, $aps); + %newconfig = (%newconfig, %security, %ip); + my $newcfg = wifi_config_create (\%newconfig); + device_config_send ($dev, $newcfg); + $reconfigured = 1; + } elsif ($operation eq "wifi_adhoc") { + # Ad-hoc connection to print server + my $adhocssid = adhoc_ask_ssid ($dev); + adhoc_send_unknown_command ($dev); + my $channel = adhoc_ask_channel ($dev, $adhocssid); + my %newconfig = ('SSID' => $adhocssid, + 'channel' => $channel, + adhoc_ask_security ($adhocssid) + ); + my $newcfg = adhoc_config_create (\%newconfig); + device_config_send ($dev, $newcfg); + $reconfigured = 1; + } elsif ($operation eq "change_hostname") { + set_hostname ($dev); + $reconfigured = 1; + } elsif ($operation eq "config_save") { + my $filename = ask_input ("Filename to save to: ", \&is_filename, ""); + device_config_save ($filename, $cfg); + } elsif ($operation eq "config_restore") { + my $filename = ask_input ("Filename to load from: ", \&is_filename, ""); + device_config_restore ($dev, $filename); + } elsif ($operation eq "reload") { + # do nothing, we'll call main below... + } elsif ($operation eq "exit") { + device_close ($dev); + exit; + } else { + printf "ERROR: unknown operation %s\n", $operation; + } + device_close ($dev); + # if the configuration was changed, print the new config after the device was reset + $dev = device_detect () or exit; + device_open ($dev); + @config = config_parse (device_config_request ($dev)); + print_current_configuration (\@config, "New configuration of the device:\n"); + + # Loop in main if "reload" is selected... + main () if ($operation eq "reload"); +} + + + + +############################################################################### +## MAIN FUNCTION +############################################################################### + +main (); diff --git a/appliance_setup/configuration.py b/appliance_setup/configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..ab58c0d388792bcd4931d8c90eb079da4fbcca95 --- /dev/null +++ b/appliance_setup/configuration.py @@ -0,0 +1,510 @@ +#!/usr/bin/python +## configuration.py - version 0.01 +## +## Linux configuration utility for several small network appliances. +## +## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> +## License: GPL v2 or later + + +class Config: + self.cfgdata = []; + self.cfgdesc = []; + def __init__ (self, ui): + self.ui = ui + + def val (self, line): + return self.cfgdata.get (line, None) + + def print_configuration (self): + # TODO: ui.progress (text); + ui.print ("\tDevice name: %(0001)s\n" % self.cfgdata); + if (val('0012') != "Enable"): + ui.print "\tTCP/IP not yet configured and/or enabled.\n\n"; + return + adhoc = (val('7024') == '1'); + if adhoc: + # ad-hoc network: + my $sec = val($config,'7103'); + printf "\tWireless mode: Ad-hoc network\n"; + printf "\tSSID: %s\n", val($config,'7100'); + printf "\tChannel: %s\n", val($config,'7102'); + printf "\tSecurity: %s\n", security2string ($sec); + if ($sec == 0) { + # None => nothing to display + } elsif ($sec == 1) { + # WEP => Print passphrase + printf "\tWEP Key: %s\n", val($config,'7106'); + } else { + # WPA 1/2 or mixed + printf "\tEncryption: %s\n", encryption2string(val($config,'7118')); + printf "\tPassphrase: %s\n", val($config,'7119'); + } + } else { + # infrastructure network + my $sec = val($config,'7003'); + printf "\tSSID: %s\n", val($config,'7000'); + printf "\tChannel: %s\n", val($config,'7002'); + printf "\tSecurity: %s\n", security2string ($sec); + if ($sec == 0) { + # None => nothing to display + } elsif ($sec == 1) { + # WEP => Print passphrase + printf "\tAuthentication: %s\n", authentication2string(val($config,'7012')); + printf "\tWEP Key: %s\n", val($config,'7006'); + } else { + # WPA 1/2 or mixed + printf "\tEncryption: %s\n", encryption2string(val($config,'7018')); + printf "\tPassphrase: %s\n", val($config,'7019'); + } + } + + my $dhcp = (val($config,'4020') eq "Enable"); + printf "\tIPv4 method: %s\n", $dhcp?"DHCP":"manual"; + my $associated = val($config,'7014') =~ m/STATE:Associated/; + if ($associated || !$dhcp) { + printf "\tIP address: %s\n", val($config,'4000'); + printf "\tGateway: %s\n", val($config,'4001'); + printf "\tNetmask: %s\n", val($config,'4002'); + } + if ($associated) { + my $assinfo = val($config,'7014'); + printf "\tLink state: %s\n", $assinfo; + } else { + # Not connected + printf "\tWireless not connected\n"; + } + print "\n\n"; + + pass + + +############################################################################### +## DEVICE CONFIGURATION HANDLING +############################################################################### + + +sub config_parse ($) { + my $cfg = shift; + my @entries = split ('\n', $cfg); + my @config = (); + foreach (@entries) { + my $key = substr ($_, 0, 4); + (my $name, my $value) = split (/:/, substr ($_, 5), 2); + push (@config, [$key, $name, $value]); + } + return @config; +} + +# Convert a list of the form [ ["OPTIONID", "OPTIONNAME", VALUE], ...] +# into a string to be sent to the device +sub config_create ($) { + my $cfg = shift; + my $result = ""; + foreach my $o (@$cfg) { + $result .= sprintf ("%s %s:%s\n", $o->[0], $o->[1], $o->[2]); + } + return $result; +} + +sub set_hostname ($) { + my $dev = shift; + my $hostname = ask_input ("New hostname: ", \&is_hostname, + "\tA hostname can only contain A-Z, a-z, 0-9 and -, and may have 1 to 64 characters\n");; + my $config = config_create ([["0001", "BOX_NAME", $hostname]]); + debug "\tChanging hostname using config\n\t%s\n", $config; + return device_config_send ($dev, $config); +} + +sub device_config_save ($$) { + my $filename = shift; + my $config = shift; + my $status = open FILE, ">$filename"; + if (!$status) { + printf "ERROR: %s\n", $!; + return; + } + print FILE $config; + progress "Current print server configuration saved to file '%s'\n", $filename; +} + +sub device_config_restore ($$) { + my $dev = shift; + my $filename = shift; + my $status = open FILE, "<$filename"; + if (!$status) { + printf "ERROR: %s\n", $!; + return; + } + my $config = do { local $/; <FILE> }; + debug "Configuration file '%s' contains the data:\n", $filename, $config; + progress "Loaded configuration data from file '%s', sending it to the device.\n"; + return device_config_send ($dev, $config); +} + + + + + + +############################################################################### +## WIRELESS NETWORK DETECTION +############################################################################### + + +sub parse_survey_data ($) { + my $survey = shift; + my $networks = {}; + my @entries = split ("\x0b", $survey); + foreach (@entries) { + (my $ssid, my $settings) = split ("\x0c", $_); + my @settings = split (",", $settings); + $networks->{$ssid} = { + 'mac' => $settings[0], + 'channel' => $settings[1], + 'wifi_ver' => $settings[2], + 'secmode' => $settings[3], + 'signal' => $settings[4], + 'adhoc' => $settings[5] + }; + } + debug Dumper ($networks); + return $networks; +} + +sub scan_for_aps ($) { + my $dev = shift; + progress "Scanning for access points: "; + my $loop = 0; + my $found = 0; + my $survey; + while (!$found && $loop <= 4) { + ++$loop; + debug ("loop=", $loop, "\n"); + my $res = bulk_data_write ($dev, "9107:\n", 6); + $res = bulk_data_read ($dev, my $d="", 0); + sleep_progress (6); + my $cfg = ""; + $res = bulk_data_write ($dev, "9100:\n", 6); + $res = bulk_data_read ($dev, $cfg, 25600); + my @config = config_parse ($cfg); + $survey = val(\@config, '7021'); + $found = (defined ($survey) && length ($survey) > 0); + } + progress "\n"; + if ($found) { + debug "Found wireless networks: \n", $survey; + } else { + print "ERROR: No wireless networks detected.\n"; + } + return parse_survey_data ($survey); +} + +sub print_wireless_networks ($) { + my $aps = shift; + print "Detected wireless networks:\n"; + my $format = " %3s %-25s%-9s%-13s%-17s%-8s\n"; + printf $format, " ", "SSID", "Signal", "Security", "Type", "channel"; + print "\t------------------------------------------------------------------------\n"; + my $i = 0; + foreach my $ssid (sort {lc $a cmp lc $b} keys (%$aps)) { + ++$i; + my $network = $aps->{$ssid}; + printf $format, $i.")", $ssid, $network->{'signal'}, + security2string ($network->{'secmode'}), + $network->{'adhoc'}?"Ad-Hoc":"Infrastructure", + $network->{'channel'}; + } + print "\n"; +} + + + +############################################################################### +## INFRASTRUCTURE WIRELESS SETUP (i.e. wlan through access point / router) +############################################################################### + + +sub wifi_ask_ssid ($) { + my $dev = shift; + my $aps = scan_for_aps ($dev); + print_wireless_networks ($aps); + my @ssids = (sort {lc $a cmp lc $b} keys (%$aps)); + my $newssid; + print "Please enter the number or the SSID of the desired wireless network.\n"; + print "\tEnter '0', 'hidden' or 'h' to connect to a hidden network.\n"; + print "\tEnter 'r' or 'rescan' to rescan for wireless networks.\n"; + do { + $newssid = input ("Desired wireless network: "); + return $newssid if (exists $aps->{$newssid}); + if ($newssid =~ /^[1-9]\d*$/ && ($newssid <= scalar(@ssids))) { + return ($aps,$ssids[$newssid-1]); + } + if ($newssid eq "0" || $newssid eq "h" || $newssid eq "hidden") { + $newssid = ""; + while (length($newssid) < 1) { + $newssid = input ("Please enter the SSID: "); + } + return ($aps,$newssid); + } + if ($newssid eq "r" || $newssid eq "rescan") { + return wifi_ask_ssid ($dev); + } + } while 1; # We'll jump out of the loop via return! +} + +sub wifi_ask_address ($$) { + my $ap = shift; + my $aps = shift; + my $dhcp = ask_choice ("IP-address assignment:", ["DHCP", "Manual"], ["Enable", "Disable"], "Enable"); + my %result = ("dhcp" => $dhcp); + if ($dhcp ne "Enable") { + $result{'ip_addr'} = ask_ip_address ("IP-address of the device:\t"); + $result{'mask'} = ask_ip_address ("Netmask:\t"); + $result{'gateway'} = ask_ip_address ("IP-address of the gateway:\t"); + } else { + $result{'ip_addr'} = "0.0.0.0"; + $result{'mask'} = "0.0.0.0"; + $result{'gateway'} = "0.0.0.0"; + } + return %result; +} + +sub wifi_ask_security ($$) { + my $ap = shift; + my $aps = shift; + my %result = (); + my $sec = 0; + if (defined $aps->{$ap}) { + $sec = $aps->{$ap}->{'secmode'}; + $result{'channel'} = $aps->{$ap}->{'channel'}; + } + $sec = ask_choice ("Security: ", ["None", -1, "WEP", "WPA-PSK", "WPA2-PSK", "WPA/WPA2-PSK"], ['0', '1', '2', '4', '6'], $sec); + $result{'secmode'} = $sec; + + if ($sec == 0) { + # No encryption, nothing to ask the user + } elsif ($sec == 1) { + # WEP + progress "Using WEP security\n"; + $result{'authen'} = ask_choice ("Type of authentication:", ["Open System","Shared Key"], ['1','2'], '1'); + $result{'key'} = ask_wep_key ($ap); + $result{'key_len'} = length ($result{'key'})/2; + } elsif ($sec == 2 || $sec == 4 || $sec == 6 || $sec == 7) { + # WPA1 / WPA2 + progress "Using %s security\n", security2string($sec); + $result{'pskalset'} = ask_choice ("Type of encryption:", ["TKIP", "AES"], ['0','1'], '0'); + $result{'pskkey'} = ask_input ("Please enter the WPA passphrase for network '$ap': ", + \&is_wpa_passphrase, "\tEnter a passprase with 1-63 characters!\n"); + } else { + printf "ERROR: security type %s not supported\n", security2string($sec); + return wifi_ask_security ($ap, $aps); + } + return %result; +} + +sub wifi_config_create ($) { + my $newconfig = shift; + my $config = [ + ["4000", "IP_ADDR", $newconfig->{'ip_addr'}], # IP-address + ["4001", "GATEWAY", $newconfig->{'gateway'}], + ["4002", "MASK ", $newconfig->{'mask'}], + ["4020", "DHCP_MODE", $newconfig->{'dhcp'}], + ["4021", "", $newconfig->{'dhcp'}], # Unknown, == 4020 + ["4022", "", $newconfig->{'dhcp'}], # Unknown, == 4020 + ["5000", "", ""], # Unknown, always empty + ["5003", "", ""], # Unknown, always empty + ["5101", "", ""], # Unknown, always empty + ["5102", "", ""], # Unknown, always empty + ["5104", "", ""], # Unknown, always empty + ["5103", "", ""], # Unknown, always empty + ["7000", "SSID", $newconfig->{'SSID'}], + ["7001", "DOMAIN", ""], + ["7002", "CHANNEL", $newconfig->{'channel'}], + ["7003", "SECMODE", $newconfig->{'secmode'}], + ["7004", "KEY_LEN", $newconfig->{'key_len'}], + ["7005", "DEFAULTKEY","0"], + ["7006", "KEY0", $newconfig->{'key'}], + ["7007", "KEY1", ""], + ["7008", "KEY2", ""], + ["7009", "KEY3", ""], + ["7012", "AUTHEN", $newconfig->{'authen'}], + ["7013", "MODE", "0"], + ["7018", "PSKALSET", $newconfig->{'pskalset'}], + ["7019", "PSKKEY", $newconfig->{'pskkey'}], + ["7024", "STAMODE", "0"], + ["7025", "APCHANNEL", "5"], + ["7026", "CHMODE", "1"], + ["7030", "WLMODE", "0"], + ]; + return config_create ($config); +} + + + +############################################################################### +## AD-HOC WIRELESS SETUP (i.e. direct connection with print server and PC) +############################################################################### + + +sub adhoc_send_unknown_command ($) { + my $dev = shift; + bulk_data_write ($dev, "9971:\n", 6); + my $val = ""; + bulk_data_read ($dev, $val, 1); + debug "\tResponse to 9971 Ad-hoc setting command (unknown purpose): %s\n", $val; + return $val; +} + +sub adhoc_ask_ssid ($) { + my $dev = shift; + my $ssid = ""; + print "\n"; + do { + $ssid = input ("SSID of the ad-hoc wireless connection:"); + if (length ($ssid) > 0) { + # scan for wireless networks to check for name clashes + my $aps = scan_for_aps ($dev); + if (defined ($aps->{$ssid})) { + print "\tERROR: wireless network '$ssid' already exists, please choose a different name.\n\n"; + } else { + return $ssid; + } + } + } while 1; # We'll jump out of the loop via return! +} + +def adhoc_ask_channel (ap): + channel = False + while (!channel): + c = input ("Wireless channel for network '%s' [auto or 0-13]: " % ap); + if (c == "auto"): + channel = "6" # FIXME + elif (re.match ('^([0-9]|1[0-3])$', c)): + channel = c + debug ("\tNew channel: %s\n" % channel) + return channel + +sub adhoc_config_create ($) { + my $newconfig = shift; + my $config = [ + ["7024", "STAMODE", "1"], + ["7025", "APCHANNEL", $newconfig->{'channel'}], + ["7026", "CHMODE", "1"], # FIXME: Shall we change this? + ["7030", "WLMODE", "1"], # FIXME: Shall we change this? + ["7100", "APSSID", $newconfig->{'SSID'}], + ["7103", "APSECMODE", $newconfig->{'secmode'}], + ["7104", "APKEY_LEN", $newconfig->{'key_len'}], + ["7105", "APDEFAULTKE","0"], + ["7106", "APKEY0", $newconfig->{'key'}], + ["7107", "APKEY1", ""], + ["7108", "APKEY2", ""], + ["7109", "APKEY3", ""], + ["7112", "APAUTHEN", $newconfig->{'authen'}], + ["7118", "APPSKALSET", $newconfig->{'pskalset'}], + ["7119", "APPSKKEY", $newconfig->{'pskkey'}] + ]; + return config_create ($config); +} + + + +############################################################################### +## MAIN OPERATIONS: ASK FOR OPERATION AND HANDLE IT +############################################################################### + + +sub ask_operation () { + return ask_choice ( + "What do you want to do?", + ["Setup a wireless connection through a wifi router or access point ('infrastructure')", + "Setup a direct wireless connection with the printer ('ad-hoc')", + -1, + "Change the name of the device", + -1, + "Save the device configuration to a file", + "Restore the device configuration from a file", + -1, + "Reload the configuration", + "Exit" + ], + ["wifi_infrastructure", + "wifi_adhoc", + "change_hostname", + "config_save", + "config_restore", + "reload", + "exit" + ], + "exit"); +} + +sub main () { + my $reconfigured = 0; + my $dev = device_detect () or exit; + device_open ($dev); + my $cfg = device_config_request ($dev); + my @config = config_parse ($cfg); + print_current_configuration (\@config, "Current configuration of the device:\n"); + # Ask what to do (set wifi AP, ad-hoc, save, restore) + my $operation = ask_operation (); + debug ("\tSelected operation: %s\n", $operation); + if ($operation eq "wifi_infrastructure") { + # Infrastructure, scan for APs + my ($aps,$newap) = wifi_ask_ssid ($dev); + my %newconfig = ('SSID' => $newap); + debug ("\tSelected wireless network: %s\n", $newap); + my %security = wifi_ask_security ($newap, $aps); + my %ip = wifi_ask_address ($newap, $aps); + %newconfig = (%newconfig, %security, %ip); + my $newcfg = wifi_config_create (\%newconfig); + device_config_send ($dev, $newcfg); + $reconfigured = 1; + } elsif ($operation eq "wifi_adhoc") { + # Ad-hoc connection to print server + my $adhocssid = adhoc_ask_ssid ($dev); + adhoc_send_unknown_command ($dev); + my $channel = adhoc_ask_channel ($dev, $adhocssid); + my %newconfig = ('SSID' => $adhocssid, + 'channel' => $channel, + adhoc_ask_security ($adhocssid) + ); + my $newcfg = adhoc_config_create (\%newconfig); + device_config_send ($dev, $newcfg); + $reconfigured = 1; + } elsif ($operation eq "change_hostname") { + set_hostname ($dev); + $reconfigured = 1; + } elsif ($operation eq "config_save") { + my $filename = ask_input ("Filename to save to: ", \&is_filename, ""); + device_config_save ($filename, $cfg); + } elsif ($operation eq "config_restore") { + my $filename = ask_input ("Filename to load from: ", \&is_filename, ""); + device_config_restore ($dev, $filename); + } elsif ($operation eq "reload") { + # do nothing, we'll call main below... + } elsif ($operation eq "exit") { + device_close ($dev); + exit; + } else { + printf "ERROR: unknown operation %s\n", $operation; + } + device_close ($dev); + # if the configuration was changed, print the new config after the device was reset + $dev = device_detect () or exit; + device_open ($dev); + @config = config_parse (device_config_request ($dev)); + print_current_configuration (\@config, "New configuration of the device:\n"); + + # Loop in main if "reload" is selected... + main () if ($operation eq "reload"); +} + + + + +############################################################################### +## MAIN FUNCTION +############################################################################### + +main (); diff --git a/appliance_setup/device.py b/appliance_setup/device.py new file mode 100644 index 0000000000000000000000000000000000000000..18fd6f393aea768d9726e74750db30b2c8aaa3f7 --- /dev/null +++ b/appliance_setup/device.py @@ -0,0 +1,2 @@ +class Device: + def __init__ ( \ No newline at end of file diff --git a/appliance_setup/device_types.py b/appliance_setup/device_types.py new file mode 100755 index 0000000000000000000000000000000000000000..2d443560682a5e9c592a51c33086c1ab5fb0f2e7 --- /dev/null +++ b/appliance_setup/device_types.py @@ -0,0 +1,105 @@ +#!/usr/bin/python +## appliance_setup/device_types.py - version 0.01 +## +## Device type definitions for appliance_setup.py +## +## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> +## License: GPL v2 or later + +import usb + +## Base class +class DeviceType: + def __init__ (self): + pass + + def detect_devices (self): + pass + + def device_open (self): + pass + def device_close (self): + pass + def config_send (self, config): + pass + def config_retrieve (self): + pass + def device_reset (self): + pass + def scan_for_aps (self): + pass + def print_supported_devices (self): + pass + +## USB-based devices like the HP2101nw +class USBDevice (DeviceType): + supported_devices = ( + {'vendor_id':0x03f0, 'product_id':0xcd02, 'desc':"HP 2101nw wireless G USB print server"} + ); + dev = None; + def __init__ (self): + DeviceType.__init__ () + + def detect_devices (): + devices = (); + for d in self.supported_devices: + for dev in (usb.list_devices (d['vendor_id'], d['product_id'])): + devices.append (dev); + devicedescriptions.append ("%s / ID %04x:%04x (%s: %s)" % (dev.filename(), + dev.idVendor(), dev.idProduct(), + dev.manufacturer(), dev.product()); + + if len(devices) > 1: + dev = ask_choice ("Multiple supported devices detected:", devicedescriptions, devices, devices[0]); + else: + def = devices[0]; + + if dev: + progress ("Using device: %04x:%04x (%s: %s)\n\n", + dev.idVendor(), dev.idProduct(), + dev.manufacturer(), dev.product() ); + else: + progress "\tNo supported devices found\n\n"; + print "ERROR: No supported device was found...\n\n"; + print "Supported devices are (USB IDs): \n"; + for d in (self.supported_devices): + print "\t%(vendor_id)04x:%(product_id)04x (%(desc)s)\n" % d; + print "\n"; + print "Please connect your HP wireless USB print server to \n"; + print "the computer with the black USB cable (micro USB plug) \n"; + print "and run this script again.\n\n"; + return dev; + + def device_open () { + res = self.dev->open; + if (res<0) { + printf "ERROR opening device (%d): %s\n", $res, $!; + } + $res = $dev->detach_kernel_driver_np(0); + # No error check, Linux always gives an error! + $res = $dev->set_configuration(1); + if ($res<0) { + printf "ERROR setting configuration 1 (%d): %s\n", $res, $!; + } + $res = $dev->claim_interface(0); + if ($res<0) { + printf "ERROR claiming interface 0 (%d): %s\n", $res, $!; + } +} + +sub device_close ($) { + my $dev = shift; + $dev->release_interface(0); + $dev->reset; +} + + +## ethernet devices (using ethernet II proto 0x8888), like the VAP11G +class Ether8888Device (DeviceType): + def __init__ (self): + DeviceType.__init__ (); + +## FTP-based devices like ??? +class FTPDevice (DeviceType): + def __init__ (self): + DeviceType.__init__ (); diff --git a/appliance_setup/helpers.py b/appliance_setup/helpers.py new file mode 100755 index 0000000000000000000000000000000000000000..222811432f33aea5a316f02f0711ec15145673d5 --- /dev/null +++ b/appliance_setup/helpers.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +## appliance_setup/helpers.py - version 0.01 +## +## Helper functions or appliance_setup +## +## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> +## License: GPL v2 or later + + +def trim (str): + return str.strip () + +def is_ip_address (ip): + return re.match ('((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$', ip) + +def security2string (sec): + return { + 0 => "None", + 1 => "WEP", + 2 => "WPA-PSK", + 4 => "WPA2-PSK", # WPA2-PSK TKIP + 6 => "WPA/WPA2-PSK", # WPA/WPA2 and WPA2-PSK CCMP + 7 => "WPA-EAP", + 8 => "WPA2-EAP" + }.get (sec, "Unknown (%d)" % sec) + +def encryption2string (enc): + return {0: "TKIP", 1: "AES"}.get (enc, "Unknown (%d)" % enc) + +def authentication2string (auth): + return {1: "Open System", 2: "Shared Key"}.get (auth, "Unknown (%d)" % auth) + +def is_wep_key (key): + return (re.match("[0-9a-fA-F]{10}$", key) || re.match('[0-9a-fA-F]{26}$', key)) + +def is_wpa_passphrase (passphrase): + return (0 < len (passphrase) && len (passphrase) < 64) + +def is_filename (fn): + return (len(fn)>0) + +def is_hostname (hostname): + return re.match("[-A-Za-z0-9]{1,63}$", hostname) diff --git a/appliance_setup/user_interface.py b/appliance_setup/user_interface.py new file mode 100755 index 0000000000000000000000000000000000000000..52d397867fae131460adf6c82f2741788035fa89 --- /dev/null +++ b/appliance_setup/user_interface.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +## appliance_setup/user_interface.py - version 0.01 +## +## User interface definitions for appliance_setup.py. Currently only a +## console-based user interface is implementd. +## +## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> +## License: GPL v2 or later + +class UserInterface: + def __init__ (self, debug): + self.debug = debug; + def progress (self, str): + pass + def debug (self, str): + pass + def input (self, str): + pass + ## Sleep for dur seconds, show some kind of progress bar / dots + def sleep_progress (self, dur): + pass + ## Give the user a number of choices to select from. + ## text ... text to print before the options + ## options ... array ref containing human-readable option values (-1 for separator) + ## values ... array ref with option values (-1 in $options are ignored!) + ## default ... default value if empty input is given + def ask_choice (self, text, options, values, default): + pass + # Ask the user for some text input, which will be validated (if invalid input + # is given, the user is notified and asked again. + # text ... text to ask the user + # validator ... callback function to validate the input + # message ... error message printed if validation fails. + def ask_input (self, text, validator, message): + pass + def display_device_settings (self): + pass + def print_wireless_networks (self): + pass + def ask_operation (self): + pass + def ask_ip_address (self, string): + pass + def ask_wep_key (self, ap): + pass + +class ConsoleInterface (UserInterface): + def __init__ (self, debug): + UserInterface.__init__ (debug); + def progress (self, str): + print str; + def debug (self, str): + if (self.debug): + print str; + def input (self, str): + s = raw_input (str).strip (); + return s + + def sleep_progress (self, dur): + for i in range(dur): + progress "." + sleep (1); + progress "\n" + + def ask_choice (self, text, options, values, default): + print "%s\n" % text + i = 1 + defindex = 1 + for o in options: + if (o == -1): + print "\t---\n" + else: + print "\t%d) %s\n" % (i, o) + if (values[i-1] == default): + defindex = i + i += 1 + while (retval == undefined): + op = self.input ("Your choice (default: %d): " % i); + if (op == ""): + retval = default; + elif (re.match ('[1-9][0-9]*$', op) && ($op <= len (values))): + retval = values.get (op-1); + else: + print "\tInvalid input. Please enter a number from 1 to %d, or simply press Return to use the default.\n" % len(values); + return retval; + + # Ask the user for some text input, which will be validated (if invalid input + # is given, the user is notified and asked again. + # text ... text to ask the user + # validator ... callback function to validate the input + # message ... error message printed if validation fails. + def ask_input (self, text, validator, message): + retval = "" + valid = False + while (!valid): + retval = self.input (text) + valid = validator (retval) + if (!valid): + print message % retval + return retval + + def display_device_settings (self): + pass + def print_wireless_networks (self): + pass + def ask_operation (self): + pass + + def ask_ip_address (self, string): + return self.ask_input (string, self.is_ip_address, "\tInvalid input %s. Please enter a string of the form xxx.xxx.xxx.xxx, where each xxx is a number from 0 to 255.\n") + + def ask_wep_key (self, ap): + return self.ask_input ( + "Please enter the WEP key (10 or 26 characters, 0-9 and a-f) for network '%s': " % ap, + helpers.is_wep_key, + "\tInvalid key %s. Length can only be 10 or 26, use only digits 0-9 and letters a-f.\n\n")