diff --git a/daemon.py b/daemon.py index 82600fa..2631b02 100644 --- a/daemon.py +++ b/daemon.py @@ -55,72 +55,13 @@ else: import wpath import networking import misc +from logfile import ManagedStdio if __name__ == '__main__': wpath.chdir(__file__) misc.RenameProcess("wicd-daemon") -logging_enabled = True - -class LogWriter: - """ A class to provide timestamped logging. """ - def __init__(self): - self.file = open(wpath.log + 'wicd.log', 'a') - self.eol = True - - - def __del__(self): - self.file.close() - - def write(self, data): - """ Writes the data to the log with a timestamp. - - This function handles writing of data to a log file. In order to - handle output redirection, we need to be careful with how we - handle the addition of timestamps. In any set of data that is - written, we replace the newlines with a timestamp + new line, - except for newlines that are the final character in data. - - When a newline is the last character in data, we set a flag to - indicate that the next write should have a timestamp prepended - as well, which ensures that the timestamps match the time at - which the data is written, rather than the previous write. - - Keyword arguments: - data -- The string to write to the log. - - """ - global logging_enabled - data = data.decode('utf-8').encode('utf-8') - if len(data) <= 0: return - if logging_enabled: - if self.eol: - self.file.write(self.get_time() + ' :: ') - self.eol = False - - if data[-1] == '\n': - self.eol = True - data = data[:-1] - - self.file.write( - data.replace('\n', '\n' + self.get_time() + ' :: ')) - if self.eol: self.file.write('\n') - self.file.flush() - - - def get_time(self): - """ Return a string with the current time nicely formatted. - - The format of the returned string is yyyy/mm/dd HH:MM:SS - - """ - x = time.localtime() - return ''.join([ - str(x[0]).rjust(4, '0'), '/', str(x[1]).rjust(2, '0'), '/', - str(x[2]).rjust(2, '0'), ' ', str(x[3]).rjust(2, '0'), ':', - str(x[4]).rjust(2, '0'), ':', str(x[5]).rjust(2, '0')]) - class ConnectionWizard(dbus.service.Object): def __init__(self, bus_name, object_path='/org/wicd/daemon', @@ -353,7 +294,6 @@ class ConnectionWizard(dbus.service.Object): """ if fresh: self.Scan() - #self.AutoConnectScan() # Also scans for hidden networks if self.CheckPluggedIn(True): self._wired_autoconnect() else: @@ -382,7 +322,7 @@ class ConnectionWizard(dbus.service.Object): return # Last-Used. - else: # Assume its last-used. + else: network = self.GetLastUsedWiredNetwork() if not network: print "no previous wired profile available, wired " + \ @@ -650,8 +590,15 @@ class ConnectionWizard(dbus.service.Object): @dbus.service.method('org.wicd.daemon') @dbus.service.signal(dbus_interface='org.wicd.daemon', signature='') - def SendScanSignal(self): - """ Emits a signal announcing a scan has occurred. """ + def SendStartScanSignal(self): + """ Emits a signal announcing a scan has started. """ + pass + + + @dbus.service.method('org.wicd.daemon') + @dbus.service.signal(dbus_interface='org.wicd.daemon', signature='') + def SendEndScanSignal(self): + """ Emits a signal announcing a scan has finished. """ pass ########## WIRELESS FUNCTIONS @@ -672,13 +619,16 @@ class ConnectionWizard(dbus.service.Object): """ if self.debug_mode: print 'scanning start' - scan = self.wifi.Scan(str(self.hidden_essid)) + self.SendStartScanSignal() + time.sleep(.2) + scan = self.wifi.Scan(str(self.hidden_essid), fast=True) self.LastScan = scan if self.debug_mode: print 'scanning done' print 'found ' + str(len(scan)) + ' networks:' for i, network in enumerate(scan): self.ReadWirelessNetworkProfile(i) + self.SendEndScanSignal() @dbus.service.method('org.wicd.daemon.wireless') def GetIwconfig(self): @@ -1353,7 +1303,6 @@ class ConnectionWizard(dbus.service.Object): self.SetAlwaysShowWiredInterface(self.get_option("Settings", "always_show_wired_interface", default=False)) - self.SetUseGlobalDNS(self.get_option("Settings", "use_global_dns", default=False)) dns1 = self.get_option("Settings", "global_dns_1", default='None') @@ -1506,6 +1455,17 @@ def daemonize(): except OSError, e: print >> sys.stderr, "Fork #2 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) + + sys.stdout.flush() + sys.stderr.flush() + os.close(sys.__stdin__.fileno()) + os.close(sys.__stdout__.fileno()) + os.close(sys.__stderr__.fileno()) + + # stdin always from /dev/null + sys.stdin = open('/dev/null', 'r') + + def main(argv): """ The main daemon program. @@ -1521,15 +1481,16 @@ def main(argv): auto_scan = True try: - opts, args = getopt.getopt(sys.argv[1:], 'fenosP:', + opts, args = getopt.getopt(sys.argv[1:], 'fenoahi:', ['help', 'no-daemon', 'no-poll', 'no-stderr', 'no-stdout', - 'no-scan']) + 'no-autoconnect', 'scan-interval']) except getopt.GetoptError: # Print help information and exit usage() sys.exit(2) no_poll = False + scan_interval = "120000" for o, a in opts: if o in ('-h', '--help'): usage() @@ -1540,17 +1501,18 @@ def main(argv): redirect_stdout = False if o in ('-f', '--no-daemon'): do_daemonize = False - if o in ('-s', '--no-scan'): + if o in ('-a', '--no-autoconnect'): auto_scan = False if o in ('-n', '--no-poll'): no_poll = True + if o in ('i', '--scan-interval'): + scan_interval = a if do_daemonize: daemonize() - if redirect_stderr or redirect_stdout: output = LogWriter() + if redirect_stderr or redirect_stdout: output = ManagedStdio(wpath.log + 'wicd.log') if redirect_stdout: sys.stdout = output if redirect_stderr: sys.stderr = output - time.sleep(1) print '---------------------------' print 'wicd initializing...' @@ -1563,7 +1525,8 @@ def main(argv): gobject.threads_init() if not no_poll: - (child_pid, x, x, x) = gobject.spawn_async([wpath.bin + "monitor.py"], + (child_pid, x, x, x) = gobject.spawn_async([wpath.bin + "monitor.py", + scan_interval], flags=gobject.SPAWN_CHILD_INHERITS_STDIN) signal.signal(signal.SIGTERM, sigterm_caught) diff --git a/gui.py b/gui.py index 56e7528..63ee5c0 100644 --- a/gui.py +++ b/gui.py @@ -706,7 +706,6 @@ class appGui: state, x = daemon.GetConnectionStatus() if self.prev_state != state or force_check: - print 'we actually update now' apbssid = wireless.GetApBssid() for entry in self.network_list: if hasattr(entry, "update_connect_button"): @@ -751,7 +750,7 @@ class appGui: """ Sets the status bar message for the GUI. """ self.statusID = self.status_bar.push(1, msg) - def dbus_refresh_networks(self): + def dbus_scan_finished(self): """ Calls for a non-fresh update of the gui window. This method is called after the daemon runs an automatic @@ -760,6 +759,9 @@ class appGui: """ if not self.connecting: self.refresh_networks(fresh=False) + + def dbus_scan_started(self): + self.network_list.set_sensitive(False) def refresh_networks(self, widget=None, fresh=True, hidden=None): """ Refreshes the network list. @@ -799,7 +801,6 @@ class appGui: wireless.Scan() num_networks = wireless.GetNumberOfNetworks() - instruct_label = self.wTree.get_widget("label_instructions") if num_networks > 0: instruct_label.show() @@ -840,7 +841,7 @@ class appGui: # First make sure all the Addresses entered are valid. if entry.chkbox_static_ip.get_active(): - enlist = [ent for ent in [entry.txt_ip, entry.txt_netmask, + entlist = [ent for ent in [entry.txt_ip, entry.txt_netmask, entry.txt_gateway]] if entry.chkbox_static_dns.get_active() and \ @@ -850,7 +851,6 @@ class appGui: for ent in [entry.txt_dns_2, entry.txt_dns_3]: if ent.get_text() != "": entlist.append(ent) - for lblent in entlist: if not misc.IsValidIP(lblent.get_text()): error(self.window, language['invalid_address']. @@ -1093,8 +1093,8 @@ class appGui: self.wait_for_events(0.1) self.window.grab_focus() gobject.idle_add(self.refresh_networks) - - + + if __name__ == '__main__': if not proxy_obj: error("Could not connect to wicd's D-Bus interface. Make sure the " + @@ -1103,6 +1103,10 @@ if __name__ == '__main__': sys.exit(1) app = appGui(standalone=True) - bus.add_signal_receiver(app.dbus_refresh_networks, 'SendScanSignal', + bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal', 'org.wicd.daemon') + bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', + 'org.wicd.daemon') + bus.add_signal_receiver(app.update_connect_buttons, 'StatusChanged', + 'org.wicd.daemon') gtk.main() diff --git a/logfile.py b/logfile.py new file mode 100644 index 0000000..1dd2622 --- /dev/null +++ b/logfile.py @@ -0,0 +1,175 @@ +#!/usr/bin/python +# +# Copyright (C) 1999-2006 Keith Dart +# Copyright (C) 2008 Dan O'Reilly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. + +""" +Managing logfile rotation. A ManagedLog object is a file-like object that +rotates itself when a maximum size is reached. + +""" + +import sys +import os +import time + +class SizeError(IOError): + pass + +class LogFile(file): + """LogFile(name, [mode="w"], [maxsize=360000]) + Opens a new file object. After writing bytes a SizeError will be + raised. """ + def __init__(self, name, mode="a", maxsize=360000): + super(LogFile, self).__init__(name, mode) + self.maxsize = maxsize + self.eol = True + try: + self.written = os.fstat(self.fileno())[6] + except OSError: + self.written = 0 + + def write(self, data): + self.written += len(data) + + data = data.decode('utf-8').encode('utf-8') + if len(data) <= 0: return + if self.eol: + super(LogFile, self).write(self.get_time() + ' :: ') + self.eol = False + + if data[-1] == '\n': + self.eol = True + data = data[:-1] + + super(LogFile, self).write(data.replace( + '\n', '\n' + self.get_time() + ' :: ')) + if self.eol: + super(LogFile, self).write('\n') + + self.flush() + if self.written > self.maxsize: + raise SizeError + + def get_time(self): + """ Return a string with the current time nicely formatted. + + The format of the returned string is yyyy/mm/dd HH:MM:SS + + """ + x = time.localtime() + return ''.join([ + str(x[0]).rjust(4, '0'), '/', str(x[1]).rjust(2, '0'), '/', + str(x[2]).rjust(2, '0'), ' ', str(x[3]).rjust(2, '0'), ':', + str(x[4]).rjust(2, '0'), ':', str(x[5]).rjust(2, '0')]) + + def rotate(self): + return rotate(self) + + def note(self, text): + """Writes a specially formated note text to the file.The note starts +with the string '\\n#*=' so you can easily filter them. """ + self.write("\n#*===== %s =====\n" % (text,)) + + +class ManagedLog(object): + """ManagedLog(name, [maxsize=360000], [maxsave=9]) + A ManagedLog instance is a persistent log object. Write data with the + write() method. The log size and rotation is handled automatically. + + """ + def __init__(self, name, maxsize=360000, maxsave=3): + self._lf = LogFile(name, "a", maxsize) + self.maxsave = maxsave + + def __repr__(self): + return "%s(%r, %r, %r)" % (self.__class__.__name__, self._lf.name, + self._lf.maxsize, self.maxsave) + + def write(self, data): + try: + self._lf.write(data) + except SizeError: + self._lf = rotate(self._lf, self.maxsave) + + def note(self, data): + try: + self._lf.note(data) + except SizeError: + self._lf = rotate(self._lf, self.maxsave) + + def written(self): + return self._lf.written + + def rotate(self): + self._lf = rotate(self._lf, self.maxsave) + + # auto-delegate remaining methods (but you should not read or seek an open + # log file). + def __getattr__(self, name): + return getattr(self._lf, name) + + +# useful for logged stdout for daemon processes +class ManagedStdio(ManagedLog): + def write(self, data): + try: + self._lf.write(data) + except SizeError: + sys.stdout.flush() + sys.stderr.flush() + self._lf = rotate(self._lf, self.maxsave) + fd = self._lf.fileno() + os.dup2(fd, 1) + os.dup2(fd, 2) + sys.stdout = sys.stderr = self + + +def rotate(fileobj, maxsave=9): + name = fileobj.name + mode = fileobj.mode + maxsize = fileobj.maxsize + fileobj.close() + shiftlogs(name, maxsave) + return LogFile(name, mode, maxsize) + + +# assumes basename logfile is closed. +def shiftlogs(basename, maxsave): + topname = "%s.%d" % (basename, maxsave) + if os.path.isfile(topname): + os.unlink(topname) + + for i in range(maxsave, 0, -1): + oldname = "%s.%d" % (basename, i) + newname = "%s.%d" % (basename, i+1) + try: + os.rename(oldname, newname) + except OSError: + pass + try: + os.rename(basename, "%s.1" % (basename)) + except OSError: + pass + + +def open(name, maxsize=360000, maxsave=9): + return ManagedLog(name, maxsize, maxsave) + +def writelog(logobj, data): + try: + logobj.write(data) + except SizeError: + return rotate(logobj) + else: + return logobj diff --git a/misc.py b/misc.py index 5709d19..e1f99e3 100644 --- a/misc.py +++ b/misc.py @@ -104,10 +104,10 @@ def PromptToStartDaemon(): if sudo_prog.endswith("gksu") or sudo_prog.endswith("ktsuss"): msg = '--message' else: - msg = '-- caption' + msg = '--caption' sudo_args = [sudo_prog, msg, - 'Wicd needs to access your computer\'s network cards.', - '--', daemonloc] + 'Wicd needs to access your computer\'s network cards.', + daemonloc] os.spawnvpe(os.P_WAIT, sudo_prog, sudo_args, os.environ) def RunRegex(regex, string): diff --git a/monitor.py b/monitor.py index 03725b6..b75e9bb 100755 --- a/monitor.py +++ b/monitor.py @@ -8,8 +8,8 @@ when appropriate. """ # -# Copyright (C) 2007 Adam Blackburn -# Copyright (C) 2007 Dan O'Reilly +# Copyright (C) 2007 - 2008 Adam Blackburn +# Copyright (C) 2007 - 2008 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 @@ -46,7 +46,7 @@ daemon = dbus.Interface(proxy_obj, 'org.wicd.daemon') wired = dbus.Interface(proxy_obj, 'org.wicd.daemon.wired') wireless = dbus.Interface(proxy_obj, 'org.wicd.daemon.wireless') -class ConnectionStatus(): +class ConnectionStatus(object): """ Class for monitoring the computer's connection status. """ def __init__(self): """ Initialize variables needed for the connection status methods. """ @@ -266,9 +266,8 @@ class ConnectionStatus(): if daemon.GetSuspend() or daemon.CheckIfConnecting(): return True wireless.Scan() - daemon.SendScanSignal() - except dbus.exceptions.DBusException: - print 'dbus exception while attempting rescan' + except dbus.exceptions.DBusException, e: + print 'dbus exception while attempting rescan: %s' % str(e) finally: return True diff --git a/netentry.py b/netentry.py index e1ee232..463eaa9 100644 --- a/netentry.py +++ b/netentry.py @@ -82,7 +82,7 @@ class LabelEntry(gtk.HBox): if self.auto_hide_text and widget: self.entry.set_visibility(False) - + class GreyLabel(gtk.Label): """ Creates a grey gtk.Label. """ def __init__(self): @@ -356,18 +356,18 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): self.txt_netmask.set_text(self.format_entry(networkID,"netmask")) self.txt_gateway.set_text(self.format_entry(networkID,"gateway")) - self.chkbox_global_dns.set_active(wireless.GetWirelessProperty(networkID, - 'use_global_dns')) + self.chkbox_global_dns.set_active(bool(wireless.GetWirelessProperty(networkID, + 'use_global_dns'))) self.txt_dns_1.set_text(self.format_entry(networkID, "dns1")) self.txt_dns_2.set_text(self.format_entry(networkID, "dns2")) self.txt_dns_3.set_text(self.format_entry(networkID, "dns3")) self.reset_static_checkboxes() - self.chkbox_encryption.set_active(wireless.GetWirelessProperty(networkID, - 'encryption')) - self.chkbox_global_settings.set_active(wireless.GetWirelessProperty(networkID, - 'use_settings_globally')) + self.chkbox_encryption.set_active(bool(wireless.GetWirelessProperty(networkID, + 'encryption'))) + self.chkbox_global_settings.set_active(bool(wireless.GetWirelessProperty(networkID, + 'use_settings_globally'))) activeID = -1 # Set the menu to this item when we are done @@ -628,7 +628,7 @@ class WiredNetworkEntry(NetworkEntry): try: sudo_prog = misc.choose_sudo_prog() msg = "You must enter your password to configure scripts" - if sudo_prog.endswith("gksu"): + if sudo_prog.endswith("gksu") or sudo_prog.endswith("ktsuss"): msg_flag = "-m" else: msg_flag = "--caption" @@ -636,7 +636,7 @@ class WiredNetworkEntry(NetworkEntry): profile, "wired"]) except misc.WicdError: error("Could not find a graphical sudo program." + \ - " Script editor could no be launched.") + " Script editor could not be launched.") def check_enable(self): """ Disable objects if the profile list is empty. """ @@ -870,8 +870,10 @@ class WirelessNetworkEntry(NetworkEntry): def update_connect_button(self, state, apbssid): """ Update the connection/disconnect button for this entry. """ - if state == misc.WIRELESS and apbssid == \ - wireless.GetWirelessProperty(self.networkID, "bssid"): + if not apbssid: + apbssid = wireless.GetApBssid() + if state == misc.WIRELESS and \ + apbssid == wireless.GetWirelessProperty(self.networkID, "bssid"): self.disconnect_button.show() self.connect_button.hide() else: @@ -908,7 +910,7 @@ class WirelessNetworkEntry(NetworkEntry): try: sudo_prog = misc.choose_sudo_prog() msg = "You must enter your password to configure scripts" - if sudo_prog.endswith("gksu"): + if sudo_prog.endswith("gksu") or sudo_prog.endswith("ktsuss"): msg_flag = "-m" else: msg_flag = "--caption" diff --git a/networking.py b/networking.py index 31ffcfd..0097900 100644 --- a/networking.py +++ b/networking.py @@ -309,7 +309,7 @@ class ConnectThread(threading.Thread): self.is_connecting = False print 'exiting connection thread' - def abort_connection(self, reason): + def abort_connection(self, reason=""): """ Schedule a connection abortion for the given reason. """ self.abort_reason = reason self.should_die = True @@ -392,6 +392,8 @@ class Wireless(Controller): else: return 0 + if not self.wireless_interface: return [] + wiface = self.wiface # Prepare the interface for scanning @@ -416,6 +418,8 @@ class Wireless(Controller): network -- network to connect to """ + if not self.wireless_interface: return False + self.connecting_thread = WirelessConnectThread(network, self.wireless_interface, self.wired_interface, self.wpa_driver, self.before_script, self.after_script, @@ -785,6 +789,7 @@ class Wired(Controller): network -- network to connect to """ + if not self.wired_interface: return False self.connecting_thread = WiredConnectThread(network, self.wireless_interface, self.wired_interface, self.before_script, self.after_script, diff --git a/wicd.py b/wicd.py index c7d3b82..9606a66 100755 --- a/wicd.py +++ b/wicd.py @@ -20,8 +20,8 @@ def main() -- Runs the wicd frontend main loop. """ # -# Copyright (C) 2007 Adam Blackburn -# Copyright (C) 2007 Dan O'Reilly +# Copyright (C) 2007 - 2008 Adam Blackburn +# Copyright (C) 2007 - 2008 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 @@ -43,6 +43,7 @@ import dbus import dbus.service import getopt import os +import pango # Wicd specific imports import wpath @@ -66,7 +67,7 @@ else: from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) -misc.RenameProcess("wicd") +misc.RenameProcess("wicd-client") if __name__ == '__main__': wpath.chdir(__file__) @@ -80,6 +81,20 @@ config = None language = misc.get_language_list_tray() +class NetworkMenuItem(gtk.ImageMenuItem): + def __init__(self, lbl, is_active=False): + gtk.ImageMenuItem.__init__(self) + self.label = gtk.Label(lbl) + if is_active: + atrlist = pango.AttrList() + atrlist.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, 50)) + self.label.set_attributes(atrlist) + self.label.set_justify(gtk.JUSTIFY_LEFT) + self.label.set_alignment(0, 0) + self.add(self.label) + self.label.show() + + class TrayIcon: """ Base Tray Icon class. @@ -320,11 +335,16 @@ class TrayIcon: props.parent) self.gui_win = None self.current_icon_path = None + self.dbus_available = True self.use_tray = use_tray def on_activate(self, data=None): """ Opens the wicd GUI. """ - self.toggle_wicd_gui() + try: + self.toggle_wicd_gui() + except dbus.DBusException: + self.dbus_available = False + gui.error(None, "Could not connect to wicd daemon. Unable to load GUI") def on_quit(self, widget=None): """ Closes the tray icon. """ @@ -340,19 +360,20 @@ class TrayIcon: dialog.run() dialog.destroy() - def _add_item_to_menu(self, net_menu, lbl, type_, n_id, is_connecting): + def _add_item_to_menu(self, net_menu, lbl, type_, + n_id, is_connecting, is_active): """ Add an item to the network list submenu. """ def network_selected(widget, net_type, net_id): """ Callback method for a menu item selection. """ - if net_type == "wired": + if net_type == "__wired__": wired.ConnectWired() else: wireless.ConnectWireless(net_id) - item = gtk.ImageMenuItem(lbl) + item = NetworkMenuItem(lbl, is_active) image = gtk.Image() - if type_ == "wired": + if type_ == "__wired__": image.set_from_icon_name("network-wired", 2) else: pb = gtk.gdk.pixbuf_new_from_file_at_size(self._get_img(n_id), @@ -411,21 +432,29 @@ class TrayIcon: net_menu = gtk.Menu() is_connecting = daemon.CheckIfConnecting() num_networks = wireless.GetNumberOfNetworks() - #TODO Eventually do something to indicate the active network. - #[status, info] = daemon.GetConnectionStatus() - + [status, info] = daemon.GetConnectionStatus() + if wired.GetAlwaysShowWiredInterface() or \ wired.CheckPluggedIn(True): - self._add_item_to_menu(net_menu, "Wired Network", "wired", 0, - is_connecting) + if status == misc.WIRED: + is_active = True + else: + is_active = False + self._add_item_to_menu(net_menu, "Wired Network", "__wired__", + 0, is_connecting, is_active) sep = gtk.SeparatorMenuItem() net_menu.append(sep) sep.show() if num_networks > 0: for x in range(0, num_networks): - self._add_item_to_menu(net_menu, get_prop(x, "essid"), - "wifi", x, is_connecting) + essid = get_prop(x, "essid") + if status == misc.WIRELESS and info[1] == essid: + is_active = True + else: + is_active = False + self._add_item_to_menu(net_menu, essid, "wifi", x, + is_connecting, is_active) net_menuitem.set_submenu(net_menu) net_menuitem.show() @@ -434,10 +463,14 @@ class TrayIcon: """ Toggles the wicd GUI. """ if not self.gui_win: self.gui_win = gui.appGui() - bus.add_signal_receiver(self.gui_win.dbus_refresh_networks, - 'SendScanSignal', 'org.wicd.daemon') + bus.add_signal_receiver(self.gui_win.dbus_scan_finished, + 'SendEndScanSignal', + 'org.wicd.daemon') + bus.add_signal_receiver(self.gui_win.dbus_scan_started, + 'SendStartScanSignal', + 'org.wicd.daemon') bus.add_signal_receiver(self.gui_win.update_connect_buttons, - 'StatusChanged', 'org.wicd.daemon') + 'StatusChanged', 'org.wicd.daemon') elif not self.gui_win.is_visible: self.gui_win.show_win() else: @@ -591,7 +624,7 @@ def main(argv): for opt, a in opts: if opt in ('-h', '--help'): usage() - sys.exit() + sys.exit(0) elif opt in ('-n', '--no-tray'): use_tray = False elif opt in ('-a', '--no-animate'): @@ -625,8 +658,22 @@ 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() + + while 1: + mainloop = gobject.MainLoop() + try: + mainloop.run() + except dbus.exceptions.DBusException: + print 'Warning. Caught a D-Bus exception! Connection to daemon lost.' + print 'Trying to reconnect...' + sleep(10) + try: + connect_to_dbus() + except: + pass + + + if __name__ == '__main__': diff --git a/wnettools.py b/wnettools.py index 9a1e98e..c0ab3e8 100644 --- a/wnettools.py +++ b/wnettools.py @@ -14,9 +14,9 @@ class WirelessInterface() -- Control a wireless network interface. """ # -# Copyright (C) 2007 Adam Blackburn -# Copyright (C) 2007 Dan O'Reilly -# Copyright (C) 2007 Byron Hillis +# Copyright (C) 2007 - 2008 Adam Blackburn +# Copyright (C) 2007 - 2008 Dan O'Reilly +# Copyright (C) 2007 - 2008 Byron Hillis # # 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 @@ -31,16 +31,17 @@ class WirelessInterface() -- Control a wireless network interface. # along with this program. If not, see . # -import misc import re import os -import wpath import time import socket import fcntl import struct import array +import wpath +import misc + # 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 essid_pattern = re.compile('.*ESSID:"(.*?)"\n', re.I | re.M | re.S) @@ -76,6 +77,16 @@ SIOCGIFFLAGS = 0x8913 SIOCGIWRANGE = 0x8B0B SIOCGIWAP = 0x8B15 +def _sanitize_string(string): + blacklist = [';', '`', '$', '!', '*', '|', '>', '<'] + new_string = [] + for c in string: + if c in blacklist: + new_string.append("\\" + c) + else: + new_string.append(c) + return ''.join(new_string) + def SetDNS(dns1=None, dns2=None, dns3=None): """ Set the DNS of the system to the specified DNS servers. @@ -90,8 +101,11 @@ def SetDNS(dns1=None, dns2=None, dns3=None): resolv = open("/etc/resolv.conf","w") for dns in [dns1, dns2, dns3]: if dns: - print 'Setting DNS : ' + dns - resolv.write('nameserver ' + dns + '\n') + if misc.IsValidIP(dns): + print 'Setting DNS : ' + dns + resolv.write('nameserver ' + dns + '\n') + else: + print 'DNS IP is not a valid IP address, not writing to resolv.conf' resolv.close() def GetDefaultGateway(): @@ -126,12 +140,13 @@ def GetWirelessInterfaces(): The first interface available. """ - iface = _fast_get_wifi_interfaces() - if not iface: - output = misc.Run('iwconfig') - iface = misc.RunRegex(re.compile('(\w*)\s*\w*\s*[a-zA-Z0-9.-_]*\s*(?=ESSID)', - re.I | re.M | re.S), output) - return iface + dev_dir = '/sys/class/net/' + ifnames = [] + + ifnames = [iface for iface in os.listdir(dev_dir) if os.path.isdir(dev_dir + iface) \ + and 'wireless' in os.listdir(dev_dir + iface)] + + return bool(ifnames) and ifnames[0] or None def GetWiredInterfaces(): basedir = '/sys/class/net/' @@ -139,16 +154,7 @@ def GetWiredInterfaces(): in os.listdir(basedir + iface) and \ open(basedir + iface + "/type").readlines()[0].strip() == "1"] -def _fast_get_wifi_interfaces(): - """ Tries to get a wireless interface using /sys/class/net. """ - dev_dir = '/sys/class/net/' - ifnames = [] - - ifnames = [iface for iface in os.listdir(dev_dir) if 'wireless' \ - in os.listdir(dev_dir + iface)] - - return bool(ifnames) and ifnames[0] or None - + def get_iw_ioctl_result(iface, call): """ Makes the given ioctl call and returns the results. @@ -182,7 +188,7 @@ class Interface(object): verbose -- whether to print every command run """ - self.iface = iface + self.iface = _sanitize_string(iface) self.verbose = verbose self.DHCP_CLIENT = None self.DHCP_CMD = None @@ -206,7 +212,7 @@ class Interface(object): iface -- the name of the interface. """ - self.iface = str(iface) + self.iface = _sanitize_string(str(iface)) def _find_client_path(self, client): """ Determines the full path for the given program. @@ -238,6 +244,7 @@ class Interface(object): True if the program exists, False otherwise. """ + client = _sanitize_string(client) output = misc.Run("which " + client) if output and not ("no " + client) in output: return True @@ -353,6 +360,7 @@ class Interface(object): True """ + if not self.iface: return False cmd = 'ifconfig ' + self.iface + ' up' if self.verbose: print cmd misc.Run(cmd) @@ -365,6 +373,7 @@ class Interface(object): True """ + if not self.iface: return False cmd = 'ifconfig ' + self.iface + ' down' if self.verbose: print cmd misc.Run(cmd) @@ -381,7 +390,14 @@ class Interface(object): """ if not self.iface: return - + + for val in [ip, netmask, broadcast]: + if not val: + continue + if not misc.IsValidIP(val): + print 'WARNING: Invalid IP address found, aborting!' + return False + cmd = ''.join(['ifconfig ', self.iface, ' ']) if ip: cmd = ''.join([cmd, ip, ' ']) @@ -492,7 +508,9 @@ class Interface(object): A string representing the result of the DHCP command. See _check_dhcp_result for the possible values. - """ + """ + if not self.iface: return False + cmd = self.DHCP_CMD + " " + self.iface if self.verbose: print cmd pipe = misc.Run(cmd, include_stderr=True, return_pipe=True) @@ -507,13 +525,13 @@ class Interface(object): def ReleaseDHCP(self): """ Release the DHCP lease for this interface. """ + if not self.iface: return False cmd = self.DHCP_RELEASE + " " + self.iface + " 2>/dev/null" misc.Run(cmd) def FlushRoutes(self): """ Flush all network routes. """ - if not self.iface: - return + if not self.iface: return False if self.IP_FOUND and self.flush_tool != misc.ROUTE: cmd = "ip route flush dev " + self.iface else: @@ -528,6 +546,9 @@ class Interface(object): gw -- gateway of the default route in dotted quad form """ + if not misc.IsValidIP(gw): + print 'WARNING: Invalid gateway found. Aborting!' + return False cmd = 'route add default gw ' + gw if self.verbose: print cmd misc.Run(cmd) @@ -539,6 +560,7 @@ class Interface(object): The IP address of the interface in dotted quad form. """ + if not self.iface: return False if fast: return self._fast_get_ip() cmd = 'ifconfig ' + self.iface @@ -572,6 +594,7 @@ class Interface(object): True if the interface is up, False otherwise. """ + if not self.iface: return False if fast: return self._fast_is_up() cmd = "ifconfig " + self.iface @@ -623,8 +646,7 @@ class WiredInterface(Interface): True if a link is detected, False otherwise. """ - if not self.iface: - return False + if not self.iface: return False if self.ETHTOOL_FOUND and self.link_detect != misc.MIITOOL: return self._eth_get_plugged_in(fast) elif self.MIITOOL_FOUND: @@ -720,6 +742,7 @@ class WiredInterface(Interface): reg = struct.unpack('16shhhh', result)[-1] return bool(reg & 0x0004) + class WirelessInterface(Interface): """ Control a wireless network interface. """ def __init__(self, iface, verbose=False, wpa_driver='wext'): @@ -736,7 +759,7 @@ class WirelessInterface(Interface): def SetWpaDriver(self, driver): """ Sets the wpa_driver. """ - self.wpa_driver = driver + self.wpa_driver = _sanitize_string(driver) def SetEssid(self, essid): """ Set the essid of the wireless interface. @@ -745,6 +768,7 @@ class WirelessInterface(Interface): essid -- essid to set the interface to """ + essid = _sanitize_string(essid) cmd = 'iwconfig %s essid "%s"' % (self.iface, essid) if self.verbose: print cmd misc.Run(cmd) @@ -762,6 +786,7 @@ class WirelessInterface(Interface): True if the killswitch is enabled, False otherwise. """ + if not self.iface: return False output = misc.Run("iwconfig " + self.iface) killswitch_pattern = re.compile('.*radio off', re.I | re.M | re.S) @@ -774,6 +799,7 @@ class WirelessInterface(Interface): def GetIwconfig(self): """ Returns the output of iwconfig for this interface. """ + if not self.iface: return "" return misc.Run("iwconfig " + self.iface) def GetNetworks(self): @@ -834,7 +860,7 @@ class WirelessInterface(Interface): try: ret = freq_dict[freq] except KeyError: - print "Couldn't determine channel number for frequency: " + freq + print "Couldn't determine channel number for frequency: " + str(freq) return ret @@ -982,6 +1008,8 @@ class WirelessInterface(Interface): mode -- mode to set the interface to """ + if not self.iface: return False + mode = _sanitize_string(mode) if mode.lower() == 'master': mode = 'managed' cmd = 'iwconfig %s mode %s' % (self.iface, mode) @@ -995,6 +1023,11 @@ class WirelessInterface(Interface): channel -- channel to set the interface to """ + if not self.iface: return False + if not channel.isdigit(): + print 'WARNING: Invalid channel found. Aborting!' + return False + cmd = 'iwconfig %s channel %s' % (self.iface, str(channel)) if self.verbose: print cmd misc.Run(cmd) @@ -1006,6 +1039,8 @@ class WirelessInterface(Interface): key -- encryption key to set """ + if not self.iface: return False + key = _sanitize_string(key) cmd = 'iwconfig %s key %s' % (self.iface, key) if self.verbose: print cmd misc.Run(cmd) @@ -1019,6 +1054,7 @@ class WirelessInterface(Interface): bssid -- bssid of the network """ + if not self.iface: return False cmd = 'iwconfig %s essid "%s"' % (self.iface, essid) if channel: cmd = ''.join([cmd, ' channel ', str(channel)]) @@ -1062,7 +1098,7 @@ class WirelessInterface(Interface): False otherwise. """ - # Right now there's no way to do this for these drivers + # Right now there's no way to do this for ralink drivers if self.wpa_driver == RALINK_DRIVER or not self.WPA_CLI_FOUND: return True @@ -1076,6 +1112,10 @@ class WirelessInterface(Interface): print 'WPA_CLI RESULT IS', result if not result: + print "WARNING: Received an unexpected result from wpa_cli!" + \ + "\nMake sure you're using the right wpa_supplicant " + \ + "driver (you probably want wext).\nIf the problem " + \ + "persists, please file a bug report." return False if result == "COMPLETED": return True @@ -1162,8 +1202,9 @@ class WirelessInterface(Interface): if self.verbose: print cmd misc.Run(cmd) - def GetBSSID(self, iwconfig=None, fast=True): + def GetBSSID(self, iwconfig="", fast=True): """ Get the MAC address for the interface. """ + if not self.iface: return "" if fast: return self._fast_get_bssid() else: @@ -1198,6 +1239,7 @@ class WirelessInterface(Interface): The signal strength. """ + if not self.iface: return -1 if fast: return self._get_signal_strength_fast() @@ -1254,6 +1296,7 @@ class WirelessInterface(Interface): The dBm signal strength. """ + if not self.iface: return -100 if fast: return self._get_dbm_strength_fast() if iwconfig: @@ -1286,6 +1329,7 @@ class WirelessInterface(Interface): The current network essid. """ + if not self.iface: return "" if fast: return self._get_essid_fast() @@ -1308,4 +1352,4 @@ class WirelessInterface(Interface): return None return buff.strip('\x00') - +