diff --git a/autoconnect.py b/autoconnect.py index 810eb09..84336ba 100644 --- a/autoconnect.py +++ b/autoconnect.py @@ -6,18 +6,9 @@ import dbus.service if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): import dbus.glib -############# -#declare our connections to our daemon. -#without them nothing useful will happen -#the daemon should be running as root bus = dbus.SystemBus() proxy_obj = bus.get_object('org.wicd.daemon', '/org/wicd/daemon') -##we don't need some of these, so I just comment them out daemon = dbus.Interface(proxy_obj, 'org.wicd.daemon') -#wireless = dbus.Interface(proxy_obj, 'org.wicd.daemon.wireless') -#wired = dbus.Interface(proxy_obj, 'org.wicd.daemon.wired') -#config = dbus.Interface(proxy_obj, 'org.wicd.daemon.config') -############# print daemon.Hello() if daemon.CheckIfConnecting() == False: diff --git a/configscript.py b/configscript.py index 648dbf3..791aeef 100755 --- a/configscript.py +++ b/configscript.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -"""Configure the scripts for a particular network +""" Configure the scripts for a particular network Script for configuring the scripts for a network passed in as a command line argument. This needs to run a separate process because @@ -25,6 +25,7 @@ run as the current user. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # + import os import sys import gtk diff --git a/daemon.py b/daemon.py index 6b7678a..88b6280 100644 --- a/daemon.py +++ b/daemon.py @@ -143,6 +143,7 @@ class ConnectionWizard(dbus.service.Object): self.need_profile_chooser = False self.current_interface = None self.vpn_session = None + self.user_init_connect = False # Load the config file self.ReadConfig() @@ -177,7 +178,7 @@ class ConnectionWizard(dbus.service.Object): #micro is for everything else. #and micro may be anything >= 0 #this number is effective starting wicd v1.2.0 - version = '1.4.0' + version = '1.4.2' print 'returned version number',version return version @@ -295,6 +296,13 @@ class ConnectionWizard(dbus.service.Object): return int(self.debug_mode) #end function GetDebugMode + @dbus.service.method('org.wicd.daemon') + def Disconnect(self): + '''disconnects all networks''' + self.SetForcedDisconnect(True) + self.wifi.Disconnect() + self.wired.Disconnect() + @dbus.service.method('org.wicd.daemon') def GetSignalDisplayType(self): ''' Returns the signal display type @@ -381,17 +389,36 @@ class ConnectionWizard(dbus.service.Object): @dbus.service.method('org.wicd.daemon') def GetCurrentInterface(self): + """ Returns the active interface """ return self.current_interface @dbus.service.method('org.wicd.daemon') def SetCurrentInterface(self, iface): + """ Sets the current active interface """ self.current_interface = str(iface) @dbus.service.method('org.wicd.daemon') def SetNeedWiredProfileChooser(self,val): + """ Sets the need_wired_profile_chooser variable. + + If set to true, that alerts the wicd frontend to display the chooser, + if false the frontend will do nothing. This function is only needed + when the frontend starts up, to determine if the chooser was requested + before the frontend was launched. + + """ self.need_profile_chooser = val #end function SetNeedWiredProfileChooser + @dbus.service.method('org.wicd.daemon') + def SetUserInitConnect(self, val): + """ Specifies if the last connection attempt was user/cpu initiated """ + self.user_init_connect = val + + @dbus.service.method('org.wicd.daemon') + def GetUserInitConnect(self): + return self.user_init_connect + @dbus.service.method('org.wicd.daemon') def GetNeedWiredProfileChooser(self): return bool(self.need_profile_chooser) @@ -459,13 +486,6 @@ class ConnectionWizard(dbus.service.Object): break self.LastScan = master_scan - @dbus.service.method('org.wicd.daemon.wireless') - def DisconnectWireless(self): - '''disconnects all wireless networks''' - self.SetForcedDisconnect(True) - self.wifi.Disconnect() - self.wired.Disconnect() - #end function DisconnectWireless @dbus.service.method('org.wicd.daemon.wireless') def GetNumberOfNetworks(self): @@ -587,12 +607,13 @@ class ConnectionWizard(dbus.service.Object): #end function GetCurrentNetwork @dbus.service.method('org.wicd.daemon.wireless') - def ConnectWireless(self,id): + def ConnectWireless(self,id, user_init=False): '''connects the the wireless network specified by id''' # Will returned instantly, that way we don't hold up dbus. # CheckIfWirelessConnecting can be used to test if the connection # is done. self.SetForcedDisconnect(False) + self.SetUserInitConnect(user_init) self.wifi.before_script = self.GetWirelessProperty(id,'beforescript') self.wifi.after_script = self.GetWirelessProperty(id,'afterscript') self.wifi.disconnect_script = self.GetWirelessProperty(id,'disconnectscript') @@ -772,8 +793,9 @@ class ConnectionWizard(dbus.service.Object): #end function CheckPluggedIn @dbus.service.method('org.wicd.daemon.wired') - def ConnectWired(self): + def ConnectWired(self, user_init=False): '''connects to a wired network''' + self.SetUserInitConnect(user_init) self.wired.before_script = self.GetWiredProperty("beforescript") self.wired.after_script = self.GetWiredProperty("afterscript") self.wired.disconnect_script = self.GetWiredProperty("disconnectscript") diff --git a/encryption/templates/eap b/encryption/templates/eap index 243b104..6a6e649 100644 --- a/encryption/templates/eap +++ b/encryption/templates/eap @@ -3,6 +3,7 @@ author = Adam Blackburn version = 2 require username *Username password *Password pac_file *Path_To_PAC_File ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" scan_ssid=$_SCAN diff --git a/encryption/templates/leap b/encryption/templates/leap index c1478ed..3427e75 100644 --- a/encryption/templates/leap +++ b/encryption/templates/leap @@ -3,6 +3,7 @@ author = Adam Blackburn version = 1 require username *Username password *Password ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" scan_ssid=$_SCAN diff --git a/encryption/templates/peap b/encryption/templates/peap index fd5a763..9010a2c 100644 --- a/encryption/templates/peap +++ b/encryption/templates/peap @@ -3,6 +3,7 @@ author = Adam Blackburn version = 2 require identity *Identity password *Password ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" proto=RSN diff --git a/encryption/templates/peap-tkip b/encryption/templates/peap-tkip index fbcf3fe..df141ae 100755 --- a/encryption/templates/peap-tkip +++ b/encryption/templates/peap-tkip @@ -3,6 +3,7 @@ author = Fralaltro version = 1 require identity *Identity password *Password ca_cert *Path_to_CA_Cert ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" scan_ssid=$_SCAN diff --git a/encryption/templates/ttls b/encryption/templates/ttls index ad396ac..c4e8af4 100644 --- a/encryption/templates/ttls +++ b/encryption/templates/ttls @@ -3,6 +3,7 @@ author = Adam Blackburn version = 1 require anonymous_identity *Anonymous_Identity identity *Identity password *Password auth *Authentication ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" scan_ssid=$_SCAN diff --git a/encryption/templates/wep b/encryption/templates/wep index 12c019a..1fdac19 100644 --- a/encryption/templates/wep +++ b/encryption/templates/wep @@ -3,6 +3,7 @@ author = Adam Blackburn version = 1 require key *Key ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" scan_ssid=$_SCAN diff --git a/encryption/templates/wpa b/encryption/templates/wpa index 97c3271..c618bcb 100644 --- a/encryption/templates/wpa +++ b/encryption/templates/wpa @@ -3,6 +3,7 @@ author = Adam Blackburn version = 1 require key *Key ----- +ctrl_interface=/var/run/wpa_supplicant network={ ssid="$_ESSID" scan_ssid=$_SCAN diff --git a/gui.py b/gui.py index 7199a9f..49d5f48 100644 --- a/gui.py +++ b/gui.py @@ -21,6 +21,8 @@ import os import sys import wpath import signal +import time + if __name__ == '__main__': wpath.chdir(__file__) try: @@ -39,21 +41,24 @@ import time, os, misc, gettext, locale, gobject, dbus, dbus.service,pango if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): import dbus.glib -#declare the connections to the daemon, so that they may be accessed -#in any class +# Declare the connections to the daemon, so that they may be accessed +# in any class. bus = dbus.SystemBus() try: - print 'Attempting to connect daemon to gui...' + print 'Attempting to connect daemon to GUI...' proxy_obj = bus.get_object('org.wicd.daemon', '/org/wicd/daemon') - print 'success' + print 'Success' except: - print 'daemon not running, running gksudo ./daemon.py...' + print 'Daemon not running, trying to start it automatically.' misc.PromptToStartDaemon() time.sleep(1) try: proxy_obj = bus.get_object('org.wicd.daemon', '/org/wicd/daemon') + print 'Daemon started succesfully' except: - print 'daemon still not running, aborting.' + print 'Daemon still not running, aborting.' + sys.exit(1) + daemon = dbus.Interface(proxy_obj, 'org.wicd.daemon') wireless = dbus.Interface(proxy_obj, 'org.wicd.daemon.wireless') wired = dbus.Interface(proxy_obj, 'org.wicd.daemon.wired') @@ -65,19 +70,19 @@ config = dbus.Interface(proxy_obj, 'org.wicd.daemon.config') #http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/ #which is also under GPLv2 -#Get the local directory since we are not installing anything +# Get the local directory since we are not installing anything. local_path = os.path.realpath(os.path.dirname(sys.argv[0])) + '/translations' -# Init the list of languages to support +# Init the list of languages to support. langs = list() -#Check the default locale +# Check the default locale. lc, encoding = locale.getdefaultlocale() if (lc): - #If we have a default, it's the first in the list + # If we have a default, it's the first in the list. langs = [lc] # Now lets get all of the supported languages on the system osLanguage = os.environ.get('LANGUAGE', None) if (osLanguage): - #langage comes back something like en_CA:en_US:en_GB:en + # Language comes back something like en_CA:en_US:en_GB:en #on linuxy systems, on Win32 it's nothing, so we need to #split it up into a list langs += osLanguage.split(":") @@ -97,10 +102,10 @@ lang = gettext.translation('wicd', local_path, languages=langs, fallback = True) #map _() to self.lang.gettext() which will translate them. _ = lang.gettext -#keep all the language strings in a dictionary -#by the english words -#I'm not sure this is the best way to do it -#but it works and makes it easy for me :) +# Keep all the language strings in a dictionary +# by the english words. +# I'm not sure this is the best way to do it +# but it works and makes it easy for me :) ########## # translations are done at # http://wicd.net/translator @@ -190,7 +195,11 @@ language['setting_broadcast_address'] = _('Setting broadcast address...') language['setting_static_dns'] = _('Setting static DNS servers...') language['setting_static_ip'] = _('Setting static IP addresses...') language['running_dhcp'] = _('Obtaining IP address...') +language['no_dhcp_offers'] = _('Connection Failed: No DHCP offers received. \ + Couldn\'t get an IP Address.') +language['dhcp_failed'] = _('Connection Failed: Unable to Get IP Address') language['aborted'] = _('Connection cancelled') +language['bad_pass'] = _('Connection Failed: Bad password') language['done'] = _('Done connecting...') ######################################## @@ -210,7 +219,7 @@ class LinkButton(gtk.EventBox): self.show_all() def __setHandCursor(self,widget): - #we need this to set the cursor to a hand for the link labels + # We need this to set the cursor to a hand for the link labels. #I'm not entirely sure what it does :P hand = gtk.gdk.Cursor(gtk.gdk.HAND1) widget.window.set_cursor(hand) @@ -239,7 +248,7 @@ class LabelEntry(gtk.HBox): self.show() def set_text(self,text): - #for compatibility... + # For compatibility... self.entry.set_text(text) def get_text(self): @@ -259,7 +268,7 @@ class LabelEntry(gtk.HBox): self.label.set_sensitive(value) def hide_characters(self,widget=None,event=None): - #when the box looses focus, hide them + # When the box looses focus, hide them if self.auto_hide_text and widget: self.entry.set_visibility(False) @@ -282,7 +291,7 @@ def noneToString(text): return str(text) def noneToBlankString(text): - '''if text is None, 'None', or '' then return '', otherwise return str(text)''' + '''If text is None, 'None', or '' then return '', otherwise return str(text)''' if text == None or text == "None" or text == "": return "" else: @@ -296,7 +305,7 @@ def stringToNone(text): return str(text) def stringToBoolean(text): - '''turns a "True" to True or a "False" to False otherwise returns the text''' + '''Turns a "True" to True or a "False" to False otherwise returns the text''' if text == "True": return True if text == "False": @@ -304,7 +313,7 @@ def stringToBoolean(text): return text def checkboxTextboxToggle(checkbox,textboxes): - #really bad practice, but checkbox == self + # Really bad practice, but checkbox == self for textbox in textboxes: textbox.set_sensitive(checkbox.get_active()) @@ -314,7 +323,7 @@ def checkboxTextboxToggle(checkbox,textboxes): class PrettyNetworkEntry(gtk.HBox): - '''adds an image and a connect button to a NetworkEntry''' + '''Adds an image and a connect button to a NetworkEntry''' def __init__(self,expander): gtk.HBox.__init__(self) # Add the stuff to the hbox (self) @@ -333,10 +342,9 @@ class PrettyNetworkEntry(gtk.HBox): class PrettyWiredNetworkEntry(PrettyNetworkEntry): def __init__(self): PrettyNetworkEntry.__init__(self,WiredNetworkEntry()) - #center the picture and pad it a bit + # Center the picture and pad it a bit self.image = gtk.Image() self.image.set_alignment(.5,0) - self.image.set_size_request(60,-1) self.image.set_from_icon_name("network-wired",6) self.image.show() @@ -381,23 +389,24 @@ class PrettyWirelessNetworkEntry(PrettyNetworkEntry): # "converted" to strength bars, so suggestions from people # for a better way would be welcome. if dbm_strength >= -60: - self.image.set_from_file(wpath.images + 'signal-100.png') + signal_img = 'signal-100.png' elif dbm_strength >= -70: - self.image.set_from_file(wpath.images + 'signal-75.png') + signal_img = 'signal-75.png' elif dbm_strength >= -80: - self.image.set_from_file(wpath.images + 'signal-50.png') + signal_img = 'signal-50.png' else: - self.image.set_from_file(wpath.images + 'signal-25.png') + signal_img = 'signal-25.png' else: # Uses normal link quality, should be fine in most cases if strength > 75: - self.image.set_from_file(wpath.images + 'signal-100.png') + signal_img = 'signal-100.png' elif strength > 50: - self.image.set_from_file(wpath.images + 'signal-75.png') + signal_img = 'signal-75.png' elif strength > 25: - self.image.set_from_file(wpath.images + 'signal-50.png') + signal_img = 'signal-50.png' else: - self.image.set_from_file(wpath.images + 'signal-25.png') + signal_img = 'signal-25.png' + self.image.set_from_file(wpath.images + signal_img) self.expander.setSignalStrength(strength, dbm_strength) def setMACAddress(self,address): @@ -412,10 +421,11 @@ class PrettyWirelessNetworkEntry(PrettyNetworkEntry): def setMode(self,mode): self.expander.setMode(mode) + class NetworkEntry(gtk.Expander): - '''the basis for the entries in the network list''' + '''The basis for the entries in the network list''' def __init__(self): - #make stuff exist, this is pretty long and boring + # Make stuff exist, this is pretty long and boring. gtk.Expander.__init__(self) self.txtIP = LabelEntry(language['ip']) self.txtIP.entry.connect('focus-out-event',self.setDefaults) @@ -449,27 +459,27 @@ class NetworkEntry(gtk.Expander): self.checkboxStaticDNS.connect("toggled",self.toggleDNSCheckbox) self.checkboxGlobalDNS.connect("toggled",self.toggleGlobalDNSCheckbox) self.add(self.vboxTop) - #start with all disabled, then they will be enabled later + # Start with all disabled, then they will be enabled later. self.checkboxStaticIP.set_active(False) self.checkboxStaticDNS.set_active(False) def setDefaults(self,widget=None,event=None): - #after the user types in the IP address, - #help them out a little - ipAddress = self.txtIP.get_text() #for easy typing :) + # After the user types in the IP address, + # help them out a little. + ipAddress = self.txtIP.get_text() # For easy typing :) netmask = self.txtNetmask gateway = self.txtGateway ip_parts = misc.IsValidIP(ipAddress) if ip_parts: - if stringToNone(gateway.get_text()) == None: #make sure the gateway box is blank - #fill it in with a .1 at the end + if stringToNone(gateway.get_text()) == None: # Make sure the gateway box is blank + # Fill it in with a .1 at the end gateway.set_text('.'.join(ip_parts[0:3]) + '.1') if stringToNone(netmask.get_text()) == None: # Make sure the netmask is blank netmask.set_text('255.255.255.0') # Fill in the most common one def resetStaticCheckboxes(self): - #enable the right stuff + # Enable the right stuff if not stringToNone(self.txtIP.get_text()) == None: self.checkboxStaticIP.set_active(True) self.checkboxStaticDNS.set_active(True) @@ -492,10 +502,9 @@ class NetworkEntry(gtk.Expander): self.toggleGlobalDNSCheckbox() def toggleIPCheckbox(self,widget=None): - #should disable the static IP text boxes - #and also enable the DNS checkbox when - #disabled and disable when enabled - + # Should disable the static IP text boxes, + # and also enable the DNS checkbox when + # disabled and disable when enabled. if self.checkboxStaticIP.get_active(): self.checkboxStaticDNS.set_active(True) self.checkboxStaticDNS.set_sensitive(False) @@ -532,7 +541,7 @@ class NetworkEntry(gtk.Expander): self.txtDNS3.set_sensitive(not self.checkboxGlobalDNS.get_active()) class WiredNetworkEntry(NetworkEntry): - #creates the wired network expander + # Creates the wired network expander. def __init__(self): NetworkEntry.__init__(self) self.set_label(language['wired_network']) @@ -541,8 +550,8 @@ class WiredNetworkEntry(NetworkEntry): self.isFullGUI = True self.profileList = config.GetWiredProfileList() - if self.profileList: #make sure there is something in it... - for x in config.GetWiredProfileList(): #add all the names to the combobox + if self.profileList: # Make sure there is something in it... + for x in config.GetWiredProfileList(): # Add all the names to the combobox self.comboProfileNames.append_text(x) self.hboxTemp = gtk.HBox(False,0) hboxDef = gtk.HBox(False,0) @@ -668,19 +677,19 @@ class WiredNetworkEntry(NetworkEntry): self.resetStaticCheckboxes() class WirelessNetworkEntry(NetworkEntry): - #this class is respsponsible for creating the expander - #in each wirelessnetwork entry + # This class is respsponsible for creating the expander + # in each wirelessnetwork entry. def __init__(self,networkID): self.networkID = networkID - #create the data labels + # Create the data labels NetworkEntry.__init__(self) print "ESSID : " + wireless.GetWirelessProperty(networkID,"essid") self.set_label(wireless.GetWirelessProperty(networkID,"essid")) self.essid = wireless.GetWirelessProperty(networkID,"essid") - #make the vbox to hold the encryption stuff + # Make the vbox to hold the encryption stuff. self.vboxEncryptionInformation = gtk.VBox(False,0) - #make the combo box + # Make the combo box. self.comboEncryption = gtk.combo_box_new_text() self.checkboxEncryption = gtk.CheckButton(language['use_encryption']) self.lblStrength = GreyLabel() @@ -888,7 +897,7 @@ class appGui: dic = { "refresh_clicked" : self.refresh_networks, "quit_clicked" : self.exit, - "disconnect_clicked" : self.disconnect_wireless, + "disconnect_clicked" : self.disconnect, "main_exit" : self.exit, "cancel_clicked" : self.cancel_connect, "connect_clicked" : self.connect_hidden, @@ -981,8 +990,8 @@ class appGui: def toggleEncryptionCheck(self,widget=None): self.keyEntry.set_sensitive(self.useEncryptionCheckbox.get_active()) - def disconnect_wireless(self,widget=None): - wireless.DisconnectWireless() + def disconnect(self,widget=None): + daemon.Disconnect() def about_dialog(self,widget,event=None): dialog = gtk.AboutDialog() @@ -1289,7 +1298,7 @@ class appGui: # if it exists. maybe kept as a value in the network entry? Not sure... print "connecting to wireless network..." config.SaveWirelessNetworkProfile(networkid) - wireless.ConnectWireless(networkid) + wireless.ConnectWireless(networkid, True) if type == "wired": print "wired" @@ -1314,7 +1323,7 @@ class appGui: wired.SetWiredProperty("dns3",'') config.SaveWiredNetworkProfile(networkentry.expander.comboProfileNames.get_active_text()) - wired.ConnectWired() + wired.ConnectWired(True) def exit(self, widget=None, event=None): self.window.hide() @@ -1327,9 +1336,6 @@ class appGui: def show_win(self): self.window.show() - # hide the status bar, as it might be confusing if it - # pops up randomly :) - self.status_area.hide_all() self.is_visible = True if __name__ == '__main__': diff --git a/launchdaemon.sh b/launchdaemon.sh new file mode 100755 index 0000000..814e670 --- /dev/null +++ b/launchdaemon.sh @@ -0,0 +1,4 @@ +#!/bin/bash +echo "Starting wicd daemon..." +/opt/wicd/daemon.py 2> /dev/null + diff --git a/misc.py b/misc.py index c8f014c..adca870 100644 --- a/misc.py +++ b/misc.py @@ -1,14 +1,20 @@ ''' Misc - miscellaneous functions for wicd ''' -# Pretty much useless to anyone else... -# But if it is useful, feel free to use under the terms of the GPL -# -# This is released under the -# GNU General Public License -# The terms can be found at -# http://www.gnu.org/copyleft/gpl.html # # Copyright (C) 2007 Adam Blackburn +# Copyright (C) 2007 Dan O'Reilly +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . # import os @@ -17,18 +23,42 @@ import locale import gettext import time import sys +from subprocess import * if __name__ == '__main__': wpath.chdir(__file__) -def Run(cmd, include_std_error = False): - ''' Run a command ''' - if not include_std_error: - f = os.popen( cmd , "r") - return f.read() +def Run(cmd, include_stderr=False, return_pipe=False): + ''' Run a command + + Runs the given command, returning either the output + of the program, or a pipe to read output from. + + keyword arguments -- + cmd - The command to execute + include_std_err - Boolean specifying if stderr should + be included in the pipe to the cmd. + return_pipe - Boolean specifying if a pipe to the + command should be returned. If it is + false, all that will be returned is + one output string from the command. + + ''' + + cmd = to_unicode(str(cmd)) + if include_stderr: + err = STDOUT + fds = True else: - input, out_err = os.popen4( cmd, 'r') - return out_err.read() + err = None + fds = False + + f = Popen(cmd, shell=True, stdout=PIPE, stderr=err, close_fds=fds) + + if return_pipe: + return f.stdout + else: + return f.communicate()[0] def IsValidIP(ip): ''' Make sure an entered IP is valid ''' @@ -41,10 +71,11 @@ def IsValidIP(ip): def PromptToStartDaemon(): ''' Prompt the user to start the daemon ''' - # script execution doesn't work correctly if daemon gets autostarted, - # so just prompt user to start manually - print 'You need to start the daemon before using the gui or tray. Use \ - the command \'sudo /etc/init.d/wicd start\'.' + daemonloc = wpath.bin + 'launchdaemon.sh' + gksudo_args = ['gksudo', '--message', + 'Wicd needs to access your computer\'s network cards.', + '--', daemonloc] + os.spawnvpe(os.P_WAIT, 'gksudo', gksudo_args, os.environ) def RunRegex(regex, string): ''' runs a regex search on a string ''' @@ -63,24 +94,8 @@ def WriteLine(my_file, text): my_file.write(text + "\n") def ExecuteScript(script): - ''' Execute a command - - Executes a given command by forking a new process and - calling run-script.py - - ''' - - pid = os.fork() - if not pid: - os.setsid() - os.umask(0) - pid = os.fork() - if not pid: - print Run('./run-script.py ' + script) - os._exit(0) - os._exit(0) - os.wait() - + ''' Execute a command ''' + os.system(script + ' &') def ReadFile(filename): ''' read in a file and return it's contents as a string ''' @@ -227,6 +242,13 @@ def to_unicode(x): else: return x.decode('utf-8').encode('utf-8') +def error(parent, message): + """ Shows an error dialog """ + dialog = gtk.MessageDialog(parent, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, + gtk.BUTTONS_OK) + dialog.set_markup(message) + dialog.run() + dialog.destroy() class LogWriter(): diff --git a/networking.py b/networking.py index 36b3ed8..79c3a9f 100644 --- a/networking.py +++ b/networking.py @@ -48,6 +48,7 @@ import misc import wnettools import wpath import os +import time if __name__ == '__main__': wpath.chdir(__file__) @@ -104,6 +105,8 @@ class ConnectThread(threading.Thread): self.wireless_interface = wireless self.wired_interface = wired self.is_connecting = False + self.is_aborted = False + self.abort_msg = None self.before_script = before_script self.after_script = after_script self.disconnect_script = disconnect_script @@ -123,8 +126,10 @@ class ConnectThread(threading.Thread): """ self.lock.acquire() - self.connecting_message = status - self.lock.release() + try: + self.connecting_message = status + finally: + self.lock.release() def GetStatus(self): @@ -135,8 +140,10 @@ class ConnectThread(threading.Thread): """ self.lock.acquire() - message = self.connecting_message - self.lock.release() + try: + message = self.connecting_message + finally: + self.lock.release() return message def connect_aborted(self, reason): @@ -147,11 +154,12 @@ class ConnectThread(threading.Thread): """ self.SetStatus(reason) + self.is_aborted = True + self.abort_msg = reason self.is_connecting = False print 'exiting connection thread' - class Wireless(Controller): """ A wrapper for common wireless interface functions. """ @@ -394,7 +402,7 @@ class WirelessConnectThread(ConnectThread): return # Execute pre-connection script if necessary - if self.before_script != '' and self.before_script != None: + if self.before_script != '' and self.before_script is not None: print 'Executing pre-connection script' misc.ExecuteScript(self.before_script) @@ -427,7 +435,7 @@ class WirelessConnectThread(ConnectThread): # Check to see if we need to generate a PSK (only for non-ralink # cards). if self.wpa_driver != 'ralink legacy': - if not self.network.get('key') == None: + if not self.network.get('key') is None: self.SetStatus('generating_psk') print 'Generating psk...' @@ -437,10 +445,11 @@ class WirelessConnectThread(ConnectThread): misc.Run('wpa_passphrase "' + self.network['essid'] + '" "' + self.network['key'] + '"')) # Generate the wpa_supplicant file... - if not self.network.get('enctype') == None: + if self.network.get('enctype') is not None: self.SetStatus('generating_wpa_config') print 'Attempting to authenticate...' wiface.Authenticate(self.network) + auth_time = time.time() if self.should_die: wiface.Up() @@ -464,16 +473,30 @@ class WirelessConnectThread(ConnectThread): self.connect_aborted('aborted') return + if self.network.get('enctype') is not None: + # Allow some time for wpa_supplicant to associate. + # Hopefully 3 sec is enough. If it proves to be inconsistent, + # we might have to try to monitor wpa_supplicant more closely, + # so we can tell exactly when it fails/succeeds. + elapsed = time.time() - auth_time + if elapsed < 3 and elapsed >= 0: + time.sleep(3 - elapsed) + + # Make sure wpa_supplicant was able to associate. + if not wiface.ValidateAuthentication(): + self.connect_aborted('bad_pass') + return + wiface.SetMode(self.network['mode']) - wiface.Associate(self.network['essid'], - self.network['channel'], self.network['bssid']) + wiface.Associate(self.network['essid'], self.network['channel'], + self.network['bssid']) # Authenticate after association for Ralink legacy cards. if self.wpa_driver == 'ralink legacy': - if self.network.get('key') != None: + if self.network.get('key') is not None: wiface.Authenticate(self.network) - if not self.network.get('broadcast') == None: + if self.network.get('broadcast') is not None: self.SetStatus('setting_broadcast_address') print 'Setting the broadcast address...' + self.network['broadcast'] @@ -483,18 +506,20 @@ class WirelessConnectThread(ConnectThread): self.connect_aborted('aborted') return - if not self.network.get('ip') == None: + if self.network.get('ip') is not None: self.SetStatus('setting_static_ip') print 'Setting static IP : ' + self.network['ip'] wiface.SetAddress(self.network['ip'], self.network['netmask']) print 'Setting default gateway : ' + self.network['gateway'] wiface.SetDefaultRoute(self.network['gateway']) else: - # Run dhcp... + # Run DHCP... self.SetStatus('running_dhcp') print "Running DHCP" - if not self.should_die: - wiface.StartDHCP() + dhcp_status = wiface.StartDHCP() + if dhcp_status in ['no_dhcp_offers', 'dhcp_failed']: + self.connect_aborted(dhcp_status) + return if ((self.network.get('dns1') or self.network.get('dns2') or self.network.get('dns3')) and self.network.get('use_static_dns')): @@ -697,8 +722,10 @@ class WiredConnectThread(ConnectThread): # Run dhcp... self.SetStatus('running_dhcp') print "Running DHCP" - if not self.should_die: - liface.StartDHCP() + dhcp_status = liface.StartDHCP() + if dhcp_status in ['no_dhcp_offers', 'dhcp_failed']: + self.connect_aborted(dhcp_status) + return if ((self.network.get('dns1') or self.network.get('dns2') or self.network.get('dns3')) and self.network.get('use_static_dns')): diff --git a/wicd.py b/wicd.py index 542fff2..dc54e86 100755 --- a/wicd.py +++ b/wicd.py @@ -42,6 +42,7 @@ import gobject import dbus import dbus.service import getopt +import time # Import egg.trayicon if we're using an older gtk version if not (gtk.gtk_version[0] >= 2 and gtk.gtk_version[1] >= 10): @@ -80,8 +81,15 @@ try: proxy_obj = bus.get_object('org.wicd.daemon', '/org/wicd/daemon') print 'Success.' except Exception: - print 'Daemon not running...' + print 'Can\'t connect to the daemon, trying to start it automatically...' misc.PromptToStartDaemon() + time.sleep(1) + try: + print 'Attempting to connect tray to daemon...' + proxy_obj = bus.get_object('org.wicd.daemon', '/org/wicd/daemon') + print 'Success.' + except: + print 'Failed to start daemon. Aborting.' sys.exit(1) daemon = dbus.Interface(proxy_obj, 'org.wicd.daemon') @@ -108,12 +116,12 @@ class TrayIcon(): self.tr = self.EggTrayIconGUI(use_tray) else: self.tr = self.StatusTrayIconGUI(use_tray) - self.icon_info = self.TrayConnectionInfo(self.tr) + self.icon_info = self.TrayConnectionInfo(self.tr, use_tray) class TrayConnectionInfo(): """Class for updating the tray icon status""" - def __init__(self, tr): + def __init__(self, tr, use_tray=True): """Initialize variables needed for the icon status methods.""" self.last_strength = -2 self.still_wired = False @@ -121,6 +129,7 @@ class TrayIcon(): self.tried_reconnect = False self.connection_lost_counter = 0 self.tr = tr + self.use_tray = use_tray self.update_tray_icon() def wired_profile_chooser(self): @@ -130,6 +139,7 @@ class TrayIcon(): def update_tray_icon(self): """Updates the tray icon and current connection status""" + if self.use_tray == False: return False # If we're currently connecting, we can shortcut all other checks if daemon.CheckIfConnecting(): @@ -205,7 +215,9 @@ class TrayIcon(): class TrayIconGUI(): """Base Tray Icon class - Implements methods and variables used by both egg/StatusIcon tray icons. + Implements methods and variables used by both egg/StatusIcon + tray icons. + """ def __init__(self, use_tray): menu = """ @@ -396,20 +408,18 @@ def main(argv): if opt in ('-h', '--help'): usage() sys.exit() - if opt in ('-n', '--no-tray'): + elif opt in ('-n', '--no-tray'): use_tray = False # Redirect stderr and stdout for logging purposes #sys.stderr = log #sys.stdout = log - print 'Done initalizing, starting...' - # Set up the tray icon GUI and backend tray_icon = TrayIcon(use_tray) # Check to see if wired profile chooser was called before icon - # was launched (typically happens on startup or daemon restart) + # was launched (typically happens on startup or daemon restart). if daemon.GetNeedWiredProfileChooser(): daemon.SetNeedWiredProfileChooser(False) tray_icon.icon_info.wired_profile_chooser() @@ -419,6 +429,7 @@ def main(argv): bus.add_signal_receiver(tray_icon.icon_info.update_tray_icon, 'StatusChanged', 'org.wicd.daemon') + print 'Done.' mainloop = gobject.MainLoop() mainloop.run() diff --git a/wnettools.py b/wnettools.py index 0337063..3db01b8 100644 --- a/wnettools.py +++ b/wnettools.py @@ -33,6 +33,7 @@ class WirelessInterface() -- Control a wireless network interface. import misc import re import wpath +import time # Compile the regex patterns that will be used to search the output of iwlist # scan for info these are well tested, should work on most cards @@ -54,6 +55,11 @@ altwpa_pattern = re.compile('(wpa_ie)', re.I | re.M | re.S) wpa1_pattern = re.compile('(WPA Version 1)', re.I | re.M | re.S) wpa2_pattern = re.compile('(WPA2)', re.I | re.M | re.S) +# Patterns for wpa_cli output +auth_pattern = re.compile('.*wpa_state=(.*?)\n', re.I | re.M | re.S) + +RALINK_DRIVER = 'ralink legacy' + def SetDNS(dns1=None, dns2=None, dns3=None): """ Set the DNS of the system to the specified DNS servers. @@ -140,12 +146,53 @@ class Interface(object): if self.verbose: print cmd misc.Run(cmd) + def _parse_dhclient(self, pipe): + """ Parse the output of dhclient + + Parses the output of dhclient and returns the status of + the connection attempt. + + """ + dhclient_complete = False + dhclient_success = False + dh_no_offers = False + + while not dhclient_complete: + line = pipe.readline() + if line == '': # Empty string means dhclient is done. + dhclient_complete = True + else: + print line.strip('\n') + if line.startswith('bound'): + dhclient_success = True + dhclient_complete = True + if line.startswith('No DHCPOFFERS'): + # We don't break in this case because dhclient will + # try to use an old lease if possible, so we may + # still make a successful connection. + dh_no_offers = True + + if dhclient_success: + print 'DHCP connection successful' + return 'success' + if dh_no_offers: + print 'DHCP connection failed: No DHCP offers recieved' + return 'no_dhcp_offers' + else: + print 'DHCP connection failed: Reason unknown' + return 'dhcp_failed' + def StartDHCP(self): """ Start the DHCP client to obtain an IP address. """ + # TODO: Not all distros use dhclient to get an IP. We should + # add a way to determine what method is used (or let the user tell + # us), and run the cmd based on that. cmd = 'dhclient ' + self.iface if self.verbose: print cmd - misc.Run(cmd) + pipe = misc.Run(cmd, include_stderr=True, return_pipe=True) + + return self._parse_dhclient(pipe) def StopDHCP(self): @@ -202,19 +249,34 @@ class WiredInterface(Interface): def GetPluggedIn(self): - """ Get the current physical connection state. """ - mii_tool_data = misc.Run( 'mii-tool ' + self.iface,True) - if not misc.RunRegex(re.compile('(Invalid argument)',re.DOTALL | re.I | re.M | re.S),mii_tool_data) == None: + """ Get the current physical connection state. + + The method will first attempt to use ethtool do determine + physical connection state. Should ethtool fail to run properly, + mii-tool will be used instead. + + """ + link_tool = 'ethtool' + tool_data = misc.Run(link_tool + ' ' + self.iface, True) + if misc.RunRegex(re.compile('(Operation not supported)|\ + (ethtool: command not found)', re.I), + tool_data) is not None: + print "ethtool check failed, falling back to mii-tool" + link_tool = 'mii-tool' + + if misc.RunRegex(re.compile('(Invalid argument)', re.I | re.M | re.S), + tool_data) is not None: print 'wired interface appears down, putting up for mii-tool check' - misc.Run( 'ifconfig ' + self.iface + ' up' ) - mii_tool_data = misc.Run( 'mii-tool ' + self.iface) - if not misc.RunRegex(re.compile('(link ok)',re.DOTALL | re.I | re.M | re.S),mii_tool_data) == None: + self.Up() + + tool_data = misc.Run(link_tool + ' ' + self.iface) + if misc.RunRegex(re.compile('(Link detected: yes)|(link ok)', + re.I | re.M | re.S), tool_data) is not None: return True else: return False - class WirelessInterface(Interface): """ Control a wireless network interface. """ def __init__(self, iface, verbose=False, wpa_driver='wext'): @@ -267,7 +329,7 @@ class WirelessInterface(Interface): # Get available network info from iwpriv get_site_survey # if we're using a ralink card (needed to get encryption info) - if self.wpa_driver == 'ralink legacy': + if self.wpa_driver == RALINK_DRIVER: ralink_info = self._GetRalinkScanInfo() else: ralink_info = None @@ -372,7 +434,7 @@ class WirelessInterface(Interface): ap['mode'] = misc.RunRegex(mode_pattern, cell) # Break off here if we're using a ralink card - if self.wpa_driver == 'ralink legacy': + if self.wpa_driver == RALINK_DRIVER: ap = self._ParseRalinkAccessPoint(ap, ralink_info, cell) elif misc.RunRegex(wep_pattern, cell) == 'on': # Encryption - Default to WEP @@ -410,7 +472,7 @@ class WirelessInterface(Interface): # quality displayed or it isn't found) if misc.RunRegex(signaldbm_pattern, cell): ap['strength'] = misc.RunRegex(signaldbm_pattern, cell) - elif self.wpa_driver != 'ralink legacy': # This is already set for ralink + elif self.wpa_driver != RALINK_DRIVER: # This is already set for ralink ap['strength'] = -1 return ap @@ -521,7 +583,7 @@ class WirelessInterface(Interface): """ misc.ParseEncryption(network) - if self.wpa_driver == 'ralink legacy': + if self.wpa_driver == RALINK_DRIVER: self._AuthenticateRalinkLegacy(network) else: cmd = ('wpa_supplicant -B -i ' + self.iface + ' -c "' @@ -530,6 +592,52 @@ class WirelessInterface(Interface): if self.verbose: print cmd misc.Run(cmd) + def ValidateAuthentication(self): + """ Validate WPA authentication. + + Validate that the wpa_supplicant authentication + process was successful. + + NOTE: It's possible this could return False, + even though in actuality wpa_supplicant just isn't + finished yet. + + """ + # Right now there's no way to do this for these drivers + if self.wpa_driver == RALINK_DRIVER: + return True + + cmd = 'wpa_cli -i ' + self.iface + ' status' + if self.verbose: print cmd + output = misc.Run(cmd) + result = misc.RunRegex(auth_pattern, output) + if result == "COMPLETED": + return True + elif result =="DISCONNECTED": + # Force a rescan to get wpa_supplicant moving again. + self._ForceSupplicantScan() + return self.ValidateAuthentication() + else: + print 'wpa_supplicant authentication may have failed.' + return False + pass + + def _ForceSupplicantScan(self): + """ Force wpa_supplicant to rescan available networks. + + This function forces wpa_supplicant to rescan, then sleeps + to allow the scan to finish and reassociation to take place. + This works around authentication validation failing for + wpa_supplicant sometimes becaues it remains in a DISCONNECTED + state for quite a while, before a rescan takes places, all before + even attempting to authenticate. + + """ + print 'wpa_supplicant rescan forced...' + cmd = 'wpa_cli -i' + self.iface + ' scan' + misc.Run(cmd) + time.sleep(5) + print 'Trying to validate authentication again' def _AuthenticateRalinkLegacy(self, network): """ Authenticate with the specified wireless network.