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