DOC FIX: correct documentation on shellcmd()
[wifi-radar.git] / wifi-radar
blob31163d093ce86827f78eefa1583a5308cab916e8
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': '',
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 if not self.get_current_ip():
450 status.update_message("Could not get IP address!")
451 if sys.modules.has_key("gtk"):
452 while gtk.events_pending():
453 gtk.main_iteration(False)
454 sleep(1)
455 if self.state:
456 self.disconnect_interface()
457 else:
458 status.update_message("Got IP address. Done.")
459 self.state = True
460 if sys.modules.has_key("gtk"):
461 while gtk.events_pending():
462 gtk.main_iteration(False)
463 sleep(2)
464 # Re-enable iwlist
465 try:
466 self.commQueue.put("scan")
467 except Queue.Full:
468 pass
469 else:
470 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'] )
471 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
472 resolv_contents = ''
473 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
474 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
475 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
476 if ( resolv_contents != '' ):
477 resolv_file=open('/etc/resolv.conf', 'w')
478 resolv_file.write(s)
479 resolv_file.close
480 if not shellcmd([ifconfig_command]): return
481 if not shellcmd([route_command]): return
482 self.state = True
483 # Let's run the connection postscript
484 con_postscript = self.profile['con_postscript']
485 if self.profile['con_postscript'].strip() != '':
486 if __debug__: print "executing connection postscript:", self.profile['con_postscript']
487 shellcmd([self.profile['con_postscript']], environment={"WIFIRADAR_IP": self.get_current_ip(), "WIFIRADAR_ESSID": self.get_current_essid(), "WIFIRADAR_BSSID": self.get_current_bssid(), "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
488 status.hide()
490 # Disconnect from the AP with which a connection has been established/attempted.
492 #Parameters:
494 # nothing
496 #Returns:
498 # nothing
499 def disconnect_interface( self ):
500 msg = "Disconnecting"
501 say( msg )
502 if __debug__: print msg
503 # Pause scanning while manipulating card
504 try:
505 self.commQueue.put("pause")
506 except Queue.Full:
507 pass
508 if self.state:
509 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
510 # Let's run the disconnection prescript
511 if self.profile['dis_prescript'].strip() != '':
512 if __debug__: print "executing disconnection prescript:", self.profile['dis_prescript']
513 shellcmd([self.profile['dis_prescript']], environment={"WIFIRADAR_IP": self.get_current_ip(), "WIFIRADAR_ESSID": self.get_current_essid(), "WIFIRADAR_BSSID": self.get_current_bssid(), "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
514 if __debug__: print "Kill off any existing DHCP clients running..."
515 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
516 if __debug__: print "Killing existing DHCP..."
517 try:
518 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
519 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
520 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
521 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
522 if __debug__: print "DHCP command", dhcp_command
523 # call DHCP client command and wait for return
524 if not shellcmd(dhcp_command): return
525 else:
526 if __debug__: print "killing DHCP manually."
527 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
528 except OSError:
529 print "failed to kill DHCP client"
530 if __debug__: print "Kill off any existing WPA supplicants running..."
531 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
532 if __debug__: print "Killing existing WPA supplicant..."
533 try:
534 if not self.confFile.get_opt('WPA.kill_command') != '':
535 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
536 if not shellcmd(wpa_command): return
537 else:
538 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
539 except OSError:
540 print "failed to kill WPA supplicant"
541 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
542 if __debug__: print "Let's clear out the wireless stuff"
543 if __debug__: print 'Now take the interface down'
544 # taking down the interface too quickly can crash my system, so pause a moment
545 sleep(1)
546 self.if_change('down')
547 if __debug__: print 'Since it may be brought back up by the next scan, lets unset its IP'
548 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), '0.0.0.0'])
549 # Let's run the disconnection postscript
550 if self.profile['dis_postscript'].strip() != '':
551 if __debug__: print "executing disconnection postscript:", self.profile['dis_postscript']
552 shellcmd([self.profile['dis_postscript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
553 self.state = False
554 if __debug__: print 'Disconnect complete.'
555 # Begin scanning again
556 try:
557 self.commQueue.put("scan")
558 except Queue.Full:
559 pass
561 # Returns the current IP, if any, by calling ifconfig.
563 #Parameters:
565 # nothing
567 #Returns:
569 # string or None -- the IP address or None (if no there is no current connection)
570 def get_current_ip( self ):
571 ifconfig_command = [ confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface') ]
572 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
573 # Be careful to the language (inet adr: in French for example)
575 # Hi Brian
577 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
578 # There the string in ifconfig is inet Adresse for the IP which isn't
579 # found by the current get_current_ip function in wifi-radar. I changed
580 # the according line (#289; gentoo, v1.9.6-r1) to
581 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
582 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
584 # I'd be happy if you could incorporate this small change because as now
585 # I've got to change the file every time it is updated.
587 # Best wishes
589 # Simon
590 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
591 line = ifconfig_info.read()
592 if ip_re.search( line ):
593 return ip_re.search( line ).group(1)
594 return None
596 # Returns the current ESSID, if any, by calling iwconfig.
598 #Parameters:
600 # nothing
602 #Returns:
604 # string or None -- the ESSID or None (if no there is no current association)
605 def get_current_essid( self ):
606 """Returns the current ESSID if any by calling iwconfig"""
607 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
608 # Be careful to the language (inet adr: in French for example)
609 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
610 line = iwconfig_info.read()
611 if essid_re.search( line ):
612 return essid_re.search( line ).group(2)
613 return None
615 # Returns the current BSSID, if any, by calling iwconfig.
617 #Parameters:
619 # nothing
621 #Returns:
623 # string or None -- the BSSID or None (if no there is no current association)
624 def get_current_bssid( self ):
625 """Returns the current BSSID if any by calling iwconfig"""
626 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
627 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
628 line = iwconfig_info.read()
629 if bssid_re.search( line ):
630 return bssid_re.search( line ).group(2)
631 return None
635 # The main user interface window for WiFi Radar. This class also is the control
636 # center for most of the rest of the operations.
637 class radar_window:
638 # Create a new radar_window.
640 #Parameters:
642 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
644 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
646 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
648 #Returns:
650 # radar_window instance
651 def __init__( self, confFile, apQueue, commQueue ):
652 global signal_xpm_none
653 global signal_xpm_low
654 global signal_xpm_barely
655 global signal_xpm_ok
656 global signal_xpm_best
657 global known_profile_icon
658 global unknown_profile_icon
659 global wifi_radar_icon
661 self.confFile = confFile
662 self.apQueue = apQueue
663 self.commandQueue = commQueue
664 self.access_points = {}
665 self.connection = None
667 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
668 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
669 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
670 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
671 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
672 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
673 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
674 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
675 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
676 self.window.set_icon( icon )
677 self.window.set_border_width( 10 )
678 self.window.set_size_request( 550, 300 )
679 self.window.set_title( "WiFi Radar" )
680 self.window.connect( 'delete_event', self.delete_event )
681 self.window.connect( 'destroy', self.destroy )
682 # let's create all our widgets
683 self.current_network = gtk.Label()
684 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
685 self.current_network.show()
686 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
687 self.close_button.show()
688 self.close_button.connect( 'clicked', self.delete_event, None )
689 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
690 self.about_button.show()
691 self.about_button.connect( 'clicked', self.show_about_info, None )
692 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
693 self.preferences_button.show()
694 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
695 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
696 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
697 self.plist = gtk.TreeView( self.pstore )
698 # The icons column, known and encryption
699 self.pix_cell = gtk.CellRendererPixbuf()
700 self.wep_cell = gtk.CellRendererPixbuf()
701 self.icons_cell = gtk.CellRendererText()
702 self.icons_col = gtk.TreeViewColumn()
703 self.icons_col.pack_start( self.pix_cell, False )
704 self.icons_col.pack_start( self.wep_cell, False )
705 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
706 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
707 self.plist.append_column( self.icons_col )
708 # The AP column
709 self.ap_cell = gtk.CellRendererText()
710 self.ap_col = gtk.TreeViewColumn( "Access Point" )
711 self.ap_col.pack_start( self.ap_cell, True )
712 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
713 self.plist.append_column( self.ap_col )
714 # The signal column
715 self.sig_cell = gtk.CellRendererPixbuf()
716 self.signal_col = gtk.TreeViewColumn( "Signal" )
717 self.signal_col.pack_start( self.sig_cell, True )
718 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
719 self.plist.append_column( self.signal_col )
720 # The mode column
721 self.mode_cell = gtk.CellRendererText()
722 self.mode_col = gtk.TreeViewColumn( "Mode" )
723 self.mode_col.pack_start( self.mode_cell, True )
724 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
725 self.plist.append_column( self.mode_col )
726 # The protocol column
727 self.prot_cell = gtk.CellRendererText()
728 self.protocol_col = gtk.TreeViewColumn( "802.11" )
729 self.protocol_col.pack_start( self.prot_cell, True )
730 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
731 self.plist.append_column( self.protocol_col )
732 # The channel column
733 self.channel_cell = gtk.CellRendererText()
734 self.channel_col = gtk.TreeViewColumn( "Channel" )
735 self.channel_col.pack_start( self.channel_cell, True )
736 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
737 self.plist.append_column( self.channel_col )
738 # DnD Ordering
739 self.plist.set_reorderable( True )
740 self.pstore.connect( 'row-changed', self.update_auto_profile_order )
741 # enable/disable buttons based on the selected network
742 self.selected_network = self.plist.get_selection()
743 self.selected_network.connect( 'changed', self.on_network_selection, None )
744 # the list scroll bar
745 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
746 sb.show()
747 self.plist.show()
748 # Add New button
749 self.new_button = gtk.Button( "_New" )
750 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
751 self.new_button.show()
752 # Add Configure button
753 self.edit_button = gtk.Button( "C_onfigure" )
754 self.edit_button.connect( 'clicked', self.edit_profile, None )
755 self.edit_button.show()
756 self.edit_button.set_sensitive(False)
757 # Add Delete button
758 self.delete_button = gtk.Button( "_Delete" )
759 self.delete_button.connect( 'clicked', self.delete_profile, None )
760 self.delete_button.show()
761 self.delete_button.set_sensitive(False)
762 # Add Connect button
763 self.connect_button = gtk.Button( "Co_nnect" )
764 self.connect_button.connect( 'clicked', self.connect_profile, None )
765 # Add Disconnect button
766 self.disconnect_button = gtk.Button( "D_isconnect" )
767 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
768 # lets add our widgets
769 rows = gtk.VBox( False, 3 )
770 net_list = gtk.HBox( False, 0 )
771 listcols = gtk.HBox( False, 0 )
772 prows = gtk.VBox( False, 0 )
773 # lets start packing
774 # the network list
775 net_list.pack_start( self.plist, True, True, 0 )
776 net_list.pack_start( sb, False, False, 0 )
777 # the rows level
778 rows.pack_start( net_list , True, True, 0 )
779 rows.pack_start( self.current_network, False, True, 0 )
780 # the list columns
781 listcols.pack_start( rows, True, True, 0 )
782 listcols.pack_start( prows, False, False, 5 )
783 # the list buttons
784 prows.pack_start( self.new_button, False, False, 2 )
785 prows.pack_start( self.edit_button, False, False, 2 )
786 prows.pack_start( self.delete_button, False, False, 2 )
787 prows.pack_end( self.connect_button, False, False, 2 )
788 prows.pack_end( self.disconnect_button, False, False, 2 )
790 self.window.action_area.pack_start( self.about_button )
791 self.window.action_area.pack_start( self.preferences_button )
792 self.window.action_area.pack_start( self.close_button )
794 rows.show()
795 prows.show()
796 listcols.show()
797 self.window.vbox.add( listcols )
798 self.window.vbox.set_spacing( 3 )
799 self.window.show_all()
801 # Now, immediately hide these two. The proper one will be
802 # displayed later, based on interface state. -BEF-
803 self.disconnect_button.hide()
804 self.connect_button.hide()
805 self.connect_button.set_sensitive(False)
807 # set up connection manager for later use
808 self.connection = ConnectionManager( self.confFile, self.commandQueue )
809 # set up status window for later use
810 self.status_window = StatusWindow( self )
811 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
813 # Add our known profiles in order
814 for ap in self.confFile.auto_profile_order:
815 ap = ap.strip()
816 self.access_points[ ap ] = self.confFile.get_profile( ap )
817 wep = None
818 if self.access_points[ ap ]['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
819 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'] ] )
820 # This is the first run (or, at least, no config file was present), so pop up the preferences window
821 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
822 self.confFile.remove_option('DEFAULT', 'new_file')
823 self.edit_preferences(self.preferences_button)
825 # Begin running radar_window in Gtk event loop.
827 #Parameters:
829 # nothing
831 #Returns:
833 # nothing
834 def main( self ):
835 gtk.main()
837 # Write the config file to disk and quit application.
839 #Parameters:
841 # 'widget' -- gtk.Widget - The widget sending the event.
843 #Returns:
845 # nothing
846 def destroy( self, widget = None):
847 if self.status_window:
848 self.status_window.destroy()
849 gtk.main_quit()
851 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
853 #Parameters:
855 # 'widget' -- gtk.Widget - The widget sending the event.
857 # 'data' -- tuple - list of arbitrary arguments (not used)
859 #Returns:
861 # boolean -- always return False (i.e. do not propigate the signal which called)
862 def delete_event( self, widget, data = None ):
863 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
864 try:
865 self.commandQueue.put("exit", True)
866 except Queue.Full:
867 pass
868 # Save the preferred networks order
869 self.update_auto_profile_order()
870 self.destroy()
871 return False
873 # Updates the on-screen profiles list.
875 #Parameters:
877 # nothing
879 #Returns:
881 # boolean -- always return True
882 def update_plist_items( self ):
883 # Indicate to PyGtk that only one Gtk thread should run here
884 gtk.gdk.threads_enter()
885 # update the current ip and essid
886 # set the state of connect/disconnect buttons based on whether we have an IP address
887 if self.connection:
888 if self.connection.state:
889 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() ) )
890 self.connect_button.hide()
891 self.disconnect_button.show()
892 else:
893 self.current_network.set_text( "Not Connected." )
894 self.disconnect_button.hide()
895 self.connect_button.show()
897 while True:
898 # Get profiles scanned by iwlist
899 try:
900 profile = self.apQueue.get_nowait()
901 except Queue.Empty:
902 break
903 else:
904 prow_iter = self.get_row_by_ap( profile['essid'], profile['bssid'] )
905 wep = None
906 if prow_iter != None:
907 # the AP is in the list of APs on the screen
908 apname = make_section_name(profile['essid'], profile['bssid'])
909 if self.access_points.has_key(apname):
910 # This AP has been configured and is/should be stored in the config file
911 profile['known'] = self.access_points[apname]['known']
912 self.access_points[apname]['available'] = profile['available']
913 self.access_points[apname]['encrypted'] = profile['encrypted']
914 self.access_points[apname]['signal'] = profile['signal']
915 self.access_points[apname]['mode'] = profile['mode']
916 self.access_points[apname]['protocol'] = profile['protocol']
917 self.access_points[apname]['channel'] = profile['channel']
918 # Set the 'known' values; False is default, overridden to True by self.access_points
919 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known( profile[ 'known' ] ))
920 self.pstore.set_value(prow_iter, 2, profile[ 'known' ])
921 self.pstore.set_value(prow_iter, 3, profile['available'])
922 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
923 self.pstore.set_value(prow_iter, 4, wep)
924 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal( profile['signal'] ))
925 self.pstore.set_value(prow_iter, 6, profile['mode'])
926 self.pstore.set_value(prow_iter, 7, profile['protocol'])
927 self.pstore.set_value(prow_iter, 8, profile['channel'])
928 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
929 #for val in self.pstore[prow_iter]:
930 #print val,
931 else:
932 # the AP is not in the list of APs on the screen
933 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'] ] )
934 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
935 # Allow other Gtk threads to run
936 gtk.gdk.threads_leave()
937 #print "update_plist_items: Empty apQueue"
938 return True
940 # Return the proper icon for a value of known.
942 #Parameters:
944 # 'known' -- boolean - Whether the AP is known (i.e. configured)
946 #Returns:
948 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
949 def pixbuf_from_known( self, known ):
950 """ return the proper icon for value of known """
951 if known:
952 return self.known_profile_icon
953 else:
954 return self.unknown_profile_icon
956 # Return an icon indicating the signal level.
958 #Parameters:
960 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
962 #Returns:
964 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
965 def pixbuf_from_signal( self, signal ):
966 signal = int( signal )
967 #print "signal level:", signal
968 if signal < 3:
969 return self.signal_none_pb
970 elif signal < 12:
971 return self.signal_low_pb
972 elif signal < 20:
973 return self.signal_barely_pb
974 elif signal < 35:
975 return self.signal_ok_pb
976 elif signal >= 35:
977 return self.signal_best_pb
978 else:
979 return None
981 # Return row which holds specified ESSID and BSSID.
983 #Parameters:
985 # 'essid' -- string - ESSID to match
987 # 'bssid' -- string - BSSID to match
989 #Returns:
991 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
992 def get_row_by_ap( self, essid, bssid ):
993 for row in self.pstore:
994 if ( row[0] == essid + "\n" + bssid ):
995 #print "matched:", row.iter, essid, bssid
996 return row.iter
997 return None
999 # Enable/disable buttons based on the selected network.
1001 #Parameters:
1003 # 'widget' -- gtk.Widget - The widget sending the event.
1005 # 'data' -- tuple - list of arbitrary arguments (not used)
1007 #Returns:
1009 # nothing
1010 def on_network_selection( self, widget, data = None ):
1011 ( store, selected_iter ) = self.selected_network.get_selected()
1012 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
1013 # if no networks are selected, disable all buttons except New
1014 # (this occurs after a drag-and-drop)
1015 if selected_iter == None:
1016 self.edit_button.set_sensitive(False)
1017 self.delete_button.set_sensitive(False)
1018 self.connect_button.set_sensitive(False)
1019 return
1020 # enable/disable buttons
1021 self.connect_button.set_sensitive(True)
1022 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1023 self.edit_button.set_sensitive(True)
1024 self.delete_button.set_sensitive(True)
1025 else:
1026 self.edit_button.set_sensitive(True)
1027 self.delete_button.set_sensitive(False)
1029 # Init and run the about dialog
1031 #Parameters:
1033 # 'widget' -- gtk.Widget - The widget sending the event.
1035 # 'data' -- tuple - list of arbitrary arguments (not used)
1037 #Returns:
1039 # nothing
1040 def show_about_info( self, widget, data=None ):
1041 about = about_dialog()
1042 about.run()
1043 about.destroy()
1045 # Init and run the preferences 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 edit_preferences( self, widget, data=None ):
1057 # get raw strings from config file
1058 self.confFile.raw = True
1059 prefs = preferences_dialog( self, self.confFile )
1060 response = prefs.run()
1061 prefs.destroy()
1062 if response == int(gtk.RESPONSE_ACCEPT):
1063 prefs.save()
1064 # get cooked strings from config file
1065 self.confFile.raw = False
1067 # Respond to a request to create a new AP profile
1069 #Parameters:
1071 # 'widget' -- gtk.Widget - The widget sending the event.
1073 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1075 # 'data' -- tuple - list of arbitrary arguments (not used)
1077 #Returns:
1079 # boolean -- True if a profile was created and False if profile creation was canceled.
1080 def create_new_profile( self, widget, profile, data=None ):
1081 profile_editor = profile_dialog( self, profile )
1082 profile = profile_editor.run()
1083 profile_editor.destroy()
1084 if profile:
1085 apname = make_section_name( profile['essid'], profile['bssid'] )
1086 # Check that the ap does not exist already
1087 if apname in self.confFile.profiles():
1088 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) )
1089 dlg.run()
1090 dlg.destroy()
1091 del dlg
1092 # try again
1093 self.access_points[ apname ] = profile
1094 self.confFile.set_section( apname, profile )
1095 # if it is not in the auto_profile_order add it
1096 if apname not in self.confFile.auto_profile_order:
1097 self.confFile.auto_profile_order.append(apname)
1098 # add to the store
1099 wep = None
1100 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1101 self.confFile.write()
1102 return True
1103 else:
1104 # Did not create new profile
1105 return False
1107 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1108 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1110 #Parameters:
1112 # 'widget' -- gtk.Widget - The widget sending the event.
1114 # 'data' -- tuple - list of arbitrary arguments (not used)
1116 #Returns:
1118 # nothing
1119 def edit_profile( self, widget, data=None ):
1120 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1121 if not selected_iter: return
1122 row_start = str(self.pstore.get_value( selected_iter, 0 )).split("\n")
1123 apname = make_section_name( row_start[0], row_start[1] )
1124 profile = self.confFile.get_profile( apname )
1125 if profile:
1126 profile_editor = profile_dialog( self, profile )
1127 profile = profile_editor.run()
1128 profile_editor.destroy()
1129 if profile:
1130 if __debug__:
1131 print "Got edited profile ", profile
1132 apname = make_section_name( profile['essid'], profile['bssid'] )
1133 self.access_points[ apname ] = profile
1134 self.confFile.set_section( apname, profile )
1135 self.confFile.write()
1136 else:
1137 profile = get_new_profile()
1138 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1139 self.create_new_profile( widget, profile, data )
1141 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1143 #Parameters:
1145 # 'widget' -- gtk.Widget - The widget sending the event.
1147 # 'data' -- tuple - list of arbitrary arguments (not used)
1149 #Returns:
1151 # nothing
1152 def delete_profile( self, widget, data=None ):
1153 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1154 if not selected_iter: return
1155 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1156 known = store.get_value( selected_iter, 1 )
1157 if not known: return
1158 dlg = gtk.MessageDialog(
1159 self.window,
1160 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1161 gtk.MESSAGE_QUESTION,
1162 gtk.BUTTONS_YES_NO,
1163 "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid) )
1164 res = dlg.run()
1165 dlg.destroy()
1166 del dlg
1167 if res == gtk.RESPONSE_NO: return
1168 # Remove it
1169 apname = make_section_name( essid, bssid )
1170 del self.access_points[ apname ]
1171 self.confFile.remove_section( apname )
1172 if __debug__: print "delete_profile: ", apname, ":", self.confFile.auto_profile_order
1173 if apname in self.confFile.auto_profile_order: self.confFile.auto_profile_order.remove(apname)
1174 self.pstore.remove( selected_iter )
1175 # Let's save our current state
1176 self.update_auto_profile_order()
1177 self.confFile.write()
1179 # Respond to a request to connect to an AP.
1181 #Parameters:
1183 # 'widget' -- gtk.Widget - The widget sending the event.
1185 # 'profile' -- dictionary - The AP profile to which to connect.
1187 # 'data' -- tuple - list of arbitrary arguments (not used)
1189 #Returns:
1191 # nothing
1192 def connect_profile( self, widget, profile, data=None ):
1193 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1194 if not selected_iter: return
1195 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1196 known = store.get_value( selected_iter, 2 )
1197 if not known:
1198 if data != 'noconnect':
1199 dlg = gtk.MessageDialog(
1200 self.window,
1201 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1202 gtk.MESSAGE_QUESTION,
1203 gtk.BUTTONS_YES_NO,
1204 "This network does not have a profile configured.\n\nWould you like to create one now?" )
1205 res = dlg.run()
1206 dlg.destroy()
1207 del dlg
1208 if res == gtk.RESPONSE_NO: return
1209 profile = get_new_profile()
1210 profile['essid'] = essid
1211 profile['bssid'] = bssid
1212 if not self.create_new_profile( widget, profile, data ):
1213 return
1214 apname = make_section_name( essid, bssid )
1215 self.connection.connect_to_network(self.access_points[apname], self.status_window)
1217 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1219 #Parameters:
1221 # 'widget' -- gtk.Widget - The widget sending the event.
1223 # 'data' -- tuple - list of arbitrary arguments (not used)
1225 #Returns:
1227 # nothing
1228 def disconnect_profile( self, widget, data=None ):
1229 if data == "cancel":
1230 self.status_window.update_message("Canceling connection...")
1231 if sys.modules.has_key("gtk"):
1232 while gtk.events_pending():
1233 gtk.main_iteration(False)
1234 sleep(1)
1235 self.connection.disconnect_interface()
1237 # Update the config file auto profile order from the on-screen order
1239 #Parameters:
1241 # 'widget' -- gtk.Widget - The widget sending the event.
1243 # 'data' -- tuple - list of arbitrary arguments (not used)
1245 # 'data2' -- tuple - list of arbitrary arguments (not used)
1247 #Returns:
1249 # nothing
1250 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1251 # recreate the auto_profile_order
1252 auto_profile_order = []
1253 piter = self.pstore.get_iter_first()
1254 while piter:
1255 # only if it's known
1256 if self.pstore.get_value( piter, 2 ) == True:
1257 row_start = str(self.pstore.get_value( piter, 0 )).split("\n")
1258 auto_profile_order.append( make_section_name( row_start[0], row_start[1] ) )
1259 piter = self.pstore.iter_next( piter )
1260 self.confFile.auto_profile_order = auto_profile_order
1263 # Button to allow user to choose a file and put value into specified gtk.Entry
1264 class file_browse_button(gtk.Button):
1265 # Create a button to simulate a File/Open
1267 #Parameters:
1269 # 'parent' -- gtk.Object -- Usually, the calling window.
1271 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1273 #Returns:
1275 # file_browse_button instance
1276 def __init__( self, parent, entry ):
1277 self.parent_window = parent
1278 self.entry = entry
1279 gtk.Button.__init__(self, "Browse", None)
1280 #self.
1281 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)
1282 self.connect("clicked", self.browse_files)
1284 # Show filechooser dialog and get user selection
1286 #Parameters:
1288 # 'widget' -- gtk.Widget -- The widget sending the event.
1290 #Returns:
1292 # nothing
1294 #NOTES:
1296 # updates entry value
1298 def browse_files( self, widget ):
1299 self.browser_dialog.set_filename(self.entry.get_text())
1300 self.browser_dialog.run()
1301 self.entry.set_text(self.browser_dialog.get_filename())
1302 self.browser_dialog.destroy()
1305 # The preferences dialog. Edits non-profile sections of the config file.
1306 class preferences_dialog:
1307 # Create a new preferences_dialog.
1309 #Parameters:
1311 # 'parent' -- gtk.Object - Usually, the calling window.
1313 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1315 #Returns:
1317 # preferences_dialog instance
1318 def __init__( self, parent, confFile ):
1319 global wifi_radar_icon
1320 self.parent = parent
1321 self.confFile = confFile
1322 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1323 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1324 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1325 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1326 self.dialog.set_icon( icon )
1327 self.dialog.set_resizable( True )
1328 self.dialog.set_transient_for( self.parent.window )
1329 self.tooltips = gtk.Tooltips()
1331 # set up preferences widgets
1333 # build everything in a tabbed notebook
1334 self.prefs_notebook = gtk.Notebook()
1336 ### General tab
1337 self.general_page = gtk.VBox()
1338 # auto detect wireless device
1339 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1341 print "prefs: ", self.confFile.get_opt('DEFAULT.interface')
1343 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1344 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1345 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1346 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1348 # network interface selecter
1349 self.w_interface = gtk.combo_box_entry_new_text()
1350 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE).stdout
1351 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1352 for device in wireless_devices:
1353 if device != self.confFile.get_opt('DEFAULT.interface'):
1354 self.w_interface.append_text(device)
1355 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1356 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1357 self.w_interface.set_active(0)
1358 self.w_interface_label = gtk.Label("Wireless device")
1359 self.w_hbox1 = gtk.HBox(False, 0)
1360 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1361 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1362 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1363 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1365 # scan timeout (spin button of integers from 1 to 100)
1366 self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
1367 self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1368 self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1369 self.w_scan_timeout.set_numeric(True)
1370 self.w_scan_timeout.set_snap_to_ticks(True)
1371 self.w_scan_timeout.set_wrap(False)
1372 self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1373 self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1374 self.w_hbox2 = gtk.HBox(False, 0)
1375 self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1376 self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1377 self.general_page.pack_start(self.w_hbox2, False, False, 5)
1379 # speak up
1380 self.w_speak_up = gtk.CheckButton("Use speak-up")
1381 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1382 self.w_speak_up.connect("toggled", self.toggle_speak)
1383 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1384 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1386 # speak up command
1387 self.w_speak_cmd = gtk.Entry()
1388 self.w_speak_cmd.set_width_chars(16)
1389 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1390 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1391 self.w_speak_cmd_label = gtk.Label("Speak Command")
1392 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1393 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1394 self.w_hbox3 = gtk.HBox(False, 0)
1395 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1396 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1397 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1398 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1399 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1401 # commit required
1402 self.w_commit_required = gtk.CheckButton("Commit required")
1403 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1404 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1405 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1407 # ifup required
1408 self.w_ifup_required = gtk.CheckButton("Ifup required")
1409 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1410 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1411 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1413 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1414 ### End of General tab
1416 ### Advanced tab
1417 # table to use for layout of following command configurations
1418 self.cmds_table = gtk.Table()
1420 # ifconfig command
1421 self.ifconfig_cmd = gtk.Entry()
1422 self.ifconfig_cmd.set_width_chars(32)
1423 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1424 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1425 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1426 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1427 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1428 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, True, False, 0, 0)
1429 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1431 # iwconfig command
1432 self.iwconfig_cmd = gtk.Entry()
1433 self.iwconfig_cmd.set_width_chars(32)
1434 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1435 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1436 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1437 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1438 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, True, False, 5, 0)
1439 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, True, False, 0, 0)
1440 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, False, False, 0, 0)
1442 # iwlist command
1443 self.iwlist_cmd = gtk.Entry()
1444 self.iwlist_cmd.set_width_chars(32)
1445 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1446 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1447 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1448 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1449 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, True, False, 5, 0)
1450 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, True, False, 0, 0)
1451 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, False, False, 0, 0)
1453 # route command
1454 self.route_cmd = gtk.Entry()
1455 self.route_cmd.set_width_chars(32)
1456 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1457 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1458 self.route_cmd_label = gtk.Label("Network route configure command")
1459 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1460 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, True, False, 5, 0)
1461 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, True, False, 0, 0)
1462 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, False, False, 0, 0)
1464 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1465 ### End of Advanced tab
1467 ### DHCP tab
1468 # table to use for layout of DHCP prefs
1469 self.dhcp_table = gtk.Table()
1471 self.dhcp_cmd = gtk.Entry()
1472 self.dhcp_cmd.set_width_chars(32)
1473 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1474 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1475 self.dhcp_cmd_label = gtk.Label("Command")
1476 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1477 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1478 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1479 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1481 self.dhcp_args = gtk.Entry()
1482 self.dhcp_args.set_width_chars(32)
1483 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1484 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1485 self.dhcp_args_label = gtk.Label("Arguments")
1486 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1487 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1489 self.dhcp_kill_args = gtk.Entry()
1490 self.dhcp_kill_args.set_width_chars(32)
1491 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1492 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1493 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1494 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1495 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1497 self.dhcp_timeout = gtk.Entry()
1498 self.dhcp_timeout.set_width_chars(32)
1499 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1500 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1501 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1502 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1503 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1505 self.dhcp_pidfile = gtk.Entry()
1506 self.dhcp_pidfile.set_width_chars(32)
1507 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1508 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1509 self.dhcp_pidfile_label = gtk.Label("PID file")
1510 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1511 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1513 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1514 ### End of DHCP tab
1516 ### WPA tab
1517 # table to use for layout of DHCP prefs
1518 self.wpa_table = gtk.Table()
1520 self.wpa_cmd = gtk.Entry()
1521 self.wpa_cmd.set_width_chars(32)
1522 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1523 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1524 self.wpa_cmd_label = gtk.Label("Command")
1525 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1526 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1527 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1528 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1530 self.wpa_args = gtk.Entry()
1531 self.wpa_args.set_width_chars(32)
1532 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1533 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1534 self.wpa_args_label = gtk.Label("Arguments")
1535 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1536 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1538 self.wpa_kill_args = gtk.Entry()
1539 self.wpa_kill_args.set_width_chars(32)
1540 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1541 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1542 self.wpa_kill_args_label = gtk.Label("Kill command")
1543 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1544 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1546 self.wpa_config = gtk.Entry()
1547 self.wpa_config.set_width_chars(32)
1548 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1549 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1550 self.wpa_config_label = gtk.Label("Configuration file")
1551 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1552 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1554 self.wpa_driver = gtk.Entry()
1555 self.wpa_driver.set_width_chars(32)
1556 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1557 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1558 self.wpa_driver_label = gtk.Label("Driver")
1559 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1560 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1562 self.wpa_pidfile = gtk.Entry()
1563 self.wpa_pidfile.set_width_chars(32)
1564 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1565 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1566 self.wpa_pidfile_label = gtk.Label("PID file")
1567 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1568 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1570 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1571 ### End of WPA tab
1573 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1575 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1577 #Parameters:
1579 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1581 # 'data' -- tuple - list of arbitrary arguments (not used)
1583 #Returns:
1585 # nothing
1586 def toggle_auto_detect(self, auto_detect_toggle, data=None):
1587 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
1589 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
1591 #Parameters:
1593 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1595 # 'data' -- tuple - list of arbitrary arguments (not used)
1597 #Returns:
1599 # nothing
1600 def toggle_speak(self, speak_toggle, data=None):
1601 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
1602 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
1604 # Display preferences dialog and operate until canceled or okayed.
1606 #Parameters:
1608 # nothing
1610 #Returns:
1612 # integer -- gtk response ID
1613 def run(self):
1614 self.dialog.show_all()
1615 return self.dialog.run()
1617 # Write updated values to config file.
1619 #Parameters:
1621 # nothing
1623 #Returns:
1625 # nothing
1626 def save(self):
1627 if self.w_auto_detect.get_active():
1628 set_network_device("auto_detect")
1629 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
1630 else:
1631 # get the selected interface, using a work-around suggested by PyGTK Tutorial
1632 interface = self.w_interface.get_model()[self.w_interface.get_active()][0]
1633 self.confFile.set_opt('DEFAULT.interface', interface)
1634 set_network_device(interface)
1635 self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
1636 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
1637 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
1638 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
1639 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
1640 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
1641 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
1642 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
1643 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
1644 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
1645 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
1646 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
1647 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
1648 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
1649 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
1650 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
1651 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
1652 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
1653 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
1654 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
1655 self.confFile.write()
1657 # Remove preferences window.
1659 #Parameters:
1661 # nothing
1663 #Returns:
1665 # nothing
1666 def destroy(self):
1667 self.dialog.destroy()
1668 del self.dialog
1671 # Edit and return an AP profile.
1672 class profile_dialog:
1673 # Create a new profile_dialog.
1675 #Parameters:
1677 # 'parent' -- gtk.Object - Usually, the calling window.
1679 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
1681 #Returns:
1683 # profile_dialog instance
1684 def __init__( self, parent, profile ):
1685 global wifi_radar_icon
1686 self.parent = parent
1687 self.profile = profile
1688 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1689 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
1690 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1691 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
1692 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1693 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
1694 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1695 self.dialog.set_icon( icon )
1696 self.dialog.set_resizable( False )
1697 self.dialog.set_transient_for( self.parent.window )
1698 #self.dialog.set_size_request( 400, 400 )
1699 #################
1700 essid_table = gtk.Table( 1, 2, False )
1701 essid_table.set_row_spacings( 3 )
1702 essid_table.set_col_spacings( 3 )
1704 # The essid labels
1705 essid_table.attach( gtk.Label( 'Network Name' ), 0, 1, 0, 1 )
1706 # The essid textboxes
1707 self.essid_entry = gtk.Entry( 32 )
1708 self.essid_entry.set_text( self.profile['essid'] )
1709 essid_table.attach( self.essid_entry, 1, 2, 0, 1 )
1710 # Add the essid table to the dialog
1711 self.dialog.vbox.pack_start( essid_table, True, True, 5 )
1713 bssid_table = gtk.Table( 1, 2, False )
1714 bssid_table.set_row_spacings( 3 )
1715 bssid_table.set_col_spacings( 3 )
1716 # The bssid labels
1717 bssid_table.attach( gtk.Label( 'Network bssid' ), 0, 1, 0, 1 )
1718 # The bssid textboxes
1719 self.bssid_entry = gtk.Entry( 32 )
1720 self.bssid_entry.set_text( self.profile['bssid'] )
1721 bssid_table.attach( self.bssid_entry, 1, 2, 0, 1 )
1722 #self.key = gtk.Entry( 32 )
1723 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1724 # Add the bssid table to the dialog
1725 self.dialog.vbox.pack_start( bssid_table, True, True, 5 )
1727 # create the wifi expander
1728 self.wifi_expander = gtk.Expander( WIFI_SET_LABEL )
1729 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1730 wifi_table = gtk.Table( 4, 2, False )
1731 wifi_table.set_row_spacings( 3 )
1732 wifi_table.set_col_spacings( 3 )
1733 # The Wifi labels
1734 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
1735 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
1736 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
1737 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
1738 # The Wifi text boxes
1739 self.mode_combo = gtk.combo_box_new_text()
1740 for mode in self.WIFI_MODES:
1741 self.mode_combo.append_text( mode )
1742 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
1743 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
1744 self.channel_combo = gtk.combo_box_new_text()
1745 for channel in self.WIFI_CHANNELS:
1746 self.channel_combo.append_text( channel )
1747 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
1748 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
1750 self.key_entry = gtk.Entry( 64 )
1751 self.key_entry.set_text( self.profile['key'] )
1752 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
1754 self.security_combo = gtk.combo_box_new_text()
1755 for security in self.WIFI_SECURITY:
1756 self.security_combo.append_text( security )
1757 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
1758 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
1759 # Add the wifi table to the expander
1760 self.wifi_expander.add( wifi_table )
1761 # Add the expander to the dialog
1762 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
1764 # create the wpa expander
1765 self.wpa_expander = gtk.Expander( NO_WPA_LABEL )
1766 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
1767 wpa_table = gtk.Table( 1, 2, False )
1768 wpa_table.set_row_spacings( 3 )
1769 wpa_table.set_col_spacings( 3 )
1770 # The labels
1771 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1772 # The text boxes
1773 self.wpa_driver_entry = gtk.Entry()
1774 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
1775 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
1776 # Add the wpa table to the expander
1777 self.wpa_expander.add( wpa_table )
1778 # Add the expander to the dialog
1779 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
1781 # create the dhcp expander
1782 self.dhcp_expander = gtk.Expander( USE_DHCP_LABEL )
1783 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1784 ip_table = gtk.Table( 6, 2, False )
1785 ip_table.set_row_spacings( 3 )
1786 ip_table.set_col_spacings( 3 )
1787 # The IP labels
1788 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
1789 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
1790 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
1791 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
1792 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
1793 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
1794 # The IP text boxes
1795 self.ip_entry = gtk.Entry( 15 )
1796 self.ip_entry.set_text( self.profile['ip'] )
1797 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
1798 self.netmask_entry = gtk.Entry( 15 )
1799 self.netmask_entry.set_text( self.profile['netmask'] )
1800 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
1801 self.gw_entry = gtk.Entry( 15 )
1802 self.gw_entry.set_text( self.profile['gateway'] )
1803 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
1804 self.domain_entry = gtk.Entry( 32 )
1805 self.domain_entry.set_text( self.profile['domain'] )
1806 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
1807 self.dns1_entry = gtk.Entry( 15 )
1808 self.dns1_entry.set_text( self.profile['dns1'] )
1809 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
1810 self.dns2_entry = gtk.Entry( 15 )
1811 self.dns2_entry.set_text( self.profile['dns2'] )
1812 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
1813 # Add the ip table to the expander
1814 self.dhcp_expander.add( ip_table )
1815 # Add the expander to the dialog
1816 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
1818 # create the connection-building postpre expander
1819 self.con_pp_expander = gtk.Expander( CON_PP_LABEL )
1820 con_pp_table = gtk.Table( 2, 2, False )
1821 con_pp_table.set_row_spacings( 3 )
1822 con_pp_table.set_col_spacings( 3 )
1823 # The labels
1824 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1825 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1826 # The text boxes
1827 self.con_prescript_entry = gtk.Entry()
1828 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
1829 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
1830 self.con_postscript_entry = gtk.Entry()
1831 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
1832 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
1833 # Add the pp table to the expander
1834 self.con_pp_expander.add( con_pp_table )
1835 # Add the expander to the dialog
1836 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
1838 # create the disconnection postpre expander
1839 self.dis_pp_expander = gtk.Expander( DIS_PP_LABEL )
1840 dis_pp_table = gtk.Table( 2, 2, False )
1841 dis_pp_table.set_row_spacings( 3 )
1842 dis_pp_table.set_col_spacings( 3 )
1843 # The labels
1844 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1845 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1846 # The text boxes
1847 self.dis_prescript_entry = gtk.Entry()
1848 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
1849 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
1850 self.dis_postscript_entry = gtk.Entry()
1851 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
1852 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
1853 # Add the pp table to the expander
1854 self.dis_pp_expander.add( dis_pp_table )
1855 # Add the expander to the dialog
1856 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
1858 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
1860 #Parameters:
1862 # nothing
1864 #Returns:
1866 # dictionary or None -- a profile, or None on cancel
1867 def run( self ):
1868 self.dialog.show_all()
1869 if self.dialog.run():
1870 self.profile['known'] = True
1871 self.profile['essid'] = self.essid_entry.get_text().strip()
1872 self.profile['bssid'] = self.bssid_entry.get_text().strip()
1873 self.profile['key'] = self.key_entry.get_text().strip()
1874 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
1875 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
1876 self.profile['encrypted'] = ( self.profile['security'] != '' )
1877 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
1878 self.profile['protocol'] = 'g'
1879 self.profile['available'] = ( self.profile['signal'] > 0 )
1880 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
1881 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
1882 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
1883 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
1884 # wpa
1885 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
1886 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
1887 # dhcp
1888 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
1889 self.profile['ip'] = self.ip_entry.get_text().strip()
1890 self.profile['netmask'] = self.netmask_entry.get_text().strip()
1891 self.profile['gateway'] = self.gw_entry.get_text().strip()
1892 self.profile['domain'] = self.domain_entry.get_text().strip()
1893 self.profile['dns1'] = self.dns1_entry.get_text().strip()
1894 self.profile['dns2'] = self.dns2_entry.get_text().strip()
1895 return self.profile
1896 return None
1898 # Remove profile dialog.
1900 #Parameters:
1902 # nothing
1904 #Returns:
1906 # nothing
1907 def destroy( self ):
1908 self.dialog.destroy()
1909 del self.dialog
1911 # Respond to expanding/hiding IP segment.
1913 #Parameters:
1915 # 'widget' -- gtk.Widget - The widget sending the event.
1917 # 'data' -- tuple - List of arbitrary arguments (not used)
1919 #Returns:
1921 # nothing
1922 def toggle_use_dhcp( self, widget, data = None ):
1923 expanded = self.dhcp_expander.get_expanded()
1924 if expanded:
1925 self.dhcp_expander.set_label( USE_IP_LABEL )
1926 else:
1927 self.dhcp_expander.set_label( USE_DHCP_LABEL )
1929 # Respond to expanding/hiding WPA segment.
1931 #Parameters:
1933 # 'widget' -- gtk.Widget - The widget sending the event.
1935 # 'data' -- tuple - List of arbitrary arguments (not used)
1937 #Returns:
1939 # nothing
1940 def toggle_use_wpa( self, widget, data = None ):
1941 expanded = self.wpa_expander.get_expanded()
1942 if expanded:
1943 self.wpa_expander.set_label( USE_WPA_LABEL )
1944 else:
1945 self.wpa_expander.set_label( NO_WPA_LABEL )
1947 # Return the index where item matches a cell in array.
1949 #Parameters:
1951 # 'item' -- string - Item to find in array
1953 # 'array' -- list - List in which to find match.
1955 #Returns:
1957 # integer - 0 (no match) or higher (index of match)
1958 def get_array_index( self, item, array ):
1959 try:
1960 return array.index( item.strip() )
1961 except:
1962 pass
1963 return 0
1965 # Return the value in array[ index ]
1967 #Parameters:
1969 # 'index' -- integer - The index to look up.
1971 # 'array' -- list - List in which to look up value.
1973 #Returns:
1975 # string -- empty string (no match) or looked up value
1976 def get_array_item( self, index, array ):
1977 try:
1978 return array[ index ]
1979 except:
1980 pass
1981 return ''
1983 # A simple class for putting up a "Please wait" dialog so the user
1984 # doesn't think we've forgotten about them. Implements the status interface.
1985 class StatusWindow:
1986 # Create a new StatusWindow.
1988 #Parameters:
1990 # 'parent' -- gtk.Object - Usually, the calling window.
1992 #Returns:
1994 # StatusWindow instance
1996 #NOTE:
1998 # Sample implementation of status interface. Status interface
1999 #requires .show(), .update_message(message), and .hide() methods.
2000 def __init__( self, parent ):
2001 global wifi_radar_icon
2002 self.parent = parent
2003 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2004 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2005 self.dialog.set_icon( icon )
2006 self.lbl = gtk.Label("Please wait...")
2007 self.bar = gtk.ProgressBar()
2008 self.dialog.vbox.pack_start(self.lbl)
2009 self.dialog.vbox.pack_start(self.bar)
2010 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2011 self.timer = None
2013 # Change the message displayed to the user.
2015 #Parameters:
2017 # 'message' -- string - The message to show to the user.
2019 #Returns:
2021 # nothing
2022 def update_message( self, message ):
2023 self.lbl.set_text(message)
2025 # Update the StatusWindow progress bar.
2027 #Parameters:
2029 # nothing
2031 #Returns:
2033 # True -- always return True
2034 def update_window( self ):
2035 self.bar.pulse()
2036 return True
2038 # Display and operate the StatusWindow.
2040 #Parameters:
2042 # nothing
2044 #Returns:
2046 # nothing
2047 def run( self ):
2048 pass
2050 # Show all the widgets of the StatusWindow.
2052 #Parameters:
2054 # nothing
2056 #Returns:
2058 # nothing
2059 def show( self ):
2060 self.dialog.show_all()
2061 self.timer = gobject.timeout_add(250, self.update_window)
2062 return False
2064 # Hide all the widgets of the StatusWindow.
2066 #Parameters:
2068 # nothing
2070 #Returns:
2072 # nothing
2073 def hide( self ):
2074 if self.timer:
2075 gobject.source_remove(self.timer)
2076 self.timer = None
2077 self.dialog.hide_all()
2078 return False
2080 # Remove the StatusWindow.
2082 #Parameters:
2084 # nothing
2086 #Returns:
2088 # nothing
2089 def destroy( self ):
2090 if self.timer:
2091 gobject.source_remove(self.timer)
2092 self.dialog.destroy()
2093 del self.dialog
2096 # Manage a GTK About Dialog
2097 class about_dialog(gtk.AboutDialog):
2098 # Subclass GTK AboutDialog
2100 #Parameters:
2102 # nothing
2104 #Returns:
2106 # nothing
2107 def __init__( self ):
2108 global wifi_radar_icon
2110 gtk.AboutDialog.__init__(self)
2111 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"])
2112 self.set_comments("WiFi connection manager")
2113 self.set_copyright("Copyright 2004-2009 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2114 self.set_documenters(["Gary Case"])
2115 license = """
2116 This program is free software; you can redistribute it and/or modify
2117 it under the terms of the GNU General Public License as published by
2118 the Free Software Foundation; either version 2 of the License, or
2119 (at your option) any later version.
2121 This program is distributed in the hope that it will be useful,
2122 but WITHOUT ANY WARRANTY; without even the implied warranty of
2123 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2124 GNU General Public License for more details.
2126 You should have received a copy of the GNU General Public License
2127 along with this program; if not, write to the Free Software
2128 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2129 self.set_license(license)
2130 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2131 self.set_logo(logo)
2132 self.set_name("WiFi Radar")
2133 self.set_version(WIFI_RADAR_VERSION)
2134 self.set_website("http://wifi-radar.berlios.de")
2138 # Manage the configuration for the application, including reading and writing the config from/to a file.
2139 class ConfigFile(ConfigParser.SafeConfigParser):
2140 # Create a new ConfigFile.
2142 #Parameters:
2144 # 'filename' -- string - The configuration file's name.
2146 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2148 #Returns:
2150 # ConfigFile instance
2151 def __init__( self, filename, defaults, raw=False ):
2152 self.filename = filename
2153 self.raw = raw
2154 self.auto_profile_order = []
2155 ConfigParser.SafeConfigParser.__init__(self, defaults)
2157 # Set the contents of a section to values from a dictionary.
2159 #Parameters:
2161 # 'section_name' -- string - Configuration file section.
2163 # 'section_dict' -- dictionary - Values to add to section.
2165 #Returns:
2167 # nothing
2168 def set_section( self, section_name, section_dict ):
2169 try:
2170 self.add_section(section_name)
2171 except ConfigParser.DuplicateSectionError:
2172 pass
2173 for key in section_dict.keys():
2174 if type(section_dict[key]) == BooleanType:
2175 self.set_bool_opt(section_name + "." + key, section_dict[key])
2176 elif type(section_dict[key]) == IntType:
2177 self.set_int_opt(section_name + "." + key, section_dict[key])
2178 elif type(section_dict[key]) == FloatType:
2179 self.set_float_opt(section_name + "." + key, section_dict[key])
2180 else:
2181 self.set_opt(section_name + "." + key, section_dict[key])
2183 # Return the profile recorded in the specified section.
2185 #Parameters:
2187 # 'section_name' -- string - Configuration file section.
2189 #Returns:
2191 # dictionary or None - The specified profile or None if not found
2192 def get_profile( self, section_name ):
2193 if section_name in self.profiles():
2194 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' ]
2195 bool_types = [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
2196 int_types = [ 'signal' ]
2197 profile = {}
2198 for option in bool_types:
2199 profile[option] = self.get_opt_as_bool( section_name + "." + option )
2200 for option in int_types:
2201 profile[option] = self.get_opt_as_int( section_name + "." + option )
2202 for option in str_types:
2203 profile[option] = self.get_opt( section_name + "." + option )
2204 return profile
2205 return None
2207 # Get a config option and handle exceptions.
2209 #Parameters:
2211 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2212 # period and the option key. (E.g. "DEFAULT.interface")
2214 #Returns:
2216 # string or None - option value as string or None on failure
2217 def get_opt( self, option_path ):
2218 #print "ConfigFile.get_opt: ", option_path
2219 (section, option) = option_path.split('.')
2220 try:
2221 return self.get(section, option, self.raw)
2222 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2223 return None
2225 # Get a config option and return as a boolean type.
2227 #Parameters:
2229 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2230 # period and the option key. (E.g. "DEFAULT.interface")
2232 #Returns:
2234 # boolean - option value as boolean
2235 def get_opt_as_bool( self, option_path ):
2236 option = self.get_opt(option_path)
2237 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2238 return option
2239 if option == 'True':
2240 return True
2241 if option == 'False':
2242 return False
2243 raise ValueError, 'boolean option was not True or False'
2245 # Get a config option and return as an integer type.
2247 #Parameters:
2249 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2250 # period and the option key. (E.g. "DEFAULT.interface")
2252 #Returns:
2254 # integer- option value as integer
2255 def get_opt_as_int( self, option_path ):
2256 return int(float(self.get_opt(option_path)))
2258 # Convert boolean type to string and set config option.
2260 #Parameters:
2262 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2263 # period and the option key. (E.g. "DEFAULT.interface")
2265 # 'value' -- boolean - Value to set.
2267 #Returns:
2269 # nothing
2270 def set_bool_opt( self, option_path, value ):
2271 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2272 value == 'True'
2273 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2274 value == 'False'
2275 else:
2276 raise ValueError, 'cannot convert value to string'
2277 self.set_opt(option_path, repr(value))
2279 # Convert integer 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' -- integer - Value to set.
2288 #Returns:
2290 # nothing
2291 def set_int_opt( self, option_path, value ):
2292 if not isinstance(value, IntType):
2293 raise ValueError, 'value is not an integer'
2294 self.set_opt(option_path, repr(value))
2296 # Convert float type to string and set config option.
2298 #Parameters:
2300 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2301 # period and the option key. (E.g. "DEFAULT.interface")
2303 # 'value' -- float - Value to set.
2305 #Returns:
2307 # nothing
2308 def set_float_opt( self, option_path, value ):
2309 if not isinstance(value, FloatType):
2310 raise ValueError, 'value is not a float'
2311 self.set_opt(option_path, repr(int(value)))
2313 # Set a config option while handling exceptions.
2315 #Parameters:
2317 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2318 # period and the option key. (E.g. "DEFAULT.interface")
2320 # 'value' -- string - Value to set.
2322 #Returns:
2324 # nothing
2325 def set_opt( self, option_path, value ):
2326 (section, option) = option_path.split('.')
2327 try:
2328 self.set(section, option, value)
2329 except ConfigParser.NoSectionError:
2330 self.add_section(section)
2331 self.set_opt(option_path, value)
2333 # Return a list of the section names which denote AP profiles.
2335 #Parameters:
2337 # nothing
2339 #Returns:
2341 # list - profile names
2342 def profiles( self ):
2343 profile_list = []
2344 for section in self.sections():
2345 if ':' in section:
2346 profile_list.append(section)
2347 return profile_list
2349 # Read configuration file from disk into instance variables.
2351 #Parameters:
2353 # nothing
2355 #Returns:
2357 # nothing
2358 def read( self ):
2359 fp = open( self.filename, "r" )
2360 self.readfp(fp)
2361 # convert the auto_profile_order to a list for ordering
2362 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2363 for ap in self.profiles():
2364 self.set_bool_opt( ap + '.known', True)
2365 if ap in self.auto_profile_order: continue
2366 self.auto_profile_order.append( ap )
2367 fp.close()
2369 # Write configuration file to disk from instance variables. Copied from
2370 # ConfigParser and modified to write options in alphabetical order.
2372 #Parameters:
2374 # nothing
2376 #Returns:
2378 # nothing
2379 def write( self ):
2380 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2381 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2382 fp = open( self.filename, "w" )
2383 # write DEFAULT section first
2384 if self._defaults:
2385 fp.write("[DEFAULT]\n")
2386 for key in sorted(self._defaults.keys()):
2387 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2388 fp.write("\n")
2389 # write non-profile sections first
2390 for section in self._sections:
2391 if section not in self.profiles():
2392 fp.write("[%s]\n" % section)
2393 for key in sorted(self._sections[section].keys()):
2394 if key != "__name__":
2395 fp.write("%s = %s\n" %
2396 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2397 fp.write("\n")
2398 # write profile sections
2399 for section in self._sections:
2400 if section in self.profiles():
2401 fp.write("[%s]\n" % section)
2402 for key in sorted(self._sections[section].keys()):
2403 if key != "__name__":
2404 fp.write("%s = %s\n" %
2405 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2406 fp.write("\n")
2407 fp.close()
2411 # Load our conf file and known profiles
2412 # Defaults, these may get overridden by values found in the conf file.
2413 config_defaults = { # The network interface you use.
2414 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2415 'interface': "auto_detect",
2416 # How long should the scan for access points last?
2417 'scan_timeout': '5',
2418 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2419 # Set the speak_up option to false if you do not have or want this.
2420 'speak_command': '/usr/bin/say',
2421 # Should I speak up when connecting to a network? (If you have a speech command)
2422 'speak_up': 'False',
2423 # You may set this to true for cards that require a "commit" command with iwconfig
2424 'commit_required': 'False',
2425 # You may set this to true for cards that require the interface to be brought up first
2426 'ifup_required': 'False',
2427 # Set the location of several important programs
2428 'iwlist_command': '/sbin/iwlist',
2429 'iwconfig_command': '/sbin/iwconfig',
2430 'ifconfig_command': '/sbin/ifconfig',
2431 'route_command': '/sbin/route',
2432 'auto_profile_order': '[]',
2433 'version': WIFI_RADAR_VERSION }
2435 config_dhcp = { # DHCP client
2436 'command': 'dhcpcd',
2437 # How long to wait for an IP addr from DHCP server
2438 'timeout': '30',
2439 # Arguments to use with DHCP client on connect
2440 'args': '-D -o -i dhcp_client -t %(timeout)s',
2441 # Argument to use with DHCP client on disconnect
2442 'kill_args': '-k',
2443 # The file where DHCP client PID is written
2444 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2446 config_wpa = { # WPA Supplicant
2447 'command': '/usr/sbin/wpa_supplicant',
2448 # Arguments to use with WPA Supplicant on connect
2449 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2450 # Arguments to use with WPA Supplicant on disconnect
2451 'kill_command': '',
2452 # Where the WPA Supplicant config file can be found
2453 'configuration': '/etc/wpa_supplicant.conf',
2454 # Driver to use with WPA Supplicant
2455 'driver': 'wext',
2456 # The file where WPA Supplicant PID is written
2457 'pidfile': '/var/run/wpa_supplicant.pid' }
2459 # initialize config, with defaults
2460 confFile = ConfigFile(CONF_FILE, config_defaults)
2461 confFile.set_section("DHCP", config_dhcp)
2462 confFile.set_section("WPA", config_wpa)
2464 if not os.path.isfile( CONF_FILE ):
2465 confFile.set_bool_opt('DEFAULT.new_file', True)
2466 else:
2467 if not os.access(CONF_FILE, os.R_OK):
2468 print "Can't open " + CONF_FILE + "."
2469 print "Are you root?"
2470 sys.exit()
2471 confFile.read()
2474 ####################################################################################################
2475 # Embedded Images
2476 wifi_radar_icon = [ ""
2477 "GdkP"
2478 "\0\0\22""7"
2479 "\2\1\0\2"
2480 "\0\0\1\214"
2481 "\0\0\0c"
2482 "\0\0\0O"
2483 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2484 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2485 "\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"
2486 "\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"
2487 "\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"
2488 "\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"
2489 "\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"
2490 "\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"
2491 "\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"
2492 "\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"
2493 "\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"
2494 "\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"
2495 "\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"
2496 "\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"
2497 "\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"
2498 "\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"
2499 "\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"
2500 "\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"
2501 "\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"
2502 "\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"
2503 "\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"
2504 "\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"
2505 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2506 "\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"
2507 "\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"
2508 "\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"
2509 "\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"
2510 "\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"
2511 "\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"
2512 "\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"
2513 "\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"
2514 "\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"
2515 "\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"
2516 "\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"
2517 "\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"
2518 "\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"
2519 "\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"
2520 "\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"
2521 "\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"
2522 "\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"
2523 "\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"
2524 "\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"
2525 "\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"
2526 "\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"
2527 "\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"
2528 "\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"
2529 "\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"
2530 "\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"
2531 "\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"
2532 "\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"
2533 "\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"
2534 "\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"
2535 "\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"
2536 "\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"
2537 "\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"
2538 "\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"
2539 "\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"
2540 "\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"
2541 "\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"
2542 "\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"
2543 "\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"
2544 "\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"
2545 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2546 "\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"
2547 "\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"
2548 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2549 "\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"
2550 "\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"
2551 "\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"
2552 "\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"
2553 "\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"
2554 "\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"
2555 "\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"
2556 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2557 "\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"
2558 "\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"
2559 "\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"
2560 "\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"
2561 "\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"
2562 "\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"
2563 "|\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"
2564 "\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"
2565 "\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"
2566 "\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"
2567 "\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"
2568 "\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"
2569 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2570 "\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"
2571 "\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"
2572 "\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"
2573 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
2574 "\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"
2575 "\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"
2576 "\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"
2577 "\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"
2578 "\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"
2579 "\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"
2580 "\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"
2581 "\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"
2582 "\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"
2583 "\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"
2584 "\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"
2585 "\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"
2586 "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"
2587 "\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"
2588 "\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"
2589 "\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"
2590 "\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"
2591 "\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"
2592 "\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"
2593 "\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"
2594 "\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"
2595 "\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"
2596 "\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"
2597 "\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"
2598 "\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"
2599 "\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"
2600 "\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"
2601 "\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|"
2602 "\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"
2603 "\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"
2604 "\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"
2605 "\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"
2606 "\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"
2607 "\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"
2608 "\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"
2609 "\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"
2610 "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"
2611 "\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"
2612 "\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"
2613 "\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"
2614 "\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"
2615 "\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"
2616 "\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"
2617 "\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"
2618 "\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"
2619 "\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"
2620 "\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"
2621 "\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"
2622 "\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"
2623 "\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"
2624 "\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"
2625 "\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"
2626 "\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"
2627 "\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"
2628 "\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"
2629 "\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"
2630 "\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"
2631 "\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"
2632 "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"
2633 "\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"
2634 "\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"
2635 "\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"
2636 "\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"
2637 "\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"
2638 "\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"
2639 "\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"
2640 "\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"
2641 "\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"
2642 "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"
2643 "\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"
2644 "\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"
2645 "\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"
2646 "\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"
2647 "\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"
2648 "\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"
2649 "\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"
2650 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2651 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2652 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2653 "\0"]
2655 known_profile_icon = [ ""
2656 "GdkP"
2657 "\0\0\5""0"
2658 "\2\1\0\2"
2659 "\0\0\0P"
2660 "\0\0\0\24"
2661 "\0\0\0\24"
2662 "\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"
2663 "\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"
2664 "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"
2665 "\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"
2666 "\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"
2667 "\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"
2668 "\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"
2669 "\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"
2670 "\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"
2671 "\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"
2672 "\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"
2673 "\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"
2674 "\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"
2675 "\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"
2676 "\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"
2677 "\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"
2678 "\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"
2679 "\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"
2680 "\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"
2681 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2682 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2683 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2684 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2685 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2686 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2687 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2688 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2689 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2690 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2691 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2692 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2693 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2694 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2695 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2696 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2697 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2698 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2699 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2700 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2701 "\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"
2702 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2703 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2704 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2705 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2706 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2707 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2708 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2709 "\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"
2710 "\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"
2711 "\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"
2712 "\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"
2713 "\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"]
2715 unknown_profile_icon = [ ""
2716 "GdkP"
2717 "\0\0\5\22"
2718 "\2\1\0\2"
2719 "\0\0\0P"
2720 "\0\0\0\24"
2721 "\0\0\0\24"
2722 "\210\0\0\0\0\4\0\0\0\1\0\0\0\4\0\0\0\6\0\0\0\3\216\0\0\0\0\11\0\0\0\4"
2723 "\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"
2724 "\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"
2725 "\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"
2726 "(\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"
2727 "\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"
2728 "#\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"
2729 "\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"
2730 "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"
2731 "\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"
2732 "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"
2733 "\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"
2734 "\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"
2735 "\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"
2736 "\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"
2737 "\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"
2738 "\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"
2739 "\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"
2740 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2741 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2742 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2743 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2744 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2745 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2746 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2747 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2748 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2749 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2750 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2751 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2752 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2753 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2754 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2755 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2756 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2757 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2758 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2759 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2760 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2761 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2762 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2763 "\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"
2764 "\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"
2765 "\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"
2766 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2768 signal_xpm_barely = [
2769 "20 20 10 1",
2770 " c None",
2771 ". c #C6C6C6",
2772 "+ c #CCCCCC",
2773 "@ c #DBDBDB",
2774 "# c #D3D3D3",
2775 "$ c #A9B099",
2776 "% c #95A173",
2777 "& c #6B8428",
2778 "* c #B4B7AC",
2779 "= c #80924D",
2780 " .+++.",
2781 " +@@@+",
2782 " +@@@+",
2783 " +@@@+",
2784 " +@@@+",
2785 " .++++#@@@+",
2786 " +@@@@@@@@+",
2787 " +@@@@@@@@+",
2788 " +@@@@@@@@+",
2789 " +@@@@@@@@+",
2790 " $%%%%#@@@@@@@@+",
2791 " %&&&&@@@@@@@@@+",
2792 " %&&&&@@@@@@@@@+",
2793 " %&&&&@@@@@@@@@+",
2794 " %&&&&@@@@@@@@@+",
2795 "*%%%%=&&&&@@@@@@@@@+",
2796 "%&&&&&&&&&@@@@@@@@@+",
2797 "%&&&&&&&&&@@@@@@@@@+",
2798 "%&&&&&&&&&@@@@@@@@@+",
2799 "*%%%%%%%%%+++++++++."
2803 signal_xpm_best = [
2804 "20 20 6 1",
2805 " c None",
2806 ". c #9DAABF",
2807 "+ c #7B96BF",
2808 "@ c #386EBF",
2809 "# c #5982BF",
2810 "$ c #AEB4BF",
2811 " .+++.",
2812 " +@@@+",
2813 " +@@@+",
2814 " +@@@+",
2815 " +@@@+",
2816 " .++++#@@@+",
2817 " +@@@@@@@@+",
2818 " +@@@@@@@@+",
2819 " +@@@@@@@@+",
2820 " +@@@@@@@@+",
2821 " .++++#@@@@@@@@+",
2822 " +@@@@@@@@@@@@@+",
2823 " +@@@@@@@@@@@@@+",
2824 " +@@@@@@@@@@@@@+",
2825 " +@@@@@@@@@@@@@+",
2826 "$++++#@@@@@@@@@@@@@+",
2827 "+@@@@@@@@@@@@@@@@@@+",
2828 "+@@@@@@@@@@@@@@@@@@+",
2829 "+@@@@@@@@@@@@@@@@@@+",
2830 "$++++++++++++++++++."
2833 signal_xpm_none = [
2834 "20 20 6 1",
2835 " c None",
2836 ". c #C6C6C6",
2837 "+ c #CCCCCC",
2838 "@ c #DBDBDB",
2839 "# c #D3D3D3",
2840 "$ c #C2C2C2",
2841 " .+++.",
2842 " +@@@+",
2843 " +@@@+",
2844 " +@@@+",
2845 " +@@@+",
2846 " .++++#@@@+",
2847 " +@@@@@@@@+",
2848 " +@@@@@@@@+",
2849 " +@@@@@@@@+",
2850 " +@@@@@@@@+",
2851 " .++++#@@@@@@@@+",
2852 " +@@@@@@@@@@@@@+",
2853 " +@@@@@@@@@@@@@+",
2854 " +@@@@@@@@@@@@@+",
2855 " +@@@@@@@@@@@@@+",
2856 "$++++#@@@@@@@@@@@@@+",
2857 "+@@@@@@@@@@@@@@@@@@+",
2858 "+@@@@@@@@@@@@@@@@@@+",
2859 "+@@@@@@@@@@@@@@@@@@+",
2860 "$++++++++++++++++++."
2863 signal_xpm_ok = [
2864 "20 20 10 1",
2865 " c None",
2866 ". c #C6C6C6",
2867 "+ c #CCCCCC",
2868 "@ c #DBDBDB",
2869 "# c #A1A5B2",
2870 "$ c #848DA5",
2871 "% c #D3D3D3",
2872 "& c #4A5B8C",
2873 "* c #677498",
2874 "= c #B0B2B8",
2875 " .+++.",
2876 " +@@@+",
2877 " +@@@+",
2878 " +@@@+",
2879 " +@@@+",
2880 " #$$$$%@@@+",
2881 " $&&&&@@@@+",
2882 " $&&&&@@@@+",
2883 " $&&&&@@@@+",
2884 " $&&&&@@@@+",
2885 " #$$$$*&&&&@@@@+",
2886 " $&&&&&&&&&@@@@+",
2887 " $&&&&&&&&&@@@@+",
2888 " $&&&&&&&&&@@@@+",
2889 " $&&&&&&&&&@@@@+",
2890 "=$$$$*&&&&&&&&&@@@@+",
2891 "$&&&&&&&&&&&&&&@@@@+",
2892 "$&&&&&&&&&&&&&&@@@@+",
2893 "$&&&&&&&&&&&&&&@@@@+",
2894 "=$$$$$$$$$$$$$$++++."
2898 signal_xpm_low = [
2899 "20 20 8 1",
2900 " c None",
2901 ". c #C6C6C6",
2902 "+ c #CCCCCC",
2903 "@ c #DBDBDB",
2904 "# c #D3D3D3",
2905 "$ c #BFB0B5",
2906 "% c #C18799",
2907 "& c #C54F74",
2908 " .+++.",
2909 " +@@@+",
2910 " +@@@+",
2911 " +@@@+",
2912 " +@@@+",
2913 " .++++#@@@+",
2914 " +@@@@@@@@+",
2915 " +@@@@@@@@+",
2916 " +@@@@@@@@+",
2917 " +@@@@@@@@+",
2918 " .++++#@@@@@@@@+",
2919 " +@@@@@@@@@@@@@+",
2920 " +@@@@@@@@@@@@@+",
2921 " +@@@@@@@@@@@@@+",
2922 " +@@@@@@@@@@@@@+",
2923 "$%%%%#@@@@@@@@@@@@@+",
2924 "%&&&&@@@@@@@@@@@@@@+",
2925 "%&&&&@@@@@@@@@@@@@@+",
2926 "%&&&&@@@@@@@@@@@@@@+",
2927 "$%%%%++++++++++++++."
2931 ####################################################################################################
2932 # Make so we can be imported
2933 if __name__ == "__main__":
2934 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
2935 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
2936 else:
2937 import gtk, gobject
2938 gtk.gdk.threads_init()
2939 apQueue = Queue.Queue(100)
2940 commQueue = Queue.Queue(2)
2941 threading.Thread( None, scanning_thread, None, ( confFile, apQueue, commQueue ) ).start()
2942 main_radar_window = radar_window(confFile, apQueue, commQueue)
2943 gobject.timeout_add( 500, main_radar_window.update_plist_items )
2944 main_radar_window.main()