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