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