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