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