Formatting changes in DEVELOPER_GUIDELINES
[wifi-radar.git] / wifi-radar
blob26a1fcb7254c3c37cf90fe22fb481de773c59eef
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': '',
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 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abg]+)", re.I | re.M | re.S )
214 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
215 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
216 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
217 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
219 access_points = {}
220 command = "scan"
221 while True:
222 try:
223 command = commandQueue.get_nowait()
224 if __debug__: print "scanning_thread received command:", command
225 command_read = True
226 except Queue.Empty:
227 command_read = False
228 if command == "scan":
229 #if __debug__: print "Beginning scan pass"
230 # Some cards need to have the interface up to scan
231 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
232 # call ifconfig command and wait for return
233 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface'), 'up'])
234 # update the signal strengths
235 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), confFile.get_opt('DEFAULT.interface'), 'scan'], stdout=PIPE).stdout.read()
236 #if __debug__:
237 #print "Current IP ", get_current_ip()
238 #print "Current ESSID ", get_current_essid()
239 #print "Current BSSID ", get_current_bssid()
240 # zero out the signal levels for all access points
241 for bssid in access_points:
242 access_points[bssid]['signal'] = 0
243 # split the scan data based on the address line
244 hits = scandata.split(' - ')
245 for hit in hits:
246 # set the defaults for profile template
247 profile = get_new_profile()
248 m = essid_pattern.search( hit )
249 if m:
250 # we found an essid
251 profile['essid'] = m.groups()[1]
252 m = bssid_pattern.search( hit ) # get BSSID from scan
253 if m: profile['bssid'] = m.groups()[1]
254 m = protocol_pattern.search( hit ) # get protocol from scan
255 if m: profile['protocol'] = m.groups()[1]
256 m = mode_pattern.search( hit ) # get mode from scan
257 if m: profile['mode'] = m.groups()[1]
258 m = channel_pattern.search( hit ) # get channel from scan
259 if m: profile['channel'] = m.groups()[1]
260 m = enckey_pattern.search( hit ) # get encryption key from scan
261 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
262 m = signal_pattern.search( hit ) # get signal strength from scan
263 if m: profile['signal'] = m.groups()[1]
264 access_points[ profile['bssid'] ] = profile
265 for bssid in access_points:
266 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
267 # Put all, now or previously, sensed access_points into apQueue
268 try:
269 apQueue.put_nowait( access_points[bssid] )
270 except Queue.Full:
271 pass
272 elif command == "exit":
273 if __debug__: print "Exiting scanning_thread"
274 return
275 if command_read: commandQueue.task_done()
276 if confFile.get_opt('DEFAULT.interface').find('ath') == 0:
277 sleep( 3 )
278 else:
279 sleep( 1 )
282 # Manage a connection; including reporting connection state,
283 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
284 class ConnectionManager():
285 # Create a new connection manager which can read a config file and send to scanning thread
286 # command Queue. A new manager checks for a pre-existing connection and takes
287 # its AP profile from the ESSID and BSSID to which it is currently attached.
289 #Parameters:
291 # 'confFile' -- ConfigFile - Config file object
293 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
295 #Returns:
297 # ConnectionManager instance
298 def __init__( self, confFile, commandQueue ):
299 self.confFile = confFile
300 self.commQueue = commandQueue
301 # is connection running?
302 self.state = False
303 if self.get_current_ip():
304 self.state = True
305 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
307 # Change the interface state: up or down.
309 #Parameters:
311 # 'state' -- string - The state to which to change the interface.
313 #Returns:
315 # nothing
316 def if_change( self, state ):
317 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
318 if __debug__: print "if_change: changing interface state to %s" % state
319 # call ifconfig command and wait for return
320 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), state])
322 # Connect to the specified AP.
324 #Parameters:
326 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
328 # 'status' -- status implementer - Object which implements status interface.
330 #Returns:
332 # nothing
333 def connect_to_network( self, profile, status ):
334 self.profile = profile
335 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
336 say( msg )
337 if __debug__: print " %s" % msg
338 # ready to dance
339 # Let's run the connection prescript
340 if self.profile['con_prescript'].strip() != '':
341 # got something to execute
342 # run connection prescript through shell and wait for return
343 if __debug__: print "executing connection prescript:", self.profile['con_prescript']
344 shellcmd([self.profile['con_prescript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
345 status.show()
346 # Some cards need to have the interface up
347 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
348 self.if_change('up')
349 # Start building iwconfig command line, command
350 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
351 iwconfig_command.append( self.confFile.get_opt('DEFAULT.interface') )
352 # Setting essid
353 iwconfig_command.append( 'essid' )
354 iwconfig_command.append( "'" + self.profile['essid'] + "'" )
355 # Setting nick
356 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
357 # Setting key
358 iwconfig_command.append( 'key' )
359 if self.profile['key'] == '':
360 iwconfig_command.append( 'off' )
361 else:
362 iwconfig_command.append( "'" + self.profile['key'] + "'" )
363 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
364 # Setting mode to Master, this will cause association with the AP to fail
365 #iwconfig_command.append( 'mode' )
366 #iwconfig_command.append( 'Master' )
367 # Setting channel
368 if self.profile['channel'] != '':
369 iwconfig_command.append( 'channel' )
370 iwconfig_command.append( self.profile['channel'] )
371 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
372 iwconfig_command.append( 'ap' )
373 iwconfig_command.append( self.profile['bssid'] )
374 # Some cards require a commit
375 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
376 if __debug__: print 'iwconfig_args %s ' % ( iwconfig_args, )
377 iwconfig_command.append( 'commit' )
378 # call iwconfig command and wait for return
379 if not shellcmd(iwconfig_command): return
380 # Now normal network stuff
381 # Kill off any existing DHCP clients running
382 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
383 if __debug__: print "Killing existing DHCP..."
384 try:
385 if self.confFile.get_opt('DHCP.kill_args') != '':
386 # call DHCP client kill command and wait for return
387 if __debug__: print self.confFile.get_opt('DHCP.command') + " " + self.confFile.get_opt('DHCP.kill_args')
388 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
389 else:
390 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
391 except OSError:
392 print "failed to kill DHCP client"
393 sys.exit()
394 finally:
395 print "Stale pid file. Removing..."
396 os.remove(self.confFile.get_opt('DHCP.pidfile'))
397 # Kill off any existing WPA supplicants running
398 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
399 if __debug__: print "Killing existing WPA supplicant..."
400 try:
401 if not self.confFile.get_opt('WPA.kill_command') != '':
402 # call WPA supplicant kill command and wait for return
403 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
404 else:
405 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
406 except OSError:
407 print "failed to kill WPA supplicant"
408 sys.exit()
409 finally:
410 print "Stale pid file. Removing..."
411 os.remove(self.confFile.get_opt('WPA.pidfile'))
412 # Begin WPA supplicant
413 if self.profile['use_wpa'] :
414 if __debug__: print "WPA args: %s" % ( wpa_options, )
415 status.update_message("WPA supplicant starting")
416 if sys.modules.has_key("gtk"):
417 while gtk.events_pending():
418 gtk.main_iteration(False)
419 # call WPA supplicant command and do not wait for return
420 wpa_proc = Popen([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args'), self.confFile.get_opt('DEFAULT.interface')])
421 if self.profile['use_dhcp'] :
422 if __debug__: print "Disable iwlist while dhcp in progress..."
423 try:
424 self.commQueue.put("pause")
425 except Queue.Full:
426 pass
427 status.update_message("Acquiring IP Address (DHCP)")
428 if sys.modules.has_key("gtk"):
429 while gtk.events_pending():
430 gtk.main_iteration(False)
431 # call DHCP client command and do not wait for return
432 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
433 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
434 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
435 dhcp_proc = Popen(dhcp_command, stdout=None)
436 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
437 tick = 0.25
438 waiting = dhcp_proc.poll()
439 while waiting == None:
440 waiting = dhcp_proc.poll()
441 if timer < 0:
442 os.kill(dhcp_proc.pid, SIGTERM)
443 break
444 if sys.modules.has_key("gtk"):
445 while gtk.events_pending():
446 gtk.main_iteration(False)
447 timer -= tick
448 sleep(tick)
449 # Re-enable iwlist
450 try:
451 self.commQueue.put("scan")
452 except Queue.Full:
453 pass
454 if not self.get_current_ip():
455 status.update_message("Could not get IP address!")
456 if sys.modules.has_key("gtk"):
457 while gtk.events_pending():
458 gtk.main_iteration(False)
459 sleep(1)
460 if self.state:
461 self.disconnect_interface()
462 status.hide()
463 return
464 else:
465 status.update_message("Got IP address. Done.")
466 self.state = True
467 if sys.modules.has_key("gtk"):
468 while gtk.events_pending():
469 gtk.main_iteration(False)
470 sleep(2)
471 else:
472 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'] )
473 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
474 resolv_contents = ''
475 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
476 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
477 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
478 if ( resolv_contents != '' ):
479 resolv_file=open('/etc/resolv.conf', 'w')
480 resolv_file.write(s)
481 resolv_file.close
482 if not shellcmd([ifconfig_command]): return
483 if not shellcmd([route_command]): return
484 self.state = True
485 # Let's run the connection postscript
486 con_postscript = self.profile['con_postscript']
487 if self.profile['con_postscript'].strip() != '':
488 if __debug__: print "executing connection postscript:", self.profile['con_postscript']
489 shellcmd([self.profile['con_postscript']],
490 environment = { "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
491 "WIFIRADAR_ESSID": self.get_current_essid() or '',
492 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
493 "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface') or ''
496 status.hide()
498 # Disconnect from the AP with which a connection has been established/attempted.
500 #Parameters:
502 # nothing
504 #Returns:
506 # nothing
507 def disconnect_interface( self ):
508 msg = "Disconnecting"
509 say( msg )
510 if __debug__: print msg
511 # Pause scanning while manipulating card
512 try:
513 self.commQueue.put("pause")
514 except Queue.Full:
515 pass
516 if self.state:
517 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
518 # Let's run the disconnection prescript
519 if self.profile['dis_prescript'].strip() != '':
520 if __debug__: print "executing disconnection prescript:", self.profile['dis_prescript']
521 shellcmd([self.profile['dis_prescript']],
522 environment = { "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
523 "WIFIRADAR_ESSID": self.get_current_essid() or '',
524 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
525 "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface') or ''
528 if __debug__: print "Kill off any existing DHCP clients running..."
529 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
530 if __debug__: print "Killing existing DHCP..."
531 try:
532 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
533 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
534 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
535 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
536 if __debug__: print "DHCP command", dhcp_command
537 # call DHCP client command and wait for return
538 if not shellcmd(dhcp_command): return
539 else:
540 if __debug__: print "killing DHCP manually."
541 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
542 except OSError:
543 print "failed to kill DHCP client"
544 if __debug__: print "Kill off any existing WPA supplicants running..."
545 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
546 if __debug__: print "Killing existing WPA supplicant..."
547 try:
548 if not self.confFile.get_opt('WPA.kill_command') != '':
549 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
550 if not shellcmd(wpa_command): return
551 else:
552 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
553 except OSError:
554 print "failed to kill WPA supplicant"
555 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
556 if __debug__: print "Let's clear out the wireless stuff"
557 if __debug__: print 'Now take the interface down'
558 # taking down the interface too quickly can crash my system, so pause a moment
559 sleep(1)
560 self.if_change('down')
561 if __debug__: print 'Since it may be brought back up by the next scan, lets unset its IP'
562 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), '0.0.0.0'])
563 # Let's run the disconnection postscript
564 if self.profile['dis_postscript'].strip() != '':
565 if __debug__: print "executing disconnection postscript:", self.profile['dis_postscript']
566 shellcmd([self.profile['dis_postscript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
567 self.state = False
568 if __debug__: print 'Disconnect complete.'
569 # Begin scanning again
570 try:
571 self.commQueue.put("scan")
572 except Queue.Full:
573 pass
575 # Returns the current IP, if any, by calling ifconfig.
577 #Parameters:
579 # nothing
581 #Returns:
583 # string or None -- the IP address or None (if no there is no current connection)
584 def get_current_ip( self ):
585 ifconfig_command = [ confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface') ]
586 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
587 # Be careful to the language (inet adr: in French for example)
589 # Hi Brian
591 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
592 # There the string in ifconfig is inet Adresse for the IP which isn't
593 # found by the current get_current_ip function in wifi-radar. I changed
594 # the according line (#289; gentoo, v1.9.6-r1) to
595 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
596 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
598 # I'd be happy if you could incorporate this small change because as now
599 # I've got to change the file every time it is updated.
601 # Best wishes
603 # Simon
604 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
605 line = ifconfig_info.read()
606 if ip_re.search( line ):
607 return ip_re.search( line ).group(1)
608 return None
610 # Returns the current ESSID, if any, by calling iwconfig.
612 #Parameters:
614 # nothing
616 #Returns:
618 # string or None -- the ESSID or None (if no there is no current association)
619 def get_current_essid( self ):
620 """Returns the current ESSID if any by calling iwconfig"""
621 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
622 # Be careful to the language (inet adr: in French for example)
623 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
624 line = iwconfig_info.read()
625 if essid_re.search( line ):
626 return essid_re.search( line ).group(2)
627 return None
629 # Returns the current BSSID, if any, by calling iwconfig.
631 #Parameters:
633 # nothing
635 #Returns:
637 # string or None -- the BSSID or None (if no there is no current association)
638 def get_current_bssid( self ):
639 """Returns the current BSSID if any by calling iwconfig"""
640 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
641 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
642 line = iwconfig_info.read()
643 if bssid_re.search( line ):
644 return bssid_re.search( line ).group(2)
645 return None
649 # The main user interface window for WiFi Radar. This class also is the control
650 # center for most of the rest of the operations.
651 class radar_window:
652 # Create a new radar_window.
654 #Parameters:
656 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
658 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
660 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
662 #Returns:
664 # radar_window instance
665 def __init__( self, confFile, apQueue, commQueue ):
666 global signal_xpm_none
667 global signal_xpm_low
668 global signal_xpm_barely
669 global signal_xpm_ok
670 global signal_xpm_best
671 global known_profile_icon
672 global unknown_profile_icon
673 global wifi_radar_icon
675 self.confFile = confFile
676 self.apQueue = apQueue
677 self.commandQueue = commQueue
678 self.access_points = {}
679 self.connection = None
681 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
682 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
683 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
684 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
685 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
686 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
687 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
688 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
689 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
690 self.window.set_icon( icon )
691 self.window.set_border_width( 10 )
692 self.window.set_size_request( 550, 300 )
693 self.window.set_title( "WiFi Radar" )
694 self.window.connect( 'delete_event', self.delete_event )
695 self.window.connect( 'destroy', self.destroy )
696 # let's create all our widgets
697 self.current_network = gtk.Label()
698 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
699 self.current_network.show()
700 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
701 self.close_button.show()
702 self.close_button.connect( 'clicked', self.delete_event, None )
703 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
704 self.about_button.show()
705 self.about_button.connect( 'clicked', self.show_about_info, None )
706 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
707 self.preferences_button.show()
708 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
709 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
710 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
711 self.plist = gtk.TreeView( self.pstore )
712 # The icons column, known and encryption
713 self.pix_cell = gtk.CellRendererPixbuf()
714 self.wep_cell = gtk.CellRendererPixbuf()
715 self.icons_cell = gtk.CellRendererText()
716 self.icons_col = gtk.TreeViewColumn()
717 self.icons_col.pack_start( self.pix_cell, False )
718 self.icons_col.pack_start( self.wep_cell, False )
719 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
720 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
721 self.plist.append_column( self.icons_col )
722 # The AP column
723 self.ap_cell = gtk.CellRendererText()
724 self.ap_col = gtk.TreeViewColumn( "Access Point" )
725 self.ap_col.pack_start( self.ap_cell, True )
726 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
727 self.plist.append_column( self.ap_col )
728 # The signal column
729 self.sig_cell = gtk.CellRendererPixbuf()
730 self.signal_col = gtk.TreeViewColumn( "Signal" )
731 self.signal_col.pack_start( self.sig_cell, True )
732 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
733 self.plist.append_column( self.signal_col )
734 # The mode column
735 self.mode_cell = gtk.CellRendererText()
736 self.mode_col = gtk.TreeViewColumn( "Mode" )
737 self.mode_col.pack_start( self.mode_cell, True )
738 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
739 self.plist.append_column( self.mode_col )
740 # The protocol column
741 self.prot_cell = gtk.CellRendererText()
742 self.protocol_col = gtk.TreeViewColumn( "802.11" )
743 self.protocol_col.pack_start( self.prot_cell, True )
744 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
745 self.plist.append_column( self.protocol_col )
746 # The channel column
747 self.channel_cell = gtk.CellRendererText()
748 self.channel_col = gtk.TreeViewColumn( "Channel" )
749 self.channel_col.pack_start( self.channel_cell, True )
750 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
751 self.plist.append_column( self.channel_col )
752 # DnD Ordering
753 self.plist.set_reorderable( True )
754 self.pstore.connect( 'row-changed', self.update_auto_profile_order )
755 # enable/disable buttons based on the selected network
756 self.selected_network = self.plist.get_selection()
757 self.selected_network.connect( 'changed', self.on_network_selection, None )
758 # the list scroll bar
759 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
760 sb.show()
761 self.plist.show()
762 # Add New button
763 self.new_button = gtk.Button( "_New" )
764 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
765 self.new_button.show()
766 # Add Configure button
767 self.edit_button = gtk.Button( "C_onfigure" )
768 self.edit_button.connect( 'clicked', self.edit_profile, None )
769 self.edit_button.show()
770 self.edit_button.set_sensitive(False)
771 # Add Delete button
772 self.delete_button = gtk.Button( "_Delete" )
773 self.delete_button.connect( 'clicked', self.delete_profile, None )
774 self.delete_button.show()
775 self.delete_button.set_sensitive(False)
776 # Add Connect button
777 self.connect_button = gtk.Button( "Co_nnect" )
778 self.connect_button.connect( 'clicked', self.connect_profile, None )
779 # Add Disconnect button
780 self.disconnect_button = gtk.Button( "D_isconnect" )
781 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
782 # lets add our widgets
783 rows = gtk.VBox( False, 3 )
784 net_list = gtk.HBox( False, 0 )
785 listcols = gtk.HBox( False, 0 )
786 prows = gtk.VBox( False, 0 )
787 # lets start packing
788 # the network list
789 net_list.pack_start( self.plist, True, True, 0 )
790 net_list.pack_start( sb, False, False, 0 )
791 # the rows level
792 rows.pack_start( net_list , True, True, 0 )
793 rows.pack_start( self.current_network, False, True, 0 )
794 # the list columns
795 listcols.pack_start( rows, True, True, 0 )
796 listcols.pack_start( prows, False, False, 5 )
797 # the list buttons
798 prows.pack_start( self.new_button, False, False, 2 )
799 prows.pack_start( self.edit_button, False, False, 2 )
800 prows.pack_start( self.delete_button, False, False, 2 )
801 prows.pack_end( self.connect_button, False, False, 2 )
802 prows.pack_end( self.disconnect_button, False, False, 2 )
804 self.window.action_area.pack_start( self.about_button )
805 self.window.action_area.pack_start( self.preferences_button )
806 self.window.action_area.pack_start( self.close_button )
808 rows.show()
809 prows.show()
810 listcols.show()
811 self.window.vbox.add( listcols )
812 self.window.vbox.set_spacing( 3 )
813 self.window.show_all()
815 # Now, immediately hide these two. The proper one will be
816 # displayed later, based on interface state. -BEF-
817 self.disconnect_button.hide()
818 self.connect_button.hide()
819 self.connect_button.set_sensitive(False)
821 # set up connection manager for later use
822 self.connection = ConnectionManager( self.confFile, self.commandQueue )
823 # set up status window for later use
824 self.status_window = StatusWindow( self )
825 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
827 # Add our known profiles in order
828 for ap in self.confFile.auto_profile_order:
829 ap = ap.strip()
830 self.access_points[ ap ] = self.confFile.get_profile( ap )
831 wep = None
832 if self.access_points[ ap ]['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
833 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'] ] )
834 # This is the first run (or, at least, no config file was present), so pop up the preferences window
835 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
836 self.confFile.remove_option('DEFAULT', 'new_file')
837 self.edit_preferences(self.preferences_button)
839 # Begin running radar_window in Gtk event loop.
841 #Parameters:
843 # nothing
845 #Returns:
847 # nothing
848 def main( self ):
849 gtk.main()
851 # Write the config file to disk and quit application.
853 #Parameters:
855 # 'widget' -- gtk.Widget - The widget sending the event.
857 #Returns:
859 # nothing
860 def destroy( self, widget = None):
861 if self.status_window:
862 self.status_window.destroy()
863 gtk.main_quit()
865 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
867 #Parameters:
869 # 'widget' -- gtk.Widget - The widget sending the event.
871 # 'data' -- tuple - list of arbitrary arguments (not used)
873 #Returns:
875 # boolean -- always return False (i.e. do not propigate the signal which called)
876 def delete_event( self, widget, data = None ):
877 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
878 try:
879 self.commandQueue.put("exit", True)
880 except Queue.Full:
881 pass
882 # Save the preferred networks order
883 self.update_auto_profile_order()
884 self.destroy()
885 return False
887 # Updates the on-screen profiles list.
889 #Parameters:
891 # nothing
893 #Returns:
895 # boolean -- always return True
896 def update_plist_items( self ):
897 # Indicate to PyGtk that only one Gtk thread should run here
898 gtk.gdk.threads_enter()
899 # update the current ip and essid
900 # set the state of connect/disconnect buttons based on whether we have an IP address
901 if self.connection:
902 if self.connection.state:
903 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() ) )
904 self.connect_button.hide()
905 self.disconnect_button.show()
906 else:
907 self.current_network.set_text( "Not Connected." )
908 self.disconnect_button.hide()
909 self.connect_button.show()
911 while True:
912 # Get profiles scanned by iwlist
913 try:
914 profile = self.apQueue.get_nowait()
915 except Queue.Empty:
916 break
917 else:
918 prow_iter = self.get_row_by_ap( profile['essid'], profile['bssid'] )
919 wep = None
920 if prow_iter != None:
921 # the AP is in the list of APs on the screen
922 apname = make_section_name(profile['essid'], profile['bssid'])
923 if self.access_points.has_key(apname):
924 # This AP has been configured and is/should be stored in the config file
925 profile['known'] = self.access_points[apname]['known']
926 self.access_points[apname]['available'] = profile['available']
927 self.access_points[apname]['encrypted'] = profile['encrypted']
928 self.access_points[apname]['signal'] = profile['signal']
929 self.access_points[apname]['mode'] = profile['mode']
930 self.access_points[apname]['protocol'] = profile['protocol']
931 self.access_points[apname]['channel'] = profile['channel']
932 # Set the 'known' values; False is default, overridden to True by self.access_points
933 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known( profile[ 'known' ] ))
934 self.pstore.set_value(prow_iter, 2, profile[ 'known' ])
935 self.pstore.set_value(prow_iter, 3, profile['available'])
936 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
937 self.pstore.set_value(prow_iter, 4, wep)
938 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal( profile['signal'] ))
939 self.pstore.set_value(prow_iter, 6, profile['mode'])
940 self.pstore.set_value(prow_iter, 7, profile['protocol'])
941 self.pstore.set_value(prow_iter, 8, profile['channel'])
942 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
943 #for val in self.pstore[prow_iter]:
944 #print val,
945 else:
946 # the AP is not in the list of APs on the screen
947 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'] ] )
948 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
949 # Allow other Gtk threads to run
950 gtk.gdk.threads_leave()
951 #print "update_plist_items: Empty apQueue"
952 return True
954 # Return the proper icon for a value of known.
956 #Parameters:
958 # 'known' -- boolean - Whether the AP is known (i.e. configured)
960 #Returns:
962 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
963 def pixbuf_from_known( self, known ):
964 """ return the proper icon for value of known """
965 if known:
966 return self.known_profile_icon
967 else:
968 return self.unknown_profile_icon
970 # Return an icon indicating the signal level.
972 #Parameters:
974 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
976 #Returns:
978 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
979 def pixbuf_from_signal( self, signal ):
980 signal = int( signal )
981 # shift signal up by 80 to convert dBm scale to arbitrary scale
982 if signal < 0: signal = signal + 80
983 #print "signal level:", signal
984 if signal < 3:
985 return self.signal_none_pb
986 elif signal < 12:
987 return self.signal_low_pb
988 elif signal < 20:
989 return self.signal_barely_pb
990 elif signal < 35:
991 return self.signal_ok_pb
992 elif signal >= 35:
993 return self.signal_best_pb
994 else:
995 return None
997 # Return row which holds specified ESSID and BSSID.
999 #Parameters:
1001 # 'essid' -- string - ESSID to match
1003 # 'bssid' -- string - BSSID to match
1005 #Returns:
1007 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
1008 def get_row_by_ap( self, essid, bssid ):
1009 for row in self.pstore:
1010 if ( row[0] == essid + "\n" + bssid ):
1011 #print "matched:", row.iter, essid, bssid
1012 return row.iter
1013 return None
1015 # Enable/disable buttons based on the selected network.
1017 #Parameters:
1019 # 'widget' -- gtk.Widget - The widget sending the event.
1021 # 'data' -- tuple - list of arbitrary arguments (not used)
1023 #Returns:
1025 # nothing
1026 def on_network_selection( self, widget, data = None ):
1027 ( store, selected_iter ) = self.selected_network.get_selected()
1028 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
1029 # if no networks are selected, disable all buttons except New
1030 # (this occurs after a drag-and-drop)
1031 if selected_iter == None:
1032 self.edit_button.set_sensitive(False)
1033 self.delete_button.set_sensitive(False)
1034 self.connect_button.set_sensitive(False)
1035 return
1036 # enable/disable buttons
1037 self.connect_button.set_sensitive(True)
1038 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1039 self.edit_button.set_sensitive(True)
1040 self.delete_button.set_sensitive(True)
1041 else:
1042 self.edit_button.set_sensitive(True)
1043 self.delete_button.set_sensitive(False)
1045 # Init and run the about dialog
1047 #Parameters:
1049 # 'widget' -- gtk.Widget - The widget sending the event.
1051 # 'data' -- tuple - list of arbitrary arguments (not used)
1053 #Returns:
1055 # nothing
1056 def show_about_info( self, widget, data=None ):
1057 about = about_dialog()
1058 about.run()
1059 about.destroy()
1061 # Init and run the preferences dialog
1063 #Parameters:
1065 # 'widget' -- gtk.Widget - The widget sending the event.
1067 # 'data' -- tuple - list of arbitrary arguments (not used)
1069 #Returns:
1071 # nothing
1072 def edit_preferences( self, widget, data=None ):
1073 # get raw strings from config file
1074 self.confFile.raw = True
1075 prefs = preferences_dialog( self, self.confFile )
1076 response = prefs.run()
1077 prefs.destroy()
1078 if response == int(gtk.RESPONSE_ACCEPT):
1079 prefs.save()
1080 # get cooked strings from config file
1081 self.confFile.raw = False
1083 # Respond to a request to create a new AP profile
1085 #Parameters:
1087 # 'widget' -- gtk.Widget - The widget sending the event.
1089 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1091 # 'data' -- tuple - list of arbitrary arguments (not used)
1093 #Returns:
1095 # boolean -- True if a profile was created and False if profile creation was canceled.
1096 def create_new_profile( self, widget, profile, data=None ):
1097 profile_editor = profile_dialog( self, profile )
1098 profile = profile_editor.run()
1099 profile_editor.destroy()
1100 if profile:
1101 apname = make_section_name( profile['essid'], profile['bssid'] )
1102 # Check that the ap does not exist already
1103 if apname in self.confFile.profiles():
1104 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) )
1105 dlg.run()
1106 dlg.destroy()
1107 del dlg
1108 # try again
1109 self.access_points[ apname ] = profile
1110 self.confFile.set_section( apname, profile )
1111 # if it is not in the auto_profile_order add it
1112 if apname not in self.confFile.auto_profile_order:
1113 self.confFile.auto_profile_order.append(apname)
1114 # add to the store
1115 wep = None
1116 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1117 self.confFile.write()
1118 # Add AP to the list displayed to user
1119 try:
1120 self.apQueue.put_nowait( self.access_points[ apname ] )
1121 except Queue.Full:
1122 pass
1123 return True
1124 else:
1125 # Did not create new profile
1126 return False
1128 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1129 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1131 #Parameters:
1133 # 'widget' -- gtk.Widget - The widget sending the event.
1135 # 'data' -- tuple - list of arbitrary arguments (not used)
1137 #Returns:
1139 # nothing
1140 def edit_profile( self, widget, data=None ):
1141 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1142 if not selected_iter: return
1143 row_start = str(self.pstore.get_value( selected_iter, 0 )).split("\n")
1144 apname = make_section_name( row_start[0], row_start[1] )
1145 profile = self.confFile.get_profile( apname )
1146 if profile:
1147 profile_editor = profile_dialog( self, profile )
1148 profile = profile_editor.run()
1149 profile_editor.destroy()
1150 if profile:
1151 if __debug__:
1152 print "Got edited profile ", profile
1153 apname = make_section_name( profile['essid'], profile['bssid'] )
1154 self.access_points[ apname ] = profile
1155 self.confFile.set_section( apname, profile )
1156 self.confFile.write()
1157 else:
1158 profile = get_new_profile()
1159 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1160 self.create_new_profile( widget, profile, data )
1162 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1164 #Parameters:
1166 # 'widget' -- gtk.Widget - The widget sending the event.
1168 # 'data' -- tuple - list of arbitrary arguments (not used)
1170 #Returns:
1172 # nothing
1173 def delete_profile( self, widget, data=None ):
1174 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1175 if not selected_iter: return
1176 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1177 known = store.get_value( selected_iter, 1 )
1178 if not known: return
1179 dlg = gtk.MessageDialog(
1180 self.window,
1181 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1182 gtk.MESSAGE_QUESTION,
1183 gtk.BUTTONS_YES_NO,
1184 "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid) )
1185 res = dlg.run()
1186 dlg.destroy()
1187 del dlg
1188 if res == gtk.RESPONSE_NO: return
1189 # Remove it
1190 apname = make_section_name( essid, bssid )
1191 del self.access_points[ apname ]
1192 self.confFile.remove_section( apname )
1193 if __debug__: print "delete_profile: ", apname, ":", self.confFile.auto_profile_order
1194 if apname in self.confFile.auto_profile_order: self.confFile.auto_profile_order.remove(apname)
1195 self.pstore.remove( selected_iter )
1196 # Let's save our current state
1197 self.update_auto_profile_order()
1198 self.confFile.write()
1200 # Respond to a request to connect to an AP.
1202 #Parameters:
1204 # 'widget' -- gtk.Widget - The widget sending the event.
1206 # 'profile' -- dictionary - The AP profile to which to connect.
1208 # 'data' -- tuple - list of arbitrary arguments (not used)
1210 #Returns:
1212 # nothing
1213 def connect_profile( self, widget, profile, data=None ):
1214 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1215 if not selected_iter: return
1216 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1217 known = store.get_value( selected_iter, 2 )
1218 if not known:
1219 if data != 'noconnect':
1220 dlg = gtk.MessageDialog(
1221 self.window,
1222 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1223 gtk.MESSAGE_QUESTION,
1224 gtk.BUTTONS_YES_NO,
1225 "This network does not have a profile configured.\n\nWould you like to create one now?" )
1226 res = dlg.run()
1227 dlg.destroy()
1228 del dlg
1229 if res == gtk.RESPONSE_NO: return
1230 profile = get_new_profile()
1231 profile['essid'] = essid
1232 profile['bssid'] = bssid
1233 if not self.create_new_profile( widget, profile, data ):
1234 return
1235 apname = make_section_name( essid, bssid )
1236 self.connection.connect_to_network(self.access_points[apname], self.status_window)
1238 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1240 #Parameters:
1242 # 'widget' -- gtk.Widget - The widget sending the event.
1244 # 'data' -- tuple - list of arbitrary arguments (not used)
1246 #Returns:
1248 # nothing
1249 def disconnect_profile( self, widget, data=None ):
1250 if data == "cancel":
1251 self.status_window.update_message("Canceling connection...")
1252 if sys.modules.has_key("gtk"):
1253 while gtk.events_pending():
1254 gtk.main_iteration(False)
1255 sleep(1)
1256 self.connection.disconnect_interface()
1258 # Update the config file auto profile order from the on-screen order
1260 #Parameters:
1262 # 'widget' -- gtk.Widget - The widget sending the event.
1264 # 'data' -- tuple - list of arbitrary arguments (not used)
1266 # 'data2' -- tuple - list of arbitrary arguments (not used)
1268 #Returns:
1270 # nothing
1271 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1272 # recreate the auto_profile_order
1273 auto_profile_order = []
1274 piter = self.pstore.get_iter_first()
1275 while piter:
1276 # only if it's known
1277 if self.pstore.get_value( piter, 2 ) == True:
1278 row_start = str(self.pstore.get_value( piter, 0 )).split("\n")
1279 auto_profile_order.append( make_section_name( row_start[0], row_start[1] ) )
1280 piter = self.pstore.iter_next( piter )
1281 self.confFile.auto_profile_order = auto_profile_order
1284 # Button to allow user to choose a file and put value into specified gtk.Entry
1285 class file_browse_button(gtk.Button):
1286 # Create a button to simulate a File/Open
1288 #Parameters:
1290 # 'parent' -- gtk.Object -- Usually, the calling window.
1292 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1294 #Returns:
1296 # file_browse_button instance
1297 def __init__( self, parent, entry ):
1298 self.parent_window = parent
1299 self.entry = entry
1300 gtk.Button.__init__(self, "Browse", None)
1301 #self.
1302 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)
1303 self.connect("clicked", self.browse_files)
1305 # Show filechooser dialog and get user selection
1307 #Parameters:
1309 # 'widget' -- gtk.Widget -- The widget sending the event.
1311 #Returns:
1313 # nothing
1315 #NOTES:
1317 # updates entry value
1319 def browse_files( self, widget ):
1320 self.browser_dialog.set_filename(self.entry.get_text())
1321 self.browser_dialog.run()
1322 self.entry.set_text(self.browser_dialog.get_filename())
1323 self.browser_dialog.destroy()
1326 # The preferences dialog. Edits non-profile sections of the config file.
1327 class preferences_dialog:
1328 # Create a new preferences_dialog.
1330 #Parameters:
1332 # 'parent' -- gtk.Object - Usually, the calling window.
1334 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1336 #Returns:
1338 # preferences_dialog instance
1339 def __init__( self, parent, confFile ):
1340 global wifi_radar_icon
1341 self.parent = parent
1342 self.confFile = confFile
1343 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1344 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1345 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1346 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1347 self.dialog.set_icon( icon )
1348 self.dialog.set_resizable( True )
1349 self.dialog.set_transient_for( self.parent.window )
1350 self.tooltips = gtk.Tooltips()
1352 # set up preferences widgets
1354 # build everything in a tabbed notebook
1355 self.prefs_notebook = gtk.Notebook()
1357 ### General tab
1358 self.general_page = gtk.VBox()
1359 # auto detect wireless device
1360 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1362 print "prefs: ", self.confFile.get_opt('DEFAULT.interface')
1364 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1365 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1366 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1367 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1369 # network interface selecter
1370 self.w_interface = gtk.combo_box_entry_new_text()
1371 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE).stdout
1372 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1373 for device in wireless_devices:
1374 if device != self.confFile.get_opt('DEFAULT.interface'):
1375 self.w_interface.append_text(device)
1376 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1377 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1378 self.w_interface.set_active(0)
1379 self.w_interface_label = gtk.Label("Wireless device")
1380 self.w_hbox1 = gtk.HBox(False, 0)
1381 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1382 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1383 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1384 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1386 # scan timeout (spin button of integers from 1 to 100)
1387 self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
1388 self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1389 self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1390 self.w_scan_timeout.set_numeric(True)
1391 self.w_scan_timeout.set_snap_to_ticks(True)
1392 self.w_scan_timeout.set_wrap(False)
1393 self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1394 self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1395 self.w_hbox2 = gtk.HBox(False, 0)
1396 self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1397 self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1398 self.general_page.pack_start(self.w_hbox2, False, False, 5)
1400 # speak up
1401 self.w_speak_up = gtk.CheckButton("Use speak-up")
1402 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1403 self.w_speak_up.connect("toggled", self.toggle_speak)
1404 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1405 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1407 # speak up command
1408 self.w_speak_cmd = gtk.Entry()
1409 self.w_speak_cmd.set_width_chars(16)
1410 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1411 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1412 self.w_speak_cmd_label = gtk.Label("Speak Command")
1413 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1414 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1415 self.w_hbox3 = gtk.HBox(False, 0)
1416 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1417 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1418 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1419 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1420 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1422 # commit required
1423 self.w_commit_required = gtk.CheckButton("Commit required")
1424 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1425 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1426 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1428 # ifup required
1429 self.w_ifup_required = gtk.CheckButton("Ifup required")
1430 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1431 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1432 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1434 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1435 ### End of General tab
1437 ### Advanced tab
1438 # table to use for layout of following command configurations
1439 self.cmds_table = gtk.Table()
1441 # ifconfig command
1442 self.ifconfig_cmd = gtk.Entry()
1443 self.ifconfig_cmd.set_width_chars(32)
1444 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1445 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1446 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1447 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1448 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1449 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, True, False, 0, 0)
1450 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1452 # iwconfig command
1453 self.iwconfig_cmd = gtk.Entry()
1454 self.iwconfig_cmd.set_width_chars(32)
1455 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1456 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1457 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1458 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1459 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, True, False, 5, 0)
1460 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, True, False, 0, 0)
1461 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, False, False, 0, 0)
1463 # iwlist command
1464 self.iwlist_cmd = gtk.Entry()
1465 self.iwlist_cmd.set_width_chars(32)
1466 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1467 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1468 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1469 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1470 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, True, False, 5, 0)
1471 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, True, False, 0, 0)
1472 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, False, False, 0, 0)
1474 # route command
1475 self.route_cmd = gtk.Entry()
1476 self.route_cmd.set_width_chars(32)
1477 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1478 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1479 self.route_cmd_label = gtk.Label("Network route configure command")
1480 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1481 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, True, False, 5, 0)
1482 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, True, False, 0, 0)
1483 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, False, False, 0, 0)
1485 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1486 ### End of Advanced tab
1488 ### DHCP tab
1489 # table to use for layout of DHCP prefs
1490 self.dhcp_table = gtk.Table()
1492 self.dhcp_cmd = gtk.Entry()
1493 self.dhcp_cmd.set_width_chars(32)
1494 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1495 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1496 self.dhcp_cmd_label = gtk.Label("Command")
1497 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1498 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1499 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1500 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1502 self.dhcp_args = gtk.Entry()
1503 self.dhcp_args.set_width_chars(32)
1504 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1505 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1506 self.dhcp_args_label = gtk.Label("Arguments")
1507 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1508 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1510 self.dhcp_kill_args = gtk.Entry()
1511 self.dhcp_kill_args.set_width_chars(32)
1512 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1513 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1514 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1515 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1516 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1518 self.dhcp_timeout = gtk.Entry()
1519 self.dhcp_timeout.set_width_chars(32)
1520 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1521 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1522 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1523 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1524 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1526 self.dhcp_pidfile = gtk.Entry()
1527 self.dhcp_pidfile.set_width_chars(32)
1528 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1529 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1530 self.dhcp_pidfile_label = gtk.Label("PID file")
1531 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1532 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1534 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1535 ### End of DHCP tab
1537 ### WPA tab
1538 # table to use for layout of DHCP prefs
1539 self.wpa_table = gtk.Table()
1541 self.wpa_cmd = gtk.Entry()
1542 self.wpa_cmd.set_width_chars(32)
1543 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1544 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1545 self.wpa_cmd_label = gtk.Label("Command")
1546 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1547 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1548 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1549 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1551 self.wpa_args = gtk.Entry()
1552 self.wpa_args.set_width_chars(32)
1553 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1554 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1555 self.wpa_args_label = gtk.Label("Arguments")
1556 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1557 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1559 self.wpa_kill_args = gtk.Entry()
1560 self.wpa_kill_args.set_width_chars(32)
1561 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1562 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1563 self.wpa_kill_args_label = gtk.Label("Kill command")
1564 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1565 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1567 self.wpa_config = gtk.Entry()
1568 self.wpa_config.set_width_chars(32)
1569 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1570 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1571 self.wpa_config_label = gtk.Label("Configuration file")
1572 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1573 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1575 self.wpa_driver = gtk.Entry()
1576 self.wpa_driver.set_width_chars(32)
1577 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1578 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1579 self.wpa_driver_label = gtk.Label("Driver")
1580 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1581 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1583 self.wpa_pidfile = gtk.Entry()
1584 self.wpa_pidfile.set_width_chars(32)
1585 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1586 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1587 self.wpa_pidfile_label = gtk.Label("PID file")
1588 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1589 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1591 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1592 ### End of WPA tab
1594 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1596 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1598 #Parameters:
1600 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1602 # 'data' -- tuple - list of arbitrary arguments (not used)
1604 #Returns:
1606 # nothing
1607 def toggle_auto_detect(self, auto_detect_toggle, data=None):
1608 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
1610 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
1612 #Parameters:
1614 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1616 # 'data' -- tuple - list of arbitrary arguments (not used)
1618 #Returns:
1620 # nothing
1621 def toggle_speak(self, speak_toggle, data=None):
1622 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
1623 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
1625 # Display preferences dialog and operate until canceled or okayed.
1627 #Parameters:
1629 # nothing
1631 #Returns:
1633 # integer -- gtk response ID
1634 def run(self):
1635 self.dialog.show_all()
1636 return self.dialog.run()
1638 # Write updated values to config file.
1640 #Parameters:
1642 # nothing
1644 #Returns:
1646 # nothing
1647 def save(self):
1648 if self.w_auto_detect.get_active():
1649 set_network_device("auto_detect")
1650 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
1651 else:
1652 # get the selected interface, using a work-around suggested by PyGTK Tutorial
1653 interface = self.w_interface.get_model()[self.w_interface.get_active()][0]
1654 self.confFile.set_opt('DEFAULT.interface', interface)
1655 set_network_device(interface)
1656 self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
1657 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
1658 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
1659 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
1660 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
1661 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
1662 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
1663 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
1664 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
1665 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
1666 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
1667 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
1668 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
1669 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
1670 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
1671 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
1672 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
1673 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
1674 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
1675 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
1676 self.confFile.write()
1678 # Remove preferences window.
1680 #Parameters:
1682 # nothing
1684 #Returns:
1686 # nothing
1687 def destroy(self):
1688 self.dialog.destroy()
1689 del self.dialog
1692 # Edit and return an AP profile.
1693 class profile_dialog:
1694 # Create a new profile_dialog.
1696 #Parameters:
1698 # 'parent' -- gtk.Object - Usually, the calling window.
1700 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
1702 #Returns:
1704 # profile_dialog instance
1705 def __init__( self, parent, profile ):
1706 global wifi_radar_icon
1707 self.parent = parent
1708 self.profile = profile
1709 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1710 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
1711 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1712 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
1713 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1714 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
1715 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1716 self.dialog.set_icon( icon )
1717 self.dialog.set_resizable( False )
1718 self.dialog.set_transient_for( self.parent.window )
1719 #self.dialog.set_size_request( 400, 400 )
1720 #################
1721 essid_table = gtk.Table( 1, 2, False )
1722 essid_table.set_row_spacings( 3 )
1723 essid_table.set_col_spacings( 3 )
1725 # The essid labels
1726 essid_table.attach( gtk.Label( 'Network Name' ), 0, 1, 0, 1 )
1727 # The essid textboxes
1728 self.essid_entry = gtk.Entry( 32 )
1729 self.essid_entry.set_text( self.profile['essid'] )
1730 essid_table.attach( self.essid_entry, 1, 2, 0, 1 )
1731 # Add the essid table to the dialog
1732 self.dialog.vbox.pack_start( essid_table, True, True, 5 )
1734 bssid_table = gtk.Table( 1, 2, False )
1735 bssid_table.set_row_spacings( 3 )
1736 bssid_table.set_col_spacings( 3 )
1737 # The bssid labels
1738 bssid_table.attach( gtk.Label( 'Network bssid' ), 0, 1, 0, 1 )
1739 # The bssid textboxes
1740 self.bssid_entry = gtk.Entry( 32 )
1741 self.bssid_entry.set_text( self.profile['bssid'] )
1742 bssid_table.attach( self.bssid_entry, 1, 2, 0, 1 )
1743 #self.key = gtk.Entry( 32 )
1744 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1745 # Add the bssid table to the dialog
1746 self.dialog.vbox.pack_start( bssid_table, True, True, 5 )
1748 # create the wifi expander
1749 self.wifi_expander = gtk.Expander( WIFI_SET_LABEL )
1750 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1751 wifi_table = gtk.Table( 4, 2, False )
1752 wifi_table.set_row_spacings( 3 )
1753 wifi_table.set_col_spacings( 3 )
1754 # The Wifi labels
1755 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
1756 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
1757 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
1758 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
1759 # The Wifi text boxes
1760 self.mode_combo = gtk.combo_box_new_text()
1761 for mode in self.WIFI_MODES:
1762 self.mode_combo.append_text( mode )
1763 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
1764 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
1765 self.channel_combo = gtk.combo_box_new_text()
1766 for channel in self.WIFI_CHANNELS:
1767 self.channel_combo.append_text( channel )
1768 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
1769 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
1771 self.key_entry = gtk.Entry( 64 )
1772 self.key_entry.set_text( self.profile['key'] )
1773 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
1775 self.security_combo = gtk.combo_box_new_text()
1776 for security in self.WIFI_SECURITY:
1777 self.security_combo.append_text( security )
1778 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
1779 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
1780 # Add the wifi table to the expander
1781 self.wifi_expander.add( wifi_table )
1782 # Add the expander to the dialog
1783 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
1785 # create the wpa expander
1786 self.wpa_expander = gtk.Expander( NO_WPA_LABEL )
1787 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
1788 wpa_table = gtk.Table( 1, 2, False )
1789 wpa_table.set_row_spacings( 3 )
1790 wpa_table.set_col_spacings( 3 )
1791 # The labels
1792 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1793 # The text boxes
1794 self.wpa_driver_entry = gtk.Entry()
1795 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
1796 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
1797 # Add the wpa table to the expander
1798 self.wpa_expander.add( wpa_table )
1799 # Add the expander to the dialog
1800 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
1802 # create the dhcp expander
1803 self.dhcp_expander = gtk.Expander( USE_DHCP_LABEL )
1804 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1805 ip_table = gtk.Table( 6, 2, False )
1806 ip_table.set_row_spacings( 3 )
1807 ip_table.set_col_spacings( 3 )
1808 # The IP labels
1809 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
1810 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
1811 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
1812 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
1813 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
1814 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
1815 # The IP text boxes
1816 self.ip_entry = gtk.Entry( 15 )
1817 self.ip_entry.set_text( self.profile['ip'] )
1818 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
1819 self.netmask_entry = gtk.Entry( 15 )
1820 self.netmask_entry.set_text( self.profile['netmask'] )
1821 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
1822 self.gw_entry = gtk.Entry( 15 )
1823 self.gw_entry.set_text( self.profile['gateway'] )
1824 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
1825 self.domain_entry = gtk.Entry( 32 )
1826 self.domain_entry.set_text( self.profile['domain'] )
1827 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
1828 self.dns1_entry = gtk.Entry( 15 )
1829 self.dns1_entry.set_text( self.profile['dns1'] )
1830 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
1831 self.dns2_entry = gtk.Entry( 15 )
1832 self.dns2_entry.set_text( self.profile['dns2'] )
1833 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
1834 # Add the ip table to the expander
1835 self.dhcp_expander.add( ip_table )
1836 # Add the expander to the dialog
1837 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
1839 # create the connection-building postpre expander
1840 self.con_pp_expander = gtk.Expander( CON_PP_LABEL )
1841 con_pp_table = gtk.Table( 2, 2, False )
1842 con_pp_table.set_row_spacings( 3 )
1843 con_pp_table.set_col_spacings( 3 )
1844 # The labels
1845 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1846 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1847 # The text boxes
1848 self.con_prescript_entry = gtk.Entry()
1849 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
1850 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
1851 self.con_postscript_entry = gtk.Entry()
1852 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
1853 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
1854 # Add the pp table to the expander
1855 self.con_pp_expander.add( con_pp_table )
1856 # Add the expander to the dialog
1857 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
1859 # create the disconnection postpre expander
1860 self.dis_pp_expander = gtk.Expander( DIS_PP_LABEL )
1861 dis_pp_table = gtk.Table( 2, 2, False )
1862 dis_pp_table.set_row_spacings( 3 )
1863 dis_pp_table.set_col_spacings( 3 )
1864 # The labels
1865 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1866 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1867 # The text boxes
1868 self.dis_prescript_entry = gtk.Entry()
1869 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
1870 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
1871 self.dis_postscript_entry = gtk.Entry()
1872 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
1873 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
1874 # Add the pp table to the expander
1875 self.dis_pp_expander.add( dis_pp_table )
1876 # Add the expander to the dialog
1877 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
1879 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
1881 #Parameters:
1883 # nothing
1885 #Returns:
1887 # dictionary or None -- a profile, or None on cancel
1888 def run( self ):
1889 self.dialog.show_all()
1890 if self.dialog.run():
1891 self.profile['known'] = True
1892 self.profile['essid'] = self.essid_entry.get_text().strip()
1893 self.profile['bssid'] = self.bssid_entry.get_text().strip()
1894 self.profile['key'] = self.key_entry.get_text().strip()
1895 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
1896 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
1897 self.profile['encrypted'] = ( self.profile['security'] != '' )
1898 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
1899 self.profile['protocol'] = 'g'
1900 self.profile['available'] = ( self.profile['signal'] > 0 )
1901 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
1902 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
1903 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
1904 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
1905 # wpa
1906 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
1907 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
1908 # dhcp
1909 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
1910 self.profile['ip'] = self.ip_entry.get_text().strip()
1911 self.profile['netmask'] = self.netmask_entry.get_text().strip()
1912 self.profile['gateway'] = self.gw_entry.get_text().strip()
1913 self.profile['domain'] = self.domain_entry.get_text().strip()
1914 self.profile['dns1'] = self.dns1_entry.get_text().strip()
1915 self.profile['dns2'] = self.dns2_entry.get_text().strip()
1916 return self.profile
1917 return None
1919 # Remove profile dialog.
1921 #Parameters:
1923 # nothing
1925 #Returns:
1927 # nothing
1928 def destroy( self ):
1929 self.dialog.destroy()
1930 del self.dialog
1932 # Respond to expanding/hiding IP segment.
1934 #Parameters:
1936 # 'widget' -- gtk.Widget - The widget sending the event.
1938 # 'data' -- tuple - List of arbitrary arguments (not used)
1940 #Returns:
1942 # nothing
1943 def toggle_use_dhcp( self, widget, data = None ):
1944 expanded = self.dhcp_expander.get_expanded()
1945 if expanded:
1946 self.dhcp_expander.set_label( USE_IP_LABEL )
1947 else:
1948 self.dhcp_expander.set_label( USE_DHCP_LABEL )
1950 # Respond to expanding/hiding WPA segment.
1952 #Parameters:
1954 # 'widget' -- gtk.Widget - The widget sending the event.
1956 # 'data' -- tuple - List of arbitrary arguments (not used)
1958 #Returns:
1960 # nothing
1961 def toggle_use_wpa( self, widget, data = None ):
1962 expanded = self.wpa_expander.get_expanded()
1963 if expanded:
1964 self.wpa_expander.set_label( USE_WPA_LABEL )
1965 else:
1966 self.wpa_expander.set_label( NO_WPA_LABEL )
1968 # Return the index where item matches a cell in array.
1970 #Parameters:
1972 # 'item' -- string - Item to find in array
1974 # 'array' -- list - List in which to find match.
1976 #Returns:
1978 # integer - 0 (no match) or higher (index of match)
1979 def get_array_index( self, item, array ):
1980 try:
1981 return array.index( item.strip() )
1982 except:
1983 pass
1984 return 0
1986 # Return the value in array[ index ]
1988 #Parameters:
1990 # 'index' -- integer - The index to look up.
1992 # 'array' -- list - List in which to look up value.
1994 #Returns:
1996 # string -- empty string (no match) or looked up value
1997 def get_array_item( self, index, array ):
1998 try:
1999 return array[ index ]
2000 except:
2001 pass
2002 return ''
2004 # A simple class for putting up a "Please wait" dialog so the user
2005 # doesn't think we've forgotten about them. Implements the status interface.
2006 class StatusWindow:
2007 # Create a new StatusWindow.
2009 #Parameters:
2011 # 'parent' -- gtk.Object - Usually, the calling window.
2013 #Returns:
2015 # StatusWindow instance
2017 #NOTE:
2019 # Sample implementation of status interface. Status interface
2020 #requires .show(), .update_message(message), and .hide() methods.
2021 def __init__( self, parent ):
2022 global wifi_radar_icon
2023 self.parent = parent
2024 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2025 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2026 self.dialog.set_icon( icon )
2027 self.lbl = gtk.Label("Please wait...")
2028 self.bar = gtk.ProgressBar()
2029 self.dialog.vbox.pack_start(self.lbl)
2030 self.dialog.vbox.pack_start(self.bar)
2031 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2032 self.timer = None
2034 # Change the message displayed to the user.
2036 #Parameters:
2038 # 'message' -- string - The message to show to the user.
2040 #Returns:
2042 # nothing
2043 def update_message( self, message ):
2044 self.lbl.set_text(message)
2046 # Update the StatusWindow progress bar.
2048 #Parameters:
2050 # nothing
2052 #Returns:
2054 # True -- always return True
2055 def update_window( self ):
2056 self.bar.pulse()
2057 return True
2059 # Display and operate the StatusWindow.
2061 #Parameters:
2063 # nothing
2065 #Returns:
2067 # nothing
2068 def run( self ):
2069 pass
2071 # Show all the widgets of the StatusWindow.
2073 #Parameters:
2075 # nothing
2077 #Returns:
2079 # nothing
2080 def show( self ):
2081 self.dialog.show_all()
2082 self.timer = gobject.timeout_add(250, self.update_window)
2083 return False
2085 # Hide all the widgets of the StatusWindow.
2087 #Parameters:
2089 # nothing
2091 #Returns:
2093 # nothing
2094 def hide( self ):
2095 if self.timer:
2096 gobject.source_remove(self.timer)
2097 self.timer = None
2098 self.dialog.hide_all()
2099 return False
2101 # Remove the StatusWindow.
2103 #Parameters:
2105 # nothing
2107 #Returns:
2109 # nothing
2110 def destroy( self ):
2111 if self.timer:
2112 gobject.source_remove(self.timer)
2113 self.dialog.destroy()
2114 del self.dialog
2117 # Manage a GTK About Dialog
2118 class about_dialog(gtk.AboutDialog):
2119 # Subclass GTK AboutDialog
2121 #Parameters:
2123 # nothing
2125 #Returns:
2127 # nothing
2128 def __init__( self ):
2129 global wifi_radar_icon
2131 gtk.AboutDialog.__init__(self)
2132 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"])
2133 self.set_comments("WiFi connection manager")
2134 self.set_copyright("Copyright 2004-2009 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2135 self.set_documenters(["Gary Case"])
2136 license = """
2137 This program is free software; you can redistribute it and/or modify
2138 it under the terms of the GNU General Public License as published by
2139 the Free Software Foundation; either version 2 of the License, or
2140 (at your option) any later version.
2142 This program is distributed in the hope that it will be useful,
2143 but WITHOUT ANY WARRANTY; without even the implied warranty of
2144 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2145 GNU General Public License for more details.
2147 You should have received a copy of the GNU General Public License
2148 along with this program; if not, write to the Free Software
2149 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2150 self.set_license(license)
2151 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2152 self.set_logo(logo)
2153 self.set_name("WiFi Radar")
2154 self.set_version(WIFI_RADAR_VERSION)
2155 self.set_website("http://wifi-radar.berlios.de")
2159 # Manage the configuration for the application, including reading and writing the config from/to a file.
2160 class ConfigFile(ConfigParser.SafeConfigParser):
2161 # Create a new ConfigFile.
2163 #Parameters:
2165 # 'filename' -- string - The configuration file's name.
2167 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2169 #Returns:
2171 # ConfigFile instance
2172 def __init__( self, filename, defaults, raw=False ):
2173 self.filename = filename
2174 self.raw = raw
2175 self.auto_profile_order = []
2176 ConfigParser.SafeConfigParser.__init__(self, defaults)
2178 # Set the contents of a section to values from a dictionary.
2180 #Parameters:
2182 # 'section_name' -- string - Configuration file section.
2184 # 'section_dict' -- dictionary - Values to add to section.
2186 #Returns:
2188 # nothing
2189 def set_section( self, section_name, section_dict ):
2190 try:
2191 self.add_section(section_name)
2192 except ConfigParser.DuplicateSectionError:
2193 pass
2194 for key in section_dict.keys():
2195 if type(section_dict[key]) == BooleanType:
2196 self.set_bool_opt(section_name + "." + key, section_dict[key])
2197 elif type(section_dict[key]) == IntType:
2198 self.set_int_opt(section_name + "." + key, section_dict[key])
2199 elif type(section_dict[key]) == FloatType:
2200 self.set_float_opt(section_name + "." + key, section_dict[key])
2201 else:
2202 self.set_opt(section_name + "." + key, section_dict[key])
2204 # Return the profile recorded in the specified section.
2206 #Parameters:
2208 # 'section_name' -- string - Configuration file section.
2210 #Returns:
2212 # dictionary or None - The specified profile or None if not found
2213 def get_profile( self, section_name ):
2214 if section_name in self.profiles():
2215 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' ]
2216 bool_types = [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
2217 int_types = [ 'signal' ]
2218 profile = {}
2219 for option in bool_types:
2220 profile[option] = self.get_opt_as_bool( section_name + "." + option )
2221 for option in int_types:
2222 profile[option] = self.get_opt_as_int( section_name + "." + option )
2223 for option in str_types:
2224 profile[option] = self.get_opt( section_name + "." + option )
2225 return profile
2226 return None
2228 # Get a config option and handle exceptions.
2230 #Parameters:
2232 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2233 # period and the option key. (E.g. "DEFAULT.interface")
2235 #Returns:
2237 # string or None - option value as string or None on failure
2238 def get_opt( self, option_path ):
2239 #print "ConfigFile.get_opt: ", option_path
2240 (section, option) = option_path.split('.')
2241 try:
2242 return self.get(section, option, self.raw)
2243 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2244 return None
2246 # Get a config option and return as a boolean type.
2248 #Parameters:
2250 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2251 # period and the option key. (E.g. "DEFAULT.interface")
2253 #Returns:
2255 # boolean - option value as boolean
2256 def get_opt_as_bool( self, option_path ):
2257 option = self.get_opt(option_path)
2258 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2259 return option
2260 if option == 'True':
2261 return True
2262 if option == 'False':
2263 return False
2264 raise ValueError, 'boolean option was not True or False'
2266 # Get a config option and return as an integer type.
2268 #Parameters:
2270 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2271 # period and the option key. (E.g. "DEFAULT.interface")
2273 #Returns:
2275 # integer- option value as integer
2276 def get_opt_as_int( self, option_path ):
2277 return int(float(self.get_opt(option_path)))
2279 # Convert boolean type to string and set config option.
2281 #Parameters:
2283 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2284 # period and the option key. (E.g. "DEFAULT.interface")
2286 # 'value' -- boolean - Value to set.
2288 #Returns:
2290 # nothing
2291 def set_bool_opt( self, option_path, value ):
2292 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2293 value == 'True'
2294 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2295 value == 'False'
2296 else:
2297 raise ValueError, 'cannot convert value to string'
2298 self.set_opt(option_path, repr(value))
2300 # Convert integer type to string and set config option.
2302 #Parameters:
2304 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2305 # period and the option key. (E.g. "DEFAULT.interface")
2307 # 'value' -- integer - Value to set.
2309 #Returns:
2311 # nothing
2312 def set_int_opt( self, option_path, value ):
2313 if not isinstance(value, IntType):
2314 raise ValueError, 'value is not an integer'
2315 self.set_opt(option_path, repr(value))
2317 # Convert float type to string and set config option.
2319 #Parameters:
2321 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2322 # period and the option key. (E.g. "DEFAULT.interface")
2324 # 'value' -- float - Value to set.
2326 #Returns:
2328 # nothing
2329 def set_float_opt( self, option_path, value ):
2330 if not isinstance(value, FloatType):
2331 raise ValueError, 'value is not a float'
2332 self.set_opt(option_path, repr(int(value)))
2334 # Set a config option while handling exceptions.
2336 #Parameters:
2338 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2339 # period and the option key. (E.g. "DEFAULT.interface")
2341 # 'value' -- string - Value to set.
2343 #Returns:
2345 # nothing
2346 def set_opt( self, option_path, value ):
2347 (section, option) = option_path.split('.')
2348 try:
2349 self.set(section, option, value)
2350 except ConfigParser.NoSectionError:
2351 self.add_section(section)
2352 self.set_opt(option_path, value)
2354 # Return a list of the section names which denote AP profiles.
2356 #Parameters:
2358 # nothing
2360 #Returns:
2362 # list - profile names
2363 def profiles( self ):
2364 profile_list = []
2365 for section in self.sections():
2366 if ':' in section:
2367 profile_list.append(section)
2368 return profile_list
2370 # Read configuration file from disk into instance variables.
2372 #Parameters:
2374 # nothing
2376 #Returns:
2378 # nothing
2379 def read( self ):
2380 fp = open( self.filename, "r" )
2381 self.readfp(fp)
2382 # convert the auto_profile_order to a list for ordering
2383 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2384 for ap in self.profiles():
2385 self.set_bool_opt( ap + '.known', True)
2386 if ap in self.auto_profile_order: continue
2387 self.auto_profile_order.append( ap )
2388 fp.close()
2390 # Write configuration file to disk from instance variables. Copied from
2391 # ConfigParser and modified to write options in alphabetical order.
2393 #Parameters:
2395 # nothing
2397 #Returns:
2399 # nothing
2400 def write( self ):
2401 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2402 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2403 fp = open( self.filename, "w" )
2404 # write DEFAULT section first
2405 if self._defaults:
2406 fp.write("[DEFAULT]\n")
2407 for key in sorted(self._defaults.keys()):
2408 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2409 fp.write("\n")
2410 # write non-profile sections first
2411 for section in self._sections:
2412 if section not in self.profiles():
2413 fp.write("[%s]\n" % section)
2414 for key in sorted(self._sections[section].keys()):
2415 if key != "__name__":
2416 fp.write("%s = %s\n" %
2417 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2418 fp.write("\n")
2419 # write profile sections
2420 for section in self._sections:
2421 if section in self.profiles():
2422 fp.write("[%s]\n" % section)
2423 for key in sorted(self._sections[section].keys()):
2424 if key != "__name__":
2425 fp.write("%s = %s\n" %
2426 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2427 fp.write("\n")
2428 fp.close()
2432 # Load our conf file and known profiles
2433 # Defaults, these may get overridden by values found in the conf file.
2434 config_defaults = { # The network interface you use.
2435 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2436 'interface': "auto_detect",
2437 # How long should the scan for access points last?
2438 'scan_timeout': '5',
2439 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2440 # Set the speak_up option to false if you do not have or want this.
2441 'speak_command': '/usr/bin/say',
2442 # Should I speak up when connecting to a network? (If you have a speech command)
2443 'speak_up': 'False',
2444 # You may set this to true for cards that require a "commit" command with iwconfig
2445 'commit_required': 'False',
2446 # You may set this to true for cards that require the interface to be brought up first
2447 'ifup_required': 'False',
2448 # Set the location of several important programs
2449 'iwlist_command': '/sbin/iwlist',
2450 'iwconfig_command': '/sbin/iwconfig',
2451 'ifconfig_command': '/sbin/ifconfig',
2452 'route_command': '/sbin/route',
2453 'auto_profile_order': '[]',
2454 'version': WIFI_RADAR_VERSION }
2456 config_dhcp = { # DHCP client
2457 'command': 'dhcpcd',
2458 # How long to wait for an IP addr from DHCP server
2459 'timeout': '30',
2460 # Arguments to use with DHCP client on connect
2461 'args': '-D -o -i dhcp_client -t %(timeout)s',
2462 # Argument to use with DHCP client on disconnect
2463 'kill_args': '-k',
2464 # The file where DHCP client PID is written
2465 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2467 config_wpa = { # WPA Supplicant
2468 'command': '/usr/sbin/wpa_supplicant',
2469 # Arguments to use with WPA Supplicant on connect
2470 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2471 # Arguments to use with WPA Supplicant on disconnect
2472 'kill_command': '',
2473 # Where the WPA Supplicant config file can be found
2474 'configuration': '/etc/wpa_supplicant.conf',
2475 # Driver to use with WPA Supplicant
2476 'driver': 'wext',
2477 # The file where WPA Supplicant PID is written
2478 'pidfile': '/var/run/wpa_supplicant.pid' }
2480 # initialize config, with defaults
2481 confFile = ConfigFile(CONF_FILE, config_defaults)
2482 confFile.set_section("DHCP", config_dhcp)
2483 confFile.set_section("WPA", config_wpa)
2485 if not os.path.isfile( CONF_FILE ):
2486 confFile.set_bool_opt('DEFAULT.new_file', True)
2487 else:
2488 if not os.access(CONF_FILE, os.R_OK):
2489 print "Can't open " + CONF_FILE + "."
2490 print "Are you root?"
2491 sys.exit()
2492 confFile.read()
2495 ####################################################################################################
2496 # Embedded Images
2497 wifi_radar_icon = [ ""
2498 "GdkP"
2499 "\0\0\22""7"
2500 "\2\1\0\2"
2501 "\0\0\1\214"
2502 "\0\0\0c"
2503 "\0\0\0O"
2504 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2505 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2506 "\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"
2507 "\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"
2508 "\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"
2509 "\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"
2510 "\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"
2511 "\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"
2512 "\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"
2513 "\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"
2514 "\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"
2515 "\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"
2516 "\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"
2517 "\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"
2518 "\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"
2519 "\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"
2520 "\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"
2521 "\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"
2522 "\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"
2523 "\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"
2524 "\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"
2525 "\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"
2526 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2527 "\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"
2528 "\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"
2529 "\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"
2530 "\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"
2531 "\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"
2532 "\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"
2533 "\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"
2534 "\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"
2535 "\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"
2536 "\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"
2537 "\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"
2538 "\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"
2539 "\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"
2540 "\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"
2541 "\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"
2542 "\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"
2543 "\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"
2544 "\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"
2545 "\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"
2546 "\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"
2547 "\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"
2548 "\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"
2549 "\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"
2550 "\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"
2551 "\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"
2552 "\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"
2553 "\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"
2554 "\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"
2555 "\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"
2556 "\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"
2557 "\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"
2558 "\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"
2559 "\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"
2560 "\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"
2561 "\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"
2562 "\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"
2563 "\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"
2564 "\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"
2565 "\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"
2566 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2567 "\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"
2568 "\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"
2569 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2570 "\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"
2571 "\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"
2572 "\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"
2573 "\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"
2574 "\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"
2575 "\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"
2576 "\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"
2577 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2578 "\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"
2579 "\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"
2580 "\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"
2581 "\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"
2582 "\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"
2583 "\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"
2584 "|\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"
2585 "\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"
2586 "\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"
2587 "\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"
2588 "\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"
2589 "\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"
2590 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2591 "\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"
2592 "\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"
2593 "\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"
2594 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
2595 "\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"
2596 "\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"
2597 "\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"
2598 "\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"
2599 "\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"
2600 "\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"
2601 "\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"
2602 "\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"
2603 "\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"
2604 "\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"
2605 "\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"
2606 "\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"
2607 "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"
2608 "\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"
2609 "\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"
2610 "\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"
2611 "\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"
2612 "\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"
2613 "\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"
2614 "\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"
2615 "\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"
2616 "\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"
2617 "\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"
2618 "\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"
2619 "\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"
2620 "\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"
2621 "\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"
2622 "\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|"
2623 "\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"
2624 "\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"
2625 "\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"
2626 "\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"
2627 "\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"
2628 "\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"
2629 "\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"
2630 "\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"
2631 "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"
2632 "\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"
2633 "\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"
2634 "\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"
2635 "\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"
2636 "\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"
2637 "\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"
2638 "\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"
2639 "\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"
2640 "\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"
2641 "\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"
2642 "\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"
2643 "\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"
2644 "\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"
2645 "\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"
2646 "\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"
2647 "\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"
2648 "\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"
2649 "\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"
2650 "\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"
2651 "\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"
2652 "\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"
2653 "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"
2654 "\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"
2655 "\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"
2656 "\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"
2657 "\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"
2658 "\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"
2659 "\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"
2660 "\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"
2661 "\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"
2662 "\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"
2663 "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"
2664 "\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"
2665 "\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"
2666 "\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"
2667 "\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"
2668 "\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"
2669 "\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"
2670 "\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"
2671 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2672 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2673 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2674 "\0"]
2676 known_profile_icon = [ ""
2677 "GdkP"
2678 "\0\0\5""0"
2679 "\2\1\0\2"
2680 "\0\0\0P"
2681 "\0\0\0\24"
2682 "\0\0\0\24"
2683 "\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"
2684 "\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"
2685 "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"
2686 "\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"
2687 "\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"
2688 "\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"
2689 "\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"
2690 "\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"
2691 "\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"
2692 "\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"
2693 "\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"
2694 "\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"
2695 "\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"
2696 "\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"
2697 "\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"
2698 "\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"
2699 "\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"
2700 "\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"
2701 "\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"
2702 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2703 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2704 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2705 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2706 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2707 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2708 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2709 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2710 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2711 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2712 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2713 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2714 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2715 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2716 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2717 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2718 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2719 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2720 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2721 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2722 "\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"
2723 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2724 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2725 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2726 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2727 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2728 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2729 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2730 "\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"
2731 "\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"
2732 "\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"
2733 "\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"
2734 "\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"]
2736 unknown_profile_icon = [ ""
2737 "GdkP"
2738 "\0\0\5\22"
2739 "\2\1\0\2"
2740 "\0\0\0P"
2741 "\0\0\0\24"
2742 "\0\0\0\24"
2743 "\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"
2744 "\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"
2745 "\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"
2746 "\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"
2747 "(\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"
2748 "\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"
2749 "#\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"
2750 "\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"
2751 "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"
2752 "\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"
2753 "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"
2754 "\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"
2755 "\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"
2756 "\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"
2757 "\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"
2758 "\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"
2759 "\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"
2760 "\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"
2761 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2762 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2763 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2764 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2765 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2766 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2767 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2768 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2769 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2770 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2771 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2772 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2773 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2774 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2775 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2776 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2777 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2778 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2779 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2780 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2781 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2782 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2783 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2784 "\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"
2785 "\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"
2786 "\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"
2787 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2789 signal_xpm_barely = [
2790 "20 20 10 1",
2791 " c None",
2792 ". c #C6C6C6",
2793 "+ c #CCCCCC",
2794 "@ c #DBDBDB",
2795 "# c #D3D3D3",
2796 "$ c #A9B099",
2797 "% c #95A173",
2798 "& c #6B8428",
2799 "* c #B4B7AC",
2800 "= c #80924D",
2801 " .+++.",
2802 " +@@@+",
2803 " +@@@+",
2804 " +@@@+",
2805 " +@@@+",
2806 " .++++#@@@+",
2807 " +@@@@@@@@+",
2808 " +@@@@@@@@+",
2809 " +@@@@@@@@+",
2810 " +@@@@@@@@+",
2811 " $%%%%#@@@@@@@@+",
2812 " %&&&&@@@@@@@@@+",
2813 " %&&&&@@@@@@@@@+",
2814 " %&&&&@@@@@@@@@+",
2815 " %&&&&@@@@@@@@@+",
2816 "*%%%%=&&&&@@@@@@@@@+",
2817 "%&&&&&&&&&@@@@@@@@@+",
2818 "%&&&&&&&&&@@@@@@@@@+",
2819 "%&&&&&&&&&@@@@@@@@@+",
2820 "*%%%%%%%%%+++++++++."
2824 signal_xpm_best = [
2825 "20 20 6 1",
2826 " c None",
2827 ". c #9DAABF",
2828 "+ c #7B96BF",
2829 "@ c #386EBF",
2830 "# c #5982BF",
2831 "$ c #AEB4BF",
2832 " .+++.",
2833 " +@@@+",
2834 " +@@@+",
2835 " +@@@+",
2836 " +@@@+",
2837 " .++++#@@@+",
2838 " +@@@@@@@@+",
2839 " +@@@@@@@@+",
2840 " +@@@@@@@@+",
2841 " +@@@@@@@@+",
2842 " .++++#@@@@@@@@+",
2843 " +@@@@@@@@@@@@@+",
2844 " +@@@@@@@@@@@@@+",
2845 " +@@@@@@@@@@@@@+",
2846 " +@@@@@@@@@@@@@+",
2847 "$++++#@@@@@@@@@@@@@+",
2848 "+@@@@@@@@@@@@@@@@@@+",
2849 "+@@@@@@@@@@@@@@@@@@+",
2850 "+@@@@@@@@@@@@@@@@@@+",
2851 "$++++++++++++++++++."
2854 signal_xpm_none = [
2855 "20 20 6 1",
2856 " c None",
2857 ". c #C6C6C6",
2858 "+ c #CCCCCC",
2859 "@ c #DBDBDB",
2860 "# c #D3D3D3",
2861 "$ c #C2C2C2",
2862 " .+++.",
2863 " +@@@+",
2864 " +@@@+",
2865 " +@@@+",
2866 " +@@@+",
2867 " .++++#@@@+",
2868 " +@@@@@@@@+",
2869 " +@@@@@@@@+",
2870 " +@@@@@@@@+",
2871 " +@@@@@@@@+",
2872 " .++++#@@@@@@@@+",
2873 " +@@@@@@@@@@@@@+",
2874 " +@@@@@@@@@@@@@+",
2875 " +@@@@@@@@@@@@@+",
2876 " +@@@@@@@@@@@@@+",
2877 "$++++#@@@@@@@@@@@@@+",
2878 "+@@@@@@@@@@@@@@@@@@+",
2879 "+@@@@@@@@@@@@@@@@@@+",
2880 "+@@@@@@@@@@@@@@@@@@+",
2881 "$++++++++++++++++++."
2884 signal_xpm_ok = [
2885 "20 20 10 1",
2886 " c None",
2887 ". c #C6C6C6",
2888 "+ c #CCCCCC",
2889 "@ c #DBDBDB",
2890 "# c #A1A5B2",
2891 "$ c #848DA5",
2892 "% c #D3D3D3",
2893 "& c #4A5B8C",
2894 "* c #677498",
2895 "= c #B0B2B8",
2896 " .+++.",
2897 " +@@@+",
2898 " +@@@+",
2899 " +@@@+",
2900 " +@@@+",
2901 " #$$$$%@@@+",
2902 " $&&&&@@@@+",
2903 " $&&&&@@@@+",
2904 " $&&&&@@@@+",
2905 " $&&&&@@@@+",
2906 " #$$$$*&&&&@@@@+",
2907 " $&&&&&&&&&@@@@+",
2908 " $&&&&&&&&&@@@@+",
2909 " $&&&&&&&&&@@@@+",
2910 " $&&&&&&&&&@@@@+",
2911 "=$$$$*&&&&&&&&&@@@@+",
2912 "$&&&&&&&&&&&&&&@@@@+",
2913 "$&&&&&&&&&&&&&&@@@@+",
2914 "$&&&&&&&&&&&&&&@@@@+",
2915 "=$$$$$$$$$$$$$$++++."
2919 signal_xpm_low = [
2920 "20 20 8 1",
2921 " c None",
2922 ". c #C6C6C6",
2923 "+ c #CCCCCC",
2924 "@ c #DBDBDB",
2925 "# c #D3D3D3",
2926 "$ c #BFB0B5",
2927 "% c #C18799",
2928 "& c #C54F74",
2929 " .+++.",
2930 " +@@@+",
2931 " +@@@+",
2932 " +@@@+",
2933 " +@@@+",
2934 " .++++#@@@+",
2935 " +@@@@@@@@+",
2936 " +@@@@@@@@+",
2937 " +@@@@@@@@+",
2938 " +@@@@@@@@+",
2939 " .++++#@@@@@@@@+",
2940 " +@@@@@@@@@@@@@+",
2941 " +@@@@@@@@@@@@@+",
2942 " +@@@@@@@@@@@@@+",
2943 " +@@@@@@@@@@@@@+",
2944 "$%%%%#@@@@@@@@@@@@@+",
2945 "%&&&&@@@@@@@@@@@@@@+",
2946 "%&&&&@@@@@@@@@@@@@@+",
2947 "%&&&&@@@@@@@@@@@@@@+",
2948 "$%%%%++++++++++++++."
2952 ####################################################################################################
2953 # Make so we can be imported
2954 if __name__ == "__main__":
2955 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
2956 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
2957 else:
2958 import gtk, gobject
2959 gtk.gdk.threads_init()
2960 apQueue = Queue.Queue(100)
2961 commQueue = Queue.Queue(2)
2962 threading.Thread( None, scanning_thread, None, ( confFile, apQueue, commQueue ) ).start()
2963 main_radar_window = radar_window(confFile, apQueue, commQueue)
2964 gobject.timeout_add( 500, main_radar_window.update_plist_items )
2965 main_radar_window.main()