Add profile_dialog.toggle_use_dhcp() HappyDoc comments
[wifi-radar.git] / wifi-radar
blob8bec75fdb41f5533b853f9d7869551655d3f66ef
1 #!/usr/bin/python
3 # $Id$
4 # vi:set filetype=python noet:
6 # A wireless profile manager for Linux
8 # Originally created for x1000 Linux:
9 # http://x1000.bitbuilder.com
11 # Created by:
12 # Ahmad Baitalmal <ahmad@baitalmal.com>
14 # Maintained by:
15 # Brian Elliott Finley <brian@thefinleys.com>
17 # License:
18 # GPL
20 # http://www.bitbuilder.com/wifi_radar
21 # http://svn.systemimager.org
23 # See CREDITS file for more contributors.
24 # See ChangeLog file for, well, changes.
27 import ConfigParser, os, Queue, re, string, sys, threading
28 from signal import SIGTERM
29 from subprocess import call, Popen, PIPE
30 from time import sleep
31 from types import *
33 WIFI_RADAR_VERSION = "0.0.0"
35 if __debug__:
36 print '__debug__ is True'
37 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
38 # turn on debugging.
41 # Where the conf file should live could be different for your distro. Please change
42 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
44 CONF_FILE = "/etc/wifi-radar.conf"
46 os.environ['LC_MESSAGES'] = 'C'
49 #####################################
50 # Labels
51 USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
52 USE_IP_LABEL = "Manual network configuration"
53 WIFI_SET_LABEL = "WiFi Options"
54 CON_PP_LABEL = "Connection Commands"
55 DIS_PP_LABEL = "Disconnection Commands"
56 USE_WPA_LABEL = "Use WPA"
57 NO_WPA_LABEL = "No WPA"
58 ####################################################################################################
60 ####################################################################################################
61 ####################################################################################################
63 # Sets the interface to the specified network device
65 #Parameters:
67 # 'device' -- string - The network device to use
69 #Returns:
71 # nothing
72 def set_network_device( device ):
73 #print "set_network_device: ", device
74 if device != "auto_detect":
75 confFile.set_opt('DEFAULT.interface', device)
76 else: # auto detect network device
77 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
78 # If no devices are found, default to eth1.
79 # call iwconfig command and read output
80 iwconfig_info = Popen(confFile.get_opt('DEFAULT.iwconfig_command'), shell=True, stdout=PIPE).stdout
81 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
82 if len(wireless_devices) > 0:
83 confFile.set_opt('DEFAULT.interface', wireless_devices[0])
84 else:
85 print "No wifi-device found. Exiting."
86 sys.exit()
88 # Return a blank profile
90 #Parameters:
92 # none
94 #Returns:
96 # dictionary -- An AP profile with defaults set.
97 def get_new_profile():
98 return { 'known': False,
99 'available': False,
100 'encrypted': False,
101 'essid': '',
102 'bssid': '',
103 'protocol': 'g',
104 'signal': 0,
105 'channel': 'auto',
106 'con_prescript': '',
107 'con_postscript': '',
108 'dis_prescript': '',
109 'dis_postscript': '',
110 'key': '',
111 'mode': '',
112 'security': '',
113 'use_wpa': False,
114 'wpa_driver': '',
115 'use_dhcp': True,
116 'ip': '',
117 'netmask': '',
118 'gateway': '',
119 'domain': '',
120 'dns1': '',
121 'dns2': ''
124 # Combine essid and bssid to make a config file section name
126 #Parameters:
128 # 'essid' -- string - AP ESSID
130 # 'bssid' -- string - AP BSSID
132 #Returns:
134 # string -- the bssid concatenated to a colon, concatenated to the essid
135 def make_section_name( essid, bssid ):
136 return essid + ':' + bssid
138 # Split a config file section name into an essid and a bssid
140 #Parameters:
142 # 'section' -- string - Config file section name
144 #Returns:
146 # list -- the essid and bssid
147 def split_section_name( section ):
148 parts = re.split(':', section)
149 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
151 # Run commands through the shell
153 #Parameters:
155 # 'command' -- tuple - The command and arguments to run
157 #Returns:
159 # boolean -- True on success, otherwise, False
160 def shellcmd( command ):
161 try:
162 command = ' '.join(command)
163 return_code = call(command, shell=True)
164 if return_code >= 0:
165 return True
166 else:
167 print >>sys.stderr, "Child was terminated by signal", -return_code
168 except OSError, exception:
169 print >>sys.stderr, "Execution failed:", exception
170 return False
172 # Speak feedback message to user
174 #Parameters:
176 # 'words' -- string - Message to speak to user
178 #Returns:
180 # nothing
181 def say( words ):
182 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
183 words = words.replace( "\"", "\\\"" )
184 shellcmd([confFile.get_opt('DEFAULT.speak_command'), words])
186 # Scan for a limited time and return AP names and bssid found.
187 # Access points we find will be put on the outgoing Queue, apQueue.
189 #Parameters:
191 # 'confFile' -- ConfigFile - Config file object
193 # 'apQueue' -- Queue - Queue on which to put AP profiles
195 # 'commandQueue' -- Queue - Queue from which to read commands
197 #Returns:
199 # nothing
200 def scanning_thread( confFile, apQueue, commandQueue ):
201 # Setup our essid pattern matcher
202 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
203 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abg]+)", re.I | re.M | re.S )
204 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
205 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
206 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
207 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
209 access_points = {}
210 command = "scan"
211 while True:
212 try:
213 command = commandQueue.get_nowait()
214 if __debug__: print "scanning_thread received command:", command
215 command_read = True
216 except Queue.Empty:
217 command_read = False
218 if command == "scan":
219 if __debug__: print "Beginning scan pass"
220 # Some cards need to have the interface up to scan
221 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
222 # call ifconfig command and wait for return
223 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface'), 'up'])
224 # update the signal strengths
225 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), confFile.get_opt('DEFAULT.interface'), 'scan'], stdout=PIPE).stdout.read()
226 #if __debug__:
227 #print "Current IP ", get_current_ip()
228 #print "Current ESSID ", get_current_essid()
229 #print "Current BSSID ", get_current_bssid()
230 # zero out the signal levels for all access points
231 for bssid in access_points:
232 access_points[bssid]['signal'] = 0
233 # split the scan data based on the address line
234 hits = scandata.split(' - ')
235 for hit in hits:
236 # set the defaults for profile template
237 profile = get_new_profile()
238 m = essid_pattern.search( hit )
239 if m:
240 # we found an essid
241 profile['essid'] = m.groups()[1]
242 m = bssid_pattern.search( hit ) # get BSSID from scan
243 if m: profile['bssid'] = m.groups()[1]
244 m = protocol_pattern.search( hit ) # get protocol from scan
245 if m: profile['protocol'] = m.groups()[1]
246 m = mode_pattern.search( hit ) # get mode from scan
247 if m: profile['mode'] = m.groups()[1]
248 m = channel_pattern.search( hit ) # get channel from scan
249 if m: profile['channel'] = m.groups()[1]
250 m = enckey_pattern.search( hit ) # get encryption key from scan
251 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
252 m = signal_pattern.search( hit ) # get signal strength from scan
253 if m: profile['signal'] = m.groups()[1]
254 access_points[ profile['bssid'] ] = profile
255 for bssid in access_points:
256 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
257 # Put all, now or previously, sensed access_points into apQueue
258 try:
259 apQueue.put_nowait( access_points[bssid] )
260 except Queue.Full:
261 pass
262 elif command == "exit":
263 if __debug__: print "Exiting scanning_thread"
264 return
265 if command_read: commandQueue.task_done()
266 if confFile.get_opt('DEFAULT.interface').find('ath') == 0:
267 sleep( 30 )
268 else:
269 sleep( 1 )
272 # Manage a connection; including reporting connection state,
273 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
274 class ConnectionManager():
275 # Create a new connection manager which can read a config file and send to scanning thread
276 # command Queue. A new manager checks for a pre-existing connection and takes
277 # its AP profile from the ESSID and BSSID to which it is currently attached.
279 #Parameters:
281 # 'confFile' -- ConfigFile - Config file object
283 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
285 #Returns:
287 # ConnectionManager instance
288 def __init__( self, confFile, commandQueue ):
289 self.confFile = confFile
290 self.commQueue = commandQueue
291 # is connection running?
292 self.state = False
293 if self.get_current_ip():
294 self.state = True
295 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
297 # Change the interface state: up or down.
299 #Parameters:
301 # 'state' -- string - The state to which to change the interface.
303 #Returns:
305 # nothing
306 def if_change( self, state ):
307 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
308 # call ifconfig command and wait for return
309 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), state])
311 # Connect to the specified AP.
313 #Parameters:
315 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
317 # 'status_window' -- status_window - Instance of window to use to update user.
319 #Returns:
321 # nothing
322 def connect_to_network( self, profile, status_window ):
323 self.profile = profile
324 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
325 say( msg )
326 if __debug__: print " %s" % msg
327 # ready to dance
328 # Let's run the connection prescript
329 if self.profile['con_prescript'].strip() != '':
330 # got something to execute
331 # run connection prescript through shell and wait for return
332 if __debug__: print "executing connection prescript:", self.profile['con_prescript']
333 shellcmd([self.profile['con_prescript']])
334 if status_window:
335 status_window.run()
336 # Some cards need to have the interface up
337 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
338 self.if_change('up')
339 # Start building iwconfig command line, command
340 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
341 iwconfig_command.append( confFile.get_opt('DEFAULT.interface') )
342 # Setting essid
343 iwconfig_command.append( 'essid' )
344 iwconfig_command.append( self.profile['essid'] )
345 # Setting nick
346 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
347 # Setting key
348 iwconfig_command.append( 'key' )
349 if self.profile['key'] == '':
350 iwconfig_command.append( 'off' )
351 else:
352 iwconfig_command.append( self.profile['key'] )
353 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
354 # Setting mode to Master, this will cause association with the AP to fail
355 #iwconfig_command.append( 'mode' )
356 #iwconfig_command.append( 'Master' )
357 # Setting channel
358 if self.profile['channel'] != '':
359 iwconfig_command.append( 'channel' )
360 iwconfig_command.append( self.profile['channel'] )
361 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
362 iwconfig_command.append( 'ap' )
363 iwconfig_command.append( self.profile['bssid'] )
364 # Some cards require a commit
365 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
366 if __debug__: print 'iwconfig_args %s ' % ( iwconfig_args, )
367 iwconfig_command.append( 'commit' )
368 # call iwconfig command and wait for return
369 if not shellcmd(iwconfig_command): return
370 # Now normal network stuff
371 # Kill off any existing DHCP clients running
372 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
373 if __debug__: print "Killing existing DHCP..."
374 try:
375 if self.confFile.get_opt('DHCP.kill_args') != '':
376 # call DHCP client kill command and wait for return
377 if __debug__: print self.confFile.get_opt('DHCP.command') + " " + self.confFile.get_opt('DHCP.kill_args')
378 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
379 else:
380 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
381 except OSError:
382 print "failed to kill DHCP client"
383 sys.exit()
384 finally:
385 print "Stale pid file. Removing..."
386 os.remove(self.confFile.get_opt('DHCP.pidfile'))
387 # Kill off any existing WPA supplicants running
388 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
389 if __debug__: print "Killing existing WPA supplicant..."
390 try:
391 if not self.confFile.get_opt('WPA.kill_command') != '':
392 # call WPA supplicant kill command and wait for return
393 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
394 else:
395 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
396 except OSError:
397 print "failed to kill WPA supplicant"
398 sys.exit()
399 finally:
400 print "Stale pid file. Removing..."
401 os.remove(self.confFile.get_opt('WPA.pidfile'))
402 # Begin WPA supplicant
403 if self.profile['use_wpa'] :
404 if __debug__: print "WPA args: %s" % ( wpa_options, )
405 if status_window:
406 status_window.update_message("WPA supplicant starting")
407 # call WPA supplicant command and do not wait for return
408 wpa_proc = Popen([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args'), self.confFile.get_opt('DEFAULT.interface')])
409 if self.profile['use_dhcp'] :
410 if __debug__: print "Disable iwlist while dhcp in progress..."
411 try:
412 self.commQueue.put("pause")
413 except Queue.Full:
414 pass
415 if status_window:
416 status_window.update_message("Acquiring IP Address (DHCP)")
417 # call DHCP client command and do not wait for return
418 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
419 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
420 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
421 dhcp_proc = Popen(dhcp_command, stdout=None)
422 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
423 tick = 0.25
424 waiting = dhcp_proc.poll()
425 while waiting == None:
426 waiting = dhcp_proc.poll()
427 if timer < 0:
428 os.kill(dhcp_proc.pid, SIGTERM)
429 break
430 if sys.modules.has_key("gtk"):
431 while gtk.events_pending():
432 gtk.main_iteration(False)
433 timer -= tick
434 sleep(tick)
435 if not self.get_current_ip():
436 if status_window:
437 status_window.update_message("Could not get IP address!")
438 if sys.modules.has_key("gtk"):
439 while gtk.events_pending():
440 gtk.main_iteration(False)
441 sleep(3)
442 else:
443 print "Could not get IP address!"
444 self.disconnect_interface()
445 else:
446 if status_window:
447 status_window.update_message("Got IP address. Done.")
448 self.state = True
449 if sys.modules.has_key("gtk"):
450 while gtk.events_pending():
451 gtk.main_iteration(False)
452 sleep(2)
453 # Re-enable iwlist
454 try:
455 self.commQueue.put("scan")
456 except Queue.Full:
457 pass
458 else:
459 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'] )
460 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
461 resolv_contents = ''
462 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
463 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
464 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
465 if ( resolv_contents != '' ):
466 resolv_file=open('/etc/resolv.conf', 'w')
467 resolv_file.write(s)
468 resolv_file.close
469 if not shellcmd([ifconfig_command]): return
470 if not shellcmd([route_command]): return
471 self.state = True
472 # Let's run the connection postscript
473 con_postscript = self.profile['con_postscript']
474 if self.profile['con_postscript'].strip() != '':
475 if __debug__: print "executing connection postscript:", self.profile['con_postscript']
476 shellcmd([self.profile['con_postscript']])
477 if status_window:
478 status_window.destroy()
480 # Disconnect from the AP with which a connection has been established/attempted.
482 #Parameters:
484 # nothing
486 #Returns:
488 # nothing
489 def disconnect_interface( self ):
490 msg = "Disconnecting"
491 say( msg )
492 if __debug__: print msg
493 # Pause scanning while manipulating card
494 try:
495 self.commQueue.put("pause")
496 except Queue.Full:
497 pass
498 if self.state:
499 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
500 # Let's run the disconnection prescript
501 if self.profile['dis_prescript'].strip() != '':
502 if __debug__: print "executing disconnection prescript:", self.profile['dis_prescript']
503 shellcmd([self.profile['dis_prescript']])
504 if __debug__: print "Kill off any existing DHCP clients running..."
505 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
506 if __debug__: print "Killing existing DHCP..."
507 try:
508 if self.confFile.get_opt('DHCP.kill_args') != '':
509 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
510 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
511 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
512 if __debug__: print "DHCP command", dhcp_command
513 # call DHCP client command and wait for return
514 if not shellcmd(dhcp_command): return
515 else:
516 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
517 except OSError:
518 print "failed to kill DHCP client"
519 if __debug__: print "Kill off any existing WPA supplicants running..."
520 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
521 if __debug__: print "Killing existing WPA supplicant..."
522 try:
523 if not self.confFile.get_opt('WPA.kill_command') != '':
524 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
525 if not shellcmd(wpa_command): return
526 else:
527 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
528 except OSError:
529 print "failed to kill WPA supplicant"
530 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
531 if __debug__: print "Let's clear out the wireless stuff"
532 if __debug__: print 'Now take the interface down'
533 self.if_change('down')
534 if __debug__: print 'Since it may be brought back up by the next scan, lets unset its IP'
535 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), '0.0.0.0'])
536 # Let's run the disconnection postscript
537 if self.profile['dis_postscript'].strip() != '':
538 if __debug__: print "executing disconnection postscript:", self.profile['dis_postscript']
539 shellcmd([self.profile['dis_postscript']])
540 self.state = False
541 if __debug__: print 'Disconnect complete.'
542 # Begin scanning again
543 try:
544 self.commQueue.put("scan")
545 except Queue.Full:
546 pass
548 # Returns the current IP, if any, by calling ifconfig.
550 #Parameters:
552 # nothing
554 #Returns:
556 # string or None -- the IP address or None (if no there is no current connection)
557 def get_current_ip( self ):
558 ifconfig_command = [ confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface') ]
559 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
560 # Be careful to the language (inet adr: in French for example)
562 # Hi Brian
564 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
565 # There the string in ifconfig is inet Adresse for the IP which isn't
566 # found by the current get_current_ip function in wifi-radar. I changed
567 # the according line (#289; gentoo, v1.9.6-r1) to
568 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
569 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
571 # I'd be happy if you could incorporate this small change because as now
572 # I've got to change the file every time it is updated.
574 # Best wishes
576 # Simon
577 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
578 line = ifconfig_info.read()
579 if ip_re.search( line ):
580 return ip_re.search( line ).group(1)
581 return None
583 # Returns the current ESSID, if any, by calling iwconfig.
585 #Parameters:
587 # nothing
589 #Returns:
591 # string or None -- the ESSID or None (if no there is no current association)
592 def get_current_essid( self ):
593 """Returns the current ESSID if any by calling iwconfig"""
594 iwconfig_info = Popen([confFile.get_opt('DEFAULT.iwconfig_command'), confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
595 # Be careful to the language (inet adr: in French for example)
596 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
597 line = iwconfig_info.read()
598 if essid_re.search( line ):
599 return essid_re.search( line ).group(2)
600 return None
602 # Returns the current BSSID, if any, by calling iwconfig.
604 #Parameters:
606 # nothing
608 #Returns:
610 # string or None -- the BSSID or None (if no there is no current association)
611 def get_current_bssid( self ):
612 """Returns the current BSSID if any by calling iwconfig"""
613 iwconfig_info = Popen([confFile.get_opt('DEFAULT.iwconfig_command'), confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
614 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
615 line = iwconfig_info.read()
616 if bssid_re.search( line ):
617 return bssid_re.search( line ).group(2)
618 return None
622 # The main user interface window for WiFi Radar. This class also is the control
623 # center for most of the rest of the operations.
624 class radar_window:
625 # Create a new radar_window.
627 #Parameters:
629 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
631 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
633 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
635 #Returns:
637 # radar_window instance
638 def __init__( self, confFile, apQueue, commQueue ):
639 global signal_xpm_none
640 global signal_xpm_low
641 global signal_xpm_barely
642 global signal_xpm_ok
643 global signal_xpm_best
644 global known_profile_icon
645 global unknown_profile_icon
646 global wifi_radar_icon
648 self.confFile = confFile
649 self.apQueue = apQueue
650 self.commandQueue = commQueue
651 self.access_points = {}
652 self.connection = None
654 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
655 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
656 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
657 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
658 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
659 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
660 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
661 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
662 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
663 self.window.set_icon( icon )
664 self.window.set_border_width( 10 )
665 self.window.set_size_request( 550, 300 )
666 self.window.set_title( "WiFi Radar" )
667 self.window.connect( 'delete_event', self.delete_event )
668 self.window.connect( 'destroy', self.destroy )
669 # let's create all our widgets
670 self.current_network = gtk.Label()
671 self.current_network.show()
672 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
673 self.close_button.show()
674 self.close_button.connect( 'clicked', self.delete_event, None )
675 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
676 self.preferences_button.show()
677 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
678 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
679 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
680 self.plist = gtk.TreeView( self.pstore )
681 # The icons column, known and encryption
682 self.pix_cell = gtk.CellRendererPixbuf()
683 self.wep_cell = gtk.CellRendererPixbuf()
684 self.icons_cell = gtk.CellRendererText()
685 self.icons_col = gtk.TreeViewColumn()
686 self.icons_col.pack_start( self.pix_cell, False )
687 self.icons_col.pack_start( self.wep_cell, False )
688 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
689 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
690 self.plist.append_column( self.icons_col )
691 # The AP column
692 self.ap_cell = gtk.CellRendererText()
693 self.ap_col = gtk.TreeViewColumn( "Access Point" )
694 self.ap_col.pack_start( self.ap_cell, True )
695 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
696 self.plist.append_column( self.ap_col )
697 # The signal column
698 self.sig_cell = gtk.CellRendererPixbuf()
699 self.signal_col = gtk.TreeViewColumn( "Signal" )
700 self.signal_col.pack_start( self.sig_cell, True )
701 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
702 self.plist.append_column( self.signal_col )
703 # The mode column
704 self.mode_cell = gtk.CellRendererText()
705 self.mode_col = gtk.TreeViewColumn( "Mode" )
706 self.mode_col.pack_start( self.mode_cell, True )
707 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
708 self.plist.append_column( self.mode_col )
709 # The protocol column
710 self.prot_cell = gtk.CellRendererText()
711 self.protocol_col = gtk.TreeViewColumn( "802.11" )
712 self.protocol_col.pack_start( self.prot_cell, True )
713 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
714 self.plist.append_column( self.protocol_col )
715 # The channel column
716 self.channel_cell = gtk.CellRendererText()
717 self.channel_col = gtk.TreeViewColumn( "Channel" )
718 self.channel_col.pack_start( self.channel_cell, True )
719 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
720 self.plist.append_column( self.channel_col )
721 # DnD Ordering
722 self.plist.set_reorderable( True )
723 self.pstore.connect( 'row-changed', self.update_auto_profile_order )
724 # enable/disable buttons based on the selected network
725 self.selected_network = self.plist.get_selection()
726 self.selected_network.connect( 'changed', self.on_network_selection, None )
727 # the list scroll bar
728 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
729 sb.show()
730 self.plist.show()
731 # Add New button
732 self.new_button = gtk.Button( "_New" )
733 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
734 self.new_button.show()
735 # Add Configure button
736 self.edit_button = gtk.Button( "C_onfigure" )
737 self.edit_button.connect( 'clicked', self.edit_profile, None )
738 self.edit_button.show()
739 self.edit_button.set_sensitive(False)
740 # Add Delete button
741 self.delete_button = gtk.Button( "_Delete" )
742 self.delete_button.connect( 'clicked', self.delete_profile, None )
743 self.delete_button.show()
744 self.delete_button.set_sensitive(False)
745 # Add Connect button
746 self.connect_button = gtk.Button( "Co_nnect" )
747 self.connect_button.connect( 'clicked', self.connect_profile, None )
748 # Add Disconnect button
749 self.disconnect_button = gtk.Button( "D_isconnect" )
750 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
751 # lets add our widgets
752 rows = gtk.VBox( False, 3 )
753 net_list = gtk.HBox( False, 0 )
754 listcols = gtk.HBox( False, 0 )
755 prows = gtk.VBox( False, 0 )
756 # lets start packing
757 # the network list
758 net_list.pack_start( self.plist, True, True, 0 )
759 net_list.pack_start( sb, False, False, 0 )
760 # the rows level
761 rows.pack_start( net_list , True, True, 0 )
762 rows.pack_start( self.current_network, False, True, 0 )
763 # the list columns
764 listcols.pack_start( rows, True, True, 0 )
765 listcols.pack_start( prows, False, False, 5 )
766 # the list buttons
767 prows.pack_start( self.new_button, False, False, 2 )
768 prows.pack_start( self.edit_button, False, False, 2 )
769 prows.pack_start( self.delete_button, False, False, 2 )
770 prows.pack_end( self.connect_button, False, False, 2 )
771 prows.pack_end( self.disconnect_button, False, False, 2 )
773 self.window.action_area.pack_start( self.preferences_button )
774 self.window.action_area.pack_start( self.close_button )
776 rows.show()
777 prows.show()
778 listcols.show()
779 self.window.vbox.add( listcols )
780 self.window.vbox.set_spacing( 3 )
781 self.window.show_all()
783 # Now, immediately hide these two. The proper one will be
784 # displayed later, based on interface state. -BEF-
785 self.disconnect_button.hide()
786 self.connect_button.hide()
787 self.connect_button.set_sensitive(False)
789 # set up connection manager for later use
790 self.connection = ConnectionManager( self.confFile, self.commandQueue )
792 # Add our known profiles in order
793 for ap in self.confFile.auto_profile_order:
794 ap = ap.strip()
795 self.access_points[ ap ] = self.confFile.get_profile( ap )
796 wep = None
797 if self.access_points[ ap ]['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
798 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'] ] )
799 # This is the first run (or, at least, no config file was present), so pop up the preferences window
800 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
801 self.confFile.remove_option('DEFAULT', 'new_file')
802 self.edit_preferences(self.preferences_button)
804 # Begin running radar_window in Gtk event loop.
806 #Parameters:
808 # nothing
810 #Returns:
812 # nothing
813 def main( self ):
814 gtk.main()
816 # Write the config file to disk and quit application.
818 #Parameters:
820 # 'widget' -- gtk.Widget - The widget sending the event.
822 #Returns:
824 # nothing
825 def destroy( self, widget = None):
826 self.confFile.write()
827 gtk.main_quit()
829 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
831 #Parameters:
833 # 'widget' -- gtk.Widget - The widget sending the event.
835 # 'data' -- tuple - list of arbitrary arguments (not used)
837 #Returns:
839 # boolean -- always return False (i.e. do not propigate the signal which called)
840 def delete_event( self, widget, data = None ):
841 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
842 try:
843 self.commandQueue.put("exit", True)
844 except Queue.Full:
845 pass
846 # Save the preferred networks order
847 self.update_auto_profile_order()
848 self.destroy()
849 return False
851 # Updates the on-screen profiles list.
853 #Parameters:
855 # nothing
857 #Returns:
859 # boolean -- always return True
860 def update_plist_items( self ):
861 # Indicate to PyGtk that only one Gtk thread should run here
862 gtk.gdk.threads_enter()
863 # update the current ip and essid
864 # set the state of connect/disconnect buttons based on whether we have an IP address
865 if self.connection:
866 if self.connection.state:
867 self.current_network.set_text( "Connected to %s ip(%s)" % ( make_section_name( self.connection.get_current_essid(), self.connection.get_current_bssid() ), self.connection.get_current_ip() ) )
868 self.connect_button.hide()
869 self.disconnect_button.show()
870 else:
871 self.current_network.set_text( "Not Connected." )
872 self.disconnect_button.hide()
873 self.connect_button.show()
875 while True:
876 # Get profiles scanned by iwlist
877 try:
878 profile = self.apQueue.get_nowait()
879 except Queue.Empty:
880 break
881 else:
882 prow_iter = self.get_row_by_ap( profile['essid'], profile['bssid'] )
883 wep = None
884 if prow_iter != None:
885 # the AP is in the list of APs on the screen
886 apname = make_section_name(profile['essid'], profile['bssid'])
887 if self.access_points.has_key(apname):
888 # This AP has been configured and is/should be stored in the config file
889 profile['known'] = self.access_points[apname]['known']
890 self.access_points[apname]['available'] = profile['available']
891 self.access_points[apname]['encrypted'] = profile['encrypted']
892 self.access_points[apname]['signal'] = profile['signal']
893 self.access_points[apname]['mode'] = profile['mode']
894 self.access_points[apname]['protocol'] = profile['protocol']
895 self.access_points[apname]['channel'] = profile['channel']
896 # Set the 'known' values; False is default, overridden to True by self.access_points
897 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known( profile[ 'known' ] ))
898 self.pstore.set_value(prow_iter, 2, profile[ 'known' ])
899 self.pstore.set_value(prow_iter, 3, profile['available'])
900 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
901 self.pstore.set_value(prow_iter, 4, wep)
902 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal( profile['signal'] ))
903 self.pstore.set_value(prow_iter, 6, profile['mode'])
904 self.pstore.set_value(prow_iter, 7, profile['protocol'])
905 self.pstore.set_value(prow_iter, 8, profile['channel'])
906 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
907 #for val in self.pstore[prow_iter]:
908 #print val,
909 else:
910 # the AP is not in the list of APs on the screen
911 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'] ] )
912 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
913 # Allow other Gtk threads to run
914 gtk.gdk.threads_leave()
915 #print "update_plist_items: Empty apQueue"
916 return True
918 # Return the proper icon for a value of known.
920 #Parameters:
922 # 'known' -- boolean - Whether the AP is known (i.e. configured)
924 #Returns:
926 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
927 def pixbuf_from_known( self, known ):
928 """ return the proper icon for value of known """
929 if known:
930 return self.known_profile_icon
931 else:
932 return self.unknown_profile_icon
934 # Return an icon indicating the signal level.
936 #Parameters:
938 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
940 #Returns:
942 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
943 def pixbuf_from_signal( self, signal ):
944 signal = int( signal )
945 #print "signal level:", signal
946 if signal < 3:
947 return self.signal_none_pb
948 elif signal < 12:
949 return self.signal_low_pb
950 elif signal < 20:
951 return self.signal_barely_pb
952 elif signal < 35:
953 return self.signal_ok_pb
954 elif signal >= 35:
955 return self.signal_best_pb
956 else:
957 return None
959 # Return row which holds specified ESSID and BSSID.
961 #Parameters:
963 # 'essid' -- string - ESSID to match
965 # 'bssid' -- string - BSSID to match
967 #Returns:
969 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
970 def get_row_by_ap( self, essid, bssid ):
971 for row in self.pstore:
972 if ( row[0] == essid + "\n" + bssid ):
973 #print "matched:", row.iter, essid, bssid
974 return row.iter
975 return None
977 # Enable/disable buttons based on the selected network.
979 #Parameters:
981 # 'widget' -- gtk.Widget - The widget sending the event.
983 # 'data' -- tuple - list of arbitrary arguments (not used)
985 #Returns:
987 # nothing
988 def on_network_selection( self, widget, data = None ):
989 ( store, selected_iter ) = self.selected_network.get_selected()
990 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
991 # if no networks are selected, disable all buttons except New
992 # (this occurs after a drag-and-drop)
993 if selected_iter == None:
994 self.edit_button.set_sensitive(False)
995 self.delete_button.set_sensitive(False)
996 self.connect_button.set_sensitive(False)
997 return
998 # enable/disable buttons
999 self.connect_button.set_sensitive(True)
1000 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1001 self.edit_button.set_sensitive(True)
1002 self.delete_button.set_sensitive(True)
1003 else:
1004 self.edit_button.set_sensitive(True)
1005 self.delete_button.set_sensitive(False)
1007 # Init and run the preferences dialog
1009 #Parameters:
1011 # 'widget' -- gtk.Widget - The widget sending the event.
1013 # 'data' -- tuple - list of arbitrary arguments (not used)
1015 #Returns:
1017 # nothing
1018 def edit_preferences( self, widget, data=None ):
1019 p = preferences_dialog( self, self.confFile )
1020 ok = p.run()
1021 p.destroy()
1023 # Respond to a request to create a new AP profile
1025 #Parameters:
1027 # 'widget' -- gtk.Widget - The widget sending the event.
1029 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1031 # 'data' -- tuple - list of arbitrary arguments (not used)
1033 #Returns:
1035 # boolean -- True if a profile was created and False if profile creation was canceled.
1036 def create_new_profile( self, widget, profile, data=None ):
1037 profile_editor = profile_dialog( self, profile )
1038 profile = profile_editor.run()
1039 profile_editor.destroy()
1040 if profile:
1041 apname = make_section_name( profile['essid'], profile['bssid'] )
1042 # Check that the ap does not exist already
1043 if apname in self.confFile.profiles():
1044 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) )
1045 dlg.run()
1046 dlg.destroy()
1047 del dlg
1048 # try again
1049 self.access_points[ apname ] = profile
1050 self.confFile.set_section( apname, profile )
1051 # if it is not in the auto_profile_order add it
1052 if not apname in self.confFile.auto_profile_order:
1053 self.confFile.auto_profile_order.append(apname)
1054 # add to the store
1055 wep = None
1056 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1057 return True
1058 else:
1059 # Did not create new profile
1060 return False
1062 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1063 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1065 #Parameters:
1067 # 'widget' -- gtk.Widget - The widget sending the event.
1069 # 'data' -- tuple - list of arbitrary arguments (not used)
1071 #Returns:
1073 # nothing
1074 def edit_profile( self, widget, data=None ):
1075 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1076 if not selected_iter: return
1077 row_start = str(self.pstore.get_value( selected_iter, 0 )).split("\n")
1078 apname = make_section_name( row_start[0], row_start[1] )
1079 profile = self.confFile.get_profile( apname )
1080 if profile:
1081 profile_editor = profile_dialog( self, profile )
1082 profile = profile_editor.run()
1083 profile_editor.destroy()
1084 if profile:
1085 if __debug__:
1086 print "Got edited profile ", profile
1087 apname = make_section_name( profile['essid'], profile['bssid'] )
1088 self.access_points[ apname ] = profile
1089 self.confFile.set_section( apname, profile )
1090 else:
1091 profile = get_new_profile()
1092 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1093 self.create_new_profile( widget, profile, data )
1095 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1097 #Parameters:
1099 # 'widget' -- gtk.Widget - The widget sending the event.
1101 # 'data' -- tuple - list of arbitrary arguments (not used)
1103 #Returns:
1105 # nothing
1106 def delete_profile( self, widget, data=None ):
1107 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1108 if not selected_iter: return
1109 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1110 known = store.get_value( selected_iter, 1 )
1111 if not known: return
1112 dlg = gtk.MessageDialog(
1113 self.window,
1114 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1115 gtk.MESSAGE_QUESTION,
1116 gtk.BUTTONS_YES_NO,
1117 "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid) )
1118 res = dlg.run()
1119 dlg.destroy()
1120 del dlg
1121 if res == gtk.RESPONSE_NO: return
1122 # Remove it
1123 apname = make_section_name( essid, bssid )
1124 del self.access_points[ apname ]
1125 self.confFile.remove_section( apname )
1126 print "delete_profile: ", apname, ":", self.confFile.auto_profile_order
1127 if apname in self.confFile.auto_profile_order: self.confFile.auto_profile_order.remove(apname)
1128 self.pstore.remove( selected_iter )
1129 # Let's save our current state
1130 self.update_auto_profile_order()
1132 # Respond to a request to connect to an AP.
1134 #Parameters:
1136 # 'widget' -- gtk.Widget - The widget sending the event.
1138 # 'profile' -- dictionary - The AP profile to which to connect.
1140 # 'data' -- tuple - list of arbitrary arguments (not used)
1142 #Returns:
1144 # nothing
1145 def connect_profile( self, widget, profile, data=None ):
1146 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1147 if not selected_iter: return
1148 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1149 known = store.get_value( selected_iter, 2 )
1150 if not known:
1151 if data != 'noconnect':
1152 dlg = gtk.MessageDialog(
1153 self.window,
1154 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1155 gtk.MESSAGE_QUESTION,
1156 gtk.BUTTONS_YES_NO,
1157 "This network does not have a profile configured.\n\nWould you like to create one now?" )
1158 res = dlg.run()
1159 dlg.destroy()
1160 del dlg
1161 if res == gtk.RESPONSE_NO: return
1162 profile = get_new_profile()
1163 profile['essid'] = essid
1164 profile['bssid'] = bssid
1165 if not self.create_new_profile( widget, profile, data ):
1166 return
1167 apname = make_section_name( essid, bssid )
1168 self.connection.connect_to_network( self.access_points[apname], status_window( self ) )
1170 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1172 #Parameters:
1174 # 'widget' -- gtk.Widget - The widget sending the event.
1176 # 'data' -- tuple - list of arbitrary arguments (not used)
1178 #Returns:
1180 # nothing
1181 def disconnect_profile( self, widget, data=None ):
1182 self.connection.disconnect_interface()
1184 # Update the config file auto profile order from the on-screen order
1186 #Parameters:
1188 # 'widget' -- gtk.Widget - The widget sending the event.
1190 # 'data' -- tuple - list of arbitrary arguments (not used)
1192 # 'data2' -- tuple - list of arbitrary arguments (not used)
1194 #Returns:
1196 # nothing
1197 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1198 # recreate the auto_profile_order
1199 auto_profile_order = []
1200 piter = self.pstore.get_iter_first()
1201 while piter:
1202 # only if it's known
1203 if self.pstore.get_value( piter, 2 ) == True:
1204 row_start = str(self.pstore.get_value( piter, 0 )).split("\n")
1205 auto_profile_order.append( make_section_name( row_start[0], row_start[1] ) )
1206 piter = self.pstore.iter_next( piter )
1207 self.confFile.auto_profile_order = auto_profile_order
1209 # The preferences dialog. Edits some items in the DEFAULT section of the config file.
1210 class preferences_dialog:
1211 # Create a new preferences_dialog.
1213 #Parameters:
1215 # 'parent' -- gtk.Object - Usually, the calling window.
1217 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1219 #Returns:
1221 # preferences_dialog inistance
1222 def __init__( self, parent, confFile ):
1223 global wifi_radar_icon
1224 self.parent = parent
1225 self.confFile = confFile
1226 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1227 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1228 ( gtk.STOCK_CLOSE, True ) )
1229 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1230 self.dialog.set_icon( icon )
1231 self.dialog.set_resizable( True )
1232 self.dialog.set_transient_for( self.parent.window )
1233 self.tooltips = gtk.Tooltips()
1235 # set up preferences widgets
1238 # auto detect wireless device
1239 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1241 print "prefs: ", self.confFile.get_opt('DEFAULT.interface')
1243 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1244 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1245 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1246 self.dialog.vbox.pack_start(self.w_auto_detect, False, False, 5)
1247 # network interface selecter
1248 self.w_interface = gtk.combo_box_entry_new_text()
1249 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE).stdout
1250 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1251 for device in wireless_devices:
1252 if device != self.confFile.get_opt('DEFAULT.interface'):
1253 self.w_interface.append_text(device)
1254 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1255 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1256 self.w_interface.set_active(0)
1257 self.w_interface_label = gtk.Label("Wireless device")
1258 self.w_hbox1 = gtk.HBox(False, 0)
1259 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1260 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1261 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1262 self.dialog.vbox.pack_start(self.w_hbox1, False, False, 5)
1263 # scan timeout (spin button of integers from 1 to 100)
1264 self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
1265 self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1266 self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1267 self.w_scan_timeout.set_numeric(True)
1268 self.w_scan_timeout.set_snap_to_ticks(True)
1269 self.w_scan_timeout.set_wrap(False)
1270 self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1271 self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1272 self.w_hbox2 = gtk.HBox(False, 0)
1273 self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1274 self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1275 self.dialog.vbox.pack_start(self.w_hbox2, False, False, 5)
1276 # speak up
1277 self.w_speak_up = gtk.CheckButton("Use speak-up")
1278 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1279 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1280 self.dialog.vbox.pack_start(self.w_speak_up, False, False, 5)
1281 # commit required
1282 self.w_commit_required = gtk.CheckButton("Commit required")
1283 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1284 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1285 self.dialog.vbox.pack_start(self.w_commit_required, False, False, 5)
1286 # ifup required
1287 self.w_ifup_required = gtk.CheckButton("Ifup required")
1288 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1289 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1290 self.dialog.vbox.pack_start(self.w_ifup_required, False, False, 5)
1292 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1294 #Parameters:
1296 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1298 # 'data' -- tuple - list of arbitrary arguments (not used)
1300 #Returns:
1302 # nothing
1303 def toggle_auto_detect(self, auto_detect_toggle, data=None):
1304 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
1306 # Display preferences dialog and operate until canceled or okayed.
1308 #Parameters:
1310 # nothing
1312 #Returns:
1314 # integer -- gtk response ID
1315 def run(self):
1316 self.dialog.show_all()
1317 return self.dialog.run()
1319 # Write updated values to config file and remove preferences window.
1321 #Parameters:
1323 # nothing
1325 #Returns:
1327 # nothing
1328 def destroy(self):
1329 if self.w_auto_detect.get_active():
1330 set_network_device("auto_detect")
1331 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
1332 else:
1333 set_network_device(self.w_interface.child.get_text())
1334 self.confFile.set_opt('DEFAULT.interface', self.w_interface.child.get_text())
1335 # reconfigure the dhcp commands in case the network device was changed
1336 self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
1337 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
1338 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
1339 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
1340 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
1341 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'up'])
1342 self.confFile.write()
1343 self.dialog.destroy()
1344 del self.dialog
1347 # Edit and return an AP profile.
1348 class profile_dialog:
1349 # Create a new profile_dialog.
1351 #Parameters:
1353 # 'parent' -- gtk.Object - Usually, the calling window.
1355 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
1357 #Returns:
1359 # profile_dialog inistance
1360 def __init__( self, parent, profile ):
1361 global wifi_radar_icon
1362 self.parent = parent
1363 self.profile = profile
1364 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1365 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
1366 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1367 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
1368 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1369 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
1370 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1371 self.dialog.set_icon( icon )
1372 self.dialog.set_resizable( False )
1373 self.dialog.set_transient_for( self.parent.window )
1374 #self.dialog.set_size_request( 400, 400 )
1375 #################
1376 essid_table = gtk.Table( 1, 2, False )
1377 essid_table.set_row_spacings( 3 )
1378 essid_table.set_col_spacings( 3 )
1380 # The essid labels
1381 essid_table.attach( gtk.Label( 'Network Name' ), 0, 1, 0, 1 )
1382 # The essid textboxes
1383 self.essid_entry = gtk.Entry( 32 )
1384 self.essid_entry.set_text( self.profile['essid'] )
1385 essid_table.attach( self.essid_entry, 1, 2, 0, 1 )
1386 # Add the essid table to the dialog
1387 self.dialog.vbox.pack_start( essid_table, True, True, 5 )
1389 bssid_table = gtk.Table( 1, 2, False )
1390 bssid_table.set_row_spacings( 3 )
1391 bssid_table.set_col_spacings( 3 )
1392 # The bssid labels
1393 bssid_table.attach( gtk.Label( 'Network bssid' ), 0, 1, 0, 1 )
1394 # The bssid textboxes
1395 self.bssid_entry = gtk.Entry( 32 )
1396 self.bssid_entry.set_text( self.profile['bssid'] )
1397 bssid_table.attach( self.bssid_entry, 1, 2, 0, 1 )
1398 #self.key = gtk.Entry( 32 )
1399 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1400 # Add the bssid table to the dialog
1401 self.dialog.vbox.pack_start( bssid_table, True, True, 5 )
1403 # create the wifi expander
1404 self.wifi_expander = gtk.Expander( WIFI_SET_LABEL )
1405 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1406 wifi_table = gtk.Table( 4, 2, False )
1407 wifi_table.set_row_spacings( 3 )
1408 wifi_table.set_col_spacings( 3 )
1409 # The Wifi labels
1410 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
1411 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
1412 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
1413 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
1414 # The Wifi text boxes
1415 self.mode_combo = gtk.combo_box_new_text()
1416 for mode in self.WIFI_MODES:
1417 self.mode_combo.append_text( mode )
1418 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
1419 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
1420 self.channel_combo = gtk.combo_box_new_text()
1421 for channel in self.WIFI_CHANNELS:
1422 self.channel_combo.append_text( channel )
1423 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
1424 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
1426 self.key_entry = gtk.Entry( 64 )
1427 self.key_entry.set_text( self.profile['key'] )
1428 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
1430 self.security_combo = gtk.combo_box_new_text()
1431 for security in self.WIFI_SECURITY:
1432 self.security_combo.append_text( security )
1433 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
1434 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
1435 # Add the wifi table to the expander
1436 self.wifi_expander.add( wifi_table )
1437 # Add the expander to the dialog
1438 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
1440 # create the wpa expander
1441 self.wpa_expander = gtk.Expander( NO_WPA_LABEL )
1442 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
1443 wpa_table = gtk.Table( 1, 2, False )
1444 wpa_table.set_row_spacings( 3 )
1445 wpa_table.set_col_spacings( 3 )
1446 # The labels
1447 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1448 # The text boxes
1449 self.wpa_driver_entry = gtk.Entry()
1450 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
1451 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
1452 # Add the wpa table to the expander
1453 self.wpa_expander.add( wpa_table )
1454 # Add the expander to the dialog
1455 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
1457 # create the dhcp expander
1458 self.dhcp_expander = gtk.Expander( USE_DHCP_LABEL )
1459 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1460 ip_table = gtk.Table( 6, 2, False )
1461 ip_table.set_row_spacings( 3 )
1462 ip_table.set_col_spacings( 3 )
1463 # The IP labels
1464 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
1465 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
1466 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
1467 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
1468 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
1469 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
1470 # The IP text boxes
1471 self.ip_entry = gtk.Entry( 15 )
1472 self.ip_entry.set_text( self.profile['ip'] )
1473 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
1474 self.netmask_entry = gtk.Entry( 15 )
1475 self.netmask_entry.set_text( self.profile['netmask'] )
1476 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
1477 self.gw_entry = gtk.Entry( 15 )
1478 self.gw_entry.set_text( self.profile['gateway'] )
1479 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
1480 self.domain_entry = gtk.Entry( 32 )
1481 self.domain_entry.set_text( self.profile['domain'] )
1482 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
1483 self.dns1_entry = gtk.Entry( 15 )
1484 self.dns1_entry.set_text( self.profile['dns1'] )
1485 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
1486 self.dns2_entry = gtk.Entry( 15 )
1487 self.dns2_entry.set_text( self.profile['dns2'] )
1488 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
1489 # Add the ip table to the expander
1490 self.dhcp_expander.add( ip_table )
1491 # Add the expander to the dialog
1492 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
1494 # create the connection-building postpre expander
1495 self.con_pp_expander = gtk.Expander( CON_PP_LABEL )
1496 con_pp_table = gtk.Table( 2, 2, False )
1497 con_pp_table.set_row_spacings( 3 )
1498 con_pp_table.set_col_spacings( 3 )
1499 # The labels
1500 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1501 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1502 # The text boxes
1503 self.con_prescript_entry = gtk.Entry()
1504 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
1505 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
1506 self.con_postscript_entry = gtk.Entry()
1507 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
1508 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
1509 # Add the pp table to the expander
1510 self.con_pp_expander.add( con_pp_table )
1511 # Add the expander to the dialog
1512 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
1514 # create the disconnection postpre expander
1515 self.dis_pp_expander = gtk.Expander( DIS_PP_LABEL )
1516 dis_pp_table = gtk.Table( 2, 2, False )
1517 dis_pp_table.set_row_spacings( 3 )
1518 dis_pp_table.set_col_spacings( 3 )
1519 # The labels
1520 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1521 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1522 # The text boxes
1523 self.dis_prescript_entry = gtk.Entry()
1524 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
1525 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
1526 self.dis_postscript_entry = gtk.Entry()
1527 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
1528 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
1529 # Add the pp table to the expander
1530 self.dis_pp_expander.add( dis_pp_table )
1531 # Add the expander to the dialog
1532 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
1534 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
1536 #Parameters:
1538 # nothing
1540 #Returns:
1542 # dictionary or None -- a profile, or None on cancel
1543 def run( self ):
1544 self.dialog.show_all()
1545 if self.dialog.run():
1546 self.profile['known'] = True
1547 self.profile['essid'] = self.essid_entry.get_text().strip()
1548 self.profile['bssid'] = self.bssid_entry.get_text().strip()
1549 self.profile['key'] = self.key_entry.get_text().strip()
1550 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
1551 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
1552 self.profile['encrypted'] = ( self.profile['security'] != '' )
1553 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
1554 self.profile['protocol'] = 'g'
1555 self.profile['available'] = ( self.profile['signal'] > 0 )
1556 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
1557 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
1558 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
1559 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
1560 # wpa
1561 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
1562 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
1563 # dhcp
1564 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
1565 self.profile['ip'] = self.ip_entry.get_text().strip()
1566 self.profile['netmask'] = self.netmask_entry.get_text().strip()
1567 self.profile['gateway'] = self.gw_entry.get_text().strip()
1568 self.profile['domain'] = self.domain_entry.get_text().strip()
1569 self.profile['dns1'] = self.dns1_entry.get_text().strip()
1570 self.profile['dns2'] = self.dns2_entry.get_text().strip()
1571 return self.profile
1572 return None
1574 # Remove profile dialog.
1576 #Parameters:
1578 # nothing
1580 #Returns:
1582 # nothing
1583 def destroy( self ):
1584 self.dialog.destroy()
1585 del self.dialog
1587 # Respond to expanding/hiding IP segment.
1589 #Parameters:
1591 # 'widget' -- gtk.Widget - The widget sending the event.
1593 # 'data' -- tuple - List of arbitrary arguments (not used)
1595 #Returns:
1597 # nothing
1598 def toggle_use_dhcp( self, widget, data = None ):
1599 expanded = self.dhcp_expander.get_expanded()
1600 if expanded:
1601 self.dhcp_expander.set_label( USE_IP_LABEL )
1602 else:
1603 self.dhcp_expander.set_label( USE_DHCP_LABEL )
1605 def toggle_use_wpa( self, widget, data = None ):
1606 expanded = self.wpa_expander.get_expanded()
1607 if expanded:
1608 self.wpa_expander.set_label( USE_WPA_LABEL )
1609 else:
1610 self.wpa_expander.set_label( NO_WPA_LABEL )
1612 def get_array_index( self, item, array ):
1613 try:
1614 return array.index( item.strip() )
1615 except:
1616 pass
1617 return 0
1619 def get_array_item( self, index, array ):
1620 try:
1621 return array[ index ]
1622 except:
1623 pass
1624 return ''
1626 ### status_window
1628 ### A simple class for putting up a "Please wait" dialog so the user
1629 ### doesn't think we've forgotten about them.
1630 class status_window:
1631 def __init__( self, parent ):
1632 global wifi_radar_icon
1633 self.parent = parent
1634 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
1635 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1636 self.dialog.set_icon( icon )
1637 self.lbl = gtk.Label("Please wait...")
1638 self.bar = gtk.ProgressBar()
1639 self.dialog.vbox.pack_start(self.lbl)
1640 self.dialog.vbox.pack_start(self.bar)
1641 self.dialog.show_all()
1643 def update_message( self, message ):
1644 self.lbl.set_text(message)
1646 def inhibit_close( self, widget, signal ):
1647 return True
1649 def update_window( self ):
1650 # Do stuff
1651 self.bar.pulse()
1652 return True
1654 def run( self ):
1655 self.dialog.show_all()
1656 self.timer = gobject.timeout_add(250,self.update_window)
1657 return
1659 def destroy( self ):
1660 gobject.source_remove(self.timer)
1661 self.dialog.destroy()
1662 del self.dialog
1664 ### ConfigFile
1665 ### class to manage the configuration file
1666 class ConfigFile(ConfigParser.SafeConfigParser):
1667 """ class to manage the configuration file """
1668 def __init__( self, filename, defaults ):
1669 self.filename = filename
1670 self.auto_profile_order = []
1671 ConfigParser.SafeConfigParser.__init__(self, defaults)
1673 def set_section( self, section_name, section_dict ):
1674 """ set the contents of a section to values from a dictionary """
1675 try:
1676 self.add_section(section_name)
1677 except ConfigParser.DuplicateSectionError:
1678 pass
1679 for key in section_dict.keys():
1680 if type(section_dict[key]) == BooleanType:
1681 self.set_bool_opt(section_name + "." + key, section_dict[key])
1682 elif type(section_dict[key]) == IntType:
1683 self.set_int_opt(section_name + "." + key, section_dict[key])
1684 elif type(section_dict[key]) == FloatType:
1685 self.set_float_opt(section_name + "." + key, section_dict[key])
1686 else:
1687 self.set_opt(section_name + "." + key, section_dict[key])
1689 def get_profile( self, section_name ):
1690 """ return the profile recorded in the specified section """
1691 if section_name in self.profiles():
1692 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' ]
1693 bool_types = [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
1694 int_types = [ 'signal' ]
1695 profile = {}
1696 for option in bool_types:
1697 profile[option] = self.get_opt_as_bool( section_name + "." + option )
1698 for option in int_types:
1699 profile[option] = self.get_opt_as_int( section_name + "." + option )
1700 for option in str_types:
1701 profile[option] = self.get_opt( section_name + "." + option )
1702 return profile
1703 return None
1705 def get_opt( self, option_path ):
1706 """ get a config option and handle exceptions """
1707 #print "ConfigFile.get_opt: ", option_path
1708 (section, option) = option_path.split('.')
1709 try:
1710 return self.get(section, option)
1711 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
1712 return None
1714 def get_opt_as_bool( self, option_path ):
1715 """ get a config option and return as a boolean type """
1716 option = self.get_opt(option_path)
1717 if isinstance(option, BooleanType) or isinstance(option, NoneType):
1718 return option
1719 if option == 'True':
1720 return True
1721 if option == 'False':
1722 return False
1723 raise ValueError, 'boolean option was not True or False'
1725 def get_opt_as_int( self, option_path ):
1726 """ get a config option and return as an integer type """
1727 return int(float(self.get_opt(option_path)))
1729 def set_bool_opt( self, option_path, value ):
1730 """ convert boolean type to string and set config option """
1731 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
1732 value == 'True'
1733 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
1734 value == 'False'
1735 else:
1736 raise ValueError, 'cannot convert value to string'
1737 self.set_opt(option_path, repr(value))
1739 def set_int_opt( self, option_path, value ):
1740 """ convert integer type to string and set config option """
1741 if not isinstance(value, IntType):
1742 raise ValueError, 'value is not an integer'
1743 self.set_opt(option_path, repr(value))
1745 def set_float_opt( self, option_path, value ):
1746 """ convert float type to string and set config option """
1747 if not isinstance(value, FloatType):
1748 raise ValueError, 'value is not a float'
1749 self.set_opt(option_path, repr(int(value)))
1751 def set_opt( self, option_path, value ):
1752 """ set a config option and handle exceptions """
1753 (section, option) = option_path.split('.')
1754 try:
1755 self.set(section, option, value)
1756 except ConfigParser.NoSectionError:
1757 self.add_section(section)
1758 self.set_opt(option_path, value)
1760 def profiles( self ):
1761 """ return a list of the section names which denote AP profiles """
1762 profile_list = []
1763 for section in self.sections():
1764 if ':' in section:
1765 profile_list.append(section)
1766 return profile_list
1768 def read ( self ):
1769 fp = open( self.filename, "r" )
1770 self.readfp(fp)
1771 # convert the auto_profile_order to a list for ordering
1772 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
1773 for ap in self.profiles():
1774 self.set_bool_opt( ap + '.known', True)
1775 if ap in self.auto_profile_order: continue
1776 self.auto_profile_order.append( ap )
1778 def write( self ):
1779 """ Copied from ConfigParser and modified to write options in alphabetical order """
1780 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
1781 fp = open( self.filename, "w" )
1782 # write DEFAULT section first
1783 if self._defaults:
1784 fp.write("[DEFAULT]\n")
1785 for key in sorted(self._defaults.keys()):
1786 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
1787 fp.write("\n")
1788 # write non-profile sections first
1789 for section in self._sections:
1790 if not section in self.profiles():
1791 fp.write("[%s]\n" % section)
1792 for key in sorted(self._sections[section].keys()):
1793 if key != "__name__":
1794 fp.write("%s = %s\n" %
1795 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
1796 fp.write("\n")
1797 # write profile sections
1798 for section in self._sections:
1799 if section in self.profiles():
1800 fp.write("[%s]\n" % section)
1801 for key in sorted(self._sections[section].keys()):
1802 if key != "__name__":
1803 fp.write("%s = %s\n" %
1804 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
1805 fp.write("\n")
1809 # load our conf file and known profiles
1810 ####################################################################################################
1811 # Defaults, these may get overridden by values found in the conf file.
1812 # The network interface you use.
1813 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
1814 config_defaults = { 'interface': "auto_detect",
1815 # How long should the scan for access points last?
1816 'scan_timeout': '5',
1817 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
1818 # Set the speak_up option to false if you do not have or want this.
1819 'speak_command': '/usr/bin/say',
1820 # Should I speak up when connecting to a network? (If you have a speech command)
1821 'speak_up': 'False',
1822 # You may set this to true for cards that require a "commit" command with iwconfig
1823 'commit_required': 'False',
1824 # You may set this to true for cards that require the interface to be brought up first
1825 'ifup_required': 'False',
1826 # Set the location of several important programs
1827 'iwlist_command': '/sbin/iwlist',
1828 'iwconfig_command': '/sbin/iwconfig',
1829 'ifconfig_command': '/sbin/ifconfig',
1830 'route_command': '/sbin/route',
1831 'auto_profile_order': '[]',
1832 'version': WIFI_RADAR_VERSION }
1834 config_dhcp = { 'command': 'dhcpcd',
1835 'timeout': '30',
1836 'args': '-D -o -i dhcp_client -t %(timeout)s',
1837 'kill_args': '-k',
1838 'pidfile': '/var/run/dhcpcd-%(interface)s.pid' }
1840 config_wpa = { 'command': '/usr/sbin/wpa_supplicant',
1841 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
1842 'kill_command': '',
1843 'configuration': '/etc/wpa_supplicant.conf',
1844 'driver': 'wext',
1845 'pidfile': '/var/run/wpa_supplicant.pid' }
1847 # initialize config, with defaults
1848 confFile = ConfigFile(CONF_FILE, config_defaults)
1849 confFile.set_section("DHCP", config_dhcp)
1850 confFile.set_section("WPA", config_wpa)
1852 if not os.path.isfile( CONF_FILE ):
1853 confFile.set_bool_opt('DEFAULT.new_file', True)
1854 else:
1855 if not os.access(CONF_FILE, os.R_OK):
1856 print "Can't open " + CONF_FILE + "."
1857 print "Are you root?"
1858 sys.exit()
1859 confFile.read()
1862 # First, we add our known profiles
1863 for apname in confFile.profiles():
1864 ap = get_new_profile()
1865 (ap['essid'], ap['bssid']) = split_section_name( apname )
1866 # read the important values
1867 for key in ap.keys():
1868 ap[key] = confFile.get_opt(apname + "." + key)
1869 # if it is not in the auto_profile_order add it
1870 if not apname in confFile.auto_profile_order:
1871 confFile.auto_profile_order.append( apname )
1873 #set_network_device(confFile.get_opt('DEFAULT.interface'))
1875 ####################################################################################################
1876 # Embedded Images
1877 wifi_radar_icon = [ ""
1878 "GdkP"
1879 "\0\0\22""7"
1880 "\2\1\0\2"
1881 "\0\0\1\214"
1882 "\0\0\0c"
1883 "\0\0\0O"
1884 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
1885 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
1886 "\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"
1887 "\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"
1888 "\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"
1889 "\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"
1890 "\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"
1891 "\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"
1892 "\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"
1893 "\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"
1894 "\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"
1895 "\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"
1896 "\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"
1897 "\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"
1898 "\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"
1899 "\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"
1900 "\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"
1901 "\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"
1902 "\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"
1903 "\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"
1904 "\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"
1905 "\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"
1906 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
1907 "\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"
1908 "\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"
1909 "\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"
1910 "\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"
1911 "\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"
1912 "\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"
1913 "\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"
1914 "\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"
1915 "\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"
1916 "\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"
1917 "\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"
1918 "\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"
1919 "\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"
1920 "\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"
1921 "\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"
1922 "\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"
1923 "\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"
1924 "\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"
1925 "\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"
1926 "\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"
1927 "\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"
1928 "\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"
1929 "\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"
1930 "\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"
1931 "\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"
1932 "\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"
1933 "\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"
1934 "\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"
1935 "\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"
1936 "\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"
1937 "\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"
1938 "\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"
1939 "\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"
1940 "\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"
1941 "\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"
1942 "\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"
1943 "\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"
1944 "\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"
1945 "\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"
1946 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
1947 "\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"
1948 "\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"
1949 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
1950 "\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"
1951 "\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"
1952 "\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"
1953 "\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"
1954 "\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"
1955 "\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"
1956 "\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"
1957 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
1958 "\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"
1959 "\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"
1960 "\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"
1961 "\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"
1962 "\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"
1963 "\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"
1964 "|\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"
1965 "\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"
1966 "\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"
1967 "\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"
1968 "\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"
1969 "\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"
1970 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
1971 "\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"
1972 "\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"
1973 "\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"
1974 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
1975 "\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"
1976 "\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"
1977 "\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"
1978 "\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"
1979 "\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"
1980 "\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"
1981 "\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"
1982 "\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"
1983 "\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"
1984 "\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"
1985 "\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"
1986 "\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"
1987 "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"
1988 "\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"
1989 "\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"
1990 "\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"
1991 "\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"
1992 "\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"
1993 "\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"
1994 "\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"
1995 "\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"
1996 "\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"
1997 "\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"
1998 "\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"
1999 "\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"
2000 "\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"
2001 "\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"
2002 "\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|"
2003 "\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"
2004 "\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"
2005 "\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"
2006 "\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"
2007 "\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"
2008 "\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"
2009 "\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"
2010 "\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"
2011 "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"
2012 "\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"
2013 "\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"
2014 "\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"
2015 "\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"
2016 "\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"
2017 "\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"
2018 "\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"
2019 "\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"
2020 "\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"
2021 "\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"
2022 "\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"
2023 "\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"
2024 "\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"
2025 "\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"
2026 "\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"
2027 "\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"
2028 "\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"
2029 "\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"
2030 "\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"
2031 "\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"
2032 "\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"
2033 "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"
2034 "\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"
2035 "\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"
2036 "\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"
2037 "\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"
2038 "\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"
2039 "\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"
2040 "\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"
2041 "\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"
2042 "\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"
2043 "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"
2044 "\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"
2045 "\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"
2046 "\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"
2047 "\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"
2048 "\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"
2049 "\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"
2050 "\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"
2051 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2052 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2053 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2054 "\0"]
2056 known_profile_icon = [ ""
2057 "GdkP"
2058 "\0\0\5""0"
2059 "\2\1\0\2"
2060 "\0\0\0P"
2061 "\0\0\0\24"
2062 "\0\0\0\24"
2063 "\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"
2064 "\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"
2065 "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"
2066 "\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"
2067 "\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"
2068 "\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"
2069 "\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"
2070 "\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"
2071 "\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"
2072 "\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"
2073 "\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"
2074 "\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"
2075 "\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"
2076 "\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"
2077 "\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"
2078 "\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"
2079 "\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"
2080 "\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"
2081 "\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"
2082 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2083 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2084 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2085 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2086 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2087 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2088 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2089 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2090 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2091 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2092 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2093 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2094 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2095 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2096 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2097 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2098 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2099 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2100 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2101 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2102 "\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"
2103 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2104 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2105 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2106 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2107 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2108 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2109 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2110 "\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"
2111 "\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"
2112 "\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"
2113 "\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"
2114 "\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"]
2116 unknown_profile_icon = [ ""
2117 "GdkP"
2118 "\0\0\5\22"
2119 "\2\1\0\2"
2120 "\0\0\0P"
2121 "\0\0\0\24"
2122 "\0\0\0\24"
2123 "\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"
2124 "\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"
2125 "\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"
2126 "\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"
2127 "(\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"
2128 "\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"
2129 "#\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"
2130 "\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"
2131 "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"
2132 "\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"
2133 "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"
2134 "\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"
2135 "\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"
2136 "\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"
2137 "\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"
2138 "\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"
2139 "\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"
2140 "\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"
2141 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2142 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2143 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2144 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2145 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2146 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2147 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2148 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2149 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2150 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2151 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2152 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2153 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2154 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2155 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2156 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2157 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2158 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2159 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2160 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2161 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2162 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2163 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2164 "\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"
2165 "\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"
2166 "\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"
2167 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2169 signal_xpm_barely = [
2170 "20 20 10 1",
2171 " c None",
2172 ". c #C6C6C6",
2173 "+ c #CCCCCC",
2174 "@ c #DBDBDB",
2175 "# c #D3D3D3",
2176 "$ c #A9B099",
2177 "% c #95A173",
2178 "& c #6B8428",
2179 "* c #B4B7AC",
2180 "= c #80924D",
2181 " .+++.",
2182 " +@@@+",
2183 " +@@@+",
2184 " +@@@+",
2185 " +@@@+",
2186 " .++++#@@@+",
2187 " +@@@@@@@@+",
2188 " +@@@@@@@@+",
2189 " +@@@@@@@@+",
2190 " +@@@@@@@@+",
2191 " $%%%%#@@@@@@@@+",
2192 " %&&&&@@@@@@@@@+",
2193 " %&&&&@@@@@@@@@+",
2194 " %&&&&@@@@@@@@@+",
2195 " %&&&&@@@@@@@@@+",
2196 "*%%%%=&&&&@@@@@@@@@+",
2197 "%&&&&&&&&&@@@@@@@@@+",
2198 "%&&&&&&&&&@@@@@@@@@+",
2199 "%&&&&&&&&&@@@@@@@@@+",
2200 "*%%%%%%%%%+++++++++."
2204 signal_xpm_best = [
2205 "20 20 6 1",
2206 " c None",
2207 ". c #9DAABF",
2208 "+ c #7B96BF",
2209 "@ c #386EBF",
2210 "# c #5982BF",
2211 "$ c #AEB4BF",
2212 " .+++.",
2213 " +@@@+",
2214 " +@@@+",
2215 " +@@@+",
2216 " +@@@+",
2217 " .++++#@@@+",
2218 " +@@@@@@@@+",
2219 " +@@@@@@@@+",
2220 " +@@@@@@@@+",
2221 " +@@@@@@@@+",
2222 " .++++#@@@@@@@@+",
2223 " +@@@@@@@@@@@@@+",
2224 " +@@@@@@@@@@@@@+",
2225 " +@@@@@@@@@@@@@+",
2226 " +@@@@@@@@@@@@@+",
2227 "$++++#@@@@@@@@@@@@@+",
2228 "+@@@@@@@@@@@@@@@@@@+",
2229 "+@@@@@@@@@@@@@@@@@@+",
2230 "+@@@@@@@@@@@@@@@@@@+",
2231 "$++++++++++++++++++."
2234 signal_xpm_none = [
2235 "20 20 6 1",
2236 " c None",
2237 ". c #C6C6C6",
2238 "+ c #CCCCCC",
2239 "@ c #DBDBDB",
2240 "# c #D3D3D3",
2241 "$ c #C2C2C2",
2242 " .+++.",
2243 " +@@@+",
2244 " +@@@+",
2245 " +@@@+",
2246 " +@@@+",
2247 " .++++#@@@+",
2248 " +@@@@@@@@+",
2249 " +@@@@@@@@+",
2250 " +@@@@@@@@+",
2251 " +@@@@@@@@+",
2252 " .++++#@@@@@@@@+",
2253 " +@@@@@@@@@@@@@+",
2254 " +@@@@@@@@@@@@@+",
2255 " +@@@@@@@@@@@@@+",
2256 " +@@@@@@@@@@@@@+",
2257 "$++++#@@@@@@@@@@@@@+",
2258 "+@@@@@@@@@@@@@@@@@@+",
2259 "+@@@@@@@@@@@@@@@@@@+",
2260 "+@@@@@@@@@@@@@@@@@@+",
2261 "$++++++++++++++++++."
2264 signal_xpm_ok = [
2265 "20 20 10 1",
2266 " c None",
2267 ". c #C6C6C6",
2268 "+ c #CCCCCC",
2269 "@ c #DBDBDB",
2270 "# c #A1A5B2",
2271 "$ c #848DA5",
2272 "% c #D3D3D3",
2273 "& c #4A5B8C",
2274 "* c #677498",
2275 "= c #B0B2B8",
2276 " .+++.",
2277 " +@@@+",
2278 " +@@@+",
2279 " +@@@+",
2280 " +@@@+",
2281 " #$$$$%@@@+",
2282 " $&&&&@@@@+",
2283 " $&&&&@@@@+",
2284 " $&&&&@@@@+",
2285 " $&&&&@@@@+",
2286 " #$$$$*&&&&@@@@+",
2287 " $&&&&&&&&&@@@@+",
2288 " $&&&&&&&&&@@@@+",
2289 " $&&&&&&&&&@@@@+",
2290 " $&&&&&&&&&@@@@+",
2291 "=$$$$*&&&&&&&&&@@@@+",
2292 "$&&&&&&&&&&&&&&@@@@+",
2293 "$&&&&&&&&&&&&&&@@@@+",
2294 "$&&&&&&&&&&&&&&@@@@+",
2295 "=$$$$$$$$$$$$$$++++."
2299 signal_xpm_low = [
2300 "20 20 8 1",
2301 " c None",
2302 ". c #C6C6C6",
2303 "+ c #CCCCCC",
2304 "@ c #DBDBDB",
2305 "# c #D3D3D3",
2306 "$ c #BFB0B5",
2307 "% c #C18799",
2308 "& c #C54F74",
2309 " .+++.",
2310 " +@@@+",
2311 " +@@@+",
2312 " +@@@+",
2313 " +@@@+",
2314 " .++++#@@@+",
2315 " +@@@@@@@@+",
2316 " +@@@@@@@@+",
2317 " +@@@@@@@@+",
2318 " +@@@@@@@@+",
2319 " .++++#@@@@@@@@+",
2320 " +@@@@@@@@@@@@@+",
2321 " +@@@@@@@@@@@@@+",
2322 " +@@@@@@@@@@@@@+",
2323 " +@@@@@@@@@@@@@+",
2324 "$%%%%#@@@@@@@@@@@@@+",
2325 "%&&&&@@@@@@@@@@@@@@+",
2326 "%&&&&@@@@@@@@@@@@@@+",
2327 "%&&&&@@@@@@@@@@@@@@+",
2328 "$%%%%++++++++++++++."
2330 signal_none_pb = None
2331 signal_low_pb = None
2332 signal_barely_pb= None
2333 signal_ok_pb = None
2334 signal_best_pb = None
2336 ####################################################################################################
2337 # Make so we can be imported
2338 if __name__ == "__main__":
2339 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
2340 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
2341 else:
2342 import gtk, gobject
2343 gtk.gdk.threads_init()
2344 apQueue = Queue.Queue(100)
2345 commQueue = Queue.Queue(2)
2346 threading.Thread( None, scanning_thread, None, ( confFile, apQueue, commQueue ) ).start()
2347 main_radar_window = radar_window(confFile, apQueue, commQueue)
2348 gobject.timeout_add( 500, main_radar_window.update_plist_items )
2349 main_radar_window.main()