diff --git a/.gitignore b/.gitignore index df8c4f952c9bc501a30687288d53c3588f2eb0e9..d1ca9460850b3536596df778b2f460f938d6ac8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ -build/ \ No newline at end of file +build/ +*.pyc diff --git a/appliance_setup/appliance_setup.py b/appliance_setup/appliance_setup.py index 542b957362577ab7986f44a30ec39a031cc87557..360dfa05cbfe3b0daa0bc01811d42bd7b0547893 100755 --- a/appliance_setup/appliance_setup.py +++ b/appliance_setup/appliance_setup.py @@ -27,17 +27,19 @@ ## 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 +from device_types import * from user_interface import * -import helpers -import configuration +from helpers import * +from configuration import * #use Data::Dumper; VERSION=0.01; -#print "appliance_setup.py - version $VERSION"; -#print "Linux configuration utility for the HP 2101nw wireless G USB print server.\n"; -#print "(C) 2011 Reinhold Kainhofer <reinhold\@kainhofer.com>"; -#print "License: GPL v2 or later\n\n"; +print """\ +appliance_setup.py - version %.2f +Linux configuration utility for wireless appliances +(C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com>"; +License: GPL v2 or later +""" % (VERSION); @@ -47,7 +49,7 @@ VERSION=0.01; def ask_device (ui): devices = []; - for f in device_types.device_factories: + for f in device_factories: devices.extend (f.detect (ui)); dev = None; @@ -58,12 +60,12 @@ def ask_device (ui): dev = devices[0]; if dev: - ui.progress ("Using device: %s" % dev.device_string ()); + ui.progress ("Using device: \n\n\t%s\n" % dev.device_string ()); else: errmsg = ("ERROR: No supported device was found...\n\n" "Supported devices are:\n"); devices = []; - for f in device_types.device_factories: + for f in device_factories: devices.extend (["\t-) %s" % d for d in f.list_supported ()]) errmsg += "\n".join( devices ) ui.progress (errmsg) @@ -72,121 +74,6 @@ def ask_device (ui): """ -############################################################################### -## USB DEVICE MANAGEMENT / COMMUNICATION FUNCTIONS -############################################################################### - - - -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 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 @@ -347,36 +234,36 @@ sub wifi_ask_security ($$) { 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"], + ("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); } @@ -448,19 +335,24 @@ sub adhoc_config_create ($) { 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, +def ask_operation (ui, dev): + # build the list of possible operations + options = [] + optionstrings = [] + if (dev.capabilities['wifi']): + optionstrings.append ("Setup a wireless connection through a wifi router or access point ('infrastructure')") + options.append ("wifi_infrastructure") + if (dev.capabilities['ad-hoc']): + optionstrings.append ("Setup a direct wireless connection with the printer ('ad-hoc')") + options.append ("wifi_adhoc") + optionstrings += [-1, "Change the name of the device", -1, "Save the device configuration to a file", @@ -468,37 +360,38 @@ sub ask_operation () { -1, "Reload the configuration", "Exit" - ], - ["wifi_infrastructure", - "wifi_adhoc", - "change_hostname", + ] + options += ["change_hostname", "config_save", "config_restore", "reload", "exit" - ], - "exit"); -} -""" + ] + + return ui.ask_choice ("What do you want to do?", optionstrings, options, "exit") + def main (): reconfigured = False operation = "" - UI = ConsoleInterface (True) + UI = ConsoleInterface (debug=True) dev = ask_device (UI) if (not dev or not dev.device_open ()): return + ## get the config and print the current configuration cfg = dev.config_retrieve () - #my @config = config_parse ($cfg); - #print_current_configuration (\@config, "Current configuration of the device:\n"); + config = Config (UI, cfgstring=cfg) + UI.progress (config.print_configuration ("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") { + operation = ask_operation (UI, dev) + UI.debug ("\tSelected operation: %s\n" % operation) + if (operation == "wifi_infrastructure"): ## Infrastructure, scan for APs + # TODO #my ($aps,$newap) = wifi_ask_ssid ($dev); #my %newconfig = ('SSID' => $newap); #debug ("\tSelected wireless network: %s\n", $newap); @@ -507,9 +400,10 @@ def main (): #%newconfig = (%newconfig, %security, %ip); #my $newcfg = wifi_config_create (\%newconfig); #device_config_send ($dev, $newcfg); - #$reconfigured = 1; - #} elsif ($operation eq "wifi_adhoc") { + reconfigured = True + elif (operation == "wifi_adhoc"): ## Ad-hoc connection to print server + # TODO #my $adhocssid = adhoc_ask_ssid ($dev); #adhoc_send_unknown_command ($dev); #my $channel = adhoc_ask_channel ($dev, $adhocssid); @@ -519,36 +413,41 @@ def main (): #); #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... - if (operation == "reload"): - main () -#} - + reconfigured = True + elif (operation == "change_hostname"): + hostname = UI.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; you entered '%s'\n") + dev.set_hostname (hostname) + reconfigured = True + elif (operation == "config_save"): + filename = UI.ask_input ("Filename to save to: ", is_filename, "") + if filename: + config.save (filename) + elif (operation == "config_restore"): + filename = UI.ask_input ("Filename to load from: ", is_filename, "") + newconfig = Config (UI) + newconfig.load (filename) + newconfig.remove_readonly () + dev.config_send (newconfig) + reconfigured = True + elif (operation == "reload"): + ## do nothing, just call main... + return main () + elif (operation == "exit"): + dev.device_close () + exit + else: + UI.error ("unknown operation %s\n" % operation) + + dev.device_close () + # if the configuration was changed, print the new config after the device was reset + if reconfigured: + dev = ask_device(UI) + if (not dev or not dev.device_open ()): + return + cfg = dev.config_retrieve () + config = Config (UI, cfgstring=cfg) + UI.progress (config.print_configuration ("New configuration of the device:\n")) diff --git a/appliance_setup/configuration.py b/appliance_setup/configuration.py index 30bd5cfb2e5a7e7926279f48b81ba9451224d020..b118a58ae0dd101192a249d9fd2d35ab6804fea6 100644 --- a/appliance_setup/configuration.py +++ b/appliance_setup/configuration.py @@ -6,22 +6,41 @@ ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> ## License: GPL v2 or later +import re +from helpers import * class Config: - cfgdata = []; - cfgdesc = []; - def __init__ (self, ui): + cfgdata = [] + cfgstring = None + # Readonly entries are: 7014 (LINKINFO) + readonly = ['7014'] + # initialize config with string or list; If both given, list takes precedence + def __init__ (self, ui, cfgstring = '', cfg = []): self.ui = ui + if (len(cfg) > 0): + self.cfgdata = cfg + else: + self.cfgstring = cfgstring + self.config_parse (self.cfgstring) + + def get (self, line): + return find (lambda d: d[0] == line, self.cfgdata) def val (self, line): - return self.cfgdata.get (line, None) + e = self.get (line) + return e[2] if (isinstance(e,(list,tuple)) and len(e)>2) else ''; + + def set_config (self, cfg): + self.cfgdata = cfg - def print_configuration (self): - # TODO: ui.progress (text); - res = "\tDevice name: %(0001)s\n" % self.cfgdata; + def print_configuration (self, text): + #print self.cfgstring + ##print self.cfgdata + #print self.val('0012') + res = text + "\tDevice name: %s\n" % self.val('0001') if (self.val('0012') != "Enable"): res += "\tTCP/IP not yet configured and/or enabled.\n\n" - return + return res adhoc = (self.val('7024') == '1') if adhoc: # ad-hoc network: @@ -43,9 +62,9 @@ class Config: else: # infrastructure network sec = self.val('7003'); - res += "\tSSID: %s\n", self.val('7000') - res += "\tChannel: %s\n", self.val('7002') - res += "\tSecurity: %s\n", security2string (sec) + res += "\tSSID: %s\n" % self.val('7000') + res += "\tChannel: %s\n" % self.val('7002') + res += "\tSecurity: %s\n" % security2string (sec) if (sec == 0): # None => nothing to display pass @@ -74,73 +93,61 @@ class Config: return res def config_parse (self, data): - entries = data.split ('\n'); - self.cfgdata = []; - self.cfgdesc = []; - entry_re = re.compile ("^([0-9]{4}) ([A-Z]+):(.*)$"); - for l in entries: + if not data: + return + self.cfgstring = data + self.cfgdata = [] + entries = data.split ('\n') + entry_re = re.compile ("^([0-9]{4}) ([A-Z_0-9 ]+):(.*)$"); + for l in filter (None, entries): m = entry_re.match (l) - (line, name, value) = m.group (1, 2, 3) - self.cfgdata[line] = value - self.cfgdesc[line] = name - print self.cfgdata - print self.cfgdesc + if m: + self.cfgdata.append (m.group (1, 2, 3)) + else: + print "Config line not in correct format:\n\t%s" % l + + def to_string (self, cfg=None): + res = "" + if (not cfg): + cfg = self.cfgdata + for (l, key, value) in cfg: + res += "%s %s:%s\n" % (l, key, value) + return res + + def save (self, filename): + try: + f = open(filename, 'w') + f.write (self.to_string ()) + f.close () + self.ui.progress ("Current print server configuration saved to file '%s'\n" % filename) + except Exception,e: + self.ui.error (e.message) + + def load (self, filename): + try: + f = open(filename, 'r') + self.cfgstring = f.read () + f.close () + self.ui.debug ("Configuration file '%s' contains the data:\n%s" % (filename, self.cfgstring)) + self.config_parse (self.cfgstring) + self.ui.progress ("Loaded configuration data from file '%s', sending it to the device.\n" % filename); + except Exception,e: + self.ui.error (e.message) + + ## Remove all entries that are read-only, and cannot be changed on the device + def remove_readonly (self): + self.cfgdata = filter (lambda d: d[0] not in self.readonly, self.cfgdata) -############################################################################### -## DEVICE CONFIGURATION HANDLING -############################################################################### -""" -sub config_parse ($) { - my $cfg = shift; -} -# 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; -} +############################################################################### +## DEVICE CONFIGURATION HANDLING +############################################################################### -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); -} +""" @@ -407,105 +414,4 @@ sub adhoc_config_create ($) { 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 (); """ \ No newline at end of file diff --git a/appliance_setup/device.py b/appliance_setup/device.py index 18fd6f393aea768d9726e74750db30b2c8aaa3f7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/appliance_setup/device.py +++ b/appliance_setup/device.py @@ -1,2 +0,0 @@ -class Device: - def __init__ ( \ No newline at end of file diff --git a/appliance_setup/device_types.py b/appliance_setup/device_types.py index 0c5bd2c3d16a8e79702837db0110b22daac1b491..6a21e9870cde6372a2ad44f3e70ee430f6fafd80 100755 --- a/appliance_setup/device_types.py +++ b/appliance_setup/device_types.py @@ -7,6 +7,9 @@ ## License: GPL v2 or later import usb +import time +from helpers import * +from configuration import * device_factories = []; @@ -21,9 +24,10 @@ class DeviceFactory: ## Base class class Device: ui = None - capabilities = {'wifi': False, 'ad-hoc': False} + capabilities = {} def __init__ (self, ui): self.ui = ui + self.capabilities = {'wifi': False, 'ad-hoc': False} def device_string (self): pass @@ -47,6 +51,11 @@ class Device: pass def error (self, msg): self.ui.error (msg) + def set_hostname (self, hostname): + config = Config (self.ui, cfg=[('0001', 'BOX_NAME', hostname)]); + self.ui.debug ("\tChanging hostname using config\n\t%s\n" % config.to_string ()) + return self.config_send (config) + class USBDeviceFactory (DeviceFactory): supported_devices = ( @@ -79,8 +88,11 @@ class USBDevice (Device): bus = "" handle = None thisboxname = "XXXXXXXX" + boxname = "" def __init__ (self, ui, dev, bus, desc): Device.__init__ (self, ui) + self.capabilities['wifi'] = True + self.capabilities['ad-hoc'] = True self.dev = dev self.desc = desc self.bus = bus @@ -112,17 +124,46 @@ class USBDevice (Device): return self.handle; def device_close (self): - self.handle.releaseInterface(0) - self.handle.reset () + if (self.handle): + self.handle.releaseInterface() + self.handle.reset () self.handle = None - + def config_retrieve (self): - self.ui.debug ("Reading current configuration...\n"); + self.ui.debug ("Reading current configuration..."); cfg = "" res = self.bulk_data_write ("9100:\n", 6) - #res = bulk_data_read ($cfg, 25600); - #return $cfg; - + return self.bulk_data_read (25600) + + def config_send (self, config): + self.ui.progress ("Sending new configuation to the device. Please wait") + cfgstring = config.to_string () + self.ui.debug ("\tNew device config: %s\n" % cfgstring) + + self.bulk_data_write (cfgstring, len(cfgstring)) + self.ui.sleep_progress (10) + # Now reset the device: + return self.device_reset () + + def device_reset (self): + self.ui.progress ("Resetting the device, please wait") + res = self.bulk_data_write ("9002:\n", 6) + self.ui.debug ("\tSent 9002 reset command: %d" % res) + # Poll the device until it doesn't respond. Then sleep for ~15 seconds + try: + i=0 + while (i < 15): + readdata = self.handle.bulkRead (2, 13) + res = len(readdata) + self.ui.debug ("\tData received: %s (len: %s)" % (readdata, res) ) + time.sleep (1) + i += 1 + except usb.USBError,e: + self.ui.debug (e.message) + + self.ui.sleep_progress (20) + self.device_close () + self.ui.wait_enter ("Device was reset. Please press Return.") def bulk_data_write (self, data, length): # Add the header (12 bytes): '@\0', packet length, boxname (="XXXXXXXX") @@ -135,41 +176,43 @@ class USBDevice (Device): self.error ("writing bulk data; message: %s" % e.message) return res -# Read USB bulk data into the second argument (output argument!), return -# size of data - def bulk_data_read ($$$): + # Read and return USB bulk data + def bulk_data_read (self, length, timeout=500): # 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 = ""; - res = self.handle.bulkRead (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); + try: + res = self.handle.bulkRead (2, length+13, timeout); + except Exception, e: + self.error ("reading bulk data; message: %s" % e.message) + return None + + # bulkRead returns a tuple, convert to a binary string + res = ''.join(chr(v) for v in res) + if (len (res) == 0): + self.ui.debug ("\tEmpty response received\n") + return '' + + self.ui.debug ("read %d bytes: \n" % len (res)) + self.ui.hex_print (res) # 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); + if (not res.startswith ("@\0\0")): + self.error ("reading data: Wrong header %s" % res[0:3]) + (datalen,) = struct.unpack ("H", res[3:5]); + realdatalen = len (res)-13 + if (datalen != realdatalen): + self.error ("reading data: Expected %d bytes of data, got %d\n" % (datalen, realdatalen)) + self.boxname = res[5:13] ## FIXME: Check the name of the box... - ## cut off the header: - $_[0]=substr ($readdata, 13); - return $res; + return res[13:] + + +#################################################################################### +# Ethernet II proto 8888 devices (e.g. VAP11G) ### +#################################################################################### + import select import socket @@ -178,16 +221,40 @@ import subprocess import re class Ether8888Packet: - def __init__ (self): - pass + ethHeaderFmt = "!6s6sH" + headerFmt = "<2s2s2sHH" + localAddr="\x00\x00\x00\x00\x00\x00" + remoteAddr="\xff\xff\xff\xff\xff\xff" + proto = 0x8888 + def __init__ (self, localAddr="\x00\x00\x00\x00\x00\x00", remoteAddr="\xff\xff\xff\xff\xff\xff", proto=0x8888): + self.proto = proto + self.localAddr = localAddr + self.remoteAddr = remoteAddr + def decode (self, data): - self.dstAddr, self.srcAddr, self.proto = struct.unpack("!6s6sH",data[:14]) + self.dstAddr, self.srcAddr, self.proto = struct.unpack(self.ethHeaderFmt,data[:14]) + if self.proto != 0x8888: + # FIXME: Error message about invalid packet + return False + self.ethData = data[14:] + self.cmd,self.unknown,self.zero,self.nrPackets,self.dataLen = \ + struct.unpack (self.headerFmt, self.ethData[:10]) + self.payload = self.ethData[10:self.dataLen+10] + + if (self.dataLen != len (self.payload)): + # FIXME: Error message about invalid packet + return False + return True + + def encode (self, cmd, unknown, nrPackets, dataLen, data): + # TODO + self.dstAddr, self.srcAddr, self.proto = struct.pack(self.ethHeaderFmt,data[:14]) if self.proto != 0x8888: # FIXME: Error message about invalid packet return False self.ethData = data[14:] self.cmd,self.unknown,self.zero,self.nrPackets,self.dataLen = \ - struct.unpack ("<2s2s2sHH", self.ethData[:10]) + struct.unpack (self.headerFmt, self.ethData[:10]) self.payload = self.ethData[10:self.dataLen+10] if (self.dataLen != len (self.payload)): @@ -255,8 +322,8 @@ device_factories.append (Ether8888DeviceFactory()); ## ethernet devices (using ethernet II proto 0x8888), like the VAP11G class Ether8888Device (Device): ifName = "" - destAddr = "" - srcAddr = "" + localAddr = "" + remoteAddr = "" proto = 0x8888; desc = "" socket = None @@ -268,6 +335,8 @@ class Ether8888Device (Device): self.desc = desc self.srcAddr = None self.destAddr = None + self.capabilities['wifi'] = True; + #self.capabilities['ad-hoc'] = False; def device_string (self): return "%s (Ethernet interface %s, MAC %12s)" % ( diff --git a/appliance_setup/helpers.py b/appliance_setup/helpers.py index ad1f09be29e54fa6223d20e63122cdee89124f8a..0e26a92bfdd4b48e92077b5daae7877b70112350 100755 --- a/appliance_setup/helpers.py +++ b/appliance_setup/helpers.py @@ -6,6 +6,7 @@ ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> ## License: GPL v2 or later +import re def trim (str): return str.strip () @@ -15,20 +16,20 @@ def is_ip_address (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) + '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 (%s)" % sec) def encryption2string (enc): - return {0: "TKIP", 1: "AES"}.get (enc, "Unknown (%d)" % enc) + return {'0': "TKIP", '1': "AES"}.get (enc, "Unknown (%s)" % enc) def authentication2string (auth): - return {1: "Open System", 2: "Shared Key"}.get (auth, "Unknown (%d)" % auth) + return {'1': "Open System", '2': "Shared Key"}.get (auth, "Unknown (%s)" % auth) def is_wep_key (key): return (re.match("[0-9a-fA-F]{10}$", key) or re.match('[0-9a-fA-F]{26}$', key)) @@ -41,3 +42,9 @@ def is_filename (fn): def is_hostname (hostname): return re.match("[-A-Za-z0-9]{1,63}$", hostname) + +def find(f, seq): + """Return first item in sequence where f(item) == True.""" + for item in seq: + if f(item): + return item \ No newline at end of file diff --git a/appliance_setup/user_interface.py b/appliance_setup/user_interface.py index 817bedd49721a42be0a3addbe82b7788770d291c..8a55de1062c2f9f83b520a4d660977d017d9569d 100755 --- a/appliance_setup/user_interface.py +++ b/appliance_setup/user_interface.py @@ -7,13 +7,16 @@ ## (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> ## License: GPL v2 or later -import re; +import re +import time +import sys +from helpers import * class UserInterface: is_debug = False def __init__ (self, debug): self.is_debug = debug; - def progress (self, str): + def progress (self, str, newline=True): pass def debug (self, str): pass @@ -50,35 +53,45 @@ class UserInterface: pass def hex_print (self, data): pass + def wait_enter (self, text): + pass class ConsoleInterface (UserInterface): def __init__ (self, debug): UserInterface.__init__ (self, debug); - def progress (self, str): - print str; + def progress (self, str, newline=True): + if newline: + print str + else: + print str, + sys.stdout.flush() def debug (self, str): - if (self.debug): + if self.is_debug: print str; + sys.stdout.flush() def error (self, str): print "ERROR " + str; + sys.stdout.flush() def hex_print (self, data): - i = 0; + if (not self.is_debug): + return + i = 0; index = 0; readable="" line = "" for c in data: line += ("%02x " % ord(c)) - readable += c; + readable += c if (ord (c) >31 and ord (c) <= 126) else "."; i += 1 if (i % 8 == 0): line += " " - if (i == 16): - line += (" %s" % readable) - print (line) - i = 0 + if (i % 16 == 0): + print ("0x%04x %34s %16s" % (index, line, readable)) + index += 0x10; readable = "" line = "" - line += (" %s" % readable) - print (line) + if (len (line) > 0): + print ("0x%04x %34s %16s" % (index, line, readable)) + sys.stdout.flush() def input (self, str): s = raw_input (str).strip (); @@ -86,9 +99,10 @@ class ConsoleInterface (UserInterface): def sleep_progress (self, dur): for i in range(dur): - progress (".") - sleep (1) - progress ("\n") + sys.stdout.write(".") + sys.stdout.flush() + time.sleep (1) + self.progress ("") def ask_choice (self, text, options, values, default): print text @@ -149,3 +163,6 @@ class ConsoleInterface (UserInterface): "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") + + def wait_enter (self, text): + raw_input (text) \ No newline at end of file