Comment out redundant code for setting up auto profile order
[wifi-radar.git] / wifi-radar
blob4f09f195f15e84a132807c5d8b4051272f043044
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 instance
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 instance
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 # Respond to expanding/hiding WPA segment.
1607 #Parameters:
1609 # 'widget' -- gtk.Widget - The widget sending the event.
1611 # 'data' -- tuple - List of arbitrary arguments (not used)
1613 #Returns:
1615 # nothing
1616 def toggle_use_wpa( self, widget, data = None ):
1617 expanded = self.wpa_expander.get_expanded()
1618 if expanded:
1619 self.wpa_expander.set_label( USE_WPA_LABEL )
1620 else:
1621 self.wpa_expander.set_label( NO_WPA_LABEL )
1623 # Return the index where item matches a cell in array.
1625 #Parameters:
1627 # 'item' -- string - Item to find in array
1629 # 'array' -- list - List in which to find match.
1631 #Returns:
1633 # integer - 0 (no match) or higher (index of match)
1634 def get_array_index( self, item, array ):
1635 try:
1636 return array.index( item.strip() )
1637 except:
1638 pass
1639 return 0
1641 # Return the value in array[ index ]
1643 #Parameters:
1645 # 'index' -- integer - The index to look up.
1647 # 'array' -- list - List in which to look up value.
1649 #Returns:
1651 # string -- empty string (no match) or looked up value
1652 def get_array_item( self, index, array ):
1653 try:
1654 return array[ index ]
1655 except:
1656 pass
1657 return ''
1659 # A simple class for putting up a "Please wait" dialog so the user
1660 # doesn't think we've forgotten about them.
1661 class status_window:
1662 # Create a new status_window.
1664 #Parameters:
1666 # 'parent' -- gtk.Object - Usually, the calling window.
1668 #Returns:
1670 # status_window instance
1671 def __init__( self, parent ):
1672 global wifi_radar_icon
1673 self.parent = parent
1674 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
1675 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1676 self.dialog.set_icon( icon )
1677 self.lbl = gtk.Label("Please wait...")
1678 self.bar = gtk.ProgressBar()
1679 self.dialog.vbox.pack_start(self.lbl)
1680 self.dialog.vbox.pack_start(self.bar)
1681 self.dialog.show_all()
1683 # Change the message displayed to the user.
1685 #Parameters:
1687 # 'message' -- string - The message to show to the user.
1689 #Returns:
1691 # nothing
1692 def update_message( self, message ):
1693 self.lbl.set_text(message)
1695 # Update the status_window progress bar.
1697 #Parameters:
1699 # nothing
1701 #Returns:
1703 # True -- always return True
1704 def update_window( self ):
1705 self.bar.pulse()
1706 return True
1708 # Display and operate the status_window.
1710 #Parameters:
1712 # nothing
1714 #Returns:
1716 # nothing
1717 def run( self ):
1718 self.dialog.show_all()
1719 self.timer = gobject.timeout_add(250,self.update_window)
1720 return
1722 # Remove the status_window.
1724 #Parameters:
1726 # nothing
1728 #Returns:
1730 # nothing
1731 def destroy( self ):
1732 gobject.source_remove(self.timer)
1733 self.dialog.destroy()
1734 del self.dialog
1736 # Manage the configuration for the application, including reading and writing the config from/to a file.
1737 class ConfigFile(ConfigParser.SafeConfigParser):
1738 # Create a new ConfigFile.
1740 #Parameters:
1742 # 'filename' -- string - The configuration file's name.
1744 # 'defaults' -- dictionary - Default values for the DEFAULT section.
1746 #Returns:
1748 # ConfigFile instance
1749 def __init__( self, filename, defaults ):
1750 self.filename = filename
1751 self.auto_profile_order = []
1752 ConfigParser.SafeConfigParser.__init__(self, defaults)
1754 # Set the contents of a section to values from a dictionary.
1756 #Parameters:
1758 # 'section_name' -- string - Configuration file section.
1760 # 'section_dict' -- dictionary - Values to add to section.
1762 #Returns:
1764 # nothing
1765 def set_section( self, section_name, section_dict ):
1766 try:
1767 self.add_section(section_name)
1768 except ConfigParser.DuplicateSectionError:
1769 pass
1770 for key in section_dict.keys():
1771 if type(section_dict[key]) == BooleanType:
1772 self.set_bool_opt(section_name + "." + key, section_dict[key])
1773 elif type(section_dict[key]) == IntType:
1774 self.set_int_opt(section_name + "." + key, section_dict[key])
1775 elif type(section_dict[key]) == FloatType:
1776 self.set_float_opt(section_name + "." + key, section_dict[key])
1777 else:
1778 self.set_opt(section_name + "." + key, section_dict[key])
1780 # Return the profile recorded in the specified section.
1782 #Parameters:
1784 # 'section_name' -- string - Configuration file section.
1786 #Returns:
1788 # dictionary or None - The specified profile or None if not found
1789 def get_profile( self, section_name ):
1790 if section_name in self.profiles():
1791 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' ]
1792 bool_types = [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
1793 int_types = [ 'signal' ]
1794 profile = {}
1795 for option in bool_types:
1796 profile[option] = self.get_opt_as_bool( section_name + "." + option )
1797 for option in int_types:
1798 profile[option] = self.get_opt_as_int( section_name + "." + option )
1799 for option in str_types:
1800 profile[option] = self.get_opt( section_name + "." + option )
1801 return profile
1802 return None
1804 # Get a config option and handle exceptions.
1806 #Parameters:
1808 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1809 # period and the option key. (E.g. "DEFAULT.interface")
1811 #Returns:
1813 # string or None - option value as string or None on failure
1814 def get_opt( self, option_path ):
1815 #print "ConfigFile.get_opt: ", option_path
1816 (section, option) = option_path.split('.')
1817 try:
1818 return self.get(section, option)
1819 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
1820 return None
1822 # Get a config option and return as a boolean type.
1824 #Parameters:
1826 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1827 # period and the option key. (E.g. "DEFAULT.interface")
1829 #Returns:
1831 # boolean - option value as boolean
1832 def get_opt_as_bool( self, option_path ):
1833 option = self.get_opt(option_path)
1834 if isinstance(option, BooleanType) or isinstance(option, NoneType):
1835 return option
1836 if option == 'True':
1837 return True
1838 if option == 'False':
1839 return False
1840 raise ValueError, 'boolean option was not True or False'
1842 # Get a config option and return as an integer type.
1844 #Parameters:
1846 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1847 # period and the option key. (E.g. "DEFAULT.interface")
1849 #Returns:
1851 # integer- option value as integer
1852 def get_opt_as_int( self, option_path ):
1853 return int(float(self.get_opt(option_path)))
1855 # Convert boolean type to string and set config option.
1857 #Parameters:
1859 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1860 # period and the option key. (E.g. "DEFAULT.interface")
1862 # 'value' -- boolean - Value to set.
1864 #Returns:
1866 # nothing
1867 def set_bool_opt( self, option_path, value ):
1868 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
1869 value == 'True'
1870 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
1871 value == 'False'
1872 else:
1873 raise ValueError, 'cannot convert value to string'
1874 self.set_opt(option_path, repr(value))
1876 # Convert integer type to string and set config option.
1878 #Parameters:
1880 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1881 # period and the option key. (E.g. "DEFAULT.interface")
1883 # 'value' -- integer - Value to set.
1885 #Returns:
1887 # nothing
1888 def set_int_opt( self, option_path, value ):
1889 if not isinstance(value, IntType):
1890 raise ValueError, 'value is not an integer'
1891 self.set_opt(option_path, repr(value))
1893 # Convert float type to string and set config option.
1895 #Parameters:
1897 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1898 # period and the option key. (E.g. "DEFAULT.interface")
1900 # 'value' -- float - Value to set.
1902 #Returns:
1904 # nothing
1905 def set_float_opt( self, option_path, value ):
1906 if not isinstance(value, FloatType):
1907 raise ValueError, 'value is not a float'
1908 self.set_opt(option_path, repr(int(value)))
1910 # Set a config option while handling exceptions.
1912 #Parameters:
1914 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1915 # period and the option key. (E.g. "DEFAULT.interface")
1917 # 'value' -- string - Value to set.
1919 #Returns:
1921 # nothing
1922 def set_opt( self, option_path, value ):
1923 (section, option) = option_path.split('.')
1924 try:
1925 self.set(section, option, value)
1926 except ConfigParser.NoSectionError:
1927 self.add_section(section)
1928 self.set_opt(option_path, value)
1930 # Return a list of the section names which denote AP profiles.
1932 #Parameters:
1934 # nothing
1936 #Returns:
1938 # list - profile names
1939 def profiles( self ):
1940 profile_list = []
1941 for section in self.sections():
1942 if ':' in section:
1943 profile_list.append(section)
1944 return profile_list
1946 # Read configuration file from disk into instance variables.
1948 #Parameters:
1950 # nothing
1952 #Returns:
1954 # nothing
1955 def read( self ):
1956 fp = open( self.filename, "r" )
1957 self.readfp(fp)
1958 # convert the auto_profile_order to a list for ordering
1959 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
1960 for ap in self.profiles():
1961 self.set_bool_opt( ap + '.known', True)
1962 if ap in self.auto_profile_order: continue
1963 self.auto_profile_order.append( ap )
1965 # Write configuration file to disk from instance variables. Copied from
1966 # ConfigParser and modified to write options in alphabetical order.
1968 #Parameters:
1970 # nothing
1972 #Returns:
1974 # nothing
1975 def write( self ):
1976 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
1977 fp = open( self.filename, "w" )
1978 # write DEFAULT section first
1979 if self._defaults:
1980 fp.write("[DEFAULT]\n")
1981 for key in sorted(self._defaults.keys()):
1982 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
1983 fp.write("\n")
1984 # write non-profile sections first
1985 for section in self._sections:
1986 if not section in self.profiles():
1987 fp.write("[%s]\n" % section)
1988 for key in sorted(self._sections[section].keys()):
1989 if key != "__name__":
1990 fp.write("%s = %s\n" %
1991 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
1992 fp.write("\n")
1993 # write profile sections
1994 for section in self._sections:
1995 if section in self.profiles():
1996 fp.write("[%s]\n" % section)
1997 for key in sorted(self._sections[section].keys()):
1998 if key != "__name__":
1999 fp.write("%s = %s\n" %
2000 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2001 fp.write("\n")
2005 # Load our conf file and known profiles
2006 # Defaults, these may get overridden by values found in the conf file.
2007 config_defaults = { # The network interface you use.
2008 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2009 'interface': "auto_detect",
2010 # How long should the scan for access points last?
2011 'scan_timeout': '5',
2012 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2013 # Set the speak_up option to false if you do not have or want this.
2014 'speak_command': '/usr/bin/say',
2015 # Should I speak up when connecting to a network? (If you have a speech command)
2016 'speak_up': 'False',
2017 # You may set this to true for cards that require a "commit" command with iwconfig
2018 'commit_required': 'False',
2019 # You may set this to true for cards that require the interface to be brought up first
2020 'ifup_required': 'False',
2021 # Set the location of several important programs
2022 'iwlist_command': '/sbin/iwlist',
2023 'iwconfig_command': '/sbin/iwconfig',
2024 'ifconfig_command': '/sbin/ifconfig',
2025 'route_command': '/sbin/route',
2026 'auto_profile_order': '[]',
2027 'version': WIFI_RADAR_VERSION }
2029 config_dhcp = { # DHCP client
2030 'command': 'dhcpcd',
2031 # How long to wait for an IP addr from DHCP server
2032 'timeout': '30',
2033 # Arguments to use with DHCP client on connect
2034 'args': '-D -o -i dhcp_client -t %(timeout)s',
2035 # Argument to use with DHCP client on disconnect
2036 'kill_args': '-k',
2037 # The file where DHCP client PID is written
2038 'pidfile': '/etc/dhcpcd-%(interface)s.pid' }
2040 config_wpa = { # WPA Supplicant
2041 'command': '/usr/sbin/wpa_supplicant',
2042 # Arguments to use with WPA Supplicant on connect
2043 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2044 # Arguments to use with WPA Supplicant on disconnect
2045 'kill_command': '',
2046 # Where the WPA Supplicant config file can be found
2047 'configuration': '/etc/wpa_supplicant.conf',
2048 # Driver to use with WPA Supplicant
2049 'driver': 'wext',
2050 # The file where WPA Supplicant PID is written
2051 'pidfile': '/var/run/wpa_supplicant.pid' }
2053 # initialize config, with defaults
2054 confFile = ConfigFile(CONF_FILE, config_defaults)
2055 confFile.set_section("DHCP", config_dhcp)
2056 confFile.set_section("WPA", config_wpa)
2058 if not os.path.isfile( CONF_FILE ):
2059 confFile.set_bool_opt('DEFAULT.new_file', True)
2060 else:
2061 if not os.access(CONF_FILE, os.R_OK):
2062 print "Can't open " + CONF_FILE + "."
2063 print "Are you root?"
2064 sys.exit()
2065 confFile.read()
2068 # First, we add our known profiles
2069 #for apname in confFile.profiles():
2070 #ap = get_new_profile()
2071 #(ap['essid'], ap['bssid']) = split_section_name( apname )
2072 ## read the important values
2073 #for key in ap.keys():
2074 #ap[key] = confFile.get_opt(apname + "." + key)
2075 ## if it is not in the auto_profile_order add it
2076 #if not apname in confFile.auto_profile_order:
2077 #confFile.auto_profile_order.append( apname )
2079 #set_network_device(confFile.get_opt('DEFAULT.interface'))
2081 ####################################################################################################
2082 # Embedded Images
2083 wifi_radar_icon = [ ""
2084 "GdkP"
2085 "\0\0\22""7"
2086 "\2\1\0\2"
2087 "\0\0\1\214"
2088 "\0\0\0c"
2089 "\0\0\0O"
2090 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2091 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2092 "\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"
2093 "\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"
2094 "\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"
2095 "\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"
2096 "\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"
2097 "\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"
2098 "\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"
2099 "\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"
2100 "\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"
2101 "\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"
2102 "\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"
2103 "\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"
2104 "\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"
2105 "\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"
2106 "\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"
2107 "\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"
2108 "\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"
2109 "\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"
2110 "\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"
2111 "\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"
2112 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2113 "\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"
2114 "\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"
2115 "\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"
2116 "\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"
2117 "\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"
2118 "\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"
2119 "\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"
2120 "\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"
2121 "\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"
2122 "\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"
2123 "\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"
2124 "\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"
2125 "\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"
2126 "\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"
2127 "\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"
2128 "\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"
2129 "\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"
2130 "\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"
2131 "\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"
2132 "\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"
2133 "\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"
2134 "\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"
2135 "\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"
2136 "\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"
2137 "\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"
2138 "\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"
2139 "\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"
2140 "\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"
2141 "\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"
2142 "\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"
2143 "\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"
2144 "\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"
2145 "\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"
2146 "\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"
2147 "\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"
2148 "\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"
2149 "\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"
2150 "\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"
2151 "\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"
2152 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2153 "\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"
2154 "\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"
2155 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2156 "\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"
2157 "\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"
2158 "\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"
2159 "\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"
2160 "\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"
2161 "\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"
2162 "\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"
2163 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2164 "\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"
2165 "\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"
2166 "\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"
2167 "\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"
2168 "\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"
2169 "\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"
2170 "|\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"
2171 "\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"
2172 "\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"
2173 "\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"
2174 "\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"
2175 "\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"
2176 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2177 "\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"
2178 "\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"
2179 "\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"
2180 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
2181 "\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"
2182 "\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"
2183 "\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"
2184 "\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"
2185 "\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"
2186 "\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"
2187 "\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"
2188 "\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"
2189 "\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"
2190 "\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"
2191 "\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"
2192 "\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"
2193 "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"
2194 "\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"
2195 "\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"
2196 "\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"
2197 "\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"
2198 "\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"
2199 "\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"
2200 "\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"
2201 "\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"
2202 "\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"
2203 "\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"
2204 "\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"
2205 "\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"
2206 "\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"
2207 "\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"
2208 "\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|"
2209 "\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"
2210 "\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"
2211 "\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"
2212 "\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"
2213 "\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"
2214 "\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"
2215 "\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"
2216 "\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"
2217 "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"
2218 "\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"
2219 "\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"
2220 "\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"
2221 "\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"
2222 "\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"
2223 "\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"
2224 "\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"
2225 "\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"
2226 "\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"
2227 "\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"
2228 "\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"
2229 "\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"
2230 "\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"
2231 "\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"
2232 "\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"
2233 "\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"
2234 "\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"
2235 "\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"
2236 "\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"
2237 "\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"
2238 "\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"
2239 "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"
2240 "\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"
2241 "\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"
2242 "\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"
2243 "\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"
2244 "\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"
2245 "\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"
2246 "\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"
2247 "\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"
2248 "\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"
2249 "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"
2250 "\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"
2251 "\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"
2252 "\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"
2253 "\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"
2254 "\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"
2255 "\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"
2256 "\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"
2257 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2258 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2259 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2260 "\0"]
2262 known_profile_icon = [ ""
2263 "GdkP"
2264 "\0\0\5""0"
2265 "\2\1\0\2"
2266 "\0\0\0P"
2267 "\0\0\0\24"
2268 "\0\0\0\24"
2269 "\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"
2270 "\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"
2271 "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"
2272 "\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"
2273 "\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"
2274 "\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"
2275 "\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"
2276 "\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"
2277 "\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"
2278 "\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"
2279 "\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"
2280 "\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"
2281 "\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"
2282 "\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"
2283 "\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"
2284 "\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"
2285 "\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"
2286 "\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"
2287 "\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"
2288 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2289 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2290 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2291 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2292 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2293 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2294 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2295 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2296 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2297 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2298 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2299 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2300 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2301 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2302 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2303 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2304 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2305 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2306 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2307 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2308 "\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"
2309 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2310 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2311 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2312 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2313 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2314 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2315 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2316 "\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"
2317 "\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"
2318 "\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"
2319 "\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"
2320 "\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"]
2322 unknown_profile_icon = [ ""
2323 "GdkP"
2324 "\0\0\5\22"
2325 "\2\1\0\2"
2326 "\0\0\0P"
2327 "\0\0\0\24"
2328 "\0\0\0\24"
2329 "\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"
2330 "\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"
2331 "\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"
2332 "\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"
2333 "(\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"
2334 "\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"
2335 "#\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"
2336 "\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"
2337 "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"
2338 "\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"
2339 "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"
2340 "\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"
2341 "\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"
2342 "\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"
2343 "\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"
2344 "\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"
2345 "\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"
2346 "\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"
2347 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2348 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2349 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2350 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2351 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2352 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2353 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2354 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2355 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2356 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2357 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2358 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2359 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2360 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2361 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2362 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2363 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2364 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2365 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2366 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2367 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2368 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2369 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2370 "\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"
2371 "\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"
2372 "\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"
2373 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2375 signal_xpm_barely = [
2376 "20 20 10 1",
2377 " c None",
2378 ". c #C6C6C6",
2379 "+ c #CCCCCC",
2380 "@ c #DBDBDB",
2381 "# c #D3D3D3",
2382 "$ c #A9B099",
2383 "% c #95A173",
2384 "& c #6B8428",
2385 "* c #B4B7AC",
2386 "= c #80924D",
2387 " .+++.",
2388 " +@@@+",
2389 " +@@@+",
2390 " +@@@+",
2391 " +@@@+",
2392 " .++++#@@@+",
2393 " +@@@@@@@@+",
2394 " +@@@@@@@@+",
2395 " +@@@@@@@@+",
2396 " +@@@@@@@@+",
2397 " $%%%%#@@@@@@@@+",
2398 " %&&&&@@@@@@@@@+",
2399 " %&&&&@@@@@@@@@+",
2400 " %&&&&@@@@@@@@@+",
2401 " %&&&&@@@@@@@@@+",
2402 "*%%%%=&&&&@@@@@@@@@+",
2403 "%&&&&&&&&&@@@@@@@@@+",
2404 "%&&&&&&&&&@@@@@@@@@+",
2405 "%&&&&&&&&&@@@@@@@@@+",
2406 "*%%%%%%%%%+++++++++."
2410 signal_xpm_best = [
2411 "20 20 6 1",
2412 " c None",
2413 ". c #9DAABF",
2414 "+ c #7B96BF",
2415 "@ c #386EBF",
2416 "# c #5982BF",
2417 "$ c #AEB4BF",
2418 " .+++.",
2419 " +@@@+",
2420 " +@@@+",
2421 " +@@@+",
2422 " +@@@+",
2423 " .++++#@@@+",
2424 " +@@@@@@@@+",
2425 " +@@@@@@@@+",
2426 " +@@@@@@@@+",
2427 " +@@@@@@@@+",
2428 " .++++#@@@@@@@@+",
2429 " +@@@@@@@@@@@@@+",
2430 " +@@@@@@@@@@@@@+",
2431 " +@@@@@@@@@@@@@+",
2432 " +@@@@@@@@@@@@@+",
2433 "$++++#@@@@@@@@@@@@@+",
2434 "+@@@@@@@@@@@@@@@@@@+",
2435 "+@@@@@@@@@@@@@@@@@@+",
2436 "+@@@@@@@@@@@@@@@@@@+",
2437 "$++++++++++++++++++."
2440 signal_xpm_none = [
2441 "20 20 6 1",
2442 " c None",
2443 ". c #C6C6C6",
2444 "+ c #CCCCCC",
2445 "@ c #DBDBDB",
2446 "# c #D3D3D3",
2447 "$ c #C2C2C2",
2448 " .+++.",
2449 " +@@@+",
2450 " +@@@+",
2451 " +@@@+",
2452 " +@@@+",
2453 " .++++#@@@+",
2454 " +@@@@@@@@+",
2455 " +@@@@@@@@+",
2456 " +@@@@@@@@+",
2457 " +@@@@@@@@+",
2458 " .++++#@@@@@@@@+",
2459 " +@@@@@@@@@@@@@+",
2460 " +@@@@@@@@@@@@@+",
2461 " +@@@@@@@@@@@@@+",
2462 " +@@@@@@@@@@@@@+",
2463 "$++++#@@@@@@@@@@@@@+",
2464 "+@@@@@@@@@@@@@@@@@@+",
2465 "+@@@@@@@@@@@@@@@@@@+",
2466 "+@@@@@@@@@@@@@@@@@@+",
2467 "$++++++++++++++++++."
2470 signal_xpm_ok = [
2471 "20 20 10 1",
2472 " c None",
2473 ". c #C6C6C6",
2474 "+ c #CCCCCC",
2475 "@ c #DBDBDB",
2476 "# c #A1A5B2",
2477 "$ c #848DA5",
2478 "% c #D3D3D3",
2479 "& c #4A5B8C",
2480 "* c #677498",
2481 "= c #B0B2B8",
2482 " .+++.",
2483 " +@@@+",
2484 " +@@@+",
2485 " +@@@+",
2486 " +@@@+",
2487 " #$$$$%@@@+",
2488 " $&&&&@@@@+",
2489 " $&&&&@@@@+",
2490 " $&&&&@@@@+",
2491 " $&&&&@@@@+",
2492 " #$$$$*&&&&@@@@+",
2493 " $&&&&&&&&&@@@@+",
2494 " $&&&&&&&&&@@@@+",
2495 " $&&&&&&&&&@@@@+",
2496 " $&&&&&&&&&@@@@+",
2497 "=$$$$*&&&&&&&&&@@@@+",
2498 "$&&&&&&&&&&&&&&@@@@+",
2499 "$&&&&&&&&&&&&&&@@@@+",
2500 "$&&&&&&&&&&&&&&@@@@+",
2501 "=$$$$$$$$$$$$$$++++."
2505 signal_xpm_low = [
2506 "20 20 8 1",
2507 " c None",
2508 ". c #C6C6C6",
2509 "+ c #CCCCCC",
2510 "@ c #DBDBDB",
2511 "# c #D3D3D3",
2512 "$ c #BFB0B5",
2513 "% c #C18799",
2514 "& c #C54F74",
2515 " .+++.",
2516 " +@@@+",
2517 " +@@@+",
2518 " +@@@+",
2519 " +@@@+",
2520 " .++++#@@@+",
2521 " +@@@@@@@@+",
2522 " +@@@@@@@@+",
2523 " +@@@@@@@@+",
2524 " +@@@@@@@@+",
2525 " .++++#@@@@@@@@+",
2526 " +@@@@@@@@@@@@@+",
2527 " +@@@@@@@@@@@@@+",
2528 " +@@@@@@@@@@@@@+",
2529 " +@@@@@@@@@@@@@+",
2530 "$%%%%#@@@@@@@@@@@@@+",
2531 "%&&&&@@@@@@@@@@@@@@+",
2532 "%&&&&@@@@@@@@@@@@@@+",
2533 "%&&&&@@@@@@@@@@@@@@+",
2534 "$%%%%++++++++++++++."
2536 signal_none_pb = None
2537 signal_low_pb = None
2538 signal_barely_pb= None
2539 signal_ok_pb = None
2540 signal_best_pb = None
2542 ####################################################################################################
2543 # Make so we can be imported
2544 if __name__ == "__main__":
2545 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
2546 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
2547 else:
2548 import gtk, gobject
2549 gtk.gdk.threads_init()
2550 apQueue = Queue.Queue(100)
2551 commQueue = Queue.Queue(2)
2552 threading.Thread( None, scanning_thread, None, ( confFile, apQueue, commQueue ) ).start()
2553 main_radar_window = radar_window(confFile, apQueue, commQueue)
2554 gobject.timeout_add( 500, main_radar_window.update_plist_items )
2555 main_radar_window.main()