Use raw config options in preferences dialog, cooked elsewhere
[wifi-radar.git] / wifi-radar
blob0501deae2fdd40754edf77d57b2ff236f158529d
1 #!/usr/bin/python
3 # A wireless profile manager for Linux
5 # Originally created for x1000 Linux:
6 # http://x1000.bitbuilder.com
8 # Created by:
9 # Ahmad Baitalmal <ahmad@baitalmal.com>
11 # Maintained 2006-2009 by:
12 # Brian Elliott Finley <brian@thefinleys.com>
14 # Maintained by:
15 # Sean Robinson <seankrobinson@gmail.com>
17 # License:
18 # GPL
20 # http://wifi-radar.berlios.de
22 # See CREDITS file for more contributors.
23 # See ChangeLog file for, well, changes.
26 import ConfigParser
27 import gtk
28 import os
29 import Queue
30 import re
31 import string
32 import sys
33 import threading
34 from signal import SIGTERM
35 from subprocess import call, Popen, PIPE
36 from time import sleep
37 from types import *
39 WIFI_RADAR_VERSION = "0.0.0"
41 if __debug__:
42 print '__debug__ is True'
43 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
44 # turn on debugging.
47 # Where the conf file should live could be different for your distro. Please change
48 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
50 CONF_FILE = "/etc/wifi-radar.conf"
52 os.environ['LC_MESSAGES'] = 'C'
55 #####################################
56 # Labels
57 USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
58 USE_IP_LABEL = "Manual network configuration"
59 WIFI_SET_LABEL = "WiFi Options"
60 CON_PP_LABEL = "Connection Commands"
61 DIS_PP_LABEL = "Disconnection Commands"
62 USE_WPA_LABEL = "Use WPA"
63 NO_WPA_LABEL = "No WPA"
64 ####################################################################################################
66 ####################################################################################################
67 ####################################################################################################
69 # Sets the interface to the specified network device
71 #Parameters:
73 # 'device' -- string - The network device to use
75 #Returns:
77 # nothing
78 def set_network_device( device ):
79 #print "set_network_device: ", device
80 if device != "auto_detect":
81 confFile.set_opt('DEFAULT.interface', device)
82 else: # auto detect network device
83 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
84 # If no devices are found, default to eth1.
85 # call iwconfig command and read output
86 iwconfig_info = Popen(confFile.get_opt('DEFAULT.iwconfig_command'), shell=True, stdout=PIPE).stdout
87 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
88 if len(wireless_devices) > 0:
89 confFile.set_opt('DEFAULT.interface', wireless_devices[0])
90 #else:
91 #print "No wifi-device found. Exiting."
92 #sys.exit()
94 # Return a blank profile
96 #Parameters:
98 # none
100 #Returns:
102 # dictionary -- An AP profile with defaults set.
103 def get_new_profile():
104 return { 'known': False,
105 'available': False,
106 'encrypted': False,
107 'essid': '',
108 'bssid': '',
109 'protocol': 'g',
110 'signal': 0,
111 'channel': 'auto',
112 'con_prescript': '',
113 'con_postscript': '',
114 'dis_prescript': '',
115 'dis_postscript': '',
116 'key': '',
117 'mode': '',
118 'security': '',
119 'use_wpa': False,
120 'wpa_driver': '',
121 'use_dhcp': True,
122 'ip': '',
123 'netmask': '',
124 'gateway': '',
125 'domain': '',
126 'dns1': '',
127 'dns2': ''
130 # Combine essid and bssid to make a config file section name
132 #Parameters:
134 # 'essid' -- string - AP ESSID
136 # 'bssid' -- string - AP BSSID
138 #Returns:
140 # string -- the bssid concatenated to a colon, concatenated to the essid
141 def make_section_name( essid, bssid ):
142 return essid + ':' + bssid
144 # Split a config file section name into an essid and a bssid
146 #Parameters:
148 # 'section' -- string - Config file section name
150 #Returns:
152 # list -- the essid and bssid
153 def split_section_name( section ):
154 parts = re.split(':', section)
155 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
157 # Run commands through the shell
159 #Parameters:
161 # 'command' -- tuple - The command and arguments to run
163 #Returns:
165 # boolean -- True on success, otherwise, False
166 def shellcmd( command, environment = None ):
167 try:
168 env_tmp = os.environ
169 env_tmp.update(environment)
170 command = ' '.join(command)
171 return_code = call(command, shell=True, env=env_tmp)
172 if return_code >= 0:
173 return True
174 else:
175 print >>sys.stderr, "Child was terminated by signal", -return_code
176 except OSError, exception:
177 print >>sys.stderr, "Execution failed:", exception
178 return False
180 # Speak feedback message to user
182 #Parameters:
184 # 'words' -- string - Message to speak to user
186 #Returns:
188 # nothing
189 def say( words ):
190 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
191 words = words.replace( "\"", "\\\"" )
192 shellcmd([confFile.get_opt('DEFAULT.speak_command'), words])
194 # Scan for a limited time and return AP names and bssid found.
195 # Access points we find will be put on the outgoing Queue, apQueue.
197 #Parameters:
199 # 'confFile' -- ConfigFile - Config file object
201 # 'apQueue' -- Queue - Queue on which to put AP profiles
203 # 'commandQueue' -- Queue - Queue from which to read commands
205 #Returns:
207 # nothing
208 def scanning_thread( confFile, apQueue, commandQueue ):
209 # Setup our essid pattern matcher
210 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
211 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abg]+)", re.I | re.M | re.S )
212 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
213 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
214 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
215 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
217 access_points = {}
218 command = "scan"
219 while True:
220 try:
221 command = commandQueue.get_nowait()
222 if __debug__: print "scanning_thread received command:", command
223 command_read = True
224 except Queue.Empty:
225 command_read = False
226 if command == "scan":
227 #if __debug__: print "Beginning scan pass"
228 # Some cards need to have the interface up to scan
229 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
230 # call ifconfig command and wait for return
231 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface'), 'up'])
232 # update the signal strengths
233 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), confFile.get_opt('DEFAULT.interface'), 'scan'], stdout=PIPE).stdout.read()
234 #if __debug__:
235 #print "Current IP ", get_current_ip()
236 #print "Current ESSID ", get_current_essid()
237 #print "Current BSSID ", get_current_bssid()
238 # zero out the signal levels for all access points
239 for bssid in access_points:
240 access_points[bssid]['signal'] = 0
241 # split the scan data based on the address line
242 hits = scandata.split(' - ')
243 for hit in hits:
244 # set the defaults for profile template
245 profile = get_new_profile()
246 m = essid_pattern.search( hit )
247 if m:
248 # we found an essid
249 profile['essid'] = m.groups()[1]
250 m = bssid_pattern.search( hit ) # get BSSID from scan
251 if m: profile['bssid'] = m.groups()[1]
252 m = protocol_pattern.search( hit ) # get protocol from scan
253 if m: profile['protocol'] = m.groups()[1]
254 m = mode_pattern.search( hit ) # get mode from scan
255 if m: profile['mode'] = m.groups()[1]
256 m = channel_pattern.search( hit ) # get channel from scan
257 if m: profile['channel'] = m.groups()[1]
258 m = enckey_pattern.search( hit ) # get encryption key from scan
259 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
260 m = signal_pattern.search( hit ) # get signal strength from scan
261 if m: profile['signal'] = m.groups()[1]
262 access_points[ profile['bssid'] ] = profile
263 for bssid in access_points:
264 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
265 # Put all, now or previously, sensed access_points into apQueue
266 try:
267 apQueue.put_nowait( access_points[bssid] )
268 except Queue.Full:
269 pass
270 elif command == "exit":
271 if __debug__: print "Exiting scanning_thread"
272 return
273 if command_read: commandQueue.task_done()
274 if confFile.get_opt('DEFAULT.interface').find('ath') == 0:
275 sleep( 3 )
276 else:
277 sleep( 1 )
280 # Manage a connection; including reporting connection state,
281 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
282 class ConnectionManager():
283 # Create a new connection manager which can read a config file and send to scanning thread
284 # command Queue. A new manager checks for a pre-existing connection and takes
285 # its AP profile from the ESSID and BSSID to which it is currently attached.
287 #Parameters:
289 # 'confFile' -- ConfigFile - Config file object
291 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
293 #Returns:
295 # ConnectionManager instance
296 def __init__( self, confFile, commandQueue ):
297 self.confFile = confFile
298 self.commQueue = commandQueue
299 # is connection running?
300 self.state = False
301 if self.get_current_ip():
302 self.state = True
303 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
305 # Change the interface state: up or down.
307 #Parameters:
309 # 'state' -- string - The state to which to change the interface.
311 #Returns:
313 # nothing
314 def if_change( self, state ):
315 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
316 if __debug__: print "if_change: changing interface state to %s" % state
317 # call ifconfig command and wait for return
318 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), state])
320 # Connect to the specified AP.
322 #Parameters:
324 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
326 # 'status' -- status implementer - Object which implements status interface.
328 #Returns:
330 # nothing
331 def connect_to_network( self, profile, status ):
332 self.profile = profile
333 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
334 say( msg )
335 if __debug__: print " %s" % msg
336 # ready to dance
337 # Let's run the connection prescript
338 if self.profile['con_prescript'].strip() != '':
339 # got something to execute
340 # run connection prescript through shell and wait for return
341 if __debug__: print "executing connection prescript:", self.profile['con_prescript']
342 shellcmd([self.profile['con_prescript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
343 status.show()
344 # Some cards need to have the interface up
345 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
346 self.if_change('up')
347 # Start building iwconfig command line, command
348 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
349 iwconfig_command.append( self.confFile.get_opt('DEFAULT.interface') )
350 # Setting essid
351 iwconfig_command.append( 'essid' )
352 iwconfig_command.append( self.profile['essid'] )
353 # Setting nick
354 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
355 # Setting key
356 iwconfig_command.append( 'key' )
357 if self.profile['key'] == '':
358 iwconfig_command.append( 'off' )
359 else:
360 iwconfig_command.append( self.profile['key'] )
361 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
362 # Setting mode to Master, this will cause association with the AP to fail
363 #iwconfig_command.append( 'mode' )
364 #iwconfig_command.append( 'Master' )
365 # Setting channel
366 if self.profile['channel'] != '':
367 iwconfig_command.append( 'channel' )
368 iwconfig_command.append( self.profile['channel'] )
369 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
370 iwconfig_command.append( 'ap' )
371 iwconfig_command.append( self.profile['bssid'] )
372 # Some cards require a commit
373 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
374 if __debug__: print 'iwconfig_args %s ' % ( iwconfig_args, )
375 iwconfig_command.append( 'commit' )
376 # call iwconfig command and wait for return
377 if not shellcmd(iwconfig_command): return
378 # Now normal network stuff
379 # Kill off any existing DHCP clients running
380 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
381 if __debug__: print "Killing existing DHCP..."
382 try:
383 if self.confFile.get_opt('DHCP.kill_args') != '':
384 # call DHCP client kill command and wait for return
385 if __debug__: print self.confFile.get_opt('DHCP.command') + " " + self.confFile.get_opt('DHCP.kill_args')
386 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
387 else:
388 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
389 except OSError:
390 print "failed to kill DHCP client"
391 sys.exit()
392 finally:
393 print "Stale pid file. Removing..."
394 os.remove(self.confFile.get_opt('DHCP.pidfile'))
395 # Kill off any existing WPA supplicants running
396 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
397 if __debug__: print "Killing existing WPA supplicant..."
398 try:
399 if not self.confFile.get_opt('WPA.kill_command') != '':
400 # call WPA supplicant kill command and wait for return
401 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
402 else:
403 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
404 except OSError:
405 print "failed to kill WPA supplicant"
406 sys.exit()
407 finally:
408 print "Stale pid file. Removing..."
409 os.remove(self.confFile.get_opt('WPA.pidfile'))
410 # Begin WPA supplicant
411 if self.profile['use_wpa'] :
412 if __debug__: print "WPA args: %s" % ( wpa_options, )
413 status.update_message("WPA supplicant starting")
414 if sys.modules.has_key("gtk"):
415 while gtk.events_pending():
416 gtk.main_iteration(False)
417 # call WPA supplicant command and do not wait for return
418 wpa_proc = Popen([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args'), self.confFile.get_opt('DEFAULT.interface')])
419 if self.profile['use_dhcp'] :
420 if __debug__: print "Disable iwlist while dhcp in progress..."
421 try:
422 self.commQueue.put("pause")
423 except Queue.Full:
424 pass
425 status.update_message("Acquiring IP Address (DHCP)")
426 if sys.modules.has_key("gtk"):
427 while gtk.events_pending():
428 gtk.main_iteration(False)
429 # call DHCP client command and do not wait for return
430 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
431 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
432 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
433 dhcp_proc = Popen(dhcp_command, stdout=None)
434 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
435 tick = 0.25
436 waiting = dhcp_proc.poll()
437 while waiting == None:
438 waiting = dhcp_proc.poll()
439 if timer < 0:
440 os.kill(dhcp_proc.pid, SIGTERM)
441 break
442 if sys.modules.has_key("gtk"):
443 while gtk.events_pending():
444 gtk.main_iteration(False)
445 timer -= tick
446 sleep(tick)
447 if not self.get_current_ip():
448 status.update_message("Could not get IP address!")
449 if sys.modules.has_key("gtk"):
450 while gtk.events_pending():
451 gtk.main_iteration(False)
452 sleep(1)
453 if self.state:
454 self.disconnect_interface()
455 else:
456 status.update_message("Got IP address. Done.")
457 self.state = True
458 if sys.modules.has_key("gtk"):
459 while gtk.events_pending():
460 gtk.main_iteration(False)
461 sleep(2)
462 # Re-enable iwlist
463 try:
464 self.commQueue.put("scan")
465 except Queue.Full:
466 pass
467 else:
468 ifconfig_command= "%s %s down; %s %s %s netmask %s" % ( self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), self.profile['ip'], self.profile['netmask'] )
469 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
470 resolv_contents = ''
471 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
472 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
473 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
474 if ( resolv_contents != '' ):
475 resolv_file=open('/etc/resolv.conf', 'w')
476 resolv_file.write(s)
477 resolv_file.close
478 if not shellcmd([ifconfig_command]): return
479 if not shellcmd([route_command]): return
480 self.state = True
481 # Let's run the connection postscript
482 con_postscript = self.profile['con_postscript']
483 if self.profile['con_postscript'].strip() != '':
484 if __debug__: print "executing connection postscript:", self.profile['con_postscript']
485 shellcmd([self.profile['con_postscript']], environment={"WIFIRADAR_IP": self.get_current_ip(), "WIFIRADAR_ESSID": self.get_current_essid(), "WIFIRADAR_BSSID": self.get_current_bssid(), "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
486 status.hide()
488 # Disconnect from the AP with which a connection has been established/attempted.
490 #Parameters:
492 # nothing
494 #Returns:
496 # nothing
497 def disconnect_interface( self ):
498 msg = "Disconnecting"
499 say( msg )
500 if __debug__: print msg
501 # Pause scanning while manipulating card
502 try:
503 self.commQueue.put("pause")
504 except Queue.Full:
505 pass
506 if self.state:
507 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
508 # Let's run the disconnection prescript
509 if self.profile['dis_prescript'].strip() != '':
510 if __debug__: print "executing disconnection prescript:", self.profile['dis_prescript']
511 shellcmd([self.profile['dis_prescript']], environment={"WIFIRADAR_IP": self.get_current_ip(), "WIFIRADAR_ESSID": self.get_current_essid(), "WIFIRADAR_BSSID": self.get_current_bssid(), "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
512 if __debug__: print "Kill off any existing DHCP clients running..."
513 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
514 if __debug__: print "Killing existing DHCP..."
515 try:
516 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
517 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
518 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
519 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
520 if __debug__: print "DHCP command", dhcp_command
521 # call DHCP client command and wait for return
522 if not shellcmd(dhcp_command): return
523 else:
524 if __debug__: print "killing DHCP manually."
525 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
526 except OSError:
527 print "failed to kill DHCP client"
528 if __debug__: print "Kill off any existing WPA supplicants running..."
529 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
530 if __debug__: print "Killing existing WPA supplicant..."
531 try:
532 if not self.confFile.get_opt('WPA.kill_command') != '':
533 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
534 if not shellcmd(wpa_command): return
535 else:
536 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
537 except OSError:
538 print "failed to kill WPA supplicant"
539 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
540 if __debug__: print "Let's clear out the wireless stuff"
541 if __debug__: print 'Now take the interface down'
542 # taking down the interface too quickly can crash my system, so pause a moment
543 sleep(1)
544 self.if_change('down')
545 if __debug__: print 'Since it may be brought back up by the next scan, lets unset its IP'
546 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), '0.0.0.0'])
547 # Let's run the disconnection postscript
548 if self.profile['dis_postscript'].strip() != '':
549 if __debug__: print "executing disconnection postscript:", self.profile['dis_postscript']
550 shellcmd([self.profile['dis_postscript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
551 self.state = False
552 if __debug__: print 'Disconnect complete.'
553 # Begin scanning again
554 try:
555 self.commQueue.put("scan")
556 except Queue.Full:
557 pass
559 # Returns the current IP, if any, by calling ifconfig.
561 #Parameters:
563 # nothing
565 #Returns:
567 # string or None -- the IP address or None (if no there is no current connection)
568 def get_current_ip( self ):
569 ifconfig_command = [ confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface') ]
570 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
571 # Be careful to the language (inet adr: in French for example)
573 # Hi Brian
575 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
576 # There the string in ifconfig is inet Adresse for the IP which isn't
577 # found by the current get_current_ip function in wifi-radar. I changed
578 # the according line (#289; gentoo, v1.9.6-r1) to
579 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
580 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
582 # I'd be happy if you could incorporate this small change because as now
583 # I've got to change the file every time it is updated.
585 # Best wishes
587 # Simon
588 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
589 line = ifconfig_info.read()
590 if ip_re.search( line ):
591 return ip_re.search( line ).group(1)
592 return None
594 # Returns the current ESSID, if any, by calling iwconfig.
596 #Parameters:
598 # nothing
600 #Returns:
602 # string or None -- the ESSID or None (if no there is no current association)
603 def get_current_essid( self ):
604 """Returns the current ESSID if any by calling iwconfig"""
605 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
606 # Be careful to the language (inet adr: in French for example)
607 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
608 line = iwconfig_info.read()
609 if essid_re.search( line ):
610 return essid_re.search( line ).group(2)
611 return None
613 # Returns the current BSSID, if any, by calling iwconfig.
615 #Parameters:
617 # nothing
619 #Returns:
621 # string or None -- the BSSID or None (if no there is no current association)
622 def get_current_bssid( self ):
623 """Returns the current BSSID if any by calling iwconfig"""
624 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
625 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
626 line = iwconfig_info.read()
627 if bssid_re.search( line ):
628 return bssid_re.search( line ).group(2)
629 return None
633 # The main user interface window for WiFi Radar. This class also is the control
634 # center for most of the rest of the operations.
635 class radar_window:
636 # Create a new radar_window.
638 #Parameters:
640 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
642 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
644 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
646 #Returns:
648 # radar_window instance
649 def __init__( self, confFile, apQueue, commQueue ):
650 global signal_xpm_none
651 global signal_xpm_low
652 global signal_xpm_barely
653 global signal_xpm_ok
654 global signal_xpm_best
655 global known_profile_icon
656 global unknown_profile_icon
657 global wifi_radar_icon
659 self.confFile = confFile
660 self.apQueue = apQueue
661 self.commandQueue = commQueue
662 self.access_points = {}
663 self.connection = None
665 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
666 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
667 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
668 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
669 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
670 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
671 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
672 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
673 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
674 self.window.set_icon( icon )
675 self.window.set_border_width( 10 )
676 self.window.set_size_request( 550, 300 )
677 self.window.set_title( "WiFi Radar" )
678 self.window.connect( 'delete_event', self.delete_event )
679 self.window.connect( 'destroy', self.destroy )
680 # let's create all our widgets
681 self.current_network = gtk.Label()
682 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
683 self.current_network.show()
684 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
685 self.close_button.show()
686 self.close_button.connect( 'clicked', self.delete_event, None )
687 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
688 self.about_button.show()
689 self.about_button.connect( 'clicked', self.show_about_info, None )
690 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
691 self.preferences_button.show()
692 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
693 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
694 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
695 self.plist = gtk.TreeView( self.pstore )
696 # The icons column, known and encryption
697 self.pix_cell = gtk.CellRendererPixbuf()
698 self.wep_cell = gtk.CellRendererPixbuf()
699 self.icons_cell = gtk.CellRendererText()
700 self.icons_col = gtk.TreeViewColumn()
701 self.icons_col.pack_start( self.pix_cell, False )
702 self.icons_col.pack_start( self.wep_cell, False )
703 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
704 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
705 self.plist.append_column( self.icons_col )
706 # The AP column
707 self.ap_cell = gtk.CellRendererText()
708 self.ap_col = gtk.TreeViewColumn( "Access Point" )
709 self.ap_col.pack_start( self.ap_cell, True )
710 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
711 self.plist.append_column( self.ap_col )
712 # The signal column
713 self.sig_cell = gtk.CellRendererPixbuf()
714 self.signal_col = gtk.TreeViewColumn( "Signal" )
715 self.signal_col.pack_start( self.sig_cell, True )
716 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
717 self.plist.append_column( self.signal_col )
718 # The mode column
719 self.mode_cell = gtk.CellRendererText()
720 self.mode_col = gtk.TreeViewColumn( "Mode" )
721 self.mode_col.pack_start( self.mode_cell, True )
722 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
723 self.plist.append_column( self.mode_col )
724 # The protocol column
725 self.prot_cell = gtk.CellRendererText()
726 self.protocol_col = gtk.TreeViewColumn( "802.11" )
727 self.protocol_col.pack_start( self.prot_cell, True )
728 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
729 self.plist.append_column( self.protocol_col )
730 # The channel column
731 self.channel_cell = gtk.CellRendererText()
732 self.channel_col = gtk.TreeViewColumn( "Channel" )
733 self.channel_col.pack_start( self.channel_cell, True )
734 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
735 self.plist.append_column( self.channel_col )
736 # DnD Ordering
737 self.plist.set_reorderable( True )
738 self.pstore.connect( 'row-changed', self.update_auto_profile_order )
739 # enable/disable buttons based on the selected network
740 self.selected_network = self.plist.get_selection()
741 self.selected_network.connect( 'changed', self.on_network_selection, None )
742 # the list scroll bar
743 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
744 sb.show()
745 self.plist.show()
746 # Add New button
747 self.new_button = gtk.Button( "_New" )
748 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
749 self.new_button.show()
750 # Add Configure button
751 self.edit_button = gtk.Button( "C_onfigure" )
752 self.edit_button.connect( 'clicked', self.edit_profile, None )
753 self.edit_button.show()
754 self.edit_button.set_sensitive(False)
755 # Add Delete button
756 self.delete_button = gtk.Button( "_Delete" )
757 self.delete_button.connect( 'clicked', self.delete_profile, None )
758 self.delete_button.show()
759 self.delete_button.set_sensitive(False)
760 # Add Connect button
761 self.connect_button = gtk.Button( "Co_nnect" )
762 self.connect_button.connect( 'clicked', self.connect_profile, None )
763 # Add Disconnect button
764 self.disconnect_button = gtk.Button( "D_isconnect" )
765 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
766 # lets add our widgets
767 rows = gtk.VBox( False, 3 )
768 net_list = gtk.HBox( False, 0 )
769 listcols = gtk.HBox( False, 0 )
770 prows = gtk.VBox( False, 0 )
771 # lets start packing
772 # the network list
773 net_list.pack_start( self.plist, True, True, 0 )
774 net_list.pack_start( sb, False, False, 0 )
775 # the rows level
776 rows.pack_start( net_list , True, True, 0 )
777 rows.pack_start( self.current_network, False, True, 0 )
778 # the list columns
779 listcols.pack_start( rows, True, True, 0 )
780 listcols.pack_start( prows, False, False, 5 )
781 # the list buttons
782 prows.pack_start( self.new_button, False, False, 2 )
783 prows.pack_start( self.edit_button, False, False, 2 )
784 prows.pack_start( self.delete_button, False, False, 2 )
785 prows.pack_end( self.connect_button, False, False, 2 )
786 prows.pack_end( self.disconnect_button, False, False, 2 )
788 self.window.action_area.pack_start( self.about_button )
789 self.window.action_area.pack_start( self.preferences_button )
790 self.window.action_area.pack_start( self.close_button )
792 rows.show()
793 prows.show()
794 listcols.show()
795 self.window.vbox.add( listcols )
796 self.window.vbox.set_spacing( 3 )
797 self.window.show_all()
799 # Now, immediately hide these two. The proper one will be
800 # displayed later, based on interface state. -BEF-
801 self.disconnect_button.hide()
802 self.connect_button.hide()
803 self.connect_button.set_sensitive(False)
805 # set up connection manager for later use
806 self.connection = ConnectionManager( self.confFile, self.commandQueue )
807 # set up status window for later use
808 self.status_window = StatusWindow( self )
809 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
811 # Add our known profiles in order
812 for ap in self.confFile.auto_profile_order:
813 ap = ap.strip()
814 self.access_points[ ap ] = self.confFile.get_profile( ap )
815 wep = None
816 if self.access_points[ ap ]['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
817 self.pstore.append( [ self.access_points[ ap ]['essid'] + "\n" + self.access_points[ ap ]['bssid'], self.known_profile_icon, self.access_points[ ap ]['known'], self.access_points[ ap ]['available'], wep, self.signal_none_pb, self.access_points[ ap ]['mode'], self.access_points[ ap ]['protocol'], self.access_points[ ap ]['channel'] ] )
818 # This is the first run (or, at least, no config file was present), so pop up the preferences window
819 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
820 self.confFile.remove_option('DEFAULT', 'new_file')
821 self.edit_preferences(self.preferences_button)
823 # Begin running radar_window in Gtk event loop.
825 #Parameters:
827 # nothing
829 #Returns:
831 # nothing
832 def main( self ):
833 gtk.main()
835 # Write the config file to disk and quit application.
837 #Parameters:
839 # 'widget' -- gtk.Widget - The widget sending the event.
841 #Returns:
843 # nothing
844 def destroy( self, widget = None):
845 if self.status_window:
846 self.status_window.destroy()
847 gtk.main_quit()
849 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
851 #Parameters:
853 # 'widget' -- gtk.Widget - The widget sending the event.
855 # 'data' -- tuple - list of arbitrary arguments (not used)
857 #Returns:
859 # boolean -- always return False (i.e. do not propigate the signal which called)
860 def delete_event( self, widget, data = None ):
861 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
862 try:
863 self.commandQueue.put("exit", True)
864 except Queue.Full:
865 pass
866 # Save the preferred networks order
867 self.update_auto_profile_order()
868 self.destroy()
869 return False
871 # Updates the on-screen profiles list.
873 #Parameters:
875 # nothing
877 #Returns:
879 # boolean -- always return True
880 def update_plist_items( self ):
881 # Indicate to PyGtk that only one Gtk thread should run here
882 gtk.gdk.threads_enter()
883 # update the current ip and essid
884 # set the state of connect/disconnect buttons based on whether we have an IP address
885 if self.connection:
886 if self.connection.state:
887 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() ) )
888 self.connect_button.hide()
889 self.disconnect_button.show()
890 else:
891 self.current_network.set_text( "Not Connected." )
892 self.disconnect_button.hide()
893 self.connect_button.show()
895 while True:
896 # Get profiles scanned by iwlist
897 try:
898 profile = self.apQueue.get_nowait()
899 except Queue.Empty:
900 break
901 else:
902 prow_iter = self.get_row_by_ap( profile['essid'], profile['bssid'] )
903 wep = None
904 if prow_iter != None:
905 # the AP is in the list of APs on the screen
906 apname = make_section_name(profile['essid'], profile['bssid'])
907 if self.access_points.has_key(apname):
908 # This AP has been configured and is/should be stored in the config file
909 profile['known'] = self.access_points[apname]['known']
910 self.access_points[apname]['available'] = profile['available']
911 self.access_points[apname]['encrypted'] = profile['encrypted']
912 self.access_points[apname]['signal'] = profile['signal']
913 self.access_points[apname]['mode'] = profile['mode']
914 self.access_points[apname]['protocol'] = profile['protocol']
915 self.access_points[apname]['channel'] = profile['channel']
916 # Set the 'known' values; False is default, overridden to True by self.access_points
917 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known( profile[ 'known' ] ))
918 self.pstore.set_value(prow_iter, 2, profile[ 'known' ])
919 self.pstore.set_value(prow_iter, 3, profile['available'])
920 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
921 self.pstore.set_value(prow_iter, 4, wep)
922 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal( profile['signal'] ))
923 self.pstore.set_value(prow_iter, 6, profile['mode'])
924 self.pstore.set_value(prow_iter, 7, profile['protocol'])
925 self.pstore.set_value(prow_iter, 8, profile['channel'])
926 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
927 #for val in self.pstore[prow_iter]:
928 #print val,
929 else:
930 # the AP is not in the list of APs on the screen
931 self.pstore.append( [ profile[ 'essid' ] + "\n" + profile['bssid'], self.pixbuf_from_known( profile['known'] ), profile['known'], profile['available'], wep, self.pixbuf_from_signal( profile['signal'] ), profile['mode'], profile['protocol'], profile['channel'] ] )
932 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
933 # Allow other Gtk threads to run
934 gtk.gdk.threads_leave()
935 #print "update_plist_items: Empty apQueue"
936 return True
938 # Return the proper icon for a value of known.
940 #Parameters:
942 # 'known' -- boolean - Whether the AP is known (i.e. configured)
944 #Returns:
946 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
947 def pixbuf_from_known( self, known ):
948 """ return the proper icon for value of known """
949 if known:
950 return self.known_profile_icon
951 else:
952 return self.unknown_profile_icon
954 # Return an icon indicating the signal level.
956 #Parameters:
958 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
960 #Returns:
962 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
963 def pixbuf_from_signal( self, signal ):
964 signal = int( signal )
965 #print "signal level:", signal
966 if signal < 3:
967 return self.signal_none_pb
968 elif signal < 12:
969 return self.signal_low_pb
970 elif signal < 20:
971 return self.signal_barely_pb
972 elif signal < 35:
973 return self.signal_ok_pb
974 elif signal >= 35:
975 return self.signal_best_pb
976 else:
977 return None
979 # Return row which holds specified ESSID and BSSID.
981 #Parameters:
983 # 'essid' -- string - ESSID to match
985 # 'bssid' -- string - BSSID to match
987 #Returns:
989 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
990 def get_row_by_ap( self, essid, bssid ):
991 for row in self.pstore:
992 if ( row[0] == essid + "\n" + bssid ):
993 #print "matched:", row.iter, essid, bssid
994 return row.iter
995 return None
997 # Enable/disable buttons based on the selected network.
999 #Parameters:
1001 # 'widget' -- gtk.Widget - The widget sending the event.
1003 # 'data' -- tuple - list of arbitrary arguments (not used)
1005 #Returns:
1007 # nothing
1008 def on_network_selection( self, widget, data = None ):
1009 ( store, selected_iter ) = self.selected_network.get_selected()
1010 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
1011 # if no networks are selected, disable all buttons except New
1012 # (this occurs after a drag-and-drop)
1013 if selected_iter == None:
1014 self.edit_button.set_sensitive(False)
1015 self.delete_button.set_sensitive(False)
1016 self.connect_button.set_sensitive(False)
1017 return
1018 # enable/disable buttons
1019 self.connect_button.set_sensitive(True)
1020 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1021 self.edit_button.set_sensitive(True)
1022 self.delete_button.set_sensitive(True)
1023 else:
1024 self.edit_button.set_sensitive(True)
1025 self.delete_button.set_sensitive(False)
1027 # Init and run the about dialog
1029 #Parameters:
1031 # 'widget' -- gtk.Widget - The widget sending the event.
1033 # 'data' -- tuple - list of arbitrary arguments (not used)
1035 #Returns:
1037 # nothing
1038 def show_about_info( self, widget, data=None ):
1039 about = about_dialog()
1040 about.run()
1041 about.destroy()
1043 # Init and run the preferences dialog
1045 #Parameters:
1047 # 'widget' -- gtk.Widget - The widget sending the event.
1049 # 'data' -- tuple - list of arbitrary arguments (not used)
1051 #Returns:
1053 # nothing
1054 def edit_preferences( self, widget, data=None ):
1055 # get raw strings from config file
1056 self.confFile.raw = True
1057 prefs = preferences_dialog( self, self.confFile )
1058 response = prefs.run()
1059 prefs.destroy()
1060 if response == int(gtk.RESPONSE_ACCEPT):
1061 prefs.save()
1062 # get cooked strings from config file
1063 self.confFile.raw = False
1065 # Respond to a request to create a new AP profile
1067 #Parameters:
1069 # 'widget' -- gtk.Widget - The widget sending the event.
1071 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1073 # 'data' -- tuple - list of arbitrary arguments (not used)
1075 #Returns:
1077 # boolean -- True if a profile was created and False if profile creation was canceled.
1078 def create_new_profile( self, widget, profile, data=None ):
1079 profile_editor = profile_dialog( self, profile )
1080 profile = profile_editor.run()
1081 profile_editor.destroy()
1082 if profile:
1083 apname = make_section_name( profile['essid'], profile['bssid'] )
1084 # Check that the ap does not exist already
1085 if apname in self.confFile.profiles():
1086 dlg = gtk.MessageDialog( self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "A profile for %s already exists" % (apname) )
1087 dlg.run()
1088 dlg.destroy()
1089 del dlg
1090 # try again
1091 self.access_points[ apname ] = profile
1092 self.confFile.set_section( apname, profile )
1093 # if it is not in the auto_profile_order add it
1094 if apname not in self.confFile.auto_profile_order:
1095 self.confFile.auto_profile_order.append(apname)
1096 # add to the store
1097 wep = None
1098 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1099 self.confFile.write()
1100 return True
1101 else:
1102 # Did not create new profile
1103 return False
1105 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1106 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1108 #Parameters:
1110 # 'widget' -- gtk.Widget - The widget sending the event.
1112 # 'data' -- tuple - list of arbitrary arguments (not used)
1114 #Returns:
1116 # nothing
1117 def edit_profile( self, widget, data=None ):
1118 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1119 if not selected_iter: return
1120 row_start = str(self.pstore.get_value( selected_iter, 0 )).split("\n")
1121 apname = make_section_name( row_start[0], row_start[1] )
1122 profile = self.confFile.get_profile( apname )
1123 if profile:
1124 profile_editor = profile_dialog( self, profile )
1125 profile = profile_editor.run()
1126 profile_editor.destroy()
1127 if profile:
1128 if __debug__:
1129 print "Got edited profile ", profile
1130 apname = make_section_name( profile['essid'], profile['bssid'] )
1131 self.access_points[ apname ] = profile
1132 self.confFile.set_section( apname, profile )
1133 self.confFile.write()
1134 else:
1135 profile = get_new_profile()
1136 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1137 self.create_new_profile( widget, profile, data )
1139 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1141 #Parameters:
1143 # 'widget' -- gtk.Widget - The widget sending the event.
1145 # 'data' -- tuple - list of arbitrary arguments (not used)
1147 #Returns:
1149 # nothing
1150 def delete_profile( self, widget, data=None ):
1151 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1152 if not selected_iter: return
1153 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1154 known = store.get_value( selected_iter, 1 )
1155 if not known: return
1156 dlg = gtk.MessageDialog(
1157 self.window,
1158 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1159 gtk.MESSAGE_QUESTION,
1160 gtk.BUTTONS_YES_NO,
1161 "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid) )
1162 res = dlg.run()
1163 dlg.destroy()
1164 del dlg
1165 if res == gtk.RESPONSE_NO: return
1166 # Remove it
1167 apname = make_section_name( essid, bssid )
1168 del self.access_points[ apname ]
1169 self.confFile.remove_section( apname )
1170 print "delete_profile: ", apname, ":", self.confFile.auto_profile_order
1171 if apname in self.confFile.auto_profile_order: self.confFile.auto_profile_order.remove(apname)
1172 self.pstore.remove( selected_iter )
1173 # Let's save our current state
1174 self.update_auto_profile_order()
1176 # Respond to a request to connect to an AP.
1178 #Parameters:
1180 # 'widget' -- gtk.Widget - The widget sending the event.
1182 # 'profile' -- dictionary - The AP profile to which to connect.
1184 # 'data' -- tuple - list of arbitrary arguments (not used)
1186 #Returns:
1188 # nothing
1189 def connect_profile( self, widget, profile, data=None ):
1190 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1191 if not selected_iter: return
1192 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1193 known = store.get_value( selected_iter, 2 )
1194 if not known:
1195 if data != 'noconnect':
1196 dlg = gtk.MessageDialog(
1197 self.window,
1198 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1199 gtk.MESSAGE_QUESTION,
1200 gtk.BUTTONS_YES_NO,
1201 "This network does not have a profile configured.\n\nWould you like to create one now?" )
1202 res = dlg.run()
1203 dlg.destroy()
1204 del dlg
1205 if res == gtk.RESPONSE_NO: return
1206 profile = get_new_profile()
1207 profile['essid'] = essid
1208 profile['bssid'] = bssid
1209 if not self.create_new_profile( widget, profile, data ):
1210 return
1211 apname = make_section_name( essid, bssid )
1212 self.connection.connect_to_network(self.access_points[apname], self.status_window)
1214 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1216 #Parameters:
1218 # 'widget' -- gtk.Widget - The widget sending the event.
1220 # 'data' -- tuple - list of arbitrary arguments (not used)
1222 #Returns:
1224 # nothing
1225 def disconnect_profile( self, widget, data=None ):
1226 if data == "cancel":
1227 self.status_window.update_message("Canceling connection...")
1228 if sys.modules.has_key("gtk"):
1229 while gtk.events_pending():
1230 gtk.main_iteration(False)
1231 sleep(1)
1232 self.connection.disconnect_interface()
1234 # Update the config file auto profile order from the on-screen order
1236 #Parameters:
1238 # 'widget' -- gtk.Widget - The widget sending the event.
1240 # 'data' -- tuple - list of arbitrary arguments (not used)
1242 # 'data2' -- tuple - list of arbitrary arguments (not used)
1244 #Returns:
1246 # nothing
1247 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1248 # recreate the auto_profile_order
1249 auto_profile_order = []
1250 piter = self.pstore.get_iter_first()
1251 while piter:
1252 # only if it's known
1253 if self.pstore.get_value( piter, 2 ) == True:
1254 row_start = str(self.pstore.get_value( piter, 0 )).split("\n")
1255 auto_profile_order.append( make_section_name( row_start[0], row_start[1] ) )
1256 piter = self.pstore.iter_next( piter )
1257 self.confFile.auto_profile_order = auto_profile_order
1260 # Button to allow user to choose a file and put value into specified gtk.Entry
1261 class file_browse_button(gtk.Button):
1262 # Create a button to simulate a File/Open
1264 #Parameters:
1266 # 'parent' -- gtk.Object -- Usually, the calling window.
1268 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1270 #Returns:
1272 # file_browse_button instance
1273 def __init__( self, parent, entry ):
1274 self.parent_window = parent
1275 self.entry = entry
1276 gtk.Button.__init__(self, "Browse", None)
1277 #self.
1278 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)
1279 self.connect("clicked", self.browse_files)
1281 # Show filechooser dialog and get user selection
1283 #Parameters:
1285 # 'widget' -- gtk.Widget -- The widget sending the event.
1287 #Returns:
1289 # nothing
1291 #NOTES:
1293 # updates entry value
1295 def browse_files( self, widget ):
1296 self.browser_dialog.set_filename(self.entry.get_text())
1297 self.browser_dialog.run()
1298 self.entry.set_text(self.browser_dialog.get_filename())
1299 self.browser_dialog.destroy()
1302 # The preferences dialog. Edits non-profile sections of the config file.
1303 class preferences_dialog:
1304 # Create a new preferences_dialog.
1306 #Parameters:
1308 # 'parent' -- gtk.Object - Usually, the calling window.
1310 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1312 #Returns:
1314 # preferences_dialog instance
1315 def __init__( self, parent, confFile ):
1316 global wifi_radar_icon
1317 self.parent = parent
1318 self.confFile = confFile
1319 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1320 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1321 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1322 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1323 self.dialog.set_icon( icon )
1324 self.dialog.set_resizable( True )
1325 self.dialog.set_transient_for( self.parent.window )
1326 self.tooltips = gtk.Tooltips()
1328 # set up preferences widgets
1330 # build everything in a tabbed notebook
1331 self.prefs_notebook = gtk.Notebook()
1333 ### General tab
1334 self.general_page = gtk.VBox()
1335 # auto detect wireless device
1336 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1338 print "prefs: ", self.confFile.get_opt('DEFAULT.interface')
1340 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1341 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1342 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1343 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1345 # network interface selecter
1346 self.w_interface = gtk.combo_box_entry_new_text()
1347 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE).stdout
1348 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1349 for device in wireless_devices:
1350 if device != self.confFile.get_opt('DEFAULT.interface'):
1351 self.w_interface.append_text(device)
1352 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1353 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1354 self.w_interface.set_active(0)
1355 self.w_interface_label = gtk.Label("Wireless device")
1356 self.w_hbox1 = gtk.HBox(False, 0)
1357 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1358 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1359 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1360 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1362 # scan timeout (spin button of integers from 1 to 100)
1363 self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
1364 self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1365 self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1366 self.w_scan_timeout.set_numeric(True)
1367 self.w_scan_timeout.set_snap_to_ticks(True)
1368 self.w_scan_timeout.set_wrap(False)
1369 self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1370 self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1371 self.w_hbox2 = gtk.HBox(False, 0)
1372 self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1373 self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1374 self.general_page.pack_start(self.w_hbox2, False, False, 5)
1376 # speak up
1377 self.w_speak_up = gtk.CheckButton("Use speak-up")
1378 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1379 self.w_speak_up.connect("toggled", self.toggle_speak)
1380 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1381 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1383 # speak up command
1384 self.w_speak_cmd = gtk.Entry()
1385 self.w_speak_cmd.set_width_chars(16)
1386 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1387 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1388 self.w_speak_cmd_label = gtk.Label("Speak Command")
1389 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1390 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1391 self.w_hbox3 = gtk.HBox(False, 0)
1392 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1393 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1394 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1395 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1396 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1398 # commit required
1399 self.w_commit_required = gtk.CheckButton("Commit required")
1400 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1401 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1402 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1404 # ifup required
1405 self.w_ifup_required = gtk.CheckButton("Ifup required")
1406 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1407 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1408 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1410 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1411 ### End of General tab
1413 ### Advanced tab
1414 # table to use for layout of following command configurations
1415 self.cmds_table = gtk.Table()
1417 # ifconfig command
1418 self.ifconfig_cmd = gtk.Entry()
1419 self.ifconfig_cmd.set_width_chars(32)
1420 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1421 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1422 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1423 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1424 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1425 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, True, False, 0, 0)
1426 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1428 # iwconfig command
1429 self.iwconfig_cmd = gtk.Entry()
1430 self.iwconfig_cmd.set_width_chars(32)
1431 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1432 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1433 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1434 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1435 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, True, False, 5, 0)
1436 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, True, False, 0, 0)
1437 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, False, False, 0, 0)
1439 # iwlist command
1440 self.iwlist_cmd = gtk.Entry()
1441 self.iwlist_cmd.set_width_chars(32)
1442 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1443 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1444 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1445 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1446 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, True, False, 5, 0)
1447 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, True, False, 0, 0)
1448 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, False, False, 0, 0)
1450 # route command
1451 self.route_cmd = gtk.Entry()
1452 self.route_cmd.set_width_chars(32)
1453 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1454 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1455 self.route_cmd_label = gtk.Label("Network route configure command")
1456 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1457 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, True, False, 5, 0)
1458 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, True, False, 0, 0)
1459 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, False, False, 0, 0)
1461 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1462 ### End of Advanced tab
1464 ### DHCP tab
1465 # table to use for layout of DHCP prefs
1466 self.dhcp_table = gtk.Table()
1468 self.dhcp_cmd = gtk.Entry()
1469 self.dhcp_cmd.set_width_chars(32)
1470 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1471 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1472 self.dhcp_cmd_label = gtk.Label("Command")
1473 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1474 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1475 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1476 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1478 self.dhcp_args = gtk.Entry()
1479 self.dhcp_args.set_width_chars(32)
1480 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1481 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1482 self.dhcp_args_label = gtk.Label("Arguments")
1483 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1484 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1486 self.dhcp_kill_args = gtk.Entry()
1487 self.dhcp_kill_args.set_width_chars(32)
1488 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1489 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1490 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1491 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1492 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1494 self.dhcp_timeout = gtk.Entry()
1495 self.dhcp_timeout.set_width_chars(32)
1496 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1497 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1498 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1499 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1500 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1502 self.dhcp_pidfile = gtk.Entry()
1503 self.dhcp_pidfile.set_width_chars(32)
1504 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1505 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1506 self.dhcp_pidfile_label = gtk.Label("PID file")
1507 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1508 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1510 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1511 ### End of DHCP tab
1513 ### WPA tab
1514 # table to use for layout of DHCP prefs
1515 self.wpa_table = gtk.Table()
1517 self.wpa_cmd = gtk.Entry()
1518 self.wpa_cmd.set_width_chars(32)
1519 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1520 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1521 self.wpa_cmd_label = gtk.Label("Command")
1522 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1523 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1524 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1525 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1527 self.wpa_args = gtk.Entry()
1528 self.wpa_args.set_width_chars(32)
1529 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1530 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1531 self.wpa_args_label = gtk.Label("Arguments")
1532 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1533 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1535 self.wpa_kill_args = gtk.Entry()
1536 self.wpa_kill_args.set_width_chars(32)
1537 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1538 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1539 self.wpa_kill_args_label = gtk.Label("Kill command")
1540 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1541 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1543 self.wpa_config = gtk.Entry()
1544 self.wpa_config.set_width_chars(32)
1545 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1546 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1547 self.wpa_config_label = gtk.Label("Configuration file")
1548 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1549 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1551 self.wpa_driver = gtk.Entry()
1552 self.wpa_driver.set_width_chars(32)
1553 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1554 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1555 self.wpa_driver_label = gtk.Label("Driver")
1556 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1557 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1559 self.wpa_pidfile = gtk.Entry()
1560 self.wpa_pidfile.set_width_chars(32)
1561 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1562 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1563 self.wpa_pidfile_label = gtk.Label("PID file")
1564 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1565 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1567 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1568 ### End of WPA tab
1570 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1572 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1574 #Parameters:
1576 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1578 # 'data' -- tuple - list of arbitrary arguments (not used)
1580 #Returns:
1582 # nothing
1583 def toggle_auto_detect(self, auto_detect_toggle, data=None):
1584 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
1586 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
1588 #Parameters:
1590 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1592 # 'data' -- tuple - list of arbitrary arguments (not used)
1594 #Returns:
1596 # nothing
1597 def toggle_speak(self, speak_toggle, data=None):
1598 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
1599 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
1601 # Display preferences dialog and operate until canceled or okayed.
1603 #Parameters:
1605 # nothing
1607 #Returns:
1609 # integer -- gtk response ID
1610 def run(self):
1611 self.dialog.show_all()
1612 return self.dialog.run()
1614 # Write updated values to config file.
1616 #Parameters:
1618 # nothing
1620 #Returns:
1622 # nothing
1623 def save(self):
1624 print "saving prefs"
1625 if self.w_auto_detect.get_active():
1626 set_network_device("auto_detect")
1627 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
1628 else:
1629 # get the selected interface, using a work-around suggested by PyGTK Tutorial
1630 interface = self.w_interface.get_model()[self.w_interface.get_active()][0]
1631 self.confFile.set_opt('DEFAULT.interface', interface)
1632 set_network_device(interface)
1633 self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
1634 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
1635 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
1636 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
1637 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
1638 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
1639 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
1640 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
1641 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
1642 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
1643 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
1644 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
1645 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
1646 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
1647 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
1648 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
1649 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
1650 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
1651 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
1652 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
1653 self.confFile.write()
1655 # Remove preferences window.
1657 #Parameters:
1659 # nothing
1661 #Returns:
1663 # nothing
1664 def destroy(self):
1665 self.dialog.destroy()
1666 del self.dialog
1669 # Edit and return an AP profile.
1670 class profile_dialog:
1671 # Create a new profile_dialog.
1673 #Parameters:
1675 # 'parent' -- gtk.Object - Usually, the calling window.
1677 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
1679 #Returns:
1681 # profile_dialog instance
1682 def __init__( self, parent, profile ):
1683 global wifi_radar_icon
1684 self.parent = parent
1685 self.profile = profile
1686 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1687 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
1688 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1689 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
1690 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1691 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
1692 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1693 self.dialog.set_icon( icon )
1694 self.dialog.set_resizable( False )
1695 self.dialog.set_transient_for( self.parent.window )
1696 #self.dialog.set_size_request( 400, 400 )
1697 #################
1698 essid_table = gtk.Table( 1, 2, False )
1699 essid_table.set_row_spacings( 3 )
1700 essid_table.set_col_spacings( 3 )
1702 # The essid labels
1703 essid_table.attach( gtk.Label( 'Network Name' ), 0, 1, 0, 1 )
1704 # The essid textboxes
1705 self.essid_entry = gtk.Entry( 32 )
1706 self.essid_entry.set_text( self.profile['essid'] )
1707 essid_table.attach( self.essid_entry, 1, 2, 0, 1 )
1708 # Add the essid table to the dialog
1709 self.dialog.vbox.pack_start( essid_table, True, True, 5 )
1711 bssid_table = gtk.Table( 1, 2, False )
1712 bssid_table.set_row_spacings( 3 )
1713 bssid_table.set_col_spacings( 3 )
1714 # The bssid labels
1715 bssid_table.attach( gtk.Label( 'Network bssid' ), 0, 1, 0, 1 )
1716 # The bssid textboxes
1717 self.bssid_entry = gtk.Entry( 32 )
1718 self.bssid_entry.set_text( self.profile['bssid'] )
1719 bssid_table.attach( self.bssid_entry, 1, 2, 0, 1 )
1720 #self.key = gtk.Entry( 32 )
1721 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1722 # Add the bssid table to the dialog
1723 self.dialog.vbox.pack_start( bssid_table, True, True, 5 )
1725 # create the wifi expander
1726 self.wifi_expander = gtk.Expander( WIFI_SET_LABEL )
1727 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1728 wifi_table = gtk.Table( 4, 2, False )
1729 wifi_table.set_row_spacings( 3 )
1730 wifi_table.set_col_spacings( 3 )
1731 # The Wifi labels
1732 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
1733 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
1734 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
1735 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
1736 # The Wifi text boxes
1737 self.mode_combo = gtk.combo_box_new_text()
1738 for mode in self.WIFI_MODES:
1739 self.mode_combo.append_text( mode )
1740 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
1741 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
1742 self.channel_combo = gtk.combo_box_new_text()
1743 for channel in self.WIFI_CHANNELS:
1744 self.channel_combo.append_text( channel )
1745 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
1746 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
1748 self.key_entry = gtk.Entry( 64 )
1749 self.key_entry.set_text( self.profile['key'] )
1750 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
1752 self.security_combo = gtk.combo_box_new_text()
1753 for security in self.WIFI_SECURITY:
1754 self.security_combo.append_text( security )
1755 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
1756 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
1757 # Add the wifi table to the expander
1758 self.wifi_expander.add( wifi_table )
1759 # Add the expander to the dialog
1760 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
1762 # create the wpa expander
1763 self.wpa_expander = gtk.Expander( NO_WPA_LABEL )
1764 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
1765 wpa_table = gtk.Table( 1, 2, False )
1766 wpa_table.set_row_spacings( 3 )
1767 wpa_table.set_col_spacings( 3 )
1768 # The labels
1769 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1770 # The text boxes
1771 self.wpa_driver_entry = gtk.Entry()
1772 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
1773 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
1774 # Add the wpa table to the expander
1775 self.wpa_expander.add( wpa_table )
1776 # Add the expander to the dialog
1777 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
1779 # create the dhcp expander
1780 self.dhcp_expander = gtk.Expander( USE_DHCP_LABEL )
1781 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1782 ip_table = gtk.Table( 6, 2, False )
1783 ip_table.set_row_spacings( 3 )
1784 ip_table.set_col_spacings( 3 )
1785 # The IP labels
1786 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
1787 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
1788 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
1789 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
1790 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
1791 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
1792 # The IP text boxes
1793 self.ip_entry = gtk.Entry( 15 )
1794 self.ip_entry.set_text( self.profile['ip'] )
1795 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
1796 self.netmask_entry = gtk.Entry( 15 )
1797 self.netmask_entry.set_text( self.profile['netmask'] )
1798 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
1799 self.gw_entry = gtk.Entry( 15 )
1800 self.gw_entry.set_text( self.profile['gateway'] )
1801 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
1802 self.domain_entry = gtk.Entry( 32 )
1803 self.domain_entry.set_text( self.profile['domain'] )
1804 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
1805 self.dns1_entry = gtk.Entry( 15 )
1806 self.dns1_entry.set_text( self.profile['dns1'] )
1807 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
1808 self.dns2_entry = gtk.Entry( 15 )
1809 self.dns2_entry.set_text( self.profile['dns2'] )
1810 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
1811 # Add the ip table to the expander
1812 self.dhcp_expander.add( ip_table )
1813 # Add the expander to the dialog
1814 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
1816 # create the connection-building postpre expander
1817 self.con_pp_expander = gtk.Expander( CON_PP_LABEL )
1818 con_pp_table = gtk.Table( 2, 2, False )
1819 con_pp_table.set_row_spacings( 3 )
1820 con_pp_table.set_col_spacings( 3 )
1821 # The labels
1822 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1823 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1824 # The text boxes
1825 self.con_prescript_entry = gtk.Entry()
1826 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
1827 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
1828 self.con_postscript_entry = gtk.Entry()
1829 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
1830 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
1831 # Add the pp table to the expander
1832 self.con_pp_expander.add( con_pp_table )
1833 # Add the expander to the dialog
1834 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
1836 # create the disconnection postpre expander
1837 self.dis_pp_expander = gtk.Expander( DIS_PP_LABEL )
1838 dis_pp_table = gtk.Table( 2, 2, False )
1839 dis_pp_table.set_row_spacings( 3 )
1840 dis_pp_table.set_col_spacings( 3 )
1841 # The labels
1842 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1843 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1844 # The text boxes
1845 self.dis_prescript_entry = gtk.Entry()
1846 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
1847 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
1848 self.dis_postscript_entry = gtk.Entry()
1849 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
1850 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
1851 # Add the pp table to the expander
1852 self.dis_pp_expander.add( dis_pp_table )
1853 # Add the expander to the dialog
1854 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
1856 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
1858 #Parameters:
1860 # nothing
1862 #Returns:
1864 # dictionary or None -- a profile, or None on cancel
1865 def run( self ):
1866 self.dialog.show_all()
1867 if self.dialog.run():
1868 self.profile['known'] = True
1869 self.profile['essid'] = self.essid_entry.get_text().strip()
1870 self.profile['bssid'] = self.bssid_entry.get_text().strip()
1871 self.profile['key'] = self.key_entry.get_text().strip()
1872 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
1873 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
1874 self.profile['encrypted'] = ( self.profile['security'] != '' )
1875 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
1876 self.profile['protocol'] = 'g'
1877 self.profile['available'] = ( self.profile['signal'] > 0 )
1878 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
1879 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
1880 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
1881 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
1882 # wpa
1883 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
1884 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
1885 # dhcp
1886 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
1887 self.profile['ip'] = self.ip_entry.get_text().strip()
1888 self.profile['netmask'] = self.netmask_entry.get_text().strip()
1889 self.profile['gateway'] = self.gw_entry.get_text().strip()
1890 self.profile['domain'] = self.domain_entry.get_text().strip()
1891 self.profile['dns1'] = self.dns1_entry.get_text().strip()
1892 self.profile['dns2'] = self.dns2_entry.get_text().strip()
1893 return self.profile
1894 return None
1896 # Remove profile dialog.
1898 #Parameters:
1900 # nothing
1902 #Returns:
1904 # nothing
1905 def destroy( self ):
1906 self.dialog.destroy()
1907 del self.dialog
1909 # Respond to expanding/hiding IP segment.
1911 #Parameters:
1913 # 'widget' -- gtk.Widget - The widget sending the event.
1915 # 'data' -- tuple - List of arbitrary arguments (not used)
1917 #Returns:
1919 # nothing
1920 def toggle_use_dhcp( self, widget, data = None ):
1921 expanded = self.dhcp_expander.get_expanded()
1922 if expanded:
1923 self.dhcp_expander.set_label( USE_IP_LABEL )
1924 else:
1925 self.dhcp_expander.set_label( USE_DHCP_LABEL )
1927 # Respond to expanding/hiding WPA segment.
1929 #Parameters:
1931 # 'widget' -- gtk.Widget - The widget sending the event.
1933 # 'data' -- tuple - List of arbitrary arguments (not used)
1935 #Returns:
1937 # nothing
1938 def toggle_use_wpa( self, widget, data = None ):
1939 expanded = self.wpa_expander.get_expanded()
1940 if expanded:
1941 self.wpa_expander.set_label( USE_WPA_LABEL )
1942 else:
1943 self.wpa_expander.set_label( NO_WPA_LABEL )
1945 # Return the index where item matches a cell in array.
1947 #Parameters:
1949 # 'item' -- string - Item to find in array
1951 # 'array' -- list - List in which to find match.
1953 #Returns:
1955 # integer - 0 (no match) or higher (index of match)
1956 def get_array_index( self, item, array ):
1957 try:
1958 return array.index( item.strip() )
1959 except:
1960 pass
1961 return 0
1963 # Return the value in array[ index ]
1965 #Parameters:
1967 # 'index' -- integer - The index to look up.
1969 # 'array' -- list - List in which to look up value.
1971 #Returns:
1973 # string -- empty string (no match) or looked up value
1974 def get_array_item( self, index, array ):
1975 try:
1976 return array[ index ]
1977 except:
1978 pass
1979 return ''
1981 # A simple class for putting up a "Please wait" dialog so the user
1982 # doesn't think we've forgotten about them. Implements the status interface.
1983 class StatusWindow:
1984 # Create a new StatusWindow.
1986 #Parameters:
1988 # 'parent' -- gtk.Object - Usually, the calling window.
1990 #Returns:
1992 # StatusWindow instance
1994 #NOTE:
1996 # Sample implementation of status interface. Status interface
1997 #requires .show(), .update_message(message), and .hide() methods.
1998 def __init__( self, parent ):
1999 global wifi_radar_icon
2000 self.parent = parent
2001 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2002 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2003 self.dialog.set_icon( icon )
2004 self.lbl = gtk.Label("Please wait...")
2005 self.bar = gtk.ProgressBar()
2006 self.dialog.vbox.pack_start(self.lbl)
2007 self.dialog.vbox.pack_start(self.bar)
2008 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2009 self.timer = None
2011 # Change the message displayed to the user.
2013 #Parameters:
2015 # 'message' -- string - The message to show to the user.
2017 #Returns:
2019 # nothing
2020 def update_message( self, message ):
2021 self.lbl.set_text(message)
2023 # Update the StatusWindow progress bar.
2025 #Parameters:
2027 # nothing
2029 #Returns:
2031 # True -- always return True
2032 def update_window( self ):
2033 self.bar.pulse()
2034 return True
2036 # Display and operate the StatusWindow.
2038 #Parameters:
2040 # nothing
2042 #Returns:
2044 # nothing
2045 def run( self ):
2046 pass
2048 # Show all the widgets of the StatusWindow.
2050 #Parameters:
2052 # nothing
2054 #Returns:
2056 # nothing
2057 def show( self ):
2058 self.dialog.show_all()
2059 self.timer = gobject.timeout_add(250, self.update_window)
2060 return False
2062 # Hide all the widgets of the StatusWindow.
2064 #Parameters:
2066 # nothing
2068 #Returns:
2070 # nothing
2071 def hide( self ):
2072 if self.timer:
2073 gobject.source_remove(self.timer)
2074 self.timer = None
2075 self.dialog.hide_all()
2076 return False
2078 # Remove the StatusWindow.
2080 #Parameters:
2082 # nothing
2084 #Returns:
2086 # nothing
2087 def destroy( self ):
2088 if self.timer:
2089 gobject.source_remove(self.timer)
2090 self.dialog.destroy()
2091 del self.dialog
2094 # Manage a GTK About Dialog
2095 class about_dialog(gtk.AboutDialog):
2096 # Subclass GTK AboutDialog
2098 #Parameters:
2100 # nothing
2102 #Returns:
2104 # nothing
2105 def __init__( self ):
2106 global wifi_radar_icon
2108 gtk.AboutDialog.__init__(self)
2109 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", u"Ante Karamati\xc4\x87", "Richard Monk", "Brouard Nicolas", "Kevin Otte", "Nathanael Rebsch", "Patrick Winnertz"])
2110 self.set_comments("WiFi connection manager")
2111 self.set_copyright("Copyright 2004-2009 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2112 self.set_documenters(["Gary Case"])
2113 license = """
2114 This program is free software; you can redistribute it and/or modify
2115 it under the terms of the GNU General Public License as published by
2116 the Free Software Foundation; either version 2 of the License, or
2117 (at your option) any later version.
2119 This program is distributed in the hope that it will be useful,
2120 but WITHOUT ANY WARRANTY; without even the implied warranty of
2121 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2122 GNU General Public License for more details.
2124 You should have received a copy of the GNU General Public License
2125 along with this program; if not, write to the Free Software
2126 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2127 self.set_license(license)
2128 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2129 self.set_logo(logo)
2130 self.set_name("WiFi Radar")
2131 self.set_version(WIFI_RADAR_VERSION)
2132 self.set_website("http://wifi-radar.berlios.de")
2136 # Manage the configuration for the application, including reading and writing the config from/to a file.
2137 class ConfigFile(ConfigParser.SafeConfigParser):
2138 # Create a new ConfigFile.
2140 #Parameters:
2142 # 'filename' -- string - The configuration file's name.
2144 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2146 #Returns:
2148 # ConfigFile instance
2149 def __init__( self, filename, defaults, raw=False ):
2150 self.filename = filename
2151 self.raw = raw
2152 self.auto_profile_order = []
2153 ConfigParser.SafeConfigParser.__init__(self, defaults)
2155 # Set the contents of a section to values from a dictionary.
2157 #Parameters:
2159 # 'section_name' -- string - Configuration file section.
2161 # 'section_dict' -- dictionary - Values to add to section.
2163 #Returns:
2165 # nothing
2166 def set_section( self, section_name, section_dict ):
2167 try:
2168 self.add_section(section_name)
2169 except ConfigParser.DuplicateSectionError:
2170 pass
2171 for key in section_dict.keys():
2172 if type(section_dict[key]) == BooleanType:
2173 self.set_bool_opt(section_name + "." + key, section_dict[key])
2174 elif type(section_dict[key]) == IntType:
2175 self.set_int_opt(section_name + "." + key, section_dict[key])
2176 elif type(section_dict[key]) == FloatType:
2177 self.set_float_opt(section_name + "." + key, section_dict[key])
2178 else:
2179 self.set_opt(section_name + "." + key, section_dict[key])
2181 # Return the profile recorded in the specified section.
2183 #Parameters:
2185 # 'section_name' -- string - Configuration file section.
2187 #Returns:
2189 # dictionary or None - The specified profile or None if not found
2190 def get_profile( self, section_name ):
2191 if section_name in self.profiles():
2192 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' ]
2193 bool_types = [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
2194 int_types = [ 'signal' ]
2195 profile = {}
2196 for option in bool_types:
2197 profile[option] = self.get_opt_as_bool( section_name + "." + option )
2198 for option in int_types:
2199 profile[option] = self.get_opt_as_int( section_name + "." + option )
2200 for option in str_types:
2201 profile[option] = self.get_opt( section_name + "." + option )
2202 return profile
2203 return None
2205 # Get a config option and handle exceptions.
2207 #Parameters:
2209 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2210 # period and the option key. (E.g. "DEFAULT.interface")
2212 #Returns:
2214 # string or None - option value as string or None on failure
2215 def get_opt( self, option_path ):
2216 #print "ConfigFile.get_opt: ", option_path
2217 (section, option) = option_path.split('.')
2218 try:
2219 return self.get(section, option, self.raw)
2220 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2221 return None
2223 # Get a config option and return as a boolean type.
2225 #Parameters:
2227 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2228 # period and the option key. (E.g. "DEFAULT.interface")
2230 #Returns:
2232 # boolean - option value as boolean
2233 def get_opt_as_bool( self, option_path ):
2234 option = self.get_opt(option_path)
2235 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2236 return option
2237 if option == 'True':
2238 return True
2239 if option == 'False':
2240 return False
2241 raise ValueError, 'boolean option was not True or False'
2243 # Get a config option and return as an integer type.
2245 #Parameters:
2247 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2248 # period and the option key. (E.g. "DEFAULT.interface")
2250 #Returns:
2252 # integer- option value as integer
2253 def get_opt_as_int( self, option_path ):
2254 return int(float(self.get_opt(option_path)))
2256 # Convert boolean type to string and set config option.
2258 #Parameters:
2260 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2261 # period and the option key. (E.g. "DEFAULT.interface")
2263 # 'value' -- boolean - Value to set.
2265 #Returns:
2267 # nothing
2268 def set_bool_opt( self, option_path, value ):
2269 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2270 value == 'True'
2271 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2272 value == 'False'
2273 else:
2274 raise ValueError, 'cannot convert value to string'
2275 self.set_opt(option_path, repr(value))
2277 # Convert integer type to string and set config option.
2279 #Parameters:
2281 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2282 # period and the option key. (E.g. "DEFAULT.interface")
2284 # 'value' -- integer - Value to set.
2286 #Returns:
2288 # nothing
2289 def set_int_opt( self, option_path, value ):
2290 if not isinstance(value, IntType):
2291 raise ValueError, 'value is not an integer'
2292 self.set_opt(option_path, repr(value))
2294 # Convert float type to string and set config option.
2296 #Parameters:
2298 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2299 # period and the option key. (E.g. "DEFAULT.interface")
2301 # 'value' -- float - Value to set.
2303 #Returns:
2305 # nothing
2306 def set_float_opt( self, option_path, value ):
2307 if not isinstance(value, FloatType):
2308 raise ValueError, 'value is not a float'
2309 self.set_opt(option_path, repr(int(value)))
2311 # Set a config option while handling exceptions.
2313 #Parameters:
2315 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2316 # period and the option key. (E.g. "DEFAULT.interface")
2318 # 'value' -- string - Value to set.
2320 #Returns:
2322 # nothing
2323 def set_opt( self, option_path, value ):
2324 (section, option) = option_path.split('.')
2325 try:
2326 self.set(section, option, value)
2327 except ConfigParser.NoSectionError:
2328 self.add_section(section)
2329 self.set_opt(option_path, value)
2331 # Return a list of the section names which denote AP profiles.
2333 #Parameters:
2335 # nothing
2337 #Returns:
2339 # list - profile names
2340 def profiles( self ):
2341 profile_list = []
2342 for section in self.sections():
2343 if ':' in section:
2344 profile_list.append(section)
2345 return profile_list
2347 # Read configuration file from disk into instance variables.
2349 #Parameters:
2351 # nothing
2353 #Returns:
2355 # nothing
2356 def read( self ):
2357 fp = open( self.filename, "r" )
2358 self.readfp(fp)
2359 # convert the auto_profile_order to a list for ordering
2360 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2361 for ap in self.profiles():
2362 self.set_bool_opt( ap + '.known', True)
2363 if ap in self.auto_profile_order: continue
2364 self.auto_profile_order.append( ap )
2365 fp.close()
2367 # Write configuration file to disk from instance variables. Copied from
2368 # ConfigParser and modified to write options in alphabetical order.
2370 #Parameters:
2372 # nothing
2374 #Returns:
2376 # nothing
2377 def write( self ):
2378 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2379 fp = open( self.filename, "w" )
2380 # write DEFAULT section first
2381 if self._defaults:
2382 fp.write("[DEFAULT]\n")
2383 for key in sorted(self._defaults.keys()):
2384 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2385 fp.write("\n")
2386 # write non-profile sections first
2387 for section in self._sections:
2388 if section not in self.profiles():
2389 fp.write("[%s]\n" % section)
2390 for key in sorted(self._sections[section].keys()):
2391 if key != "__name__":
2392 fp.write("%s = %s\n" %
2393 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2394 fp.write("\n")
2395 # write profile sections
2396 for section in self._sections:
2397 if section in self.profiles():
2398 fp.write("[%s]\n" % section)
2399 for key in sorted(self._sections[section].keys()):
2400 if key != "__name__":
2401 fp.write("%s = %s\n" %
2402 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2403 fp.write("\n")
2404 fp.close()
2408 # Load our conf file and known profiles
2409 # Defaults, these may get overridden by values found in the conf file.
2410 config_defaults = { # The network interface you use.
2411 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2412 'interface': "auto_detect",
2413 # How long should the scan for access points last?
2414 'scan_timeout': '5',
2415 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2416 # Set the speak_up option to false if you do not have or want this.
2417 'speak_command': '/usr/bin/say',
2418 # Should I speak up when connecting to a network? (If you have a speech command)
2419 'speak_up': 'False',
2420 # You may set this to true for cards that require a "commit" command with iwconfig
2421 'commit_required': 'False',
2422 # You may set this to true for cards that require the interface to be brought up first
2423 'ifup_required': 'False',
2424 # Set the location of several important programs
2425 'iwlist_command': '/sbin/iwlist',
2426 'iwconfig_command': '/sbin/iwconfig',
2427 'ifconfig_command': '/sbin/ifconfig',
2428 'route_command': '/sbin/route',
2429 'auto_profile_order': '[]',
2430 'version': WIFI_RADAR_VERSION }
2432 config_dhcp = { # DHCP client
2433 'command': 'dhcpcd',
2434 # How long to wait for an IP addr from DHCP server
2435 'timeout': '30',
2436 # Arguments to use with DHCP client on connect
2437 'args': '-D -o -i dhcp_client -t %(timeout)s',
2438 # Argument to use with DHCP client on disconnect
2439 'kill_args': '-k',
2440 # The file where DHCP client PID is written
2441 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2443 config_wpa = { # WPA Supplicant
2444 'command': '/usr/sbin/wpa_supplicant',
2445 # Arguments to use with WPA Supplicant on connect
2446 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2447 # Arguments to use with WPA Supplicant on disconnect
2448 'kill_command': '',
2449 # Where the WPA Supplicant config file can be found
2450 'configuration': '/etc/wpa_supplicant.conf',
2451 # Driver to use with WPA Supplicant
2452 'driver': 'wext',
2453 # The file where WPA Supplicant PID is written
2454 'pidfile': '/var/run/wpa_supplicant.pid' }
2456 # initialize config, with defaults
2457 confFile = ConfigFile(CONF_FILE, config_defaults)
2458 confFile.set_section("DHCP", config_dhcp)
2459 confFile.set_section("WPA", config_wpa)
2461 if not os.path.isfile( CONF_FILE ):
2462 confFile.set_bool_opt('DEFAULT.new_file', True)
2463 else:
2464 if not os.access(CONF_FILE, os.R_OK):
2465 print "Can't open " + CONF_FILE + "."
2466 print "Are you root?"
2467 sys.exit()
2468 confFile.read()
2471 ####################################################################################################
2472 # Embedded Images
2473 wifi_radar_icon = [ ""
2474 "GdkP"
2475 "\0\0\22""7"
2476 "\2\1\0\2"
2477 "\0\0\1\214"
2478 "\0\0\0c"
2479 "\0\0\0O"
2480 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2481 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2482 "\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"
2483 "\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"
2484 "\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"
2485 "\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"
2486 "\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"
2487 "\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"
2488 "\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"
2489 "\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"
2490 "\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"
2491 "\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"
2492 "\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"
2493 "\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"
2494 "\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"
2495 "\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"
2496 "\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"
2497 "\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"
2498 "\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"
2499 "\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"
2500 "\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"
2501 "\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"
2502 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2503 "\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"
2504 "\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"
2505 "\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"
2506 "\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"
2507 "\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"
2508 "\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"
2509 "\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"
2510 "\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"
2511 "\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"
2512 "\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"
2513 "\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"
2514 "\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"
2515 "\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"
2516 "\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"
2517 "\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"
2518 "\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"
2519 "\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"
2520 "\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"
2521 "\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"
2522 "\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"
2523 "\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"
2524 "\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"
2525 "\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"
2526 "\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"
2527 "\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"
2528 "\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"
2529 "\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"
2530 "\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"
2531 "\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"
2532 "\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"
2533 "\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"
2534 "\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"
2535 "\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"
2536 "\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"
2537 "\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"
2538 "\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"
2539 "\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"
2540 "\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"
2541 "\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"
2542 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2543 "\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"
2544 "\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"
2545 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2546 "\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"
2547 "\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"
2548 "\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"
2549 "\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"
2550 "\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"
2551 "\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"
2552 "\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"
2553 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2554 "\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"
2555 "\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"
2556 "\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"
2557 "\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"
2558 "\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"
2559 "\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"
2560 "|\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"
2561 "\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"
2562 "\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"
2563 "\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"
2564 "\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"
2565 "\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"
2566 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2567 "\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"
2568 "\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"
2569 "\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"
2570 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
2571 "\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"
2572 "\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"
2573 "\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"
2574 "\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"
2575 "\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"
2576 "\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"
2577 "\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"
2578 "\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"
2579 "\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"
2580 "\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"
2581 "\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"
2582 "\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"
2583 "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"
2584 "\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"
2585 "\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"
2586 "\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"
2587 "\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"
2588 "\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"
2589 "\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"
2590 "\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"
2591 "\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"
2592 "\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"
2593 "\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"
2594 "\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"
2595 "\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"
2596 "\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"
2597 "\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"
2598 "\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|"
2599 "\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"
2600 "\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"
2601 "\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"
2602 "\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"
2603 "\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"
2604 "\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"
2605 "\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"
2606 "\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"
2607 "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"
2608 "\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"
2609 "\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"
2610 "\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"
2611 "\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"
2612 "\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"
2613 "\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"
2614 "\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"
2615 "\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"
2616 "\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"
2617 "\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"
2618 "\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"
2619 "\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"
2620 "\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"
2621 "\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"
2622 "\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"
2623 "\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"
2624 "\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"
2625 "\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"
2626 "\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"
2627 "\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"
2628 "\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"
2629 "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"
2630 "\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"
2631 "\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"
2632 "\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"
2633 "\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"
2634 "\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"
2635 "\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"
2636 "\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"
2637 "\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"
2638 "\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"
2639 "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"
2640 "\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"
2641 "\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"
2642 "\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"
2643 "\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"
2644 "\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"
2645 "\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"
2646 "\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"
2647 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2648 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2649 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2650 "\0"]
2652 known_profile_icon = [ ""
2653 "GdkP"
2654 "\0\0\5""0"
2655 "\2\1\0\2"
2656 "\0\0\0P"
2657 "\0\0\0\24"
2658 "\0\0\0\24"
2659 "\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"
2660 "\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"
2661 "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"
2662 "\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"
2663 "\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"
2664 "\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"
2665 "\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"
2666 "\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"
2667 "\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"
2668 "\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"
2669 "\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"
2670 "\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"
2671 "\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"
2672 "\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"
2673 "\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"
2674 "\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"
2675 "\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"
2676 "\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"
2677 "\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"
2678 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2679 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2680 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2681 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2682 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2683 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2684 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2685 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2686 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2687 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2688 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2689 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2690 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2691 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2692 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2693 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2694 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2695 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2696 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2697 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2698 "\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"
2699 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2700 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2701 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2702 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2703 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2704 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2705 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2706 "\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"
2707 "\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"
2708 "\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"
2709 "\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"
2710 "\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"]
2712 unknown_profile_icon = [ ""
2713 "GdkP"
2714 "\0\0\5\22"
2715 "\2\1\0\2"
2716 "\0\0\0P"
2717 "\0\0\0\24"
2718 "\0\0\0\24"
2719 "\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"
2720 "\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"
2721 "\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"
2722 "\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"
2723 "(\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"
2724 "\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"
2725 "#\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"
2726 "\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"
2727 "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"
2728 "\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"
2729 "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"
2730 "\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"
2731 "\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"
2732 "\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"
2733 "\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"
2734 "\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"
2735 "\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"
2736 "\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"
2737 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2738 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2739 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2740 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2741 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2742 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2743 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2744 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2745 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2746 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2747 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2748 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2749 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2750 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2751 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2752 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2753 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2754 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2755 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2756 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2757 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2758 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2759 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2760 "\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"
2761 "\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"
2762 "\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"
2763 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2765 signal_xpm_barely = [
2766 "20 20 10 1",
2767 " c None",
2768 ". c #C6C6C6",
2769 "+ c #CCCCCC",
2770 "@ c #DBDBDB",
2771 "# c #D3D3D3",
2772 "$ c #A9B099",
2773 "% c #95A173",
2774 "& c #6B8428",
2775 "* c #B4B7AC",
2776 "= c #80924D",
2777 " .+++.",
2778 " +@@@+",
2779 " +@@@+",
2780 " +@@@+",
2781 " +@@@+",
2782 " .++++#@@@+",
2783 " +@@@@@@@@+",
2784 " +@@@@@@@@+",
2785 " +@@@@@@@@+",
2786 " +@@@@@@@@+",
2787 " $%%%%#@@@@@@@@+",
2788 " %&&&&@@@@@@@@@+",
2789 " %&&&&@@@@@@@@@+",
2790 " %&&&&@@@@@@@@@+",
2791 " %&&&&@@@@@@@@@+",
2792 "*%%%%=&&&&@@@@@@@@@+",
2793 "%&&&&&&&&&@@@@@@@@@+",
2794 "%&&&&&&&&&@@@@@@@@@+",
2795 "%&&&&&&&&&@@@@@@@@@+",
2796 "*%%%%%%%%%+++++++++."
2800 signal_xpm_best = [
2801 "20 20 6 1",
2802 " c None",
2803 ". c #9DAABF",
2804 "+ c #7B96BF",
2805 "@ c #386EBF",
2806 "# c #5982BF",
2807 "$ c #AEB4BF",
2808 " .+++.",
2809 " +@@@+",
2810 " +@@@+",
2811 " +@@@+",
2812 " +@@@+",
2813 " .++++#@@@+",
2814 " +@@@@@@@@+",
2815 " +@@@@@@@@+",
2816 " +@@@@@@@@+",
2817 " +@@@@@@@@+",
2818 " .++++#@@@@@@@@+",
2819 " +@@@@@@@@@@@@@+",
2820 " +@@@@@@@@@@@@@+",
2821 " +@@@@@@@@@@@@@+",
2822 " +@@@@@@@@@@@@@+",
2823 "$++++#@@@@@@@@@@@@@+",
2824 "+@@@@@@@@@@@@@@@@@@+",
2825 "+@@@@@@@@@@@@@@@@@@+",
2826 "+@@@@@@@@@@@@@@@@@@+",
2827 "$++++++++++++++++++."
2830 signal_xpm_none = [
2831 "20 20 6 1",
2832 " c None",
2833 ". c #C6C6C6",
2834 "+ c #CCCCCC",
2835 "@ c #DBDBDB",
2836 "# c #D3D3D3",
2837 "$ c #C2C2C2",
2838 " .+++.",
2839 " +@@@+",
2840 " +@@@+",
2841 " +@@@+",
2842 " +@@@+",
2843 " .++++#@@@+",
2844 " +@@@@@@@@+",
2845 " +@@@@@@@@+",
2846 " +@@@@@@@@+",
2847 " +@@@@@@@@+",
2848 " .++++#@@@@@@@@+",
2849 " +@@@@@@@@@@@@@+",
2850 " +@@@@@@@@@@@@@+",
2851 " +@@@@@@@@@@@@@+",
2852 " +@@@@@@@@@@@@@+",
2853 "$++++#@@@@@@@@@@@@@+",
2854 "+@@@@@@@@@@@@@@@@@@+",
2855 "+@@@@@@@@@@@@@@@@@@+",
2856 "+@@@@@@@@@@@@@@@@@@+",
2857 "$++++++++++++++++++."
2860 signal_xpm_ok = [
2861 "20 20 10 1",
2862 " c None",
2863 ". c #C6C6C6",
2864 "+ c #CCCCCC",
2865 "@ c #DBDBDB",
2866 "# c #A1A5B2",
2867 "$ c #848DA5",
2868 "% c #D3D3D3",
2869 "& c #4A5B8C",
2870 "* c #677498",
2871 "= c #B0B2B8",
2872 " .+++.",
2873 " +@@@+",
2874 " +@@@+",
2875 " +@@@+",
2876 " +@@@+",
2877 " #$$$$%@@@+",
2878 " $&&&&@@@@+",
2879 " $&&&&@@@@+",
2880 " $&&&&@@@@+",
2881 " $&&&&@@@@+",
2882 " #$$$$*&&&&@@@@+",
2883 " $&&&&&&&&&@@@@+",
2884 " $&&&&&&&&&@@@@+",
2885 " $&&&&&&&&&@@@@+",
2886 " $&&&&&&&&&@@@@+",
2887 "=$$$$*&&&&&&&&&@@@@+",
2888 "$&&&&&&&&&&&&&&@@@@+",
2889 "$&&&&&&&&&&&&&&@@@@+",
2890 "$&&&&&&&&&&&&&&@@@@+",
2891 "=$$$$$$$$$$$$$$++++."
2895 signal_xpm_low = [
2896 "20 20 8 1",
2897 " c None",
2898 ". c #C6C6C6",
2899 "+ c #CCCCCC",
2900 "@ c #DBDBDB",
2901 "# c #D3D3D3",
2902 "$ c #BFB0B5",
2903 "% c #C18799",
2904 "& c #C54F74",
2905 " .+++.",
2906 " +@@@+",
2907 " +@@@+",
2908 " +@@@+",
2909 " +@@@+",
2910 " .++++#@@@+",
2911 " +@@@@@@@@+",
2912 " +@@@@@@@@+",
2913 " +@@@@@@@@+",
2914 " +@@@@@@@@+",
2915 " .++++#@@@@@@@@+",
2916 " +@@@@@@@@@@@@@+",
2917 " +@@@@@@@@@@@@@+",
2918 " +@@@@@@@@@@@@@+",
2919 " +@@@@@@@@@@@@@+",
2920 "$%%%%#@@@@@@@@@@@@@+",
2921 "%&&&&@@@@@@@@@@@@@@+",
2922 "%&&&&@@@@@@@@@@@@@@+",
2923 "%&&&&@@@@@@@@@@@@@@+",
2924 "$%%%%++++++++++++++."
2928 ####################################################################################################
2929 # Make so we can be imported
2930 if __name__ == "__main__":
2931 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
2932 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
2933 else:
2934 import gtk, gobject
2935 gtk.gdk.threads_init()
2936 apQueue = Queue.Queue(100)
2937 commQueue = Queue.Queue(2)
2938 threading.Thread( None, scanning_thread, None, ( confFile, apQueue, commQueue ) ).start()
2939 main_radar_window = radar_window(confFile, apQueue, commQueue)
2940 gobject.timeout_add( 500, main_radar_window.update_plist_items )
2941 main_radar_window.main()