FIX: do not set a separate logging level for the console
[wifi-radar.git] / wifi-radar
blob0a94290b8e20916114095e9d954b0c8e63794405
1 #!/usr/bin/python -OO
3 # A utility for managing WiFi profiles on GNU/Linux.
5 # Copyright (C) 2004-2005 Ahmad Baitalmal <ahmad@baitalmal.com>
6 # Copyright (C) 2005 Nicolas Brouard <nicolas.brouard@mandrake.org>
7 # Copyright (C) 2005-2009 Brian Elliott Finley <brian@thefinleys.com>
8 # Copyright (C) 2006 David Decotigny <com.d2@free.fr>
9 # Copyright (C) 2006 Simon Gerber <gesimu@gmail.com>
10 # Copyright (C) 2006-2007 Joey Hurst <jhurst@lucubrate.org>
11 # Copyright (C) 2006, 2009 Ante Karamatic <ivoks@ubuntu.com>
12 # Copyright (C) 2009 Sean Robinson <seankrobinson@gmail.com>
14 # This program is free software; you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; version 2 of the License.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License in LICENSE.GPL for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 # http://wifi-radar.berlios.de
29 # See CREDITS file for more contributors.
30 # See HISTORY file for, well, changes.
32 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
33 # turn on console debugging.
35 import ConfigParser
36 import errno
37 import gtk
38 import logging
39 import logging.handlers
40 import os
41 import Queue
42 import re
43 import string
44 import sys
45 import tempfile
46 import threading
47 from shutil import move
48 from signal import SIGTERM
49 from subprocess import call, Popen, PIPE, STDOUT
50 from time import sleep
51 from types import *
53 WIFI_RADAR_VERSION = "0.0.0"
56 # Where the conf file should live could be different for your distro. Please change
57 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
59 CONF_FILE = "/etc/wifi-radar/wifi-radar.conf"
61 os.environ['LC_MESSAGES'] = 'C'
64 ####################################################################################################
65 ####################################################################################################
67 # Gets the network interface device
69 #Parameters:
71 # 'device' -- string - The proposed network device to use
73 #Returns:
75 # string -- The actual network device to use
76 def get_network_device(device):
77 #print "get_network_device: %s" % (device, )
78 if device != "auto_detect":
79 return confFile.get_opt('DEFAULT.interface')
80 else:
81 # auto detect network device
82 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
83 # If no devices are found, default to eth1.
84 # call iwconfig command and read output
85 iwconfig_info = Popen(confFile.get_opt('DEFAULT.iwconfig_command'), shell=True, stdout=PIPE, stderr=STDOUT).stdout
86 wireless_devices = [(x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
87 if len(wireless_devices) > 0:
88 return wireless_devices[0]
89 logger.critical("No WiFi device found, please set this in the preferences.")
90 return ""
92 # Return a blank profile
94 #Parameters:
96 # none
98 #Returns:
100 # dictionary -- An AP profile with defaults set.
101 def get_new_profile():
102 return { 'known': False,
103 'available': False,
104 'encrypted': False,
105 'essid': '',
106 'bssid': '',
107 'roaming': False,
108 'protocol': 'g',
109 'signal': 0,
110 'channel': 'auto',
111 'con_prescript': '',
112 'con_postscript': '',
113 'dis_prescript': '',
114 'dis_postscript': '',
115 'key': '',
116 'mode': 'auto',
117 'security': '',
118 'use_wpa': False,
119 'wpa_driver': '',
120 'use_dhcp': True,
121 'ip': '',
122 'netmask': '',
123 'gateway': '',
124 'domain': '',
125 'dns1': '',
126 'dns2': ''
129 # Combine essid and bssid to make a config file section name
131 #Parameters:
133 # 'essid' -- string - AP ESSID
135 # 'bssid' -- string - AP BSSID
137 #Returns:
139 # string -- the bssid concatenated to a colon, concatenated to the essid
140 def make_section_name( essid, bssid ):
141 return essid + ':' + bssid
143 # Split a config file section name into an essid and a bssid
145 #Parameters:
147 # 'section' -- string - Config file section name
149 #Returns:
151 # list -- the essid and bssid
152 def split_section_name( section ):
153 parts = re.split(':', section)
154 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
156 # Run commands through the shell
158 #Parameters:
160 # 'command' -- tuple - The command and arguments to run.
162 # 'environment' -- dictionary - Environment variables (as keys) and their values.
164 #Returns:
166 # boolean -- True on success, otherwise, False
167 def shellcmd( command, environment = None ):
168 try:
169 env_tmp = os.environ
170 env_tmp.update(environment)
171 command = ' '.join(command)
172 return_code = call(command, shell=True, env=env_tmp)
173 if return_code >= 0:
174 return True
175 else:
176 print >>sys.stderr, "Child was terminated by signal", -return_code
177 except OSError, exception:
178 print >>sys.stderr, "Execution failed:", exception
179 return False
181 # Speak feedback message to user
183 #Parameters:
185 # 'words' -- string - Message to speak to user
187 #Returns:
189 # nothing
190 def say( words ):
191 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
192 words = words.replace( "\"", "\\\"" )
193 shellcmd([confFile.get_opt('DEFAULT.speak_command'), words])
195 # Scan for a limited time and return AP names and bssid found.
196 # Access points we find will be put on the outgoing Queue, apQueue.
198 #Parameters:
200 # 'confFile' -- ConfigFile - Config file object
202 # 'apQueue' -- Queue - Queue on which to put AP profiles
204 # 'commandQueue' -- Queue - Queue from which to read commands
206 # 'logger' -- Logger - Python's logging facility
208 #Returns:
210 # nothing
211 def scanning_thread(confFile, apQueue, commandQueue, logger, exit_event):
212 logger.info("Begin thread.")
213 # Setup our essid pattern matcher
214 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
215 bssid_pattern = re.compile( "Address\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
216 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abgn]+)", re.I | re.M | re.S )
217 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
218 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
219 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
220 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
222 access_points = {}
223 command = "scan"
224 while True:
225 try:
226 command = commandQueue.get_nowait()
227 logger.info("received command: %s" % (command, ))
228 command_read = True
229 except Queue.Empty:
230 command_read = False
231 device = get_network_device(confFile.get_opt('DEFAULT.interface'))
232 if command == "scan":
233 logger.debug("Beginning scan pass")
234 # Some cards need to have the interface up to scan
235 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
236 # call ifconfig command and wait for return
237 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), device, 'up'])
238 # update the signal strengths
239 try:
240 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), device, 'scan'], stdout=PIPE).stdout.read()
241 except OSError, (errno, strerror):
242 if errno == 2:
243 logger.critical("iwlist command not found, please set this in the preferences.")
244 scandata = ""
245 # zero out the signal levels for all access points
246 for bssid in access_points:
247 access_points[bssid]['signal'] = 0
248 # split the scan data based on the address line
249 hits = scandata.split(' - ')
250 for hit in hits:
251 # set the defaults for profile template
252 profile = get_new_profile()
253 m = essid_pattern.search( hit )
254 if m:
255 # we found an essid
256 profile['essid'] = m.groups()[1]
257 m = bssid_pattern.search( hit ) # get BSSID from scan
258 if m: profile['bssid'] = m.groups()[1]
259 m = protocol_pattern.search( hit ) # get protocol from scan
260 if m: profile['protocol'] = m.groups()[1]
261 m = mode_pattern.search( hit ) # get mode from scan
262 if m: profile['mode'] = m.groups()[1]
263 m = channel_pattern.search( hit ) # get channel from scan
264 if m: profile['channel'] = m.groups()[1]
265 m = enckey_pattern.search( hit ) # get encryption key from scan
266 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
267 m = signal_pattern.search( hit ) # get signal strength from scan
268 if m: profile['signal'] = m.groups()[1]
269 access_points[ profile['bssid'] ] = profile
270 for bssid in access_points:
271 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
272 # Put all, now or previously, sensed access_points into apQueue
273 try:
274 logger.debug("Scanned profile: %s" % (access_points[ bssid ], ))
275 apQueue.put_nowait( access_points[bssid] )
276 except Queue.Full:
277 pass
278 if command_read:
279 commandQueue.task_done()
280 if exit_event.isSet():
281 logger.info("Exiting.")
282 return
283 if device.find('ath') == 0:
284 sleep( 3 )
285 else:
286 sleep( 1 )
289 # Manage a connection; including reporting connection state,
290 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
291 class ConnectionManager():
292 # Create a new connection manager which can read a config file and send to scanning thread
293 # command Queue. A new manager checks for a pre-existing connection and takes
294 # its AP profile from the ESSID and BSSID to which it is currently attached.
296 #Parameters:
298 # 'confFile' -- ConfigFile - Config file object
300 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
302 # 'logger' -- Logger - Python's logging facility
304 #Returns:
306 # ConnectionManager instance
307 def __init__( self, confFile, commandQueue, logger ):
308 self.confFile = confFile
309 self.commQueue = commandQueue
310 self.logger = logger
311 # is connection running?
312 self.state = False
313 if self.get_current_ip():
314 self.state = True
315 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
317 # Change the interface state: up or down.
319 #Parameters:
321 # 'state' -- string - The state to which to change the interface.
323 #Returns:
325 # nothing
326 def if_change( self, state ):
327 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
328 self.logger.info("changing interface state to %s" % (state, ))
329 # call ifconfig command and wait for return
330 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface')), state])
332 # Connect to the specified AP.
334 #Parameters:
336 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
338 # 'status' -- status implementer - Object which implements status interface.
340 #Returns:
342 # nothing
343 def connect_to_network( self, profile, status ):
344 self.profile = profile
345 if self.profile['bssid'] == '':
346 raise TypeError("Empty AP address")
347 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
348 say( msg )
349 self.logger.info(msg)
350 device = get_network_device(self.confFile.get_opt('DEFAULT.interface'))
351 # ready to dance
352 # Let's run the connection prescript
353 if self.profile['con_prescript'].strip() != '':
354 # got something to execute
355 # run connection prescript through shell and wait for return
356 self.logger.info("executing connection prescript: %s" % (self.profile['con_prescript'], ))
357 shellcmd([self.profile['con_prescript']], environment={"WIFIRADAR_IF": device})
358 status.show()
359 # Some cards need to have the interface up
360 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
361 self.if_change('up')
362 # Start building iwconfig command line, command
363 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
364 iwconfig_command.append(device)
365 # Setting essid
366 iwconfig_command.append( 'essid' )
367 iwconfig_command.append( "'" + self.profile['essid'] + "'" )
368 # Setting nick
369 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
370 # Setting key
371 iwconfig_command.append( 'key' )
372 if self.profile['key'] == '':
373 iwconfig_command.append( 'off' )
374 else:
375 # Setting this stops association from working, so remove it for now
376 #if self.profile['security'] != '':
377 #iwconfig_command.append(self.profile['security'])
378 iwconfig_command.append( "'" + self.profile['key'] + "'" )
379 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
380 # Setting mode
381 if self.profile['mode'].lower() == 'master' or self.profile['mode'].lower() == 'auto':
382 self.profile['mode'] = 'Managed'
383 iwconfig_command.append( 'mode' )
384 iwconfig_command.append( self.profile['mode'] )
385 # Setting channel
386 if self.profile['channel'] != '':
387 iwconfig_command.append( 'channel' )
388 iwconfig_command.append( self.profile['channel'] )
389 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
390 iwconfig_command.append( 'ap' )
391 iwconfig_command.append( self.profile['bssid'] )
392 # Some cards require a commit
393 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
394 iwconfig_command.append( 'commit' )
395 self.logger.info("iwconfig_command: %s" % (iwconfig_command, ))
396 # call iwconfig command and wait for return
397 if not shellcmd(iwconfig_command): return
398 # Now normal network stuff
399 # Kill off any existing DHCP clients running
400 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
401 self.logger.info("Killing existing DHCP...")
402 try:
403 if self.confFile.get_opt('DHCP.kill_args') != '':
404 # call DHCP client kill command and wait for return
405 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
406 else:
407 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
408 except OSError:
409 print "failed to kill DHCP client"
410 sys.exit()
411 finally:
412 print "Stale pid file. Removing..."
413 os.remove(self.confFile.get_opt('DHCP.pidfile'))
414 # Kill off any existing WPA supplicants running
415 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
416 self.logger.info("Killing existing WPA supplicant...")
417 try:
418 if not self.confFile.get_opt('WPA.kill_command') != '':
419 # call WPA supplicant kill command and wait for return
420 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
421 else:
422 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
423 except OSError:
424 print "failed to kill WPA supplicant"
425 sys.exit()
426 finally:
427 print "Stale pid file. Removing..."
428 os.remove(self.confFile.get_opt('WPA.pidfile'))
429 try:
430 self.commQueue.put("pause")
431 except Queue.Full:
432 pass
433 # Begin WPA supplicant
434 if self.profile['use_wpa'] :
435 self.logger.info("WPA args: %s" % (self.confFile.get_opt('WPA.args'), ))
436 status.update_message("WPA supplicant starting")
437 if sys.modules.has_key("gtk"):
438 while gtk.events_pending():
439 gtk.main_iteration(False)
440 # call WPA supplicant command and do not wait for return
441 try:
442 wpa_proc = shellcmd([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args')])
443 if sys.modules.has_key("gtk"):
444 while gtk.events_pending():
445 gtk.main_iteration(False)
446 #sleep(2)
447 except OSError, (errno, strerror):
448 if errno == 2:
449 logger.critical("WPA supplicant not found, please set this in the preferences.")
450 if self.profile['use_dhcp'] :
451 self.logger.debug("Disable iwlist while dhcp in progress...")
452 status.update_message("Acquiring IP Address (DHCP)")
453 if sys.modules.has_key("gtk"):
454 while gtk.events_pending():
455 gtk.main_iteration(False)
456 # call DHCP client command and do not wait for return
457 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
458 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
459 dhcp_command.append(device)
460 self.logger.info("dhcp_command: %s" % (dhcp_command, ))
461 try:
462 dhcp_proc = Popen(dhcp_command, stdout=None)
463 except OSError, (errno, strerror):
464 if errno == 2:
465 logger.critical("DHCP client not found, please set this in the preferences.")
466 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
467 tick = 0.25
468 waiting = dhcp_proc.poll()
469 while waiting == None:
470 waiting = dhcp_proc.poll()
471 if timer < 0:
472 os.kill(dhcp_proc.pid, SIGTERM)
473 break
474 if sys.modules.has_key("gtk"):
475 while gtk.events_pending():
476 gtk.main_iteration(False)
477 timer -= tick
478 sleep(tick)
479 if not self.get_current_ip():
480 status.update_message("Could not get IP address!")
481 if sys.modules.has_key("gtk"):
482 while gtk.events_pending():
483 gtk.main_iteration(False)
484 sleep(1)
485 if self.state:
486 self.disconnect_interface()
487 status.hide()
488 return
489 else:
490 status.update_message("Got IP address. Done.")
491 self.state = True
492 if sys.modules.has_key("gtk"):
493 while gtk.events_pending():
494 gtk.main_iteration(False)
495 sleep(2)
496 else:
497 ifconfig_command= "%s %s down; %s %s %s netmask %s" % ( self.confFile.get_opt('DEFAULT.ifconfig_command'), device, self.confFile.get_opt('DEFAULT.ifconfig_command'), device, self.profile['ip'], self.profile['netmask'] )
498 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
499 resolv_contents = ''
500 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
501 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
502 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
503 if ( resolv_contents != '' ):
504 resolv_file=open('/etc/resolv.conf', 'w')
505 resolv_file.write(s)
506 resolv_file.close
507 if not shellcmd([ifconfig_command]): return
508 if not shellcmd([route_command]): return
509 self.state = True
510 # Re-enable iwlist
511 try:
512 self.commQueue.put("scan")
513 except Queue.Full:
514 pass
515 # Let's run the connection postscript
516 con_postscript = self.profile['con_postscript']
517 if self.profile['con_postscript'].strip() != '':
518 self.logger.info("executing connection postscript: %s" % (self.profile['con_postscript'], ))
519 shellcmd([self.profile['con_postscript']],
520 environment = { "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
521 "WIFIRADAR_ESSID": self.get_current_essid() or '',
522 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
523 "WIFIRADAR_IF": device or ''
526 status.hide()
528 # Disconnect from the AP with which a connection has been established/attempted.
530 #Parameters:
532 # nothing
534 #Returns:
536 # nothing
537 def disconnect_interface( self ):
538 msg = "Disconnecting"
539 say( msg )
540 self.logger.info(msg)
541 device = get_network_device(self.confFile.get_opt('DEFAULT.interface'))
542 # Pause scanning while manipulating card
543 try:
544 self.commQueue.put("pause")
545 self.commQueue.join()
546 except Queue.Full:
547 pass
548 if self.state:
549 self.profile = self.confFile.get_profile(make_section_name(self.get_current_essid(), self.get_current_bssid()))
550 if not self.profile:
551 self.profile = self.confFile.get_profile(make_section_name(self.get_current_essid(), ''))
552 if not self.profile:
553 raise KeyError
554 # Let's run the disconnection prescript
555 if self.profile['dis_prescript'].strip() != '':
556 self.logger.info("executing disconnection prescript: %s" % (self.profile['dis_prescript'], ))
557 shellcmd([self.profile['dis_prescript']],
558 environment = { "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
559 "WIFIRADAR_ESSID": self.get_current_essid() or '',
560 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
561 "WIFIRADAR_IF": device or ''
564 self.logger.info("Kill off any existing DHCP clients running...")
565 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
566 self.logger.info("Killing existing DHCP...")
567 try:
568 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
569 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
570 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
571 dhcp_command.append(device)
572 self.logger.info("DHCP command: %s" % (dhcp_command, ))
573 # call DHCP client command and wait for return
574 if not shellcmd(dhcp_command): return
575 else:
576 self.logger.info("Killing DHCP manually...")
577 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
578 except OSError:
579 print "failed to kill DHCP client"
580 self.logger.info("Kill off any existing WPA supplicants running...")
581 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
582 self.logger.info("Killing existing WPA supplicant...")
583 try:
584 if self.confFile.get_opt('WPA.kill_command') != '':
585 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
586 if not shellcmd(wpa_command): return
587 else:
588 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
589 except OSError:
590 print "failed to kill WPA supplicant"
591 self.logger.info("Let's clear out the wireless stuff")
592 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), device, 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
593 self.logger.info("Now take the interface down")
594 self.logger.info("Since it may be brought back up by the next scan, lets unset its IP")
595 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), device, '0.0.0.0'])
596 # taking down the interface too quickly can crash my system, so pause a moment
597 sleep(1)
598 self.if_change('down')
599 # Let's run the disconnection postscript
600 if self.profile['dis_postscript'].strip() != '':
601 self.logger.info("executing disconnection postscript: %s" % (self.profile['dis_postscript'], ))
602 shellcmd([self.profile['dis_postscript']], environment={"WIFIRADAR_IF": device})
603 self.state = False
604 self.logger.info("Disconnect complete.")
605 # Begin scanning again
606 try:
607 self.commQueue.put("scan")
608 except Queue.Full:
609 pass
611 # Returns the current IP, if any, by calling ifconfig.
613 #Parameters:
615 # nothing
617 #Returns:
619 # string or None -- the IP address or None (if no there is no current connection)
620 def get_current_ip( self ):
621 ifconfig_command = [confFile.get_opt('DEFAULT.ifconfig_command'), get_network_device(confFile.get_opt('DEFAULT.interface'))]
622 try:
623 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
624 except OSError, (errno, strerror):
625 if errno == 2:
626 logger.critical("ifconfig command not found, please set this in the preferences.")
627 ifconfig_info = open("/dev/null", "r")
628 # Be careful to the language (inet adr: in French for example)
630 # Hi Brian
632 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
633 # There the string in ifconfig is inet Adresse for the IP which isn't
634 # found by the current get_current_ip function in wifi-radar. I changed
635 # the according line (#289; gentoo, v1.9.6-r1) to
636 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
637 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
639 # I'd be happy if you could incorporate this small change because as now
640 # I've got to change the file every time it is updated.
642 # Best wishes
644 # Simon
645 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
646 line = ifconfig_info.read()
647 if ip_re.search( line ):
648 return ip_re.search( line ).group(1)
649 return None
651 # Returns the current ESSID, if any, by calling iwconfig.
653 #Parameters:
655 # nothing
657 #Returns:
659 # string or None -- the ESSID or None (if no there is no current association)
660 def get_current_essid( self ):
661 """Returns the current ESSID if any by calling iwconfig"""
662 try:
663 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface'))], stdout=PIPE, stderr=STDOUT).stdout
664 except OSError, (errno, strerror):
665 if errno == 2:
666 logger.critical("iwconfig command not found, please set this in the preferences.")
667 iwconfig_info = open("/dev/null", "r")
668 # Be careful to the language (inet adr: in French for example)
669 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
670 line = iwconfig_info.read()
671 if essid_re.search( line ):
672 return essid_re.search( line ).group(2)
673 return None
675 # Returns the current BSSID, if any, by calling iwconfig.
677 #Parameters:
679 # nothing
681 #Returns:
683 # string or None -- the BSSID or None (if no there is no current association)
684 def get_current_bssid( self ):
685 """Returns the current BSSID if any by calling iwconfig"""
686 try:
687 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface'))], stdout=PIPE, stderr=STDOUT).stdout
688 except OSError, (errno, strerror):
689 if errno == 2:
690 logger.critical("iwconfig command not found, please set this in the preferences.")
691 iwconfig_info = open("/dev/null", "r")
692 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
693 line = iwconfig_info.read()
694 if bssid_re.search( line ):
695 return bssid_re.search( line ).group(2)
696 return None
700 # The main user interface window for WiFi Radar. This class also is the control
701 # center for most of the rest of the operations.
702 class radar_window:
703 # Create a new radar_window.
705 #Parameters:
707 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
709 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
711 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
713 # 'logger' -- Logger - Python's logging facility
715 #Returns:
717 # radar_window instance
718 def __init__(self, confFile, apQueue, commQueue, logger, exit_event):
719 global signal_xpm_none
720 global signal_xpm_low
721 global signal_xpm_barely
722 global signal_xpm_ok
723 global signal_xpm_best
724 global known_profile_icon
725 global unknown_profile_icon
726 global wifi_radar_icon
728 self.confFile = confFile
729 self.apQueue = apQueue
730 self.commandQueue = commQueue
731 self.logger = logger
732 self.access_points = {}
733 self.exit_event = exit_event
734 self.connection = None
736 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
737 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
738 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
739 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
740 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
741 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
742 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
743 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
744 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
745 self.window.set_icon( icon )
746 self.window.set_border_width( 10 )
747 self.window.set_size_request( 550, 300 )
748 self.window.set_title( "WiFi Radar" )
749 self.window.connect( 'delete_event', self.delete_event )
750 self.window.connect( 'destroy', self.destroy )
751 # let's create all our widgets
752 self.current_network = gtk.Label()
753 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
754 self.current_network.show()
755 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
756 self.close_button.show()
757 self.close_button.connect( 'clicked', self.delete_event, None )
758 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
759 self.about_button.show()
760 self.about_button.connect( 'clicked', self.show_about_info, None )
761 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
762 self.preferences_button.show()
763 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
764 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
765 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
766 self.plist = gtk.TreeView( self.pstore )
767 # The icons column, known and encryption
768 self.pix_cell = gtk.CellRendererPixbuf()
769 self.wep_cell = gtk.CellRendererPixbuf()
770 self.icons_cell = gtk.CellRendererText()
771 self.icons_col = gtk.TreeViewColumn()
772 self.icons_col.pack_start( self.pix_cell, False )
773 self.icons_col.pack_start( self.wep_cell, False )
774 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
775 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
776 self.plist.append_column( self.icons_col )
777 # The AP column
778 self.ap_cell = gtk.CellRendererText()
779 self.ap_col = gtk.TreeViewColumn( "Access Point" )
780 self.ap_col.pack_start( self.ap_cell, True )
781 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
782 self.plist.append_column( self.ap_col )
783 # The signal column
784 self.sig_cell = gtk.CellRendererPixbuf()
785 self.signal_col = gtk.TreeViewColumn( "Signal" )
786 self.signal_col.pack_start( self.sig_cell, True )
787 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
788 self.plist.append_column( self.signal_col )
789 # The mode column
790 self.mode_cell = gtk.CellRendererText()
791 self.mode_col = gtk.TreeViewColumn( "Mode" )
792 self.mode_col.pack_start( self.mode_cell, True )
793 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
794 self.plist.append_column( self.mode_col )
795 # The protocol column
796 self.prot_cell = gtk.CellRendererText()
797 self.protocol_col = gtk.TreeViewColumn( "802.11" )
798 self.protocol_col.pack_start( self.prot_cell, True )
799 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
800 self.plist.append_column( self.protocol_col )
801 # The channel column
802 self.channel_cell = gtk.CellRendererText()
803 self.channel_col = gtk.TreeViewColumn( "Channel" )
804 self.channel_col.pack_start( self.channel_cell, True )
805 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
806 self.plist.append_column( self.channel_col )
807 # DnD Ordering
808 self.plist.set_reorderable( True )
809 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
810 self.pstore.connect( 'row-deleted', self.update_auto_profile_order )
811 # enable/disable buttons based on the selected network
812 self.selected_network = self.plist.get_selection()
813 self.selected_network.connect( 'changed', self.on_network_selection, None )
814 # the list scroll bar
815 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
816 sb.show()
817 self.plist.show()
818 # Add New button
819 self.new_button = gtk.Button( "_New" )
820 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
821 self.new_button.show()
822 # Add Configure button
823 self.edit_button = gtk.Button( "C_onfigure" )
824 self.edit_button.connect( 'clicked', self.edit_profile, None )
825 self.edit_button.show()
826 self.edit_button.set_sensitive(False)
827 # Add Delete button
828 self.delete_button = gtk.Button( "_Delete" )
829 self.delete_button.connect('clicked', self.delete_profile_with_check, None)
830 self.delete_button.show()
831 self.delete_button.set_sensitive(False)
832 # Add Connect button
833 self.connect_button = gtk.Button( "Co_nnect" )
834 self.connect_button.connect( 'clicked', self.connect_profile, None )
835 # Add Disconnect button
836 self.disconnect_button = gtk.Button( "D_isconnect" )
837 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
838 # lets add our widgets
839 rows = gtk.VBox( False, 3 )
840 net_list = gtk.HBox( False, 0 )
841 listcols = gtk.HBox( False, 0 )
842 prows = gtk.VBox( False, 0 )
843 # lets start packing
844 # the network list
845 net_list.pack_start( self.plist, True, True, 0 )
846 net_list.pack_start( sb, False, False, 0 )
847 # the rows level
848 rows.pack_start( net_list , True, True, 0 )
849 rows.pack_start( self.current_network, False, True, 0 )
850 # the list columns
851 listcols.pack_start( rows, True, True, 0 )
852 listcols.pack_start( prows, False, False, 5 )
853 # the list buttons
854 prows.pack_start( self.new_button, False, False, 2 )
855 prows.pack_start( self.edit_button, False, False, 2 )
856 prows.pack_start( self.delete_button, False, False, 2 )
857 prows.pack_end( self.connect_button, False, False, 2 )
858 prows.pack_end( self.disconnect_button, False, False, 2 )
860 self.window.action_area.pack_start( self.about_button )
861 self.window.action_area.pack_start( self.preferences_button )
862 self.window.action_area.pack_start( self.close_button )
864 rows.show()
865 prows.show()
866 listcols.show()
867 self.window.vbox.add( listcols )
868 self.window.vbox.set_spacing( 3 )
869 self.window.show_all()
871 # Now, immediately hide these two. The proper one will be
872 # displayed later, based on interface state. -BEF-
873 self.disconnect_button.hide()
874 self.connect_button.hide()
875 self.connect_button.set_sensitive(False)
877 # set up connection manager for later use
878 self.connection = ConnectionManager( self.confFile, self.commandQueue, self.logger )
879 # set up status window for later use
880 self.status_window = StatusWindow( self )
881 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
883 # Add our known profiles in order
884 for profile_name in self.confFile.auto_profile_order:
885 profile_name = profile_name.strip()
886 self.access_points[profile_name] = self.confFile.get_profile(profile_name)
887 wep = None
888 if self.access_points[profile_name]['encrypted']:
889 wep = gtk.STOCK_DIALOG_AUTHENTICATION
890 if self.access_points[profile_name]['roaming']:
891 ap_name = self.access_points[profile_name]['essid'] + "\n" + ' Multiple APs'
892 else:
893 ap_name = self.access_points[profile_name]['essid'] + "\n" + self.access_points[profile_name]['bssid']
894 self.pstore.append([ap_name, self.known_profile_icon, self.access_points[profile_name]['known'], self.access_points[profile_name]['available'], wep, self.signal_none_pb, self.access_points[profile_name]['mode'], self.access_points[profile_name]['protocol'], self.access_points[profile_name]['channel'] ] )
895 # This is the first run (or, at least, no config file was present), so pop up the preferences window
896 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
897 self.confFile.remove_option('DEFAULT', 'new_file')
898 self.edit_preferences(self.preferences_button)
900 # Begin running radar_window in Gtk event loop.
902 #Parameters:
904 # nothing
906 #Returns:
908 # nothing
909 def main( self ):
910 gtk.main()
912 # Quit application.
914 #Parameters:
916 # 'widget' -- gtk.Widget - The widget sending the event.
918 #Returns:
920 # nothing
921 def destroy( self, widget = None):
922 if self.status_window:
923 self.status_window.destroy()
924 gtk.main_quit()
926 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
928 #Parameters:
930 # 'widget' -- gtk.Widget - The widget sending the event.
932 # 'data' -- tuple - list of arbitrary arguments (not used)
934 #Returns:
936 # boolean -- always return False (i.e. do not propigate the signal which called)
937 def delete_event( self, widget, data = None ):
938 # Let other threads know it is time to exit
939 self.exit_event.set()
940 # Wait for all other threads to exit before continuing
941 while threading.activeCount() > 1:
942 sleep(0.25)
943 self.destroy()
944 return False
946 # Update the current ip and essid shown to user.
948 #Parameters:
950 # nothing
952 #Returns:
954 # nothing
955 def update_network_info(self):
956 if self.connection and self.connection.state:
957 self.current_network.set_text( "Connected to %s\nIP Address %s" % (make_section_name(self.connection.get_current_essid(), self.connection.get_current_bssid()), self.connection.get_current_ip()))
958 else:
959 self.current_network.set_text("Not Connected.")
961 # Set the state of connect/disconnect buttons based on whether we have a connection.
963 #Parameters:
965 # nothing
967 #Returns:
969 # nothing
970 def update_connect_buttons(self):
971 if self.connection and self.connection.state:
972 self.connect_button.hide()
973 self.disconnect_button.show()
974 else:
975 self.disconnect_button.hide()
976 self.connect_button.show()
978 # Updates the on-screen profiles list.
980 #Parameters:
982 # 'ap' -- dictionary -- The AP found by scanning_thread.
984 #Returns:
986 # nothing
987 def update_plist_items(self, ap):
988 # Check for roaming profile.
989 ap_name = make_section_name(ap['essid'], '')
990 profile = self.confFile.get_profile(ap_name)
991 prow_iter = self.get_row_by_ap(ap['essid'], ' Multiple APs')
992 ap_display = ap['essid'] + "\n" + ' Multiple APs'
993 if not profile:
994 # Check for normal profile.
995 ap_name = make_section_name(ap['essid'], ap['bssid'])
996 profile = self.confFile.get_profile(ap_name)
997 prow_iter = self.get_row_by_ap(ap['essid'], ap['bssid'])
998 ap_display = ap['essid'] + "\n" + ap['bssid']
999 if not profile:
1000 # No profile, so make a new one.
1001 profile = get_new_profile()
1002 wep = None
1003 if prow_iter != None:
1004 # the AP is in the list of APs on the screen
1005 # Set the 'known' values; False is default, overridden to True by self.access_points
1006 ap['known'] = self.access_points[ap_name]['known']
1007 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known(ap['known']))
1008 self.pstore.set_value(prow_iter, 2, ap['known'])
1009 self.pstore.set_value(prow_iter, 3, ap['available'])
1010 if ap['encrypted']:
1011 wep = gtk.STOCK_DIALOG_AUTHENTICATION
1012 self.pstore.set_value(prow_iter, 4, wep)
1013 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal(self.access_points[ap_name]['signal']))
1014 self.pstore.set_value(prow_iter, 6, ap['mode'])
1015 self.pstore.set_value(prow_iter, 7, ap['protocol'])
1016 self.pstore.set_value(prow_iter, 8, self.access_points[ap_name]['channel'])
1017 else:
1018 # the AP is not in the list of APs on the screen
1019 self.pstore.append([ap_display, self.pixbuf_from_known(ap['known']), ap['known'], ap['available'], wep, self.pixbuf_from_signal(ap['signal']), ap['mode'], ap['protocol'], ap['channel']])
1020 #print "update_plist_items: new ap", ap[ 'essid' ], ap['bssid']
1022 # Updates the record-keeping profiles list.
1024 #Parameters:
1026 # 'ap' -- dictionary -- The AP found by scanning_thread.
1028 #Returns:
1030 # nothing
1031 def update_ap_list(self, ap):
1032 # Check for roaming profile.
1033 ap_name = make_section_name(ap['essid'], '')
1034 profile = self.confFile.get_profile(ap_name)
1035 if not profile:
1036 # Check for normal profile.
1037 ap_name = make_section_name(ap['essid'], ap['bssid'])
1038 profile = self.confFile.get_profile(ap_name)
1039 if not profile:
1040 # No profile, so make a new one.
1041 profile = get_new_profile()
1042 if self.access_points.has_key(ap_name):
1043 # This AP has been configured and should be updated
1044 self.access_points[ap_name]['known'] = profile['known']
1045 self.access_points[ap_name]['available'] = ap['available']
1046 self.access_points[ap_name]['encrypted'] = ap['encrypted']
1047 self.access_points[ap_name]['mode'] = ap['mode']
1048 self.access_points[ap_name]['protocol'] = ap['protocol']
1049 if self.access_points[ap_name]['roaming']:
1050 # Roaming
1051 if self.access_points[ap_name]['bssid'] == ap['bssid']:
1052 # Same AP for this roaming profile
1053 self.access_points[ap_name]['signal'] = ap['signal']
1054 else:
1055 # Different AP
1056 if int(self.access_points[ap_name]['signal']) < int(ap['signal']):
1057 # Stronger signal with this AP, so promote it to preferred
1058 self.access_points[ap_name]['signal'] = ap['signal']
1059 self.access_points[ap_name]['channel'] = ap['channel']
1060 self.access_points[ap_name]['bssid'] = ap['bssid']
1061 else:
1062 # Not roaming
1063 self.access_points[ap_name]['signal'] = ap['signal']
1064 self.access_points[ap_name]['channel'] = ap['channel']
1065 else:
1066 # Not seen before, begin tracking it.
1067 self.access_points[ap_name] = profile
1069 # Updates the main user interface.
1071 #Parameters:
1073 # nothing
1075 #Returns:
1077 # boolean -- always return True
1078 def update_window(self):
1079 # Indicate to PyGtk that only one Gtk thread should run here
1080 gtk.gdk.threads_enter()
1081 self.update_network_info()
1082 self.update_connect_buttons()
1083 while True:
1084 # Get APs scanned by iwlist
1085 try:
1086 ap = self.apQueue.get_nowait()
1087 except Queue.Empty:
1088 break
1089 else:
1090 self.update_ap_list(ap)
1091 self.update_plist_items(ap)
1092 # Allow other Gtk threads to run
1093 gtk.gdk.threads_leave()
1094 return True
1096 # Return the proper icon for a value of known.
1098 #Parameters:
1100 # 'known' -- boolean - Whether the AP is known (i.e. configured)
1102 #Returns:
1104 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
1105 def pixbuf_from_known( self, known ):
1106 """ return the proper icon for value of known """
1107 if known:
1108 return self.known_profile_icon
1109 else:
1110 return self.unknown_profile_icon
1112 # Return an icon indicating the signal level.
1114 #Parameters:
1116 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
1118 #Returns:
1120 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
1121 def pixbuf_from_signal( self, signal ):
1122 signal = int( signal )
1123 # shift signal up by 80 to convert dBm scale to arbitrary scale
1124 if signal < 0: signal = signal + 80
1125 #print "signal level:", signal
1126 if signal < 3:
1127 return self.signal_none_pb
1128 elif signal < 12:
1129 return self.signal_low_pb
1130 elif signal < 20:
1131 return self.signal_barely_pb
1132 elif signal < 35:
1133 return self.signal_ok_pb
1134 elif signal >= 35:
1135 return self.signal_best_pb
1136 else:
1137 return None
1139 # Return row which holds specified ESSID and BSSID.
1141 #Parameters:
1143 # 'essid' -- string - ESSID to match
1145 # 'bssid' -- string - BSSID to match
1147 #Returns:
1149 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
1150 def get_row_by_ap( self, essid, bssid ):
1151 if bssid == "roaming":
1152 for row in self.pstore:
1153 if (row[0][:row[0].find("\n")] == essid):
1154 #print "roaming match:", row.iter, essid, bssid
1155 return row.iter
1156 else:
1157 for row in self.pstore:
1158 if (row[0] == essid + "\n" + bssid):
1159 #print "normal match:", row.iter, essid, bssid
1160 return row.iter
1161 return None
1163 # Enable/disable buttons based on the selected network.
1165 #Parameters:
1167 # 'widget' -- gtk.Widget - The widget sending the event.
1169 # 'data' -- tuple - list of arbitrary arguments (not used)
1171 #Returns:
1173 # nothing
1174 def on_network_selection( self, widget, data = None ):
1175 ( store, selected_iter ) = self.selected_network.get_selected()
1176 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
1177 # if no networks are selected, disable all buttons except New
1178 # (this occurs after a drag-and-drop)
1179 if selected_iter == None:
1180 self.edit_button.set_sensitive(False)
1181 self.delete_button.set_sensitive(False)
1182 self.connect_button.set_sensitive(False)
1183 return
1184 # enable/disable buttons
1185 self.connect_button.set_sensitive(True)
1186 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1187 self.edit_button.set_sensitive(True)
1188 self.delete_button.set_sensitive(True)
1189 else:
1190 self.edit_button.set_sensitive(True)
1191 self.delete_button.set_sensitive(False)
1193 # Init and run the about dialog
1195 #Parameters:
1197 # 'widget' -- gtk.Widget - The widget sending the event.
1199 # 'data' -- tuple - list of arbitrary arguments (not used)
1201 #Returns:
1203 # nothing
1204 def show_about_info( self, widget, data=None ):
1205 about = about_dialog()
1206 about.run()
1207 about.destroy()
1209 # Init and run the preferences dialog
1211 #Parameters:
1213 # 'widget' -- gtk.Widget - The widget sending the event.
1215 # 'data' -- tuple - list of arbitrary arguments (not used)
1217 #Returns:
1219 # nothing
1220 def edit_preferences( self, widget, data=None ):
1221 # get raw strings from config file
1222 self.confFile.raw = True
1223 prefs = preferences_dialog( self, self.confFile )
1224 response = prefs.run()
1225 if response == int(gtk.RESPONSE_ACCEPT):
1226 prefs.save()
1227 prefs.destroy()
1228 # get cooked strings from config file
1229 self.confFile.raw = False
1231 # Respond to a request to create a new AP profile
1233 #Parameters:
1235 # 'widget' -- gtk.Widget - The widget sending the event.
1237 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1239 # 'data' -- tuple - list of arbitrary arguments (not used)
1241 #Returns:
1243 # boolean -- True if a profile was created and False if profile creation was canceled.
1244 def create_new_profile( self, widget, profile, data=None ):
1245 profile_editor = profile_dialog( self, profile )
1246 try:
1247 profile = profile_editor.run()
1248 except ValueError:
1249 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1250 del error_dlg
1251 return False
1252 finally:
1253 profile_editor.destroy()
1254 if profile:
1255 (store, selected_iter) = self.plist.get_selection().get_selected()
1256 if selected_iter is not None:
1257 store.remove(selected_iter)
1258 if profile['roaming']:
1259 apname = make_section_name(profile['essid'], '')
1260 else:
1261 apname = make_section_name(profile['essid'], profile['bssid'])
1262 # Check that the ap does not exist already
1263 if apname in self.confFile.profiles():
1264 error_dlg = ErrorDialog(None, "A profile for %s already exists" % (apname))
1265 del error_dlg
1266 # try again
1267 self.access_points[ apname ] = profile
1268 self.confFile.set_section( apname, profile )
1269 # if it is not in the auto_profile_order add it
1270 if apname not in self.confFile.auto_profile_order:
1271 self.confFile.auto_profile_order.insert(0, apname)
1272 # add to the store
1273 wep = None
1274 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1275 try:
1276 self.confFile.write()
1277 except IOError, (error_number, error_str):
1278 if error_number == errno.ENOENT:
1279 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1280 del error_dlg
1281 else:
1282 raise IOError(error_number, error_str)
1283 # Add AP to the list displayed to user
1284 if profile['roaming']:
1285 ap_name = profile['essid'] + "\n" + ' Multiple APs'
1286 while True:
1287 prow_iter = self.get_row_by_ap(profile['essid'], 'roaming')
1288 if prow_iter:
1289 self.pstore.remove(prow_iter)
1290 else:
1291 break
1292 else:
1293 ap_name = profile['essid'] + "\n" + profile['bssid']
1294 self.pstore.prepend([ap_name, self.pixbuf_from_known(profile['known']), profile['known'], profile['available'], wep, self.pixbuf_from_signal(profile['signal']), profile['mode'], profile['protocol'], profile['channel']])
1295 return True
1296 else:
1297 # Did not create new profile
1298 return False
1300 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1301 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1303 #Parameters:
1305 # 'widget' -- gtk.Widget - The widget sending the event.
1307 # 'data' -- tuple - list of arbitrary arguments (not used)
1309 #Returns:
1311 # nothing
1312 def edit_profile(self, widget, data=None):
1313 (store, selected_iter) = self.plist.get_selection().get_selected()
1314 if not selected_iter:
1315 # No AP is selected
1316 return
1317 (essid, bssid) = str(self.pstore.get_value(selected_iter, 0)).split("\n")
1318 if bssid == ' Multiple APs':
1319 # AP list says this is a roaming profile
1320 apname = make_section_name(essid, '')
1321 else:
1322 # AP list says this is NOT a roaming profile
1323 apname = make_section_name(essid, bssid)
1324 profile = self.confFile.get_profile(apname)
1325 if profile:
1326 # A profile was found in the config file
1327 profile['bssid'] = self.access_points[apname]['bssid']
1328 profile_editor = profile_dialog(self, profile)
1329 try:
1330 # try editing the profile
1331 edited_profile = profile_editor.run()
1332 except ValueError:
1333 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1334 del error_dlg
1335 return False
1336 finally:
1337 # Always remove profile editor window from screen
1338 profile_editor.destroy()
1339 if edited_profile:
1340 # A profile was returned by the editor
1341 old_index = None
1342 row = None
1343 if edited_profile['essid'] != profile['essid'] or edited_profile['bssid'] != profile['bssid'] or edited_profile['roaming'] != profile['roaming']:
1344 # ESSID, BSSID, or roaming was changed in profile editor
1345 try:
1346 self.commandQueue.put('pause')
1347 self.commandQueue.join()
1348 except Queue.Full:
1349 pass
1350 if profile['roaming']:
1351 # The old profile was a roaming profile
1352 old_ap = make_section_name(profile['essid'], '')
1353 else:
1354 # The old profile was NOT a roaming profile
1355 old_ap = make_section_name(profile['essid'], profile['bssid'])
1356 # Find where old profile was in auto order
1357 old_index = self.confFile.auto_profile_order.index(old_ap)
1358 # Remove old profile and get its place in AP list
1359 row = self.delete_profile(selected_iter, old_ap)
1360 self.confFile.remove_section(old_ap)
1361 try:
1362 # Add AP to the list displayed to user
1363 self.apQueue.put_nowait(edited_profile)
1364 self.commandQueue.put('scan')
1365 except Queue.Full:
1366 pass
1367 if edited_profile['roaming']:
1368 # New profile is a roaming profile
1369 apname = make_section_name(edited_profile['essid'], '')
1370 ap_display = edited_profile['essid'] + "\n" + ' Multiple APs'
1371 # Remove all other profiles that match the new profile ESSID
1372 while True:
1373 prow_iter = self.get_row_by_ap(edited_profile['essid'], 'roaming')
1374 if prow_iter:
1375 self.pstore.remove(prow_iter)
1376 else:
1377 break
1378 else:
1379 # New profile is NOT a roaming profile
1380 apname = make_section_name(edited_profile['essid'], edited_profile['bssid'])
1381 ap_display = edited_profile['essid'] + "\n" + edited_profile['bssid']
1382 # Insert the new profile in the same position as the one being replaced
1383 if old_index != None:
1384 # Old profile was in auto order list
1385 self.confFile.auto_profile_order.insert(old_index, apname)
1386 if row != None:
1387 self.pstore.insert_before(row, [ap_display, None, None, None, None, None, None, None, None])
1388 self.access_points[apname] = edited_profile
1389 self.confFile.set_section(apname, edited_profile)
1390 try:
1391 # Save updated profile to config file
1392 self.confFile.write()
1393 except IOError, (error_number, error_str):
1394 if error_number == errno.ENOENT:
1395 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1396 del error_dlg
1397 else:
1398 raise IOError(error_number, error_str)
1399 else:
1400 # The AP does not already have a profile
1401 profile = get_new_profile()
1402 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1403 self.create_new_profile( widget, profile, data )
1405 # Delete an AP profile (i.e. make profile unknown)
1407 #Parameters:
1409 # 'selected_iter' -- gtk.TreeIter - The selected row.
1411 # 'apname' -- string - The configuration file section to remove
1413 #Returns:
1415 # gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
1416 def delete_profile(self, selected_iter, apname):
1417 # Remove it
1418 del self.access_points[apname]
1419 self.confFile.remove_section(apname)
1420 self.logger.info(apname)
1421 if apname in self.confFile.auto_profile_order:
1422 self.confFile.auto_profile_order.remove(apname)
1423 self.pstore.remove(selected_iter)
1424 # Let's save our current state
1425 self.update_auto_profile_order()
1426 try:
1427 self.confFile.write()
1428 except IOError, (error_number, error_str):
1429 if error_number == errno.ENOENT:
1430 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1431 del error_dlg
1432 else:
1433 raise IOError(error_number, error_str)
1434 return selected_iter
1436 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1437 # Check with user first.
1439 #Parameters:
1441 # 'widget' -- gtk.Widget - The widget sending the event.
1443 # 'data' -- tuple - list of arbitrary arguments (not used)
1445 #Returns:
1447 # nothing
1448 def delete_profile_with_check(self, widget, data=None):
1449 (store, selected_iter) = self.plist.get_selection().get_selected()
1450 if not selected_iter: return
1451 (essid, bssid) = store.get_value(selected_iter, 0).split("\n")
1452 if bssid == ' Multiple APs':
1453 apname = make_section_name(essid, '')
1454 else:
1455 apname = make_section_name(essid, bssid)
1456 profile = self.confFile.get_profile(apname)
1457 if profile['roaming']:
1458 dlg = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Are you sure you want to delete the %s profile?" % (essid, ))
1459 else:
1460 dlg = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid))
1461 known = store.get_value( selected_iter, 1 )
1462 if not known: return
1463 res = dlg.run()
1464 dlg.destroy()
1465 del dlg
1466 if res == gtk.RESPONSE_NO:
1467 return
1468 self.delete_profile(selected_iter, apname)
1470 # Respond to a request to connect to an AP.
1472 #Parameters:
1474 # 'widget' -- gtk.Widget - The widget sending the event.
1476 # 'profile' -- dictionary - The AP profile to which to connect.
1478 # 'data' -- tuple - list of arbitrary arguments (not used)
1480 #Returns:
1482 # nothing
1483 def connect_profile( self, widget, profile, data=None ):
1484 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1485 if not selected_iter: return
1486 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1487 known = store.get_value( selected_iter, 2 )
1488 if not known:
1489 dlg = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "This network does not have a profile configured.\n\nWould you like to create one now?")
1490 res = dlg.run()
1491 dlg.destroy()
1492 del dlg
1493 if res == gtk.RESPONSE_NO:
1494 return
1495 profile = get_new_profile()
1496 profile['essid'] = essid
1497 profile['bssid'] = bssid
1498 if not self.create_new_profile( widget, profile, data ):
1499 return
1500 else:
1501 # Check for roaming profile.
1502 ap_name = make_section_name(essid, '')
1503 profile = self.confFile.get_profile(ap_name)
1504 if not profile:
1505 # Check for normal profile.
1506 ap_name = make_section_name(essid, bssid)
1507 profile = self.confFile.get_profile(ap_name)
1508 if not profile:
1509 # No configured profile
1510 return
1511 profile['bssid'] = self.access_points[ap_name]['bssid']
1512 profile['channel'] = self.access_points[ap_name]['channel']
1513 self.connection.connect_to_network(profile, self.status_window)
1515 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1517 #Parameters:
1519 # 'widget' -- gtk.Widget - The widget sending the event.
1521 # 'data' -- tuple - list of arbitrary arguments (not used)
1523 #Returns:
1525 # nothing
1526 def disconnect_profile( self, widget, data=None ):
1527 if data == "cancel":
1528 self.status_window.update_message("Canceling connection...")
1529 if sys.modules.has_key("gtk"):
1530 while gtk.events_pending():
1531 gtk.main_iteration(False)
1532 sleep(1)
1533 self.connection.disconnect_interface()
1535 # Update the config file auto profile order from the on-screen order
1537 #Parameters:
1539 # 'widget' -- gtk.Widget - The widget sending the event.
1541 # 'data' -- tuple - list of arbitrary arguments (not used)
1543 # 'data2' -- tuple - list of arbitrary arguments (not used)
1545 #Returns:
1547 # nothing
1548 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1549 # recreate the auto_profile_order
1550 auto_profile_order = []
1551 piter = self.pstore.get_iter_first()
1552 while piter:
1553 # only if it's known
1554 if self.pstore.get_value(piter, 2) == True:
1555 (essid, bssid) = self.pstore.get_value(piter, 0).split("\n")
1556 if bssid == ' Multiple APs':
1557 apname = make_section_name(essid, '')
1558 else:
1559 apname = make_section_name(essid, bssid)
1560 auto_profile_order.append(apname)
1561 piter = self.pstore.iter_next(piter)
1562 self.confFile.auto_profile_order = auto_profile_order
1563 try:
1564 self.confFile.write()
1565 except IOError, (error_number, error_str):
1566 if error_number == errno.ENOENT:
1567 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1568 del error_dlg
1569 else:
1570 raise IOError(error_number, error_str)
1573 # Button to allow user to choose a file and put value into specified gtk.Entry
1574 class file_browse_button(gtk.Button):
1575 # Create a button to simulate a File/Open
1577 #Parameters:
1579 # 'parent' -- gtk.Object -- Usually, the calling window.
1581 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1583 #Returns:
1585 # file_browse_button instance
1586 def __init__( self, parent, entry ):
1587 self.parent_window = parent
1588 self.entry = entry
1589 gtk.Button.__init__(self, "Browse", None)
1590 #self.
1591 self.browser_dialog = gtk.FileChooserDialog(None, self.parent_window, gtk.FILE_CHOOSER_ACTION_OPEN, ( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK ), None)
1592 self.connect("clicked", self.browse_files)
1594 # Show filechooser dialog and get user selection
1596 #Parameters:
1598 # 'widget' -- gtk.Widget -- The widget sending the event.
1600 #Returns:
1602 # nothing
1604 #NOTES:
1606 # updates entry value
1608 def browse_files( self, widget ):
1609 self.browser_dialog.set_filename(self.entry.get_text())
1610 self.browser_dialog.run()
1611 filename = self.browser_dialog.get_filename()
1612 if filename:
1613 self.entry.set_text(filename)
1614 self.browser_dialog.destroy()
1617 # Simple dialog to report an error to the user.
1618 class ErrorDialog:
1619 # Create a new ErrorDialog.
1621 #Parameters:
1623 # 'parent' -- gtk.Object - Usually, the calling window.
1625 # 'message' -- string - The message to display to the user.
1627 #Returns:
1629 # ErrorDialog instance
1630 def __init__( self, parent, message ):
1631 dialog = gtk.MessageDialog( parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message )
1632 dialog.run()
1633 dialog.destroy()
1634 del dialog
1637 # The preferences dialog. Edits non-profile sections of the config file.
1638 class preferences_dialog:
1639 # Create a new preferences_dialog.
1641 #Parameters:
1643 # 'parent' -- gtk.Object - Usually, the calling window.
1645 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1647 #Returns:
1649 # preferences_dialog instance
1650 def __init__( self, parent, confFile ):
1651 global wifi_radar_icon
1652 self.parent = parent
1653 self.confFile = confFile
1654 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1655 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1656 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1657 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1658 self.dialog.set_icon( icon )
1659 self.dialog.set_resizable( True )
1660 self.dialog.set_transient_for( self.parent.window )
1661 self.tooltips = gtk.Tooltips()
1663 # set up preferences widgets
1665 # build everything in a tabbed notebook
1666 self.prefs_notebook = gtk.Notebook()
1668 ### General tab
1669 self.general_page = gtk.VBox()
1670 # auto detect wireless device
1671 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1673 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1674 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1675 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1676 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1678 # network interface selecter
1679 self.w_interface = gtk.combo_box_entry_new_text()
1680 try:
1681 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE, stderr=STDOUT).stdout
1682 except OSError, (errno, strerror):
1683 if errno == 2:
1684 logger.critical("iwconfig command not found, please set this in the preferences.")
1685 iwconfig_info = ""
1686 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1687 for device in wireless_devices:
1688 if device != self.confFile.get_opt('DEFAULT.interface'):
1689 self.w_interface.append_text(device)
1690 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1691 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1692 self.w_interface.set_active(0)
1693 self.w_interface_label = gtk.Label("Wireless device")
1694 self.w_hbox1 = gtk.HBox(False, 0)
1695 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1696 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1697 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1698 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1700 # scan timeout (spin button of integers from 1 to 100)
1701 #self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,0 )
1702 #self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1703 #self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1704 #self.w_scan_timeout.set_numeric(True)
1705 #self.w_scan_timeout.set_snap_to_ticks(True)
1706 #self.w_scan_timeout.set_wrap(False)
1707 #self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1708 #self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1709 #self.w_hbox2 = gtk.HBox(False, 0)
1710 #self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1711 #self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1712 #self.general_page.pack_start(self.w_hbox2, False, False, 5)
1714 # speak up
1715 self.w_speak_up = gtk.CheckButton("Use speak-up")
1716 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1717 self.w_speak_up.connect("toggled", self.toggle_speak)
1718 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1719 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1721 # speak up command
1722 self.w_speak_cmd = gtk.Entry()
1723 self.w_speak_cmd.set_width_chars(16)
1724 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1725 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1726 self.w_speak_cmd_label = gtk.Label("Speak Command")
1727 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1728 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1729 self.w_hbox3 = gtk.HBox(False, 0)
1730 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1731 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1732 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1733 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1734 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1736 # commit required
1737 self.w_commit_required = gtk.CheckButton("Commit required")
1738 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1739 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1740 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1742 # ifup required
1743 self.w_ifup_required = gtk.CheckButton("Ifup required")
1744 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1745 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1746 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1748 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1749 ### End of General tab
1751 ### Advanced tab
1752 # table to use for layout of following command configurations
1753 self.cmds_table = gtk.Table()
1755 # ifconfig command
1756 self.ifconfig_cmd = gtk.Entry()
1757 self.ifconfig_cmd.set_width_chars(32)
1758 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1759 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1760 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1761 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1762 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1763 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1764 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1765 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1767 # iwconfig command
1768 self.iwconfig_cmd = gtk.Entry()
1769 self.iwconfig_cmd.set_width_chars(32)
1770 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1771 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1772 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1773 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1774 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1775 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1776 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1777 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1779 # iwlist command
1780 self.iwlist_cmd = gtk.Entry()
1781 self.iwlist_cmd.set_width_chars(32)
1782 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1783 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1784 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1785 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1786 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1787 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1788 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1789 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1791 # route command
1792 self.route_cmd = gtk.Entry()
1793 self.route_cmd.set_width_chars(32)
1794 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1795 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1796 self.route_cmd_label = gtk.Label("Network route configure command")
1797 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1798 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1799 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1800 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1801 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1803 # log file
1804 self.logfile_entry = gtk.Entry()
1805 self.logfile_entry.set_width_chars(32)
1806 self.tooltips.set_tip(self.logfile_entry, "The file in which to save logging info")
1807 self.logfile_entry.set_text(self.confFile.get_opt('DEFAULT.logfile'))
1808 self.logfile_label = gtk.Label("Log file")
1809 self.logfile_button = file_browse_button(self.dialog, self.logfile_entry)
1810 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1811 self.cmds_table.attach(self.logfile_label, 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1812 self.cmds_table.attach(self.logfile_entry, 2, 3, 5, 6, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1813 self.cmds_table.attach(self.logfile_button, 3, 4, 5, 6, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1815 # log level (spin button of integers from 0 to 50 by 5's)
1816 self.loglevel = gtk.SpinButton(gtk.Adjustment(self.confFile.get_opt_as_int('DEFAULT.loglevel'), 0, 50, 5, 5, 0), 1, 0)
1817 self.loglevel.set_update_policy(gtk.UPDATE_IF_VALID)
1818 self.loglevel.set_numeric(True)
1819 self.loglevel.set_snap_to_ticks(True)
1820 self.loglevel.set_wrap(False)
1821 self.tooltips.set_tip(self.loglevel, "How much detail to save in log file. Larger numbers provide less detail and smaller numbers, more detail.")
1822 self.loglevel.set_text(self.confFile.get_opt('DEFAULT.loglevel'))
1823 self.loglevel_label = gtk.Label("Log level")
1824 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1825 self.cmds_table.attach(self.loglevel_label, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1826 self.cmds_table.attach(self.loglevel, 2, 3, 6, 7, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1828 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1829 ### End of Advanced tab
1831 ### DHCP tab
1832 # table to use for layout of DHCP prefs
1833 self.dhcp_table = gtk.Table()
1835 self.dhcp_cmd = gtk.Entry()
1836 self.dhcp_cmd.set_width_chars(32)
1837 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1838 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1839 self.dhcp_cmd_label = gtk.Label("Command")
1840 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1841 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1842 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1843 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1845 self.dhcp_args = gtk.Entry()
1846 self.dhcp_args.set_width_chars(32)
1847 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1848 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1849 self.dhcp_args_label = gtk.Label("Arguments")
1850 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1851 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1853 self.dhcp_kill_args = gtk.Entry()
1854 self.dhcp_kill_args.set_width_chars(32)
1855 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1856 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1857 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1858 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1859 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1861 self.dhcp_timeout = gtk.Entry()
1862 self.dhcp_timeout.set_width_chars(32)
1863 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1864 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1865 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1866 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1867 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1869 self.dhcp_pidfile = gtk.Entry()
1870 self.dhcp_pidfile.set_width_chars(32)
1871 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1872 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1873 self.dhcp_pidfile_label = gtk.Label("PID file")
1874 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1875 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1877 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1878 ### End of DHCP tab
1880 ### WPA tab
1881 # table to use for layout of DHCP prefs
1882 self.wpa_table = gtk.Table()
1884 self.wpa_cmd = gtk.Entry()
1885 self.wpa_cmd.set_width_chars(32)
1886 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1887 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1888 self.wpa_cmd_label = gtk.Label("Command")
1889 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1890 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1891 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1892 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1894 self.wpa_args = gtk.Entry()
1895 self.wpa_args.set_width_chars(32)
1896 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1897 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1898 self.wpa_args_label = gtk.Label("Arguments")
1899 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1900 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1902 self.wpa_kill_args = gtk.Entry()
1903 self.wpa_kill_args.set_width_chars(32)
1904 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1905 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1906 self.wpa_kill_args_label = gtk.Label("Kill command")
1907 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1908 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1910 self.wpa_config = gtk.Entry()
1911 self.wpa_config.set_width_chars(32)
1912 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1913 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1914 self.wpa_config_label = gtk.Label("Configuration file")
1915 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1916 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1918 self.wpa_driver = gtk.Entry()
1919 self.wpa_driver.set_width_chars(32)
1920 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1921 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1922 self.wpa_driver_label = gtk.Label("Driver")
1923 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1924 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1926 self.wpa_pidfile = gtk.Entry()
1927 self.wpa_pidfile.set_width_chars(32)
1928 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1929 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1930 self.wpa_pidfile_label = gtk.Label("PID file")
1931 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1932 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1934 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1935 ### End of WPA tab
1937 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1939 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1941 #Parameters:
1943 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1945 # 'data' -- tuple - list of arbitrary arguments (not used)
1947 #Returns:
1949 # nothing
1950 def toggle_auto_detect(self, auto_detect_toggle, data=None):
1951 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
1953 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
1955 #Parameters:
1957 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1959 # 'data' -- tuple - list of arbitrary arguments (not used)
1961 #Returns:
1963 # nothing
1964 def toggle_speak(self, speak_toggle, data=None):
1965 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
1966 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
1968 # Display preferences dialog and operate until canceled or okayed.
1970 #Parameters:
1972 # nothing
1974 #Returns:
1976 # integer -- gtk response ID
1977 def run(self):
1978 self.dialog.show_all()
1979 return self.dialog.run()
1981 # Write updated values to config file.
1983 #Parameters:
1985 # nothing
1987 #Returns:
1989 # nothing
1990 def save(self):
1991 if self.w_auto_detect.get_active():
1992 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
1993 else:
1994 # get the selected interface, using a work-around suggested by PyGTK Tutorial
1995 interface = self.w_interface.get_model()[self.w_interface.get_active()][0]
1996 self.confFile.set_opt('DEFAULT.interface', interface)
1997 #self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
1998 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
1999 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
2000 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
2001 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
2002 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
2003 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
2004 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
2005 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
2006 self.confFile.set_opt('DEFAULT.logfile', self.logfile_entry.get_text())
2007 self.confFile.set_int_opt('DEFAULT.loglevel', int(self.loglevel.get_value()))
2008 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
2009 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
2010 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
2011 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
2012 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
2013 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
2014 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
2015 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
2016 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
2017 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
2018 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
2019 try:
2020 self.confFile.write()
2021 except IOError, (error_number, error_str):
2022 if error_number == errno.ENOENT:
2023 error_dlg = ErrorDialog( self.dialog, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
2024 del error_dlg
2025 else:
2026 raise IOError(error_number, error_str)
2028 # Remove preferences window.
2030 #Parameters:
2032 # nothing
2034 #Returns:
2036 # nothing
2037 def destroy(self):
2038 self.dialog.destroy()
2039 del self.dialog
2042 # Edit and return an AP profile.
2043 class profile_dialog:
2044 # Create a new profile_dialog.
2046 #Parameters:
2048 # 'parent' -- gtk.Object - Usually, the calling window.
2050 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
2052 #Returns:
2054 # profile_dialog instance
2055 def __init__( self, parent, profile ):
2056 global wifi_radar_icon
2058 # Labels
2059 self.WIFI_SET_LABEL = "WiFi Options"
2060 self.USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
2061 self.USE_IP_LABEL = "Manual network configuration"
2062 self.USE_WPA_LABEL = "Use WPA"
2063 self.NO_WPA_LABEL = "No WPA"
2064 self.CON_PP_LABEL = "Connection Commands"
2065 self.DIS_PP_LABEL = "Disconnection Commands"
2067 self.parent = parent
2068 self.profile = profile.copy()
2069 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
2070 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
2071 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
2072 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
2073 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
2074 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
2075 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2076 self.dialog.set_icon( icon )
2077 self.dialog.set_resizable( False )
2078 self.dialog.set_transient_for( self.parent.window )
2079 #self.dialog.set_size_request( 400, 400 )
2080 #################
2081 self.tooltips = gtk.Tooltips()
2083 general_table = gtk.Table()
2084 general_table.set_row_spacings(3)
2085 general_table.set_col_spacings(3)
2086 # The essid labels
2087 general_table.attach(gtk.Label('Network Name'), 0, 1, 0, 1)
2088 # The essid textboxes
2089 self.essid_entry = gtk.Entry(32)
2090 self.essid_entry.set_text(self.profile['essid'])
2091 general_table.attach(self.essid_entry, 1, 2, 0, 1)
2092 # Add the essid table to the dialog
2093 self.dialog.vbox.pack_start(general_table, True, True, 5)
2094 self.tooltips.set_tip(self.essid_entry, "The name of the network with which to connect.")
2096 # The bssid labels
2097 general_table.attach(gtk.Label('Network Address'), 0, 1, 1, 2)
2098 # The bssid textboxes
2099 self.bssid_entry = gtk.Entry(32)
2100 self.bssid_entry.set_text(self.profile['bssid'])
2101 self.bssid_entry.set_sensitive(not self.profile['roaming'])
2102 # Add the bssid table to the dialog
2103 general_table.attach(self.bssid_entry, 1, 2, 1, 2)
2104 self.tooltips.set_tip(self.bssid_entry, "The address of the network with which to connect.")
2105 # Add the roaming checkbox
2106 self.roaming_cb = gtk.CheckButton('Roaming')
2107 self.roaming_cb.set_active(self.profile['roaming'])
2108 self.roaming_cb.connect("toggled", self.toggle_roaming)
2109 general_table.attach(self.roaming_cb, 1, 2, 2, 3)
2110 self.tooltips.set_tip(self.roaming_cb, "Use the AP in this network which provides strongest signal?")
2111 # create the WiFi expander
2112 self.wifi_expander = gtk.Expander( self.WIFI_SET_LABEL )
2113 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2114 wifi_table = gtk.Table( 4, 2, False )
2115 wifi_table.set_row_spacings( 3 )
2116 wifi_table.set_col_spacings( 3 )
2117 # The WiFi labels
2118 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
2119 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
2120 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
2121 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
2122 # The WiFi text boxes
2123 self.mode_combo = gtk.combo_box_new_text()
2124 for mode in self.WIFI_MODES:
2125 self.mode_combo.append_text( mode )
2126 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
2127 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
2128 self.tooltips.set_tip(self.mode_combo, "Method to use for connection. You probably want auto mode.")
2129 self.channel_combo = gtk.combo_box_new_text()
2130 for channel in self.WIFI_CHANNELS:
2131 self.channel_combo.append_text( channel )
2132 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
2133 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
2134 self.tooltips.set_tip(self.channel_combo, "Channel the network uses. You probably want auto mode.")
2136 self.key_entry = gtk.Entry( 64 )
2137 self.key_entry.set_text( self.profile['key'] )
2138 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
2139 self.tooltips.set_tip(self.key_entry, "WEP key: Plain language or hex string to use for encrypted communication with the network.")
2141 self.security_combo = gtk.combo_box_new_text()
2142 for security in self.WIFI_SECURITY:
2143 self.security_combo.append_text( security )
2144 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
2145 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
2146 self.tooltips.set_tip(self.security_combo, "Use Open to allow unencrypted communication and Restricted to force encrypted-only communication.")
2147 # Add the wifi table to the expander
2148 self.wifi_expander.add( wifi_table )
2149 # Add the expander to the dialog
2150 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
2152 # create the wpa expander
2153 self.wpa_expander = gtk.Expander( self.NO_WPA_LABEL )
2154 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
2155 wpa_table = gtk.Table( 1, 2, False )
2156 wpa_table.set_row_spacings( 3 )
2157 wpa_table.set_col_spacings( 3 )
2158 # The labels
2159 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
2160 # The text boxes
2161 self.wpa_driver_entry = gtk.Entry()
2162 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
2163 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
2164 # Add the wpa table to the expander
2165 self.wpa_expander.add( wpa_table )
2166 # Add the expander to the dialog
2167 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
2169 # create the dhcp expander
2170 self.dhcp_expander = gtk.Expander( self.USE_DHCP_LABEL )
2171 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2172 ip_table = gtk.Table( 6, 2, False )
2173 ip_table.set_row_spacings( 3 )
2174 ip_table.set_col_spacings( 3 )
2175 # The IP labels
2176 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
2177 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
2178 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
2179 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
2180 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
2181 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
2182 # The IP text boxes
2183 self.ip_entry = gtk.Entry( 15 )
2184 self.ip_entry.set_text( self.profile['ip'] )
2185 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
2186 self.netmask_entry = gtk.Entry( 15 )
2187 self.netmask_entry.set_text( self.profile['netmask'] )
2188 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
2189 self.gw_entry = gtk.Entry( 15 )
2190 self.gw_entry.set_text( self.profile['gateway'] )
2191 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
2192 self.domain_entry = gtk.Entry( 32 )
2193 self.domain_entry.set_text( self.profile['domain'] )
2194 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
2195 self.dns1_entry = gtk.Entry( 15 )
2196 self.dns1_entry.set_text( self.profile['dns1'] )
2197 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
2198 self.dns2_entry = gtk.Entry( 15 )
2199 self.dns2_entry.set_text( self.profile['dns2'] )
2200 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
2201 # Add the ip table to the expander
2202 self.dhcp_expander.add( ip_table )
2203 # Add the expander to the dialog
2204 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
2206 # create the connection-building postpre expander
2207 self.con_pp_expander = gtk.Expander( self.CON_PP_LABEL )
2208 con_pp_table = gtk.Table( 2, 2, False )
2209 con_pp_table.set_row_spacings( 3 )
2210 con_pp_table.set_col_spacings( 3 )
2211 # The labels
2212 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2213 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2214 # The text boxes
2215 self.con_prescript_entry = gtk.Entry()
2216 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
2217 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
2218 self.tooltips.set_tip(self.con_prescript_entry, "The local command to execute before trying to connect to the network.")
2219 self.con_postscript_entry = gtk.Entry()
2220 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
2221 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
2222 self.tooltips.set_tip(self.con_postscript_entry, "The local command to execute after connecting to the network.")
2223 # Add the pp table to the expander
2224 self.con_pp_expander.add( con_pp_table )
2225 # Add the expander to the dialog
2226 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
2228 # create the disconnection postpre expander
2229 self.dis_pp_expander = gtk.Expander( self.DIS_PP_LABEL )
2230 dis_pp_table = gtk.Table( 2, 2, False )
2231 dis_pp_table.set_row_spacings( 3 )
2232 dis_pp_table.set_col_spacings( 3 )
2233 # The labels
2234 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2235 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2236 # The text boxes
2237 self.dis_prescript_entry = gtk.Entry()
2238 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
2239 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
2240 self.tooltips.set_tip(self.dis_prescript_entry, "The local command to execute before disconnecting from the network.")
2241 self.dis_postscript_entry = gtk.Entry()
2242 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
2243 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
2244 self.tooltips.set_tip(self.dis_postscript_entry, "The local command to execute after disconnecting from the network.")
2245 # Add the pp table to the expander
2246 self.dis_pp_expander.add( dis_pp_table )
2247 # Add the expander to the dialog
2248 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
2250 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
2252 #Parameters:
2254 # nothing
2256 #Returns:
2258 # dictionary or None -- a profile, or None on cancel
2260 #NOTES:
2262 # Raises ValueError if an attempt is made to save an ESSID with no name.
2263 def run( self ):
2264 self.dialog.show_all()
2265 if self.dialog.run():
2266 if self.essid_entry.get_text().strip() == "":
2267 raise ValueError
2268 self.profile['known'] = True
2269 self.profile['essid'] = self.essid_entry.get_text().strip()
2270 if self.roaming_cb.get_active():
2271 self.profile['bssid'] = ''
2272 else:
2273 self.profile['bssid'] = self.bssid_entry.get_text().strip()
2274 self.profile['roaming'] = self.roaming_cb.get_active()
2275 self.profile['key'] = self.key_entry.get_text().strip()
2276 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
2277 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
2278 self.profile['encrypted'] = ( self.profile['security'] != '' )
2279 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
2280 self.profile['protocol'] = 'g'
2281 self.profile['available'] = ( self.profile['signal'] > 0 )
2282 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
2283 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
2284 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
2285 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
2286 # wpa
2287 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
2288 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
2289 # dhcp
2290 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
2291 self.profile['ip'] = self.ip_entry.get_text().strip()
2292 self.profile['netmask'] = self.netmask_entry.get_text().strip()
2293 self.profile['gateway'] = self.gw_entry.get_text().strip()
2294 self.profile['domain'] = self.domain_entry.get_text().strip()
2295 self.profile['dns1'] = self.dns1_entry.get_text().strip()
2296 self.profile['dns2'] = self.dns2_entry.get_text().strip()
2297 return self.profile
2298 return None
2300 # Remove profile dialog.
2302 #Parameters:
2304 # nothing
2306 #Returns:
2308 # nothing
2309 def destroy( self ):
2310 self.dialog.destroy()
2311 del self.dialog
2313 # Respond to roaming checkbox toggle by activating/de-activating the BSSID text entry.
2315 #Parameters:
2317 # 'roaming_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2319 # 'data' -- tuple - list of arbitrary arguments (not used)
2321 #Returns:
2323 # nothing
2324 def toggle_roaming(self, roaming_toggle, data=None):
2325 self.bssid_entry.set_sensitive(not roaming_toggle.get_active())
2327 # Respond to expanding/hiding IP segment.
2329 #Parameters:
2331 # 'widget' -- gtk.Widget - The widget sending the event.
2333 # 'data' -- tuple - List of arbitrary arguments (not used)
2335 #Returns:
2337 # nothing
2338 def toggle_use_dhcp( self, widget, data = None ):
2339 expanded = self.dhcp_expander.get_expanded()
2340 if expanded:
2341 self.dhcp_expander.set_label( self.USE_IP_LABEL )
2342 else:
2343 self.dhcp_expander.set_label( self.USE_DHCP_LABEL )
2345 # Respond to expanding/hiding WPA segment.
2347 #Parameters:
2349 # 'widget' -- gtk.Widget - The widget sending the event.
2351 # 'data' -- tuple - List of arbitrary arguments (not used)
2353 #Returns:
2355 # nothing
2356 def toggle_use_wpa( self, widget, data = None ):
2357 expanded = self.wpa_expander.get_expanded()
2358 if expanded:
2359 self.wpa_expander.set_label( self.USE_WPA_LABEL )
2360 else:
2361 self.wpa_expander.set_label( self.NO_WPA_LABEL )
2363 # Return the index where item matches a cell in array.
2365 #Parameters:
2367 # 'item' -- string - Item to find in array
2369 # 'array' -- list - List in which to find match.
2371 #Returns:
2373 # integer - 0 (no match) or higher (index of match)
2374 def get_array_index( self, item, array ):
2375 try:
2376 return array.index( item.strip() )
2377 except:
2378 pass
2379 return 0
2381 # Return the value in array[ index ]
2383 #Parameters:
2385 # 'index' -- integer - The index to look up.
2387 # 'array' -- list - List in which to look up value.
2389 #Returns:
2391 # string -- empty string (no match) or looked up value
2392 def get_array_item( self, index, array ):
2393 try:
2394 return array[ index ]
2395 except:
2396 pass
2397 return ''
2400 # A simple class for putting up a "Please wait" dialog so the user
2401 # doesn't think we've forgotten about them. Implements the status interface.
2402 class StatusWindow:
2403 # Create a new StatusWindow.
2405 #Parameters:
2407 # 'parent' -- gtk.Object - Usually, the calling window.
2409 #Returns:
2411 # StatusWindow instance
2413 #NOTE:
2415 # Sample implementation of status interface. Status interface
2416 #requires .show(), .update_message(message), and .hide() methods.
2417 def __init__( self, parent ):
2418 global wifi_radar_icon
2419 self.parent = parent
2420 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2421 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2422 self.dialog.set_icon( icon )
2423 self.lbl = gtk.Label("Please wait...")
2424 self.bar = gtk.ProgressBar()
2425 self.dialog.vbox.pack_start(self.lbl)
2426 self.dialog.vbox.pack_start(self.bar)
2427 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2428 self.timer = None
2430 # Change the message displayed to the user.
2432 #Parameters:
2434 # 'message' -- string - The message to show to the user.
2436 #Returns:
2438 # nothing
2439 def update_message( self, message ):
2440 self.lbl.set_text(message)
2442 # Update the StatusWindow progress bar.
2444 #Parameters:
2446 # nothing
2448 #Returns:
2450 # True -- always return True
2451 def update_window( self ):
2452 self.bar.pulse()
2453 return True
2455 # Display and operate the StatusWindow.
2457 #Parameters:
2459 # nothing
2461 #Returns:
2463 # nothing
2464 def run( self ):
2465 pass
2467 # Show all the widgets of the StatusWindow.
2469 #Parameters:
2471 # nothing
2473 #Returns:
2475 # nothing
2476 def show( self ):
2477 self.dialog.show_all()
2478 self.timer = gobject.timeout_add(250, self.update_window)
2479 return False
2481 # Hide all the widgets of the StatusWindow.
2483 #Parameters:
2485 # nothing
2487 #Returns:
2489 # nothing
2490 def hide( self ):
2491 if self.timer:
2492 gobject.source_remove(self.timer)
2493 self.timer = None
2494 self.dialog.hide_all()
2495 return False
2497 # Remove the StatusWindow.
2499 #Parameters:
2501 # nothing
2503 #Returns:
2505 # nothing
2506 def destroy( self ):
2507 if self.timer:
2508 gobject.source_remove(self.timer)
2509 self.dialog.destroy()
2510 del self.dialog
2513 # Manage a GTK About Dialog
2514 class about_dialog(gtk.AboutDialog):
2515 # Subclass GTK AboutDialog
2517 #Parameters:
2519 # nothing
2521 #Returns:
2523 # nothing
2524 def __init__( self ):
2525 global wifi_radar_icon
2527 gtk.AboutDialog.__init__(self)
2528 self.set_authors(["Ahmad Baitalmal <ahmad@baitalmal.com>", "Brian Elliott Finley <brian@thefinleys.com>", "Sean Robinson <seankrobinson@gmail.com>", "", "Contributors", "Douglas Breault", "Jon Collette", "David Decotigny", "Simon Gerber", "Joey Hurst", "Ante Karamatic", "Richard Monk", "Nicolas Brouard", "Kevin Otte", "Nathanael Rebsch", "Andrea Scarpino", "Patrick Winnertz"])
2529 self.set_comments("WiFi connection manager")
2530 self.set_copyright("Copyright 2004-2009 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2531 self.set_documenters(["Gary Case"])
2532 license = """
2533 This program is free software; you can redistribute it and/or modify
2534 it under the terms of the GNU General Public License as published by
2535 the Free Software Foundation; either version 2 of the License, or
2536 (at your option) any later version.
2538 This program is distributed in the hope that it will be useful,
2539 but WITHOUT ANY WARRANTY; without even the implied warranty of
2540 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2541 GNU General Public License for more details.
2543 You should have received a copy of the GNU General Public License
2544 along with this program; if not, write to the Free Software
2545 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2546 self.set_license(license)
2547 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2548 self.set_logo(logo)
2549 self.set_name("WiFi Radar")
2550 self.set_version(WIFI_RADAR_VERSION)
2551 self.set_website("http://wifi-radar.berlios.de")
2555 # Manage the configuration for the application, including reading and writing the config from/to a file.
2556 class ConfigFile(ConfigParser.SafeConfigParser):
2557 # Create a new ConfigFile.
2559 #Parameters:
2561 # 'filename' -- string - The configuration file's name.
2563 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2565 #Returns:
2567 # ConfigFile instance
2568 def __init__( self, filename, defaults, raw=False ):
2569 self.filename = filename
2570 self.raw = raw
2571 self.auto_profile_order = []
2572 ConfigParser.SafeConfigParser.__init__(self, defaults)
2574 # Set the contents of a section to values from a dictionary.
2576 #Parameters:
2578 # 'section_name' -- string - Configuration file section.
2580 # 'section_dict' -- dictionary - Values to add to section.
2582 #Returns:
2584 # nothing
2585 def set_section( self, section_name, section_dict ):
2586 try:
2587 self.add_section(section_name)
2588 except ConfigParser.DuplicateSectionError:
2589 pass
2590 for key in section_dict.keys():
2591 if type(section_dict[key]) == BooleanType:
2592 self.set_bool_opt(section_name + "." + key, section_dict[key])
2593 elif type(section_dict[key]) == IntType:
2594 self.set_int_opt(section_name + "." + key, section_dict[key])
2595 elif type(section_dict[key]) == FloatType:
2596 self.set_float_opt(section_name + "." + key, section_dict[key])
2597 else:
2598 self.set_opt(section_name + "." + key, section_dict[key])
2600 # Return the profile recorded in the specified section.
2602 #Parameters:
2604 # 'section_name' -- string - Configuration file section.
2606 #Returns:
2608 # dictionary or None - The specified profile or None if not found
2609 def get_profile( self, section_name ):
2610 if section_name in self.profiles():
2611 str_types = [ 'bssid', 'channel', 'essid', 'protocol', 'con_prescript', 'con_postscript', 'dis_prescript', 'dis_postscript', 'key', 'mode', 'security', 'wpa_driver', 'ip', 'netmask', 'gateway', 'domain', 'dns1', 'dns2' ]
2612 bool_types = [ 'known', 'available', 'roaming', 'encrypted', 'use_wpa', 'use_dhcp' ]
2613 int_types = [ 'signal' ]
2614 profile = get_new_profile()
2615 for option in bool_types:
2616 option_tmp = self.get_opt_as_bool(section_name + "." + option)
2617 if option_tmp:
2618 profile[option] = option_tmp
2619 for option in int_types:
2620 option_tmp = self.get_opt_as_int(section_name + "." + option)
2621 if option_tmp:
2622 profile[option] = option_tmp
2623 for option in str_types:
2624 option_tmp = self.get_opt(section_name + "." + option)
2625 if option_tmp:
2626 profile[option] = option_tmp
2627 return profile
2628 return None
2630 # Get a config option and handle exceptions.
2632 #Parameters:
2634 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2635 # period and the option key. (E.g. "DEFAULT.interface")
2637 #Returns:
2639 # string or None - option value as string or None on failure
2640 def get_opt( self, option_path ):
2641 #print "ConfigFile.get_opt: ", option_path
2642 (section, option) = option_path.split('.')
2643 try:
2644 return self.get(section, option, self.raw)
2645 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2646 return None
2648 # Get a config option and return as a boolean type.
2650 #Parameters:
2652 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2653 # period and the option key. (E.g. "DEFAULT.interface")
2655 #Returns:
2657 # boolean - option value as boolean
2658 def get_opt_as_bool( self, option_path ):
2659 option = self.get_opt(option_path)
2660 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2661 return option
2662 if option == 'True':
2663 return True
2664 if option == 'False':
2665 return False
2666 raise ValueError, 'boolean option was not True or False'
2668 # Get a config option and return as an integer type.
2670 #Parameters:
2672 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2673 # period and the option key. (E.g. "DEFAULT.interface")
2675 #Returns:
2677 # integer- option value as integer
2678 def get_opt_as_int( self, option_path ):
2679 return int(float(self.get_opt(option_path)))
2681 # Convert boolean type to string and set config option.
2683 #Parameters:
2685 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2686 # period and the option key. (E.g. "DEFAULT.interface")
2688 # 'value' -- boolean - Value to set.
2690 #Returns:
2692 # nothing
2693 def set_bool_opt( self, option_path, value ):
2694 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2695 value == 'True'
2696 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2697 value == 'False'
2698 else:
2699 raise ValueError, 'cannot convert value to string'
2700 self.set_opt(option_path, repr(value))
2702 # Convert integer type to string and set config option.
2704 #Parameters:
2706 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2707 # period and the option key. (E.g. "DEFAULT.interface")
2709 # 'value' -- integer - Value to set.
2711 #Returns:
2713 # nothing
2714 def set_int_opt( self, option_path, value ):
2715 if not isinstance(value, IntType):
2716 raise ValueError, 'value is not an integer'
2717 self.set_opt(option_path, repr(value))
2719 # Convert float type to string and set config option.
2721 #Parameters:
2723 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2724 # period and the option key. (E.g. "DEFAULT.interface")
2726 # 'value' -- float - Value to set.
2728 #Returns:
2730 # nothing
2731 def set_float_opt( self, option_path, value ):
2732 if not isinstance(value, FloatType):
2733 raise ValueError, 'value is not a float'
2734 self.set_opt(option_path, repr(int(value)))
2736 # Set a config option while handling exceptions.
2738 #Parameters:
2740 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2741 # period and the option key. (E.g. "DEFAULT.interface")
2743 # 'value' -- string - Value to set.
2745 #Returns:
2747 # nothing
2748 def set_opt( self, option_path, value ):
2749 (section, option) = option_path.split('.')
2750 try:
2751 self.set(section, option, value)
2752 except ConfigParser.NoSectionError:
2753 self.add_section(section)
2754 self.set_opt(option_path, value)
2756 # Return a list of the section names which denote AP profiles.
2758 #Parameters:
2760 # nothing
2762 #Returns:
2764 # list - profile names
2765 def profiles( self ):
2766 profile_list = []
2767 for section in self.sections():
2768 if ':' in section:
2769 profile_list.append(section)
2770 return profile_list
2772 # Read configuration file from disk into instance variables.
2774 #Parameters:
2776 # nothing
2778 #Returns:
2780 # nothing
2781 def read( self ):
2782 fp = open( self.filename, "r" )
2783 self.readfp(fp)
2784 # convert the auto_profile_order to a list for ordering
2785 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2786 for ap in self.profiles():
2787 self.set_bool_opt( ap + '.known', True)
2788 if ap in self.auto_profile_order: continue
2789 self.auto_profile_order.append( ap )
2790 fp.close()
2792 # Write configuration file to disk from instance variables. Copied from
2793 # ConfigParser and modified to write options in alphabetical order.
2795 #Parameters:
2797 # nothing
2799 #Returns:
2801 # nothing
2802 def write( self ):
2803 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2804 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2805 (fd, tempfilename) = tempfile.mkstemp(prefix="wifi-radar.conf.")
2806 fp = os.fdopen(fd, "w")
2807 # write DEFAULT section first
2808 if self._defaults:
2809 fp.write("[DEFAULT]\n")
2810 for key in sorted(self._defaults.keys()):
2811 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2812 fp.write("\n")
2813 # write other non-profile sections next
2814 for section in self._sections:
2815 if section not in self.profiles():
2816 fp.write("[%s]\n" % section)
2817 for key in sorted(self._sections[section].keys()):
2818 if key != "__name__":
2819 fp.write("%s = %s\n" %
2820 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2821 fp.write("\n")
2822 # write profile sections
2823 for section in self._sections:
2824 if section in self.profiles():
2825 fp.write("[%s]\n" % section)
2826 for key in sorted(self._sections[section].keys()):
2827 if key != "__name__":
2828 fp.write("%s = %s\n" %
2829 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2830 fp.write("\n")
2831 fp.close()
2832 move(tempfilename, self.filename)
2834 # Load our conf file and known profiles
2835 # Defaults, these may get overridden by values found in the conf file.
2836 config_defaults = { # The network interface you use.
2837 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2838 'interface': "auto_detect",
2839 # How long should the scan for access points last?
2840 #'scan_timeout': '5',
2841 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2842 # Set the speak_up option to false if you do not have or want this.
2843 'speak_command': '/usr/bin/say',
2844 # Should I speak up when connecting to a network? (If you have a speech command)
2845 'speak_up': 'False',
2846 # You may set this to true for cards that require a "commit" command with iwconfig
2847 'commit_required': 'False',
2848 # You may set this to true for cards that require the interface to be brought up first
2849 'ifup_required': 'False',
2850 # set the location and verbosity of the log file
2851 'logfile': '/var/log/wifi-radar.log',
2852 'loglevel': '50',
2853 # Set the location of several important programs
2854 'iwlist_command': '/sbin/iwlist',
2855 'iwconfig_command': '/sbin/iwconfig',
2856 'ifconfig_command': '/sbin/ifconfig',
2857 'route_command': '/sbin/route',
2858 'auto_profile_order': '[]',
2859 'version': WIFI_RADAR_VERSION }
2861 config_dhcp = { # DHCP client
2862 'command': '/sbin/dhcpcd',
2863 # How long to wait for an IP addr from DHCP server
2864 'timeout': '30',
2865 # Arguments to use with DHCP client on connect
2866 'args': '-D -o -i dhcp_client -t %(timeout)s',
2867 # Argument to use with DHCP client on disconnect
2868 'kill_args': '-k',
2869 # The file where DHCP client PID is written
2870 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2872 config_wpa = { # WPA Supplicant
2873 'command': '/usr/sbin/wpa_supplicant',
2874 # Arguments to use with WPA Supplicant on connect
2875 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2876 # Arguments to use with WPA Supplicant on disconnect
2877 'kill_command': '',
2878 # Where the WPA Supplicant config file can be found
2879 'configuration': '/etc/wpa_supplicant.conf',
2880 # Driver to use with WPA Supplicant
2881 'driver': 'wext',
2882 # The file where WPA Supplicant PID is written
2883 'pidfile': '/var/run/wpa_supplicant.pid' }
2885 # initialize config, with defaults
2886 confFile = ConfigFile(CONF_FILE, config_defaults)
2887 confFile.set_section("DHCP", config_dhcp)
2888 confFile.set_section("WPA", config_wpa)
2890 if not os.path.isfile( CONF_FILE ):
2891 confFile.set_bool_opt('DEFAULT.new_file', True)
2892 else:
2893 if not os.access(CONF_FILE, os.R_OK):
2894 print "Can't open " + CONF_FILE + "."
2895 print "Are you root?"
2896 sys.exit()
2897 confFile.read()
2900 ####################################################################################################
2901 # Embedded Images
2902 wifi_radar_icon = [ ""
2903 "GdkP"
2904 "\0\0\22""7"
2905 "\2\1\0\2"
2906 "\0\0\1\214"
2907 "\0\0\0c"
2908 "\0\0\0O"
2909 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2910 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2911 "\377\0\7\0\0\0\10\0\0\0\25\0\0\0\35\0\0\0%\0\0\0-\0\0\0\"\0\0\0\11\327"
2912 "\377\377\377\0\6\0\0\0\"\0\0\0_\0\0\0\213\0\0\0\266\0\0\0\341\0\0\0\376"
2913 "\206\0\0\0\377\6\0\0\0\356\0\0\0\324\0\0\0\265\0\0\0~\0\0\0@\0\0\0\10"
2914 "\315\377\377\377\0\4\0\0\0\2\0\0\0;\0\0\0\210\0\0\0\325\221\0\0\0\377"
2915 "\4\0\0\0\371\0\0\0\303\0\0\0w\0\0\0\31\310\377\377\377\0\3\0\0\0\6\0"
2916 "\0\0m\0\0\0\342\227\0\0\0\377\4\0\0\0\374\0\0\0\264\0\0\0Q\0\0\0\5\303"
2917 "\377\377\377\0\3\0\0\0\4\0\0\0d\0\0\0\341\234\0\0\0\377\3\0\0\0\341\0"
2918 "\0\0`\0\0\0\2\277\377\377\377\0\3\0\0\0\2\0\0\0[\0\0\0\333\240\0\0\0"
2919 "\377\2\0\0\0\323\0\0\0K\274\377\377\377\0\3\0\0\0\1\0\0\0R\0\0\0\324"
2920 "\244\0\0\0\377\2\0\0\0\276\0\0\0#\271\377\377\377\0\2\0\0\0\31\0\0\0"
2921 "\277\247\0\0\0\377\2\0\0\0\363\0\0\0c\267\377\377\377\0\2\0\0\0/\0\0"
2922 "\0\343\252\0\0\0\377\2\0\0\0\257\0\0\0\24\264\377\377\377\0\2\0\0\0M"
2923 "\0\0\0\363\220\0\0\0\377\14\0\0\0\357\0\0\0\304\0\0\0\230\0\0\0v\0\0"
2924 "\0l\0\0\0c\0\0\0[\0\0\0j\0\0\0\205\0\0\0\240\0\0\0\311\0\0\0\373\220"
2925 "\0\0\0\377\2\0\0\0\346\0\0\0""4\262\377\377\377\0\2\0\0\0q\0\0\0\375"
2926 "\215\0\0\0\377\4\0\0\0\373\0\0\0\300\0\0\0t\0\0\0)\213\377\377\377\0"
2927 "\4\0\0\0\14\0\0\0E\0\0\0\205\0\0\0\334\216\0\0\0\377\2\0\0\0\363\0\0"
2928 "\0D\257\377\377\377\0\2\0\0\0\4\0\0\0\230\215\0\0\0\377\3\0\0\0\372\0"
2929 "\0\0\231\0\0\0\34\221\377\377\377\0\4\0\0\0\1\0\0\0C\0\0\0\251\0\0\0"
2930 "\372\214\0\0\0\377\2\0\0\0\371\0\0\0W\255\377\377\377\0\2\0\0\0\17\0"
2931 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2932 "\0\2\0\0\0\"\0\0\0\252\214\0\0\0\377\2\0\0\0\375\0\0\0k\253\377\377\377"
2933 "\0\2\0\0\0\25\0\0\0\324\213\0\0\0\377\3\0\0\0\376\0\0\0\252\0\0\0(\232"
2934 "\377\377\377\0\2\0\0\0""9\0\0\0\312\214\0\0\0\377\1\0\0\0\200\251\377"
2935 "\377\377\0\2\0\0\0\5\0\0\0\303\213\0\0\0\377\2\0\0\0\332\0\0\0""1\235"
2936 "\377\377\377\0\3\0\0\0\4\0\0\0\201\0\0\0\374\213\0\0\0\377\1\0\0\0p\250"
2937 "\377\377\377\0\1\0\0\0\222\213\0\0\0\377\2\0\0\0\301\0\0\0\22\240\377"
2938 "\377\377\0\2\0\0\0:\0\0\0\336\212\0\0\0\377\2\0\0\0\374\0\0\0I\246\377"
2939 "\377\377\0\1\0\0\0[\213\0\0\0\377\2\0\0\0\241\0\0\0\6\212\377\377\377"
2940 "\0\15\0\0\0\2\0\0\0&\0\0\0U\0\0\0\203\0\0\0\242\0\0\0\243\0\0\0\234\0"
2941 "\0\0\225\0\0\0\215\0\0\0\206\0\0\0}\0\0\0\\\0\0\0!\213\377\377\377\0"
2942 "\2\0\0\0\22\0\0\0\307\212\0\0\0\377\2\0\0\0\361\0\0\0+\244\377\377\377"
2943 "\0\2\0\0\0.\0\0\0\365\211\0\0\0\377\2\0\0\0\376\0\0\0|\211\377\377\377"
2944 "\0\4\0\0\0#\0\0\0d\0\0\0\223\0\0\0\277\214\0\0\0\310\4\0\0\0\253\0\0"
2945 "\0l\0\0\0-\0\0\0\2\210\377\377\377\0\2\0\0\0\12\0\0\0\267\212\0\0\0\377"
2946 "\2\0\0\0\336\0\0\0\24\242\377\377\377\0\2\0\0\0\20\0\0\0\334\211\0\0"
2947 "\0\377\2\0\0\0\367\0\0\0W\210\377\377\377\0\2\0\0\0#\0\0\0\211\223\0"
2948 "\0\0\310\3\0\0\0\266\0\0\0t\0\0\0\27\207\377\377\377\0\2\0\0\0\5\0\0"
2949 "\0\244\212\0\0\0\377\2\0\0\0\302\0\0\0\6\240\377\377\377\0\2\0\0\0\1"
2950 "\0\0\0\264\211\0\0\0\377\2\0\0\0\363\0\0\0""9\207\377\377\377\0\3\0\0"
2951 "\0\34\0\0\0\201\0\0\0\306\226\0\0\0\310\3\0\0\0\277\0\0\0Y\0\0\0\2\206"
2952 "\377\377\377\0\2\0\0\0\1\0\0\0\217\212\0\0\0\377\1\0\0\0\203\240\377"
2953 "\377\377\0\1\0\0\0\177\212\0\0\0\377\1\0\0\0T\206\377\377\377\0\3\0\0"
2954 "\0\25\0\0\0z\0\0\0\305\232\0\0\0\310\2\0\0\0\242\0\0\0*\207\377\377\377"
2955 "\0\1\0\0\0\243\211\0\0\0\377\2\0\0\0\372\0\0\0,\236\377\377\377\0\2\0"
2956 "\0\0D\0\0\0\375\211\0\0\0\377\1\0\0\0\213\206\377\377\377\0\2\0\0\0""8"
2957 "\0\0\0\274\235\0\0\0\310\3\0\0\0\306\0\0\0u\0\0\0\14\205\377\377\377"
2958 "\0\2\0\0\0\7\0\0\0\306\211\0\0\0\377\2\0\0\0\306\0\0\0\2\234\377\377"
2959 "\377\0\2\0\0\0\4\0\0\0\331\211\0\0\0\377\2\0\0\0\276\0\0\0\3\205\377"
2960 "\377\377\0\2\0\0\0T\0\0\0\306\214\0\0\0\310\10\0\0\0\260\0\0\0\202\0"
2961 "\0\0v\0\0\0~\0\0\0\207\0\0\0\217\0\0\0\227\0\0\0\264\214\0\0\0\310\2"
2962 "\0\0\0\264\0\0\0""2\205\377\377\377\0\2\0\0\0\27\0\0\0\341\211\0\0\0"
2963 "\377\1\0\0\0k\234\377\377\377\0\1\0\0\0c\211\0\0\0\377\2\0\0\0\343\0"
2964 "\0\0\26\204\377\377\377\0\2\0\0\0\2\0\0\0s\212\0\0\0\310\4\0\0\0\265"
2965 "\0\0\0s\0\0\0D\0\0\0\26\207\377\377\377\0\4\0\0\0\1\0\0\0+\0\0\0j\0\0"
2966 "\0\250\212\0\0\0\310\2\0\0\0\303\0\0\0A\205\377\377\377\0\2\0\0\0/\0"
2967 "\0\0\364\210\0\0\0\377\2\0\0\0\362\0\0\0\33\232\377\377\377\0\2\0\0\0"
2968 "\7\0\0\0\341\210\0\0\0\377\2\0\0\0\371\0\0\0""7\204\377\377\377\0\2\0"
2969 "\0\0\12\0\0\0\217\211\0\0\0\310\3\0\0\0\271\0\0\0]\0\0\0\10\216\377\377"
2970 "\377\0\3\0\0\0\36\0\0\0t\0\0\0\306\210\0\0\0\310\2\0\0\0\306\0\0\0P\205"
2971 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2972 "\0\0\0n\211\0\0\0\377\1\0\0\0h\204\377\377\377\0\2\0\0\0\20\0\0\0\245"
2973 "\210\0\0\0\310\3\0\0\0\274\0\0\0c\0\0\0\12\222\377\377\377\0\2\0\0\0"
2974 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2975 "\0\0\0\377\1\0\0\0:\230\377\377\377\0\2\0\0\0\13\0\0\0\350\210\0\0\0"
2976 "\377\1\0\0\0\250\204\377\377\377\0\2\0\0\0\3\0\0\0\230\210\0\0\0\310"
2977 "\2\0\0\0\213\0\0\0\15\225\377\377\377\0\3\0\0\0\2\0\0\0Z\0\0\0\277\210"
2978 "\0\0\0\310\1\0\0\0U\204\377\377\377\0\2\0\0\0%\0\0\0\370\210\0\0\0\377"
2979 "\1\0\0\0\265\230\377\377\377\0\1\0\0\0y\210\0\0\0\377\2\0\0\0\372\0\0"
2980 "\0\40\204\377\377\377\0\1\0\0\0o\210\0\0\0\310\2\0\0\0o\0\0\0\2\230\377"
2981 "\377\377\0\2\0\0\0\30\0\0\0\226\207\0\0\0\310\2\0\0\0\306\0\0\0""7\204"
2982 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2983 "\0\0\0\20\0\0\0\356\210\0\0\0\377\1\0\0\0\226\204\377\377\377\0\1\0\0"
2984 "\0C\207\0\0\0\310\2\0\0\0\305\0\0\0R\233\377\377\377\0\2\0\0\0\5\0\0"
2985 "\0\210\207\0\0\0\310\2\0\0\0\273\0\0\0\37\203\377\377\377\0\2\0\0\0\6"
2986 "\0\0\0\325\210\0\0\0\377\1\0\0\0\251\226\377\377\377\0\1\0\0\0\204\210"
2987 "\0\0\0\377\2\0\0\0\366\0\0\0\32\203\377\377\377\0\2\0\0\0!\0\0\0\277"
2988 "\206\0\0\0\310\2\0\0\0\275\0\0\0""8\235\377\377\377\0\2\0\0\0\2\0\0\0"
2989 "|\207\0\0\0\310\2\0\0\0\254\0\0\0\15\203\377\377\377\0\1\0\0\0J\210\0"
2990 "\0\0\377\2\0\0\0\375\0\0\0&\224\377\377\377\0\2\0\0\0\26\0\0\0\364\210"
2991 "\0\0\0\377\1\0\0\0\214\203\377\377\377\0\2\0\0\0\12\0\0\0\251\206\0\0"
2992 "\0\310\2\0\0\0\305\0\0\0""0\240\377\377\377\0\1\0\0\0r\207\0\0\0\310"
2993 "\1\0\0\0[\204\377\377\377\0\1\0\0\0\317\210\0\0\0\377\1\0\0\0\236\224"
2994 "\377\377\377\0\1\0\0\0\204\210\0\0\0\377\2\0\0\0\362\0\0\0\24\203\377"
2995 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2996 "\0\0\5\0\0\0$\0\0\0G\0\0\0X\0\0\0T\0\0\0O\0\0\0K\0\0\0B\0\0\0\35\214"
2997 "\377\377\377\0\2\0\0\0\2\0\0\0\214\206\0\0\0\310\2\0\0\0\307\0\0\0""1"
2998 "\203\377\377\377\0\1\0\0\0V\210\0\0\0\377\2\0\0\0\372\0\0\0\27\223\377"
2999 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
3000 "\0\0\0@\207\0\0\0\310\1\0\0\0\204\212\377\377\377\0\4\0\0\0\7\0\0\0E"
3001 "\0\0\0u\0\0\0\222\210\0\0\0\226\4\0\0\0\204\0\0\0T\0\0\0$\0\0\0\1\211"
3002 "\377\377\377\0\2\0\0\0\12\0\0\0\245\206\0\0\0\310\2\0\0\0\251\0\0\0\5"
3003 "\202\377\377\377\0\2\0\0\0\2\0\0\0\331\210\0\0\0\377\1\0\0\0C\223\377"
3004 "\377\377\0\1\0\0\0\342\207\0\0\0\377\2\0\0\0\356\0\0\0\17\202\377\377"
3005 "\377\0\2\0\0\0\2\0\0\0\246\206\0\0\0\310\2\0\0\0\246\0\0\0\11\210\377"
3006 "\377\377\0\3\0\0\0\5\0\0\0D\0\0\0\212\216\0\0\0\226\2\0\0\0z\0\0\0\40"
3007 "\211\377\377\377\0\2\0\0\0\32\0\0\0\274\206\0\0\0\310\1\0\0\0d\203\377"
3008 "\377\377\0\1\0\0\0a\210\0\0\0\377\1\0\0\0b\222\377\377\377\0\2\0\0\0"
3009 "\10\0\0\0\375\207\0\0\0\377\1\0\0\0x\203\377\377\377\0\1\0\0\0G\206\0"
3010 "\0\0\310\2\0\0\0\275\0\0\0\36\210\377\377\377\0\2\0\0\0""3\0\0\0\207"
3011 "\221\0\0\0\226\3\0\0\0\225\0\0\0X\0\0\0\11\210\377\377\377\0\1\0\0\0"
3012 "R\206\0\0\0\310\2\0\0\0\302\0\0\0\23\202\377\377\377\0\2\0\0\0\5\0\0"
3013 "\0\342\207\0\0\0\377\1\0\0\0\201\223\377\377\377\0\1\0\0\0m\206\0\0\0"
3014 "\377\2\0\0\0\321\0\0\0\12\202\377\377\377\0\2\0\0\0\3\0\0\0\254\206\0"
3015 "\0\0\310\1\0\0\0J\207\377\377\377\0\2\0\0\0\1\0\0\0O\210\0\0\0\226\1"
3016 "\0\0\0\206\202\0\0\0h\3\0\0\0m\0\0\0s\0\0\0\214\207\0\0\0\226\2\0\0\0"
3017 "\210\0\0\0)\207\377\377\377\0\2\0\0\0\1\0\0\0\233\206\0\0\0\310\1\0\0"
3018 "\0l\203\377\377\377\0\2\0\0\0P\0\0\0\374\205\0\0\0\377\2\0\0\0\337\0"
3019 "\0\0\"\224\377\377\377\0\1\0\0\0s\204\0\0\0\377\2\0\0\0\315\0\0\0\23"
3020 "\203\377\377\377\0\1\0\0\0N\206\0\0\0\310\2\0\0\0\245\0\0\0\2\206\377"
3021 "\377\377\0\2\0\0\0\6\0\0\0f\206\0\0\0\226\3\0\0\0w\0\0\0""7\0\0\0\23"
3022 "\205\377\377\377\0\4\0\0\0\3\0\0\0*\0\0\0[\0\0\0\212\205\0\0\0\226\2"
3023 "\0\0\0\222\0\0\0*\207\377\377\377\0\2\0\0\0#\0\0\0\304\205\0\0\0\310"
3024 "\2\0\0\0\277\0\0\0\16\203\377\377\377\0\2\0\0\0]\0\0\0\376\203\0\0\0"
3025 "\377\2\0\0\0\332\0\0\0\35\226\377\377\377\0\5\0\0\0;\0\0\0j\0\0\0\223"
3026 "\0\0\0\244\0\0\0\20\203\377\377\377\0\2\0\0\0\5\0\0\0\260\206\0\0\0\310"
3027 "\1\0\0\0>\206\377\377\377\0\2\0\0\0\14\0\0\0z\205\0\0\0\226\2\0\0\0|"
3028 "\0\0\0/\213\377\377\377\0\3\0\0\0\10\0\0\0U\0\0\0\224\204\0\0\0\226\2"
3029 "\0\0\0\221\0\0\0%\207\377\377\377\0\1\0\0\0s\206\0\0\0\310\1\0\0\0d\204"
3030 "\377\377\377\0\5\0\0\0a\0\0\0\240\0\0\0\177\0\0\0]\0\0\0\26\237\377\377"
3031 "\377\0\1\0\0\0U\206\0\0\0\310\1\0\0\0\235\206\377\377\377\0\2\0\0\0\2"
3032 "\0\0\0r\204\0\0\0\226\3\0\0\0\225\0\0\0J\0\0\0\1\216\377\377\377\0\2"
3033 "\0\0\0\35\0\0\0w\204\0\0\0\226\2\0\0\0\217\0\0\0\40\206\377\377\377\0"
3034 "\2\0\0\0\27\0\0\0\304\205\0\0\0\310\2\0\0\0\273\0\0\0\12\247\377\377"
3035 "\377\0\1\0\0\0\236\206\0\0\0\310\1\0\0\0""5\206\377\377\377\0\1\0\0\0"
3036 "T\204\0\0\0\226\2\0\0\0\221\0\0\0""3\221\377\377\377\0\2\0\0\0\4\0\0"
3037 "\0l\204\0\0\0\226\2\0\0\0\215\0\0\0\34\206\377\377\377\0\1\0\0\0}\206"
3038 "\0\0\0\310\1\0\0\0E\247\377\377\377\0\1\0\0\0\276\205\0\0\0\310\1\0\0"
3039 "\0\224\206\377\377\377\0\1\0\0\0""4\204\0\0\0\226\2\0\0\0\214\0\0\0\40"
3040 "\223\377\377\377\0\2\0\0\0\5\0\0\0q\204\0\0\0\226\2\0\0\0\211\0\0\0\14"
3041 "\205\377\377\377\0\2\0\0\0\37\0\0\0\306\205\0\0\0\310\1\0\0\0`\246\377"
3042 "\377\377\0\2\0\0\0\12\0\0\0\277\205\0\0\0\310\1\0\0\0+\205\377\377\377"
3043 "\0\2\0\0\0\30\0\0\0\220\203\0\0\0\226\2\0\0\0\225\0\0\0*\225\377\377"
3044 "\377\0\2\0\0\0\10\0\0\0v\204\0\0\0\226\1\0\0\0X\206\377\377\377\0\1\0"
3045 "\0\0\207\205\0\0\0\310\1\0\0\0m\247\377\377\377\0\2\0\0\0""3\0\0\0\301"
3046 "\203\0\0\0\310\1\0\0\0[\206\377\377\377\0\1\0\0\0n\204\0\0\0\226\1\0"
3047 "\0\0G\227\377\377\377\0\2\0\0\0\12\0\0\0z\203\0\0\0\226\2\0\0\0\224\0"
3048 "\0\0\27\205\377\377\377\0\2\0\0\0\20\0\0\0\246\203\0\0\0\310\2\0\0\0"
3049 "\224\0\0\0\11\250\377\377\377\0\4\0\0\0,\0\0\0h\0\0\0\210\0\0\0R\206"
3050 "\377\377\377\0\1\0\0\0&\204\0\0\0\226\2\0\0\0f\0\0\0\1\230\377\377\377"
3051 "\0\2\0\0\0\26\0\0\0\224\203\0\0\0\226\1\0\0\0g\206\377\377\377\0\5\0"
3052 "\0\0\22\0\0\0\206\0\0\0y\0\0\0]\0\0\0\6\263\377\377\377\0\1\0\0\0t\203"
3053 "\0\0\0\226\2\0\0\0\216\0\0\0\13\232\377\377\377\0\1\0\0\0X\204\0\0\0"
3054 "\226\1\0\0\0#\274\377\377\377\0\1\0\0\0-\204\0\0\0\226\1\0\0\0K\233\377"
3055 "\377\377\0\2\0\0\0\15\0\0\0\217\203\0\0\0\226\1\0\0\0v\274\377\377\377"
3056 "\0\1\0\0\0t\203\0\0\0\226\2\0\0\0\213\0\0\0\10\213\377\377\377\0\5\0"
3057 "\0\0\5\0\0\0\30\0\0\0\40\0\0\0\36\0\0\0\22\214\377\377\377\0\1\0\0\0"
3058 "J\204\0\0\0\226\1\0\0\0*\273\377\377\377\0\1\0\0\0`\203\0\0\0\226\1\0"
3059 "\0\0E\212\377\377\377\0\3\0\0\0\13\0\0\0@\0\0\0Y\204\0\0\0Z\3\0\0\0Q"
3060 "\0\0\0""1\0\0\0\5\211\377\377\377\0\2\0\0\0\6\0\0\0\207\203\0\0\0\226"
3061 "\1\0\0\0\26\273\377\377\377\0\5\0\0\0""1\0\0\0\226\0\0\0\224\0\0\0n\0"
3062 "\0\0\5\211\377\377\377\0\2\0\0\0$\0\0\0U\202\0\0\0Z\4\0\0\0P\0\0\0E\0"
3063 "\0\0I\0\0\0X\202\0\0\0Z\2\0\0\0P\0\0\0\33\211\377\377\377\0\4\0\0\0""3"
3064 "\0\0\0\206\0\0\0\226\0\0\0\201\274\377\377\377\0\3\0\0\0\6\0\0\0""8\0"
3065 "\0\0\13\211\377\377\377\0\2\0\0\0\7\0\0\0A\202\0\0\0Z\2\0\0\0I\0\0\0"
3066 "\20\203\377\377\377\0\6\0\0\0\4\0\0\0\37\0\0\0O\0\0\0Z\0\0\0Y\0\0\0\36"
3067 "\212\377\377\377\0\2\0\0\0\34\0\0\0)\310\377\377\377\0\5\0\0\0<\0\0\0"
3068 "Z\0\0\0Y\0\0\0.\0\0\0\2\206\377\377\377\0\5\0\0\0\3\0\0\0;\0\0\0Z\0\0"
3069 "\0X\0\0\0\32\322\377\377\377\0\1\0\0\0\34\202\0\0\0Z\1\0\0\0\30\211\377"
3070 "\377\377\0\5\0\0\0\1\0\0\0>\0\0\0Z\0\0\0W\0\0\0\13\320\377\377\377\0"
3071 "\4\0\0\0\5\0\0\0P\0\0\0Z\0\0\0""5\213\377\377\377\0\4\0\0\0\2\0\0\0H"
3072 "\0\0\0Z\0\0\0:\320\377\377\377\0\4\0\0\0""4\0\0\0Z\0\0\0P\0\0\0\5\214"
3073 "\377\377\377\0\1\0\0\0\26\202\0\0\0Z\1\0\0\0\22\317\377\377\377\0\3\0"
3074 "\0\0+\0\0\0X\0\0\0\33\216\377\377\377\0\3\0\0\0>\0\0\0I\0\0\0\23\320"
3075 "\377\377\377\0\1\0\0\0\12\217\377\377\377\0\2\0\0\0\6\0\0\0\1\377\377"
3076 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
3077 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
3078 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
3079 "\0"]
3081 known_profile_icon = [ ""
3082 "GdkP"
3083 "\0\0\5""0"
3084 "\2\1\0\2"
3085 "\0\0\0P"
3086 "\0\0\0\24"
3087 "\0\0\0\24"
3088 "\210\0\0\0\0\4\0\0\0\3\0\0\0\16\0\0\0\23\0\0\0\11\216\0\0\0\0\11\0\0"
3089 "\0\16\0\0\0h\0\0\0\301\0\0\0\345\0\0\0\352\0\0\0\331\0\0\0\237\0\0\0"
3090 "9\0\0\0\3\212\0\0\0\0\13\0\0\0@\0\0\0\323\0\0\0\376\0\0\0\350\0\0\0\304"
3091 "\0\0\0\271\0\0\0\323\0\0\0\367\0\0\0\370\0\0\0\227\0\0\0\17\210\0\0\0"
3092 "\0\15\0\0\0K\0\0\0\354\0\0\0\365\0\0\0\206\0\0\0#\0\0\0\6\0\0\0\3\0\0"
3093 "\0\15\0\0\0C\0\0\0\304\0\0\0\376\0\0\0\260\0\0\0\22\206\0\0\0\0\17\0"
3094 "\0\0""2\0\0\0\346\0\0\0\351\0\0\0L\0\0\0#\0\0\0u\0\0\0\246\0\0\0\257"
3095 "\0\0\0\223\0\0\0M\0\0\0\27\0\0\0\235\0\0\0\375\0\0\0\242\0\0\0\7\204"
3096 "\0\0\0\0\20\0\0\0\13\0\0\0\300\0\0\0\372\0\0\0W\0\0\0O\0\0\0\271\0\0"
3097 "\0\233\0\0\0b\0\0\0V\0\0\0z\0\0\0\267\0\0\0\223\0\0\0$\0\0\0\267\0\0"
3098 "\0\374\0\0\0X\204\0\0\0\0\7\0\0\0S\0\0\0\374\0\0\0\240\0\0\0H\0\0\0\275"
3099 "\0\0\0a\0\0\0\12\202\0\0\0\0\10\0\0\0\1\0\0\0%\0\0\0\240\0\0\0\241\0"
3100 "\0\0""9\0\0\0\352\0\0\0\320\0\0\0\12\203\0\0\0\0\21\0\0\0\262\0\0\0\351"
3101 "\0\0\0A\0\0\0\272\0\0\0g\0\0\0\6\0\0\0""4\0\0\0e\0\0\0l\0\0\0T\0\0\0"
3102 "\25\0\0\0\27\0\0\0\251\0\0\0v\0\0\0\214\0\0\0\367\0\0\0<\203\0\0\0\0"
3103 "\21\0\0\0""6\0\0\0G\0\0\0r\0\0\0\244\0\0\0\17\0\0\0P\0\0\0b\0\0\0#\0"
3104 "\0\0\27\0\0\0;\0\0\0s\0\0\0\33\0\0\0E\0\0\0\270\0\0\0""6\0\0\0\\\0\0"
3105 "\0\15\205\0\0\0\0\15\0\0\0T\0\0\0""8\0\0\0""0\0\0\0f\0\0\0\6\0\0\0\0"
3106 "\0\0\0\1\0\0\0\0\0\0\0(\0\0\0l\0\0\0\13\0\0\0k\0\0\0\33\206\0\0\0\0\16"
3107 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
3108 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
3109 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
3110 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
3111 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
3112 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
3113 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
3114 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
3115 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
3116 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
3117 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
3118 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
3119 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
3120 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
3121 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
3122 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
3123 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
3124 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
3125 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
3126 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
3127 "\313\377\272\272\272\377\24\24\24\226\0\0\0\30\0\0\0\10\0\0\0\5\0\0\0"
3128 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
3129 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
3130 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
3131 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
3132 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
3133 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
3134 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
3135 "\377\231\231\231\376\16\16\16\240\0\0\0\35\0\0\0\6\0\0\0\2\0\0\0\12\0"
3136 "\0\0/\0\0\0n\0\0\0|\0\0\0\177\202\0\0\0\200\202\0\0\0\201\1\0\0\0\203"
3137 "\204\0\0\0\205\12\0\0\0\201\0\0\0y\0\0\0<\0\0\0\15\0\0\0\2\0\0\0\0\0"
3138 "\0\0\2\0\0\0\6\0\0\0\14\0\0\0\20\204\0\0\0\24\202\0\0\0\25\203\0\0\0"
3139 "\26\6\0\0\0\25\0\0\0\22\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\0"]
3141 unknown_profile_icon = [ ""
3142 "GdkP"
3143 "\0\0\5\22"
3144 "\2\1\0\2"
3145 "\0\0\0P"
3146 "\0\0\0\24"
3147 "\0\0\0\24"
3148 "\210\0\0\0\0\4\0\0\0\1\0\0\0\4\0\0\0\6\0\0\0\3\216\0\0\0\0\11\0\0\0\4"
3149 "\0\0\0\37\0\0\0""9\0\0\0D\0\0\0F\0\0\0@\0\0\0/\0\0\0\21\0\0\0\1\212\0"
3150 "\0\0\0\7\0\0\0\23\0\0\0\77\0\0\0K\0\0\0E\0\0\0:\0\0\0""7\0\0\0\77\202"
3151 "\0\0\0I\2\0\0\0-\0\0\0\4\210\0\0\0\0\15\0\0\0\26\0\0\0F\0\0\0I\0\0\0"
3152 "(\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\4\0\0\0\24\0\0\0:\0\0\0K\0\0\0""4\0"
3153 "\0\0\6\206\0\0\0\0\17\0\0\0\17\0\0\0D\0\0\0E\0\0\0\26\0\0\0\13\0\0\0"
3154 "#\0\0\0""1\0\0\0""4\0\0\0,\0\0\0\27\0\0\0\7\0\0\0/\0\0\0K\0\0\0""0\0"
3155 "\0\0\2\204\0\0\0\0\20\0\0\0\3\0\0\0""9\0\0\0J\0\0\0\32\0\0\0\30\0\0\0"
3156 "7\0\0\0.\0\0\0\35\0\0\0\32\0\0\0$\0\0\0""6\0\0\0,\0\0\0\13\0\0\0""6\0"
3157 "\0\0K\0\0\0\32\204\0\0\0\0\7\0\0\0\31\0\0\0K\0\0\0""0\0\0\0\25\0\0\0"
3158 "8\0\0\0\35\0\0\0\3\202\0\0\0\0\2\0\0\0\1\0\0\0\13\202\0\0\0""0\4\0\0"
3159 "\0\21\0\0\0F\0\0\0>\0\0\0\3\203\0\0\0\0\21\0\0\0""5\0\0\0E\0\0\0\23\0"
3160 "\0\0""7\0\0\0\37\0\0\0\2\0\0\0\20\0\0\0\36\0\0\0\40\0\0\0\31\0\0\0\6"
3161 "\0\0\0\7\0\0\0""2\0\0\0#\0\0\0)\0\0\0I\0\0\0\22\203\0\0\0\0\21\0\0\0"
3162 "\20\0\0\0\25\0\0\0\"\0\0\0""1\0\0\0\4\0\0\0\30\0\0\0\35\0\0\0\13\0\0"
3163 "\0\7\0\0\0\21\0\0\0\"\0\0\0\10\0\0\0\25\0\0\0""6\0\0\0\20\0\0\0\33\0"
3164 "\0\0\4\205\0\0\0\0\15\0\0\0\31\0\0\0\21\0\0\0\16\0\0\0\36\0\0\0\2\0\0"
3165 "\0\0\0\0\0\1\0\0\0\0\0\0\0\14\0\0\0\40\0\0\0\3\0\0\0\40\0\0\0\10\206"
3166 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
3167 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
3168 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
3169 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
3170 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
3171 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
3172 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
3173 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
3174 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
3175 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
3176 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
3177 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
3178 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
3179 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
3180 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
3181 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
3182 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
3183 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
3184 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
3185 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
3186 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
3187 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
3188 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
3189 "\16\16""0\0\0\0\10\0\0\0\2\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0!\0\0\0%\205"
3190 "\0\0\0&\205\0\0\0'\12\0\0\0&\0\0\0$\0\0\0\22\0\0\0\4\0\0\0\1\0\0\0\0"
3191 "\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\206\0\0\0\6\203\0\0\0\7\202\0\0\0\6"
3192 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
3194 signal_xpm_barely = [
3195 "20 20 10 1",
3196 " c None",
3197 ". c #C6C6C6",
3198 "+ c #CCCCCC",
3199 "@ c #DBDBDB",
3200 "# c #D3D3D3",
3201 "$ c #A9B099",
3202 "% c #95A173",
3203 "& c #6B8428",
3204 "* c #B4B7AC",
3205 "= c #80924D",
3206 " .+++.",
3207 " +@@@+",
3208 " +@@@+",
3209 " +@@@+",
3210 " +@@@+",
3211 " .++++#@@@+",
3212 " +@@@@@@@@+",
3213 " +@@@@@@@@+",
3214 " +@@@@@@@@+",
3215 " +@@@@@@@@+",
3216 " $%%%%#@@@@@@@@+",
3217 " %&&&&@@@@@@@@@+",
3218 " %&&&&@@@@@@@@@+",
3219 " %&&&&@@@@@@@@@+",
3220 " %&&&&@@@@@@@@@+",
3221 "*%%%%=&&&&@@@@@@@@@+",
3222 "%&&&&&&&&&@@@@@@@@@+",
3223 "%&&&&&&&&&@@@@@@@@@+",
3224 "%&&&&&&&&&@@@@@@@@@+",
3225 "*%%%%%%%%%+++++++++."
3229 signal_xpm_best = [
3230 "20 20 6 1",
3231 " c None",
3232 ". c #9DAABF",
3233 "+ c #7B96BF",
3234 "@ c #386EBF",
3235 "# c #5982BF",
3236 "$ c #AEB4BF",
3237 " .+++.",
3238 " +@@@+",
3239 " +@@@+",
3240 " +@@@+",
3241 " +@@@+",
3242 " .++++#@@@+",
3243 " +@@@@@@@@+",
3244 " +@@@@@@@@+",
3245 " +@@@@@@@@+",
3246 " +@@@@@@@@+",
3247 " .++++#@@@@@@@@+",
3248 " +@@@@@@@@@@@@@+",
3249 " +@@@@@@@@@@@@@+",
3250 " +@@@@@@@@@@@@@+",
3251 " +@@@@@@@@@@@@@+",
3252 "$++++#@@@@@@@@@@@@@+",
3253 "+@@@@@@@@@@@@@@@@@@+",
3254 "+@@@@@@@@@@@@@@@@@@+",
3255 "+@@@@@@@@@@@@@@@@@@+",
3256 "$++++++++++++++++++."
3259 signal_xpm_none = [
3260 "20 20 6 1",
3261 " c None",
3262 ". c #C6C6C6",
3263 "+ c #CCCCCC",
3264 "@ c #DBDBDB",
3265 "# c #D3D3D3",
3266 "$ c #C2C2C2",
3267 " .+++.",
3268 " +@@@+",
3269 " +@@@+",
3270 " +@@@+",
3271 " +@@@+",
3272 " .++++#@@@+",
3273 " +@@@@@@@@+",
3274 " +@@@@@@@@+",
3275 " +@@@@@@@@+",
3276 " +@@@@@@@@+",
3277 " .++++#@@@@@@@@+",
3278 " +@@@@@@@@@@@@@+",
3279 " +@@@@@@@@@@@@@+",
3280 " +@@@@@@@@@@@@@+",
3281 " +@@@@@@@@@@@@@+",
3282 "$++++#@@@@@@@@@@@@@+",
3283 "+@@@@@@@@@@@@@@@@@@+",
3284 "+@@@@@@@@@@@@@@@@@@+",
3285 "+@@@@@@@@@@@@@@@@@@+",
3286 "$++++++++++++++++++."
3289 signal_xpm_ok = [
3290 "20 20 10 1",
3291 " c None",
3292 ". c #C6C6C6",
3293 "+ c #CCCCCC",
3294 "@ c #DBDBDB",
3295 "# c #A1A5B2",
3296 "$ c #848DA5",
3297 "% c #D3D3D3",
3298 "& c #4A5B8C",
3299 "* c #677498",
3300 "= c #B0B2B8",
3301 " .+++.",
3302 " +@@@+",
3303 " +@@@+",
3304 " +@@@+",
3305 " +@@@+",
3306 " #$$$$%@@@+",
3307 " $&&&&@@@@+",
3308 " $&&&&@@@@+",
3309 " $&&&&@@@@+",
3310 " $&&&&@@@@+",
3311 " #$$$$*&&&&@@@@+",
3312 " $&&&&&&&&&@@@@+",
3313 " $&&&&&&&&&@@@@+",
3314 " $&&&&&&&&&@@@@+",
3315 " $&&&&&&&&&@@@@+",
3316 "=$$$$*&&&&&&&&&@@@@+",
3317 "$&&&&&&&&&&&&&&@@@@+",
3318 "$&&&&&&&&&&&&&&@@@@+",
3319 "$&&&&&&&&&&&&&&@@@@+",
3320 "=$$$$$$$$$$$$$$++++."
3324 signal_xpm_low = [
3325 "20 20 8 1",
3326 " c None",
3327 ". c #C6C6C6",
3328 "+ c #CCCCCC",
3329 "@ c #DBDBDB",
3330 "# c #D3D3D3",
3331 "$ c #BFB0B5",
3332 "% c #C18799",
3333 "& c #C54F74",
3334 " .+++.",
3335 " +@@@+",
3336 " +@@@+",
3337 " +@@@+",
3338 " +@@@+",
3339 " .++++#@@@+",
3340 " +@@@@@@@@+",
3341 " +@@@@@@@@+",
3342 " +@@@@@@@@+",
3343 " +@@@@@@@@+",
3344 " .++++#@@@@@@@@+",
3345 " +@@@@@@@@@@@@@+",
3346 " +@@@@@@@@@@@@@+",
3347 " +@@@@@@@@@@@@@+",
3348 " +@@@@@@@@@@@@@+",
3349 "$%%%%#@@@@@@@@@@@@@+",
3350 "%&&&&@@@@@@@@@@@@@@+",
3351 "%&&&&@@@@@@@@@@@@@@+",
3352 "%&&&&@@@@@@@@@@@@@@+",
3353 "$%%%%++++++++++++++."
3357 ####################################################################################################
3358 # Make so we can be imported
3359 if __name__ == "__main__":
3360 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
3361 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3362 elif len( sys.argv ) > 1 and ( sys.argv[1] == '--help' or sys.argv[1] == '-h' ):
3363 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3364 print "For help, check man pages for wifi-radar and wifi-radar.conf,"
3365 print "or visit http://wifi-radar.berlios.de"
3366 else:
3367 import gtk, gobject
3368 gtk.gdk.threads_init()
3369 apQueue = Queue.Queue(100)
3370 commQueue = Queue.Queue(2)
3372 logger = logging.getLogger("wrlog")
3373 logger.setLevel(confFile.get_opt_as_int('DEFAULT.loglevel'))
3374 fileLogHandler = logging.handlers.RotatingFileHandler(confFile.get_opt('DEFAULT.logfile'), maxBytes=64*1024, backupCount=5)
3375 fileLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(funcName)s: %(message)s'))
3376 logger.addHandler(fileLogHandler)
3377 consoleLogHandler = logging.StreamHandler()
3378 consoleLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(funcName)s: %(message)s'))
3379 logger.addHandler(consoleLogHandler)
3380 if __debug__:
3381 logger.setLevel(logging.INFO)
3383 exit_event = threading.Event()
3384 exit_event.clear()
3385 threading.Thread(None, scanning_thread, None, (confFile, apQueue, commQueue, logger, exit_event)).start()
3386 main_radar_window = radar_window(confFile, apQueue, commQueue, logger, exit_event)
3387 gobject.timeout_add( 500, main_radar_window.update_window )
3388 main_radar_window.main()