Check whether profile is known via profile, not profile list in UI
[wifi-radar.git] / wifi-radar
blobd09dde2b569789a893dc679c2446f547b4f96d3d
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 console debugging.
28 import ConfigParser
29 import errno
30 import gtk
31 import logging
32 import logging.handlers
33 import os
34 import Queue
35 import re
36 import string
37 import sys
38 import threading
39 from signal import SIGTERM
40 from subprocess import call, Popen, PIPE
41 from time import sleep
42 from types import *
44 WIFI_RADAR_VERSION = "0.0.0"
47 # Where the conf file should live could be different for your distro. Please change
48 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
50 CONF_FILE = "/etc/wifi-radar/wifi-radar.conf"
52 os.environ['LC_MESSAGES'] = 'C'
55 ####################################################################################################
56 ####################################################################################################
58 # Sets the interface to the specified network device
60 #Parameters:
62 # 'device' -- string - The network device to use
64 #Returns:
66 # nothing
67 def set_network_device( device ):
68 #print "set_network_device: ", device
69 if device != "auto_detect":
70 confFile.set_opt('DEFAULT.interface', device)
71 else: # auto detect network device
72 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
73 # If no devices are found, default to eth1.
74 # call iwconfig command and read output
75 iwconfig_info = Popen(confFile.get_opt('DEFAULT.iwconfig_command'), shell=True, stdout=PIPE).stdout
76 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
77 if len(wireless_devices) > 0:
78 confFile.set_opt('DEFAULT.interface', wireless_devices[0])
79 #else:
80 #print "No wifi-device found. Exiting."
81 #sys.exit()
83 # Return a blank profile
85 #Parameters:
87 # none
89 #Returns:
91 # dictionary -- An AP profile with defaults set.
92 def get_new_profile():
93 return { 'known': False,
94 'available': False,
95 'encrypted': False,
96 'essid': '',
97 'bssid': '',
98 'roaming': False,
99 'protocol': 'g',
100 'signal': 0,
101 'channel': 'auto',
102 'con_prescript': '',
103 'con_postscript': '',
104 'dis_prescript': '',
105 'dis_postscript': '',
106 'key': '',
107 'mode': 'auto',
108 'security': '',
109 'use_wpa': False,
110 'wpa_driver': '',
111 'use_dhcp': True,
112 'ip': '',
113 'netmask': '',
114 'gateway': '',
115 'domain': '',
116 'dns1': '',
117 'dns2': ''
120 # Combine essid and bssid to make a config file section name
122 #Parameters:
124 # 'essid' -- string - AP ESSID
126 # 'bssid' -- string - AP BSSID
128 #Returns:
130 # string -- the bssid concatenated to a colon, concatenated to the essid
131 def make_section_name( essid, bssid ):
132 return essid + ':' + bssid
134 # Split a config file section name into an essid and a bssid
136 #Parameters:
138 # 'section' -- string - Config file section name
140 #Returns:
142 # list -- the essid and bssid
143 def split_section_name( section ):
144 parts = re.split(':', section)
145 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
147 # Run commands through the shell
149 #Parameters:
151 # 'command' -- tuple - The command and arguments to run.
153 # 'environment' -- dictionary - Environment variables (as keys) and their values.
155 #Returns:
157 # boolean -- True on success, otherwise, False
158 def shellcmd( command, environment = None ):
159 try:
160 env_tmp = os.environ
161 env_tmp.update(environment)
162 command = ' '.join(command)
163 return_code = call(command, shell=True, env=env_tmp)
164 if return_code >= 0:
165 return True
166 else:
167 print >>sys.stderr, "Child was terminated by signal", -return_code
168 except OSError, exception:
169 print >>sys.stderr, "Execution failed:", exception
170 return False
172 # Speak feedback message to user
174 #Parameters:
176 # 'words' -- string - Message to speak to user
178 #Returns:
180 # nothing
181 def say( words ):
182 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
183 words = words.replace( "\"", "\\\"" )
184 shellcmd([confFile.get_opt('DEFAULT.speak_command'), words])
186 # Scan for a limited time and return AP names and bssid found.
187 # Access points we find will be put on the outgoing Queue, apQueue.
189 #Parameters:
191 # 'confFile' -- ConfigFile - Config file object
193 # 'apQueue' -- Queue - Queue on which to put AP profiles
195 # 'commandQueue' -- Queue - Queue from which to read commands
197 # 'logger' -- Logger - Python's logging facility
199 #Returns:
201 # nothing
202 def scanning_thread( confFile, apQueue, commandQueue, logger ):
203 logger.debug("Begin thread.")
204 # Setup our essid pattern matcher
205 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
206 bssid_pattern = re.compile( "Address\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
207 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abgn]+)", re.I | re.M | re.S )
208 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
209 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
210 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
211 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
213 access_points = {}
214 command = "scan"
215 while True:
216 try:
217 command = commandQueue.get_nowait()
218 logger.debug("received command: %s" % ( command, ))
219 command_read = True
220 except Queue.Empty:
221 command_read = False
222 if command == "scan":
223 #logger.debug("Beginning scan pass")
224 # Some cards need to have the interface up to scan
225 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
226 # call ifconfig command and wait for return
227 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface'), 'up'])
228 # update the signal strengths
229 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), confFile.get_opt('DEFAULT.interface'), 'scan'], stdout=PIPE).stdout.read()
230 #logger.debug("Current IP: %s\nCurrent ESSID: %s\nCurrent BSSID: %s" % (get_current_ip(), get_current_essid(), get_current_bssid()))
231 # zero out the signal levels for all access points
232 for bssid in access_points:
233 access_points[bssid]['signal'] = 0
234 # split the scan data based on the address line
235 hits = scandata.split(' - ')
236 for hit in hits:
237 # set the defaults for profile template
238 profile = get_new_profile()
239 m = essid_pattern.search( hit )
240 if m:
241 # we found an essid
242 profile['essid'] = m.groups()[1]
243 m = bssid_pattern.search( hit ) # get BSSID from scan
244 if m: profile['bssid'] = m.groups()[1]
245 m = protocol_pattern.search( hit ) # get protocol from scan
246 if m: profile['protocol'] = m.groups()[1]
247 m = mode_pattern.search( hit ) # get mode from scan
248 if m: profile['mode'] = m.groups()[1]
249 m = channel_pattern.search( hit ) # get channel from scan
250 if m: profile['channel'] = m.groups()[1]
251 m = enckey_pattern.search( hit ) # get encryption key from scan
252 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
253 m = signal_pattern.search( hit ) # get signal strength from scan
254 if m: profile['signal'] = m.groups()[1]
255 access_points[ profile['bssid'] ] = profile
256 for bssid in access_points:
257 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
258 # Put all, now or previously, sensed access_points into apQueue
259 try:
260 apQueue.put_nowait( access_points[bssid] )
261 except Queue.Full:
262 pass
263 elif command == "exit":
264 logger.debug("Exiting.")
265 return
266 if command_read: commandQueue.task_done()
267 if confFile.get_opt('DEFAULT.interface').find('ath') == 0:
268 sleep( 3 )
269 else:
270 sleep( 1 )
273 # Read scanned AP info from ap_queue and combine with
274 # configured profiles, then update the UI.
276 #Parameters:
278 # 'ui' -- Object - Must provide update_list() method
280 # 'confFile' -- ConfigFile - Config file object
282 # 'apQueue' -- Queue - Queue on which to put AP profiles
284 # 'commandQueue' -- Queue - Queue from which to read commands
286 # 'logger' -- Logger - Python's logging facility
288 #Returns:
290 # nothing
291 def update_profiles(ui, conf_file, ap_queue, command_queue, logger):
292 logger.debug("Begin thread.")
293 command = 'scan'
294 while True:
295 # Get APs scanned by iwlist
296 try:
297 ap = ap_queue.get_nowait()
298 except Queue.Empty:
299 pass
300 else:
301 # Attempt to match to known profiles, normal then roaming
302 profile = conf_file.get_profile(make_section_name(ap['essid'], ap['bssid']))
303 if not profile:
304 profile = conf_file.get_profile(make_section_name(ap['essid'], ''))
305 if not profile:
306 profile = {}
307 profile.update(ap)
308 # update UI
309 ui.update_list(profile)
310 # Check for exit command.
311 try:
312 command = command_queue.get_nowait()
313 except Queue.Empty:
314 pass
315 if command == "exit":
316 logger.debug("Exiting.")
317 return
318 # Sleep before starting over. Should be less than 1/2 sleep length of scanning_thread()
319 sleep(0.1)
322 # Manage a connection; including reporting connection state,
323 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
324 class ConnectionManager():
325 # Create a new connection manager which can read a config file and send to scanning thread
326 # command Queue. A new manager checks for a pre-existing connection and takes
327 # its AP profile from the ESSID and BSSID to which it is currently attached.
329 #Parameters:
331 # 'confFile' -- ConfigFile - Config file object
333 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
335 # 'logger' -- Logger - Python's logging facility
337 #Returns:
339 # ConnectionManager instance
340 def __init__( self, confFile, commandQueue, logger ):
341 self.confFile = confFile
342 self.commQueue = commandQueue
343 self.logger = logger
344 # is connection running?
345 self.state = False
346 if self.get_current_ip():
347 self.state = True
348 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
350 # Change the interface state: up or down.
352 #Parameters:
354 # 'state' -- string - The state to which to change the interface.
356 #Returns:
358 # nothing
359 def if_change( self, state ):
360 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
361 self.logger.debug("changing interface state to %s" % ( state, ))
362 # call ifconfig command and wait for return
363 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), state])
365 # Connect to the specified AP.
367 #Parameters:
369 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
371 # 'status' -- status implementer - Object which implements status interface.
373 #Returns:
375 # nothing
376 def connect_to_network( self, profile, status ):
377 self.profile = profile
378 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
379 say( msg )
380 self.logger.debug(msg)
381 # ready to dance
382 # Let's run the connection prescript
383 if self.profile['con_prescript'].strip() != '':
384 # got something to execute
385 # run connection prescript through shell and wait for return
386 self.logger.debug("executing connection prescript: %s" % ( self.profile['con_prescript'], ))
387 shellcmd([self.profile['con_prescript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
388 status.show()
389 # Some cards need to have the interface up
390 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
391 self.if_change('up')
392 # Start building iwconfig command line, command
393 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
394 iwconfig_command.append( self.confFile.get_opt('DEFAULT.interface') )
395 # Setting essid
396 iwconfig_command.append( 'essid' )
397 iwconfig_command.append( "'" + self.profile['essid'] + "'" )
398 # Setting nick
399 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
400 # Setting key
401 iwconfig_command.append( 'key' )
402 if self.profile['key'] == '':
403 iwconfig_command.append( 'off' )
404 else:
405 iwconfig_command.append( "'" + self.profile['key'] + "'" )
406 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
407 # Setting mode
408 if self.profile['mode'].lower() == 'master':
409 self.profile['mode'] = 'Managed'
410 iwconfig_command.append( 'mode' )
411 iwconfig_command.append( self.profile['mode'] )
412 # Setting channel
413 if self.profile['channel'] != '':
414 iwconfig_command.append( 'channel' )
415 iwconfig_command.append( self.profile['channel'] )
416 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
417 iwconfig_command.append( 'ap' )
418 iwconfig_command.append( self.profile['bssid'] )
419 # Some cards require a commit
420 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
421 self.logger.debug("iwconfig_args %s " % ( iwconfig_args, ))
422 iwconfig_command.append( 'commit' )
423 # call iwconfig command and wait for return
424 if not shellcmd(iwconfig_command): return
425 # Now normal network stuff
426 # Kill off any existing DHCP clients running
427 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
428 self.logger.debug("Killing existing DHCP...")
429 try:
430 if self.confFile.get_opt('DHCP.kill_args') != '':
431 # call DHCP client kill command and wait for return
432 self.logger.debug("%s %s", ( self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args'), ))
433 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
434 else:
435 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
436 except OSError:
437 print "failed to kill DHCP client"
438 sys.exit()
439 finally:
440 print "Stale pid file. Removing..."
441 os.remove(self.confFile.get_opt('DHCP.pidfile'))
442 # Kill off any existing WPA supplicants running
443 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
444 self.logger.debug("Killing existing WPA supplicant...")
445 try:
446 if not self.confFile.get_opt('WPA.kill_command') != '':
447 # call WPA supplicant kill command and wait for return
448 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
449 else:
450 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
451 except OSError:
452 print "failed to kill WPA supplicant"
453 sys.exit()
454 finally:
455 print "Stale pid file. Removing..."
456 os.remove(self.confFile.get_opt('WPA.pidfile'))
457 # Begin WPA supplicant
458 if self.profile['use_wpa'] :
459 self.logger.debug("WPA args: %s" % ( self.confFile.get_opt('WPA.args'), ))
460 status.update_message("WPA supplicant starting")
461 if sys.modules.has_key("gtk"):
462 while gtk.events_pending():
463 gtk.main_iteration(False)
464 # call WPA supplicant command and do not wait for return
465 wpa_proc = Popen([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args'), self.confFile.get_opt('DEFAULT.interface')])
466 if self.profile['use_dhcp'] :
467 self.logger.debug("Disable iwlist while dhcp in progress...")
468 try:
469 self.commQueue.put("pause")
470 except Queue.Full:
471 pass
472 status.update_message("Acquiring IP Address (DHCP)")
473 if sys.modules.has_key("gtk"):
474 while gtk.events_pending():
475 gtk.main_iteration(False)
476 # call DHCP client command and do not wait for return
477 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
478 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
479 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
480 dhcp_proc = Popen(dhcp_command, stdout=None)
481 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
482 tick = 0.25
483 waiting = dhcp_proc.poll()
484 while waiting == None:
485 waiting = dhcp_proc.poll()
486 if timer < 0:
487 os.kill(dhcp_proc.pid, SIGTERM)
488 break
489 if sys.modules.has_key("gtk"):
490 while gtk.events_pending():
491 gtk.main_iteration(False)
492 timer -= tick
493 sleep(tick)
494 # Re-enable iwlist
495 try:
496 self.commQueue.put("scan")
497 except Queue.Full:
498 pass
499 if not self.get_current_ip():
500 status.update_message("Could not get IP address!")
501 if sys.modules.has_key("gtk"):
502 while gtk.events_pending():
503 gtk.main_iteration(False)
504 sleep(1)
505 if self.state:
506 self.disconnect_interface()
507 status.hide()
508 return
509 else:
510 status.update_message("Got IP address. Done.")
511 self.state = True
512 if sys.modules.has_key("gtk"):
513 while gtk.events_pending():
514 gtk.main_iteration(False)
515 sleep(2)
516 else:
517 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'] )
518 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
519 resolv_contents = ''
520 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
521 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
522 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
523 if ( resolv_contents != '' ):
524 resolv_file=open('/etc/resolv.conf', 'w')
525 resolv_file.write(s)
526 resolv_file.close
527 if not shellcmd([ifconfig_command]): return
528 if not shellcmd([route_command]): return
529 self.state = True
530 # Let's run the connection postscript
531 con_postscript = self.profile['con_postscript']
532 if self.profile['con_postscript'].strip() != '':
533 self.logger.debug("executing connection postscript: %s" % ( self.profile['con_postscript'], ))
534 shellcmd([self.profile['con_postscript']],
535 environment = { "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
536 "WIFIRADAR_ESSID": self.get_current_essid() or '',
537 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
538 "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface') or ''
541 status.hide()
543 # Disconnect from the AP with which a connection has been established/attempted.
545 #Parameters:
547 # nothing
549 #Returns:
551 # nothing
552 def disconnect_interface( self ):
553 msg = "Disconnecting"
554 say( msg )
555 self.logger.debug(msg)
556 # Pause scanning while manipulating card
557 try:
558 self.commQueue.put("pause")
559 except Queue.Full:
560 pass
561 if self.state:
562 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
563 # Let's run the disconnection prescript
564 if self.profile['dis_prescript'].strip() != '':
565 self.logger.debug("executing disconnection prescript: %s" % ( self.profile['dis_prescript'], ))
566 shellcmd([self.profile['dis_prescript']],
567 environment = { "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
568 "WIFIRADAR_ESSID": self.get_current_essid() or '',
569 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
570 "WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface') or ''
573 self.logger.debug("Kill off any existing DHCP clients running...")
574 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
575 self.logger.debug("Killing existing DHCP...")
576 try:
577 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
578 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
579 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
580 dhcp_command.append( self.confFile.get_opt('DEFAULT.interface') )
581 self.logger.debug("DHCP command: %s" % ( dhcp_command, ))
582 # call DHCP client command and wait for return
583 if not shellcmd(dhcp_command): return
584 else:
585 self.logger.debug("Killing DHCP manually...")
586 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
587 except OSError:
588 print "failed to kill DHCP client"
589 self.logger.debug("Kill off any existing WPA supplicants running...")
590 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
591 self.logger.debug("Killing existing WPA supplicant...")
592 try:
593 if not self.confFile.get_opt('WPA.kill_command') != '':
594 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
595 if not shellcmd(wpa_command): return
596 else:
597 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
598 except OSError:
599 print "failed to kill WPA supplicant"
600 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
601 self.logger.debug("Let's clear out the wireless stuff")
602 self.logger.debug("Now take the interface down")
603 # taking down the interface too quickly can crash my system, so pause a moment
604 sleep(1)
605 self.if_change('down')
606 self.logger.debug("Since it may be brought back up by the next scan, lets unset its IP")
607 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), '0.0.0.0'])
608 # Let's run the disconnection postscript
609 if self.profile['dis_postscript'].strip() != '':
610 self.logger.debug("executing disconnection postscript: %s" % ( self.profile['dis_postscript'], ))
611 shellcmd([self.profile['dis_postscript']], environment={"WIFIRADAR_IF": self.confFile.get_opt('DEFAULT.interface')})
612 self.state = False
613 self.logger.debug("Disconnect complete.")
614 # Begin scanning again
615 try:
616 self.commQueue.put("scan")
617 except Queue.Full:
618 pass
620 # Returns the current IP, if any, by calling ifconfig.
622 #Parameters:
624 # nothing
626 #Returns:
628 # string or None -- the IP address or None (if no there is no current connection)
629 def get_current_ip( self ):
630 ifconfig_command = [ confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface') ]
631 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
632 # Be careful to the language (inet adr: in French for example)
634 # Hi Brian
636 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
637 # There the string in ifconfig is inet Adresse for the IP which isn't
638 # found by the current get_current_ip function in wifi-radar. I changed
639 # the according line (#289; gentoo, v1.9.6-r1) to
640 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
641 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
643 # I'd be happy if you could incorporate this small change because as now
644 # I've got to change the file every time it is updated.
646 # Best wishes
648 # Simon
649 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
650 line = ifconfig_info.read()
651 if ip_re.search( line ):
652 return ip_re.search( line ).group(1)
653 return None
655 # Returns the current ESSID, if any, by calling iwconfig.
657 #Parameters:
659 # nothing
661 #Returns:
663 # string or None -- the ESSID or None (if no there is no current association)
664 def get_current_essid( self ):
665 """Returns the current ESSID if any by calling iwconfig"""
666 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
667 # Be careful to the language (inet adr: in French for example)
668 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
669 line = iwconfig_info.read()
670 if essid_re.search( line ):
671 return essid_re.search( line ).group(2)
672 return None
674 # Returns the current BSSID, if any, by calling iwconfig.
676 #Parameters:
678 # nothing
680 #Returns:
682 # string or None -- the BSSID or None (if no there is no current association)
683 def get_current_bssid( self ):
684 """Returns the current BSSID if any by calling iwconfig"""
685 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface')], stdout=PIPE).stdout
686 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
687 line = iwconfig_info.read()
688 if bssid_re.search( line ):
689 return bssid_re.search( line ).group(2)
690 return None
694 # The main user interface window for WiFi Radar. This class also is the control
695 # center for most of the rest of the operations.
696 class radar_window:
697 # Create a new radar_window.
699 #Parameters:
701 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
703 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
705 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
707 # 'logger' -- Logger - Python's logging facility
709 #Returns:
711 # radar_window instance
712 def __init__( self, confFile, apQueue, commQueue, logger ):
713 global signal_xpm_none
714 global signal_xpm_low
715 global signal_xpm_barely
716 global signal_xpm_ok
717 global signal_xpm_best
718 global known_profile_icon
719 global unknown_profile_icon
720 global wifi_radar_icon
722 self.confFile = confFile
723 self.apQueue = apQueue
724 self.commandQueue = commQueue
725 self.logger = logger
726 self.connection = None
728 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
729 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
730 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
731 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
732 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
733 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
734 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
735 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
736 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
737 self.window.set_icon( icon )
738 self.window.set_border_width( 10 )
739 self.window.set_size_request( 550, 300 )
740 self.window.set_title( "WiFi Radar" )
741 self.window.connect( 'delete_event', self.delete_event )
742 self.window.connect( 'destroy', self.destroy )
743 # let's create all our widgets
744 self.current_network = gtk.Label()
745 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
746 self.current_network.show()
747 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
748 self.close_button.show()
749 self.close_button.connect( 'clicked', self.delete_event, None )
750 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
751 self.about_button.show()
752 self.about_button.connect( 'clicked', self.show_about_info, None )
753 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
754 self.preferences_button.show()
755 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
756 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
757 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
758 self.plist = gtk.TreeView( self.pstore )
759 # The icons column, known and encryption
760 self.pix_cell = gtk.CellRendererPixbuf()
761 self.wep_cell = gtk.CellRendererPixbuf()
762 self.icons_cell = gtk.CellRendererText()
763 self.icons_col = gtk.TreeViewColumn()
764 self.icons_col.pack_start( self.pix_cell, False )
765 self.icons_col.pack_start( self.wep_cell, False )
766 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
767 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
768 self.plist.append_column( self.icons_col )
769 # The AP column
770 self.ap_cell = gtk.CellRendererText()
771 self.ap_col = gtk.TreeViewColumn( "Access Point" )
772 self.ap_col.pack_start( self.ap_cell, True )
773 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
774 self.plist.append_column( self.ap_col )
775 # The signal column
776 self.sig_cell = gtk.CellRendererPixbuf()
777 self.signal_col = gtk.TreeViewColumn( "Signal" )
778 self.signal_col.pack_start( self.sig_cell, True )
779 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
780 self.plist.append_column( self.signal_col )
781 # The mode column
782 self.mode_cell = gtk.CellRendererText()
783 self.mode_col = gtk.TreeViewColumn( "Mode" )
784 self.mode_col.pack_start( self.mode_cell, True )
785 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
786 self.plist.append_column( self.mode_col )
787 # The protocol column
788 self.prot_cell = gtk.CellRendererText()
789 self.protocol_col = gtk.TreeViewColumn( "802.11" )
790 self.protocol_col.pack_start( self.prot_cell, True )
791 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
792 self.plist.append_column( self.protocol_col )
793 # The channel column
794 self.channel_cell = gtk.CellRendererText()
795 self.channel_col = gtk.TreeViewColumn( "Channel" )
796 self.channel_col.pack_start( self.channel_cell, True )
797 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
798 self.plist.append_column( self.channel_col )
799 # DnD Ordering
800 self.plist.set_reorderable( True )
801 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
802 self.pstore.connect( 'row-deleted', self.update_auto_profile_order )
803 # enable/disable buttons based on the selected network
804 self.selected_network = self.plist.get_selection()
805 self.selected_network.connect( 'changed', self.on_network_selection, None )
806 # the list scroll bar
807 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
808 sb.show()
809 self.plist.show()
810 # Add New button
811 self.new_button = gtk.Button( "_New" )
812 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
813 self.new_button.show()
814 # Add Configure button
815 self.edit_button = gtk.Button( "C_onfigure" )
816 self.edit_button.connect( 'clicked', self.edit_profile, None )
817 self.edit_button.show()
818 self.edit_button.set_sensitive(False)
819 # Add Delete button
820 self.delete_button = gtk.Button( "_Delete" )
821 self.delete_button.connect( 'clicked', self.delete_profile, None )
822 self.delete_button.show()
823 self.delete_button.set_sensitive(False)
824 # Add Connect button
825 self.connect_button = gtk.Button( "Co_nnect" )
826 self.connect_button.connect( 'clicked', self.connect_profile, None )
827 # Add Disconnect button
828 self.disconnect_button = gtk.Button( "D_isconnect" )
829 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
830 # lets add our widgets
831 rows = gtk.VBox( False, 3 )
832 net_list = gtk.HBox( False, 0 )
833 listcols = gtk.HBox( False, 0 )
834 prows = gtk.VBox( False, 0 )
835 # lets start packing
836 # the network list
837 net_list.pack_start( self.plist, True, True, 0 )
838 net_list.pack_start( sb, False, False, 0 )
839 # the rows level
840 rows.pack_start( net_list , True, True, 0 )
841 rows.pack_start( self.current_network, False, True, 0 )
842 # the list columns
843 listcols.pack_start( rows, True, True, 0 )
844 listcols.pack_start( prows, False, False, 5 )
845 # the list buttons
846 prows.pack_start( self.new_button, False, False, 2 )
847 prows.pack_start( self.edit_button, False, False, 2 )
848 prows.pack_start( self.delete_button, False, False, 2 )
849 prows.pack_end( self.connect_button, False, False, 2 )
850 prows.pack_end( self.disconnect_button, False, False, 2 )
852 self.window.action_area.pack_start( self.about_button )
853 self.window.action_area.pack_start( self.preferences_button )
854 self.window.action_area.pack_start( self.close_button )
856 rows.show()
857 prows.show()
858 listcols.show()
859 self.window.vbox.add( listcols )
860 self.window.vbox.set_spacing( 3 )
861 self.window.show_all()
863 # Now, immediately hide these two. The proper one will be
864 # displayed later, based on interface state. -BEF-
865 self.disconnect_button.hide()
866 self.connect_button.hide()
867 self.connect_button.set_sensitive(False)
869 # set up connection manager for later use
870 self.connection = ConnectionManager( self.confFile, self.commandQueue, self.logger )
871 # set up status window for later use
872 self.status_window = StatusWindow( self )
873 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
875 # Add our known profiles in order
876 for profile_name in self.confFile.auto_profile_order:
877 profile = self.confFile.get_profile( profile_name.strip() )
878 wep = None
879 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
880 self.update_list(profile)
881 # This is the first run (or, at least, no config file was present), so pop up the preferences window
882 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
883 self.confFile.remove_option('DEFAULT', 'new_file')
884 self.edit_preferences(self.preferences_button)
886 # Begin running radar_window in Gtk event loop.
888 #Parameters:
890 # nothing
892 #Returns:
894 # nothing
895 def main( self ):
896 gtk.main()
898 # Quit application.
900 #Parameters:
902 # 'widget' -- gtk.Widget - The widget sending the event.
904 #Returns:
906 # nothing
907 def destroy( self, widget = None):
908 if self.status_window:
909 self.status_window.destroy()
910 gtk.main_quit()
912 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
914 #Parameters:
916 # 'widget' -- gtk.Widget - The widget sending the event.
918 # 'data' -- tuple - list of arbitrary arguments (not used)
920 #Returns:
922 # boolean -- always return False (i.e. do not propigate the signal which called)
923 def delete_event( self, widget, data = None ):
924 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
925 # signal each listening thread, should be two
926 try:
927 self.commandQueue.put("exit", True)
928 self.commandQueue.put("exit", True)
929 except Queue.Full:
930 pass
931 self.destroy()
932 return False
934 # Updates the on-screen profiles list.
936 #Parameters:
938 # nothing
940 #Returns:
942 # boolean -- always return True
943 def update_list( self, profile ):
944 # Indicate to PyGtk that only one Gtk thread should run here
945 gtk.gdk.threads_enter()
946 # update the current ip and essid
947 # set the state of connect/disconnect buttons based on whether we have an IP address
948 if self.connection:
949 if self.connection.state:
950 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() ) )
951 self.connect_button.hide()
952 self.disconnect_button.show()
953 else:
954 self.current_network.set_text( "Not Connected." )
955 self.disconnect_button.hide()
956 self.connect_button.show()
958 if profile['roaming']:
959 prow_iter = self.get_row_by_ap(profile['essid'], ' Multiple APs')
960 else:
961 prow_iter = self.get_row_by_ap(profile['essid'], profile['bssid'])
962 wep = None
963 if prow_iter != None:
964 # the AP is in the list of APs on the screen
965 # Set the 'known' values
966 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known( profile[ 'known' ] ))
967 self.pstore.set_value(prow_iter, 2, profile[ 'known' ])
968 self.pstore.set_value(prow_iter, 3, profile['available'])
969 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
970 self.pstore.set_value(prow_iter, 4, wep)
971 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal( profile['signal'] ))
972 self.pstore.set_value(prow_iter, 6, profile['mode'])
973 self.pstore.set_value(prow_iter, 7, profile['protocol'])
974 self.pstore.set_value(prow_iter, 8, profile['channel'])
975 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
976 #for val in self.pstore[prow_iter]:
977 #print val,
978 else:
979 # the AP is not in the list of APs on the screen
980 if profile['roaming']:
981 ap_name = profile[ 'essid' ] + "\n" + ' Multiple APs'
982 else:
983 ap_name = profile[ 'essid' ] + "\n" + profile['bssid']
984 self.pstore.append([ap_name, self.pixbuf_from_known(profile['known']), profile['known'], profile['available'], wep, self.pixbuf_from_signal(profile['signal']), profile['mode'], profile['protocol'], profile['channel']])
985 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
986 # Allow other Gtk threads to run
987 gtk.gdk.threads_leave()
988 #print "update_plist_items: Empty apQueue"
989 return True
991 # Return the proper icon for a value of known.
993 #Parameters:
995 # 'known' -- boolean - Whether the AP is known (i.e. configured)
997 #Returns:
999 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
1000 def pixbuf_from_known( self, known ):
1001 """ return the proper icon for value of known """
1002 if known:
1003 return self.known_profile_icon
1004 else:
1005 return self.unknown_profile_icon
1007 # Return an icon indicating the signal level.
1009 #Parameters:
1011 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
1013 #Returns:
1015 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
1016 def pixbuf_from_signal( self, signal ):
1017 signal = int( signal )
1018 # shift signal up by 80 to convert dBm scale to arbitrary scale
1019 if signal < 0: signal = signal + 80
1020 #print "signal level:", signal
1021 if signal < 3:
1022 return self.signal_none_pb
1023 elif signal < 12:
1024 return self.signal_low_pb
1025 elif signal < 20:
1026 return self.signal_barely_pb
1027 elif signal < 35:
1028 return self.signal_ok_pb
1029 elif signal >= 35:
1030 return self.signal_best_pb
1031 else:
1032 return None
1034 # Return row which holds specified ESSID and BSSID.
1036 #Parameters:
1038 # 'essid' -- string - ESSID to match
1040 # 'bssid' -- string - BSSID to match
1042 #Returns:
1044 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
1045 def get_row_by_ap( self, essid, bssid ):
1046 for row in self.pstore:
1047 if ( row[0] == essid + "\n" + bssid ):
1048 #print "matched:", row.iter, essid, bssid
1049 return row.iter
1050 return None
1052 # Enable/disable buttons based on the selected network.
1054 #Parameters:
1056 # 'widget' -- gtk.Widget - The widget sending the event.
1058 # 'data' -- tuple - list of arbitrary arguments (not used)
1060 #Returns:
1062 # nothing
1063 def on_network_selection( self, widget, data = None ):
1064 ( store, selected_iter ) = self.selected_network.get_selected()
1065 # if no networks are selected, disable all buttons except New
1066 # (this occurs after a drag-and-drop)
1067 if selected_iter == None:
1068 self.edit_button.set_sensitive(False)
1069 self.delete_button.set_sensitive(False)
1070 self.connect_button.set_sensitive(False)
1071 return
1072 # enable/disable buttons
1073 self.connect_button.set_sensitive(True)
1074 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1075 self.edit_button.set_sensitive(True)
1076 self.delete_button.set_sensitive(True)
1077 else:
1078 self.edit_button.set_sensitive(True)
1079 self.delete_button.set_sensitive(False)
1081 # Init and run the about dialog
1083 #Parameters:
1085 # 'widget' -- gtk.Widget - The widget sending the event.
1087 # 'data' -- tuple - list of arbitrary arguments (not used)
1089 #Returns:
1091 # nothing
1092 def show_about_info( self, widget, data=None ):
1093 about = about_dialog()
1094 about.run()
1095 about.destroy()
1097 # Init and run the preferences dialog
1099 #Parameters:
1101 # 'widget' -- gtk.Widget - The widget sending the event.
1103 # 'data' -- tuple - list of arbitrary arguments (not used)
1105 #Returns:
1107 # nothing
1108 def edit_preferences( self, widget, data=None ):
1109 # get raw strings from config file
1110 self.confFile.raw = True
1111 prefs = preferences_dialog( self, self.confFile )
1112 response = prefs.run()
1113 prefs.destroy()
1114 if response == int(gtk.RESPONSE_ACCEPT):
1115 prefs.save()
1116 # get cooked strings from config file
1117 self.confFile.raw = False
1119 # Respond to a request to create a new AP profile
1121 #Parameters:
1123 # 'widget' -- gtk.Widget - The widget sending the event.
1125 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1127 # 'data' -- tuple - list of arbitrary arguments (not used)
1129 #Returns:
1131 # boolean -- True if a profile was created and False if profile creation was canceled.
1132 def create_new_profile( self, widget, profile, data=None ):
1133 profile_editor = profile_dialog( self, profile )
1134 try:
1135 profile = profile_editor.run()
1136 except ValueError:
1137 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1138 del error_dlg
1139 return False
1140 finally:
1141 profile_editor.destroy()
1142 if profile:
1143 apname = make_section_name( profile['essid'], profile['bssid'] )
1144 # Check that the ap does not exist already
1145 if apname in self.confFile.profiles():
1146 error_dlg = ErrorDialog(None, "A profile for %s already exists" % (apname))
1147 del error_dlg
1148 # try again
1149 self.confFile.set_section( apname, profile )
1150 # if it is not in the auto_profile_order add it
1151 if apname not in self.confFile.auto_profile_order:
1152 self.confFile.auto_profile_order.append(apname)
1153 # add to the store
1154 wep = None
1155 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1156 try:
1157 self.confFile.write()
1158 except IOError, (error_number, error_str):
1159 if error_number == errno.ENOENT:
1160 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1161 del error_dlg
1162 else:
1163 raise IOError(error_number, error_str)
1164 # Add AP to the list displayed to user
1165 try:
1166 self.apQueue.put_nowait(profile)
1167 except Queue.Full:
1168 pass
1169 return True
1170 else:
1171 # Did not create new profile
1172 return False
1174 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1175 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1177 #Parameters:
1179 # 'widget' -- gtk.Widget - The widget sending the event.
1181 # 'data' -- tuple - list of arbitrary arguments (not used)
1183 #Returns:
1185 # nothing
1186 def edit_profile(self, widget, data=None):
1187 (store, selected_iter) = self.plist.get_selection().get_selected()
1188 if not selected_iter: return
1189 (essid, bssid) = str(self.pstore.get_value(selected_iter, 0)).split("\n")
1190 if bssid == ' Multiple APs':
1191 apname = make_section_name(essid, '')
1192 else:
1193 apname = make_section_name(essid, bssid)
1194 profile = self.confFile.get_profile(apname)
1195 if profile:
1196 profile_editor = profile_dialog(self, profile)
1197 try:
1198 edited_profile = profile_editor.run()
1199 except ValueError:
1200 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1201 del error_dlg
1202 return False
1203 finally:
1204 profile_editor.destroy()
1205 if edited_profile:
1206 if edited_profile['essid'] != profile['essid'] or edited_profile['bssid'] != profile['bssid'] or edited_profile['roaming'] != profile['roaming']:
1207 self.delete_profile(widget)
1208 self.confFile.remove_section(make_section_name(profile['essid'], profile['bssid']))
1209 apname = make_section_name(edited_profile['essid'], edited_profile['bssid'])
1210 self.confFile.set_section(apname, edited_profile)
1211 try:
1212 self.confFile.write()
1213 except IOError, (error_number, error_str):
1214 if error_number == errno.ENOENT:
1215 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1216 del error_dlg
1217 else:
1218 raise IOError(error_number, error_str)
1219 else:
1220 # The AP does not already have a profile
1221 profile = get_new_profile()
1222 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1223 self.create_new_profile( widget, profile, data )
1225 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1227 #Parameters:
1229 # 'widget' -- gtk.Widget - The widget sending the event.
1231 # 'data' -- tuple - list of arbitrary arguments (not used)
1233 #Returns:
1235 # nothing
1236 def delete_profile( self, widget, data=None ):
1237 (store, selected_iter) = self.plist.get_selection().get_selected()
1238 if not selected_iter: return
1239 (essid, bssid) = store.get_value(selected_iter, 0).split("\n")
1240 if bssid == ' Multiple APs':
1241 apname = make_section_name(essid, '')
1242 else:
1243 apname = make_section_name(essid, bssid)
1244 profile = self.confFile.get_profile(apname)
1245 if not profile:
1246 return
1247 if profile['roaming']:
1248 dlg = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Are you sure you want to delete the %s profile?" % (essid, ))
1249 else:
1250 dlg = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid))
1251 res = dlg.run()
1252 dlg.destroy()
1253 del dlg
1254 if res == gtk.RESPONSE_NO:
1255 return
1256 # Remove it
1257 self.confFile.remove_section(apname)
1258 self.logger.debug(apname)
1259 if apname in self.confFile.auto_profile_order:
1260 self.confFile.auto_profile_order.remove(apname)
1261 self.pstore.remove(selected_iter)
1262 # Let's save our current state
1263 self.update_auto_profile_order()
1264 try:
1265 self.confFile.write()
1266 except IOError, (error_number, error_str):
1267 if error_number == errno.ENOENT:
1268 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1269 del error_dlg
1270 else:
1271 raise IOError(error_number, error_str)
1273 # Respond to a request to connect to an AP.
1275 #Parameters:
1277 # 'widget' -- gtk.Widget - The widget sending the event.
1279 # 'profile' -- dictionary - The AP profile to which to connect.
1281 # 'data' -- tuple - list of arbitrary arguments (not used)
1283 #Returns:
1285 # nothing
1286 def connect_profile( self, widget, profile, data=None ):
1287 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1288 if not selected_iter: return
1289 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1290 known = store.get_value( selected_iter, 2 )
1291 if not known:
1292 if data != 'noconnect':
1293 dlg = gtk.MessageDialog(
1294 self.window,
1295 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1296 gtk.MESSAGE_QUESTION,
1297 gtk.BUTTONS_YES_NO,
1298 "This network does not have a profile configured.\n\nWould you like to create one now?" )
1299 res = dlg.run()
1300 dlg.destroy()
1301 del dlg
1302 if res == gtk.RESPONSE_NO: return
1303 profile = get_new_profile()
1304 profile['essid'] = essid
1305 profile['bssid'] = bssid
1306 if not self.create_new_profile( widget, profile, data ):
1307 return
1308 self.connection.connect_to_network(self.confFile.get_profile(make_section_name(essid, bssid)), self.status_window)
1310 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1312 #Parameters:
1314 # 'widget' -- gtk.Widget - The widget sending the event.
1316 # 'data' -- tuple - list of arbitrary arguments (not used)
1318 #Returns:
1320 # nothing
1321 def disconnect_profile( self, widget, data=None ):
1322 if data == "cancel":
1323 self.status_window.update_message("Canceling connection...")
1324 if sys.modules.has_key("gtk"):
1325 while gtk.events_pending():
1326 gtk.main_iteration(False)
1327 sleep(1)
1328 self.connection.disconnect_interface()
1330 # Update the config file auto profile order from the on-screen order
1332 #Parameters:
1334 # 'widget' -- gtk.Widget - The widget sending the event.
1336 # 'data' -- tuple - list of arbitrary arguments (not used)
1338 # 'data2' -- tuple - list of arbitrary arguments (not used)
1340 #Returns:
1342 # nothing
1343 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1344 # recreate the auto_profile_order
1345 auto_profile_order = []
1346 piter = self.pstore.get_iter_first()
1347 while piter:
1348 # only if it's known
1349 if self.pstore.get_value(piter, 2) == True:
1350 (essid, bssid) = self.pstore.get_value(piter, 0).split("\n")
1351 if bssid == ' Multiple APs':
1352 apname = make_section_name(essid, '')
1353 else:
1354 apname = make_section_name(essid, bssid)
1355 auto_profile_order.append(apname)
1356 piter = self.pstore.iter_next(piter)
1357 self.confFile.auto_profile_order = auto_profile_order
1358 try:
1359 self.confFile.write()
1360 except IOError, (error_number, error_str):
1361 if error_number == errno.ENOENT:
1362 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1363 del error_dlg
1364 else:
1365 raise IOError(error_number, error_str)
1368 # Button to allow user to choose a file and put value into specified gtk.Entry
1369 class file_browse_button(gtk.Button):
1370 # Create a button to simulate a File/Open
1372 #Parameters:
1374 # 'parent' -- gtk.Object -- Usually, the calling window.
1376 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1378 #Returns:
1380 # file_browse_button instance
1381 def __init__( self, parent, entry ):
1382 self.parent_window = parent
1383 self.entry = entry
1384 gtk.Button.__init__(self, "Browse", None)
1385 #self.
1386 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)
1387 self.connect("clicked", self.browse_files)
1389 # Show filechooser dialog and get user selection
1391 #Parameters:
1393 # 'widget' -- gtk.Widget -- The widget sending the event.
1395 #Returns:
1397 # nothing
1399 #NOTES:
1401 # updates entry value
1403 def browse_files( self, widget ):
1404 self.browser_dialog.set_filename(self.entry.get_text())
1405 self.browser_dialog.run()
1406 self.entry.set_text(self.browser_dialog.get_filename())
1407 self.browser_dialog.destroy()
1410 # Simple dialog to report an error to the user.
1411 class ErrorDialog:
1412 # Create a new ErrorDialog.
1414 #Parameters:
1416 # 'parent' -- gtk.Object - Usually, the calling window.
1418 # 'message' -- string - The message to display to the user.
1420 #Returns:
1422 # ErrorDialog instance
1423 def __init__( self, parent, message ):
1424 dialog = gtk.MessageDialog( parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message )
1425 dialog.run()
1426 dialog.destroy()
1427 del dialog
1430 # The preferences dialog. Edits non-profile sections of the config file.
1431 class preferences_dialog:
1432 # Create a new preferences_dialog.
1434 #Parameters:
1436 # 'parent' -- gtk.Object - Usually, the calling window.
1438 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1440 #Returns:
1442 # preferences_dialog instance
1443 def __init__( self, parent, confFile ):
1444 global wifi_radar_icon
1445 self.parent = parent
1446 self.confFile = confFile
1447 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1448 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1449 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1450 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1451 self.dialog.set_icon( icon )
1452 self.dialog.set_resizable( True )
1453 self.dialog.set_transient_for( self.parent.window )
1454 self.tooltips = gtk.Tooltips()
1456 # set up preferences widgets
1458 # build everything in a tabbed notebook
1459 self.prefs_notebook = gtk.Notebook()
1461 ### General tab
1462 self.general_page = gtk.VBox()
1463 # auto detect wireless device
1464 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1466 print "prefs: ", self.confFile.get_opt('DEFAULT.interface')
1468 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1469 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1470 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1471 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1473 # network interface selecter
1474 self.w_interface = gtk.combo_box_entry_new_text()
1475 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE).stdout
1476 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1477 for device in wireless_devices:
1478 if device != self.confFile.get_opt('DEFAULT.interface'):
1479 self.w_interface.append_text(device)
1480 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1481 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1482 self.w_interface.set_active(0)
1483 self.w_interface_label = gtk.Label("Wireless device")
1484 self.w_hbox1 = gtk.HBox(False, 0)
1485 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1486 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1487 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1488 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1490 # scan timeout (spin button of integers from 1 to 100)
1491 #self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,0 )
1492 #self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1493 #self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1494 #self.w_scan_timeout.set_numeric(True)
1495 #self.w_scan_timeout.set_snap_to_ticks(True)
1496 #self.w_scan_timeout.set_wrap(False)
1497 #self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1498 #self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1499 #self.w_hbox2 = gtk.HBox(False, 0)
1500 #self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1501 #self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1502 #self.general_page.pack_start(self.w_hbox2, False, False, 5)
1504 # speak up
1505 self.w_speak_up = gtk.CheckButton("Use speak-up")
1506 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1507 self.w_speak_up.connect("toggled", self.toggle_speak)
1508 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1509 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1511 # speak up command
1512 self.w_speak_cmd = gtk.Entry()
1513 self.w_speak_cmd.set_width_chars(16)
1514 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1515 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1516 self.w_speak_cmd_label = gtk.Label("Speak Command")
1517 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1518 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1519 self.w_hbox3 = gtk.HBox(False, 0)
1520 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1521 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1522 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1523 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1524 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1526 # commit required
1527 self.w_commit_required = gtk.CheckButton("Commit required")
1528 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1529 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1530 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1532 # ifup required
1533 self.w_ifup_required = gtk.CheckButton("Ifup required")
1534 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1535 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1536 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1538 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1539 ### End of General tab
1541 ### Advanced tab
1542 # table to use for layout of following command configurations
1543 self.cmds_table = gtk.Table()
1545 # ifconfig command
1546 self.ifconfig_cmd = gtk.Entry()
1547 self.ifconfig_cmd.set_width_chars(32)
1548 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1549 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1550 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1551 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1552 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1553 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1554 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, True, False, 0, 0)
1555 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1557 # iwconfig command
1558 self.iwconfig_cmd = gtk.Entry()
1559 self.iwconfig_cmd.set_width_chars(32)
1560 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1561 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1562 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1563 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1564 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1565 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, True, False, 5, 0)
1566 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, True, False, 0, 0)
1567 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, False, False, 0, 0)
1569 # iwlist command
1570 self.iwlist_cmd = gtk.Entry()
1571 self.iwlist_cmd.set_width_chars(32)
1572 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1573 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1574 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1575 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1576 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1577 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, True, False, 5, 0)
1578 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, True, False, 0, 0)
1579 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, False, False, 0, 0)
1581 # route command
1582 self.route_cmd = gtk.Entry()
1583 self.route_cmd.set_width_chars(32)
1584 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1585 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1586 self.route_cmd_label = gtk.Label("Network route configure command")
1587 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1588 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1589 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, True, False, 5, 0)
1590 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, True, False, 0, 0)
1591 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, False, False, 0, 0)
1593 # log file
1594 self.logfile_entry = gtk.Entry()
1595 self.logfile_entry.set_width_chars(32)
1596 self.tooltips.set_tip(self.logfile_entry, "The file in which to save logging info")
1597 self.logfile_entry.set_text(self.confFile.get_opt('DEFAULT.logfile'))
1598 self.logfile_label = gtk.Label("Log file")
1599 self.logfile_button = file_browse_button(self.dialog, self.logfile_entry)
1600 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1601 self.cmds_table.attach(self.logfile_label, 1, 2, 5, 6, True, False, 5, 0)
1602 self.cmds_table.attach(self.logfile_entry, 2, 3, 5, 6, True, False, 0, 0)
1603 self.cmds_table.attach(self.logfile_button, 3, 4, 5, 6, False, False, 0, 0)
1605 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1606 ### End of Advanced tab
1608 ### DHCP tab
1609 # table to use for layout of DHCP prefs
1610 self.dhcp_table = gtk.Table()
1612 self.dhcp_cmd = gtk.Entry()
1613 self.dhcp_cmd.set_width_chars(32)
1614 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1615 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1616 self.dhcp_cmd_label = gtk.Label("Command")
1617 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1618 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1619 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1620 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1622 self.dhcp_args = gtk.Entry()
1623 self.dhcp_args.set_width_chars(32)
1624 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1625 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1626 self.dhcp_args_label = gtk.Label("Arguments")
1627 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1628 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1630 self.dhcp_kill_args = gtk.Entry()
1631 self.dhcp_kill_args.set_width_chars(32)
1632 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1633 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1634 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1635 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1636 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1638 self.dhcp_timeout = gtk.Entry()
1639 self.dhcp_timeout.set_width_chars(32)
1640 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1641 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1642 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1643 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1644 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1646 self.dhcp_pidfile = gtk.Entry()
1647 self.dhcp_pidfile.set_width_chars(32)
1648 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1649 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1650 self.dhcp_pidfile_label = gtk.Label("PID file")
1651 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1652 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1654 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1655 ### End of DHCP tab
1657 ### WPA tab
1658 # table to use for layout of DHCP prefs
1659 self.wpa_table = gtk.Table()
1661 self.wpa_cmd = gtk.Entry()
1662 self.wpa_cmd.set_width_chars(32)
1663 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1664 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1665 self.wpa_cmd_label = gtk.Label("Command")
1666 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1667 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1668 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1669 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1671 self.wpa_args = gtk.Entry()
1672 self.wpa_args.set_width_chars(32)
1673 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1674 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1675 self.wpa_args_label = gtk.Label("Arguments")
1676 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1677 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1679 self.wpa_kill_args = gtk.Entry()
1680 self.wpa_kill_args.set_width_chars(32)
1681 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1682 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1683 self.wpa_kill_args_label = gtk.Label("Kill command")
1684 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1685 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1687 self.wpa_config = gtk.Entry()
1688 self.wpa_config.set_width_chars(32)
1689 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1690 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1691 self.wpa_config_label = gtk.Label("Configuration file")
1692 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1693 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1695 self.wpa_driver = gtk.Entry()
1696 self.wpa_driver.set_width_chars(32)
1697 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1698 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1699 self.wpa_driver_label = gtk.Label("Driver")
1700 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1701 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1703 self.wpa_pidfile = gtk.Entry()
1704 self.wpa_pidfile.set_width_chars(32)
1705 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1706 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1707 self.wpa_pidfile_label = gtk.Label("PID file")
1708 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1709 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1711 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1712 ### End of WPA tab
1714 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1716 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1718 #Parameters:
1720 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1722 # 'data' -- tuple - list of arbitrary arguments (not used)
1724 #Returns:
1726 # nothing
1727 def toggle_auto_detect(self, auto_detect_toggle, data=None):
1728 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
1730 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
1732 #Parameters:
1734 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1736 # 'data' -- tuple - list of arbitrary arguments (not used)
1738 #Returns:
1740 # nothing
1741 def toggle_speak(self, speak_toggle, data=None):
1742 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
1743 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
1745 # Display preferences dialog and operate until canceled or okayed.
1747 #Parameters:
1749 # nothing
1751 #Returns:
1753 # integer -- gtk response ID
1754 def run(self):
1755 self.dialog.show_all()
1756 return self.dialog.run()
1758 # Write updated values to config file.
1760 #Parameters:
1762 # nothing
1764 #Returns:
1766 # nothing
1767 def save(self):
1768 if self.w_auto_detect.get_active():
1769 set_network_device("auto_detect")
1770 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
1771 else:
1772 # get the selected interface, using a work-around suggested by PyGTK Tutorial
1773 interface = self.w_interface.get_model()[self.w_interface.get_active()][0]
1774 self.confFile.set_opt('DEFAULT.interface', interface)
1775 set_network_device(interface)
1776 #self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
1777 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
1778 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
1779 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
1780 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
1781 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
1782 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
1783 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
1784 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
1785 self.confFile.set_opt('DEFAULT.logfile', self.logfile_entry.get_text())
1786 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
1787 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
1788 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
1789 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
1790 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
1791 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
1792 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
1793 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
1794 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
1795 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
1796 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
1797 try:
1798 self.confFile.write()
1799 except IOError, (error_number, error_str):
1800 if error_number == errno.ENOENT:
1801 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1802 del error_dlg
1803 else:
1804 raise IOError(error_number, error_str)
1806 # Remove preferences window.
1808 #Parameters:
1810 # nothing
1812 #Returns:
1814 # nothing
1815 def destroy(self):
1816 self.dialog.destroy()
1817 del self.dialog
1820 # Edit and return an AP profile.
1821 class profile_dialog:
1822 # Create a new profile_dialog.
1824 #Parameters:
1826 # 'parent' -- gtk.Object - Usually, the calling window.
1828 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
1830 #Returns:
1832 # profile_dialog instance
1833 def __init__( self, parent, profile ):
1834 global wifi_radar_icon
1836 # Labels
1837 WIFI_SET_LABEL = "WiFi Options"
1838 USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
1839 USE_IP_LABEL = "Manual network configuration"
1840 USE_WPA_LABEL = "Use WPA"
1841 NO_WPA_LABEL = "No WPA"
1842 CON_PP_LABEL = "Connection Commands"
1843 DIS_PP_LABEL = "Disconnection Commands"
1845 self.parent = parent
1846 self.profile = profile.copy()
1847 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1848 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
1849 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1850 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
1851 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1852 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
1853 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1854 self.dialog.set_icon( icon )
1855 self.dialog.set_resizable( False )
1856 self.dialog.set_transient_for( self.parent.window )
1857 #self.dialog.set_size_request( 400, 400 )
1858 #################
1859 self.tooltips = gtk.Tooltips()
1861 general_table = gtk.Table()
1862 general_table.set_row_spacings(3)
1863 general_table.set_col_spacings(3)
1864 # The essid labels
1865 general_table.attach(gtk.Label('Network Name'), 0, 1, 0, 1)
1866 # The essid textboxes
1867 self.essid_entry = gtk.Entry(32)
1868 self.essid_entry.set_text(self.profile['essid'])
1869 general_table.attach(self.essid_entry, 1, 2, 0, 1)
1870 # Add the essid table to the dialog
1871 self.dialog.vbox.pack_start(general_table, True, True, 5)
1872 self.tooltips.set_tip(self.essid_entry, "The name of the network with which to connect.")
1874 # The bssid labels
1875 general_table.attach(gtk.Label('Network Address'), 0, 1, 1, 2)
1876 # The bssid textboxes
1877 self.bssid_entry = gtk.Entry(32)
1878 self.bssid_entry.set_text(self.profile['bssid'])
1879 self.bssid_entry.set_sensitive(not self.profile['roaming'])
1880 # Add the bssid table to the dialog
1881 general_table.attach(self.bssid_entry, 1, 2, 1, 2)
1882 self.tooltips.set_tip(self.bssid_entry, "The address of the network with which to connect.")
1883 # Add the roaming checkbox
1884 self.roaming_cb = gtk.CheckButton('Roaming')
1885 self.roaming_cb.set_active(self.profile['roaming'])
1886 self.roaming_cb.connect("toggled", self.toggle_roaming)
1887 general_table.attach(self.roaming_cb, 1, 2, 2, 3)
1888 self.tooltips.set_tip(self.roaming_cb, "Use the AP in this network which provides strongest signal?")
1889 # create the WiFi expander
1890 self.wifi_expander = gtk.Expander( WIFI_SET_LABEL )
1891 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1892 wifi_table = gtk.Table( 4, 2, False )
1893 wifi_table.set_row_spacings( 3 )
1894 wifi_table.set_col_spacings( 3 )
1895 # The WiFi labels
1896 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
1897 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
1898 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
1899 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
1900 # The WiFi text boxes
1901 self.mode_combo = gtk.combo_box_new_text()
1902 for mode in self.WIFI_MODES:
1903 self.mode_combo.append_text( mode )
1904 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
1905 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
1906 self.tooltips.set_tip(self.mode_combo, "Method to use for connection. You probably want auto mode.")
1907 self.channel_combo = gtk.combo_box_new_text()
1908 for channel in self.WIFI_CHANNELS:
1909 self.channel_combo.append_text( channel )
1910 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
1911 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
1912 self.tooltips.set_tip(self.channel_combo, "Channel the network uses. You probably want auto mode.")
1914 self.key_entry = gtk.Entry( 64 )
1915 self.key_entry.set_text( self.profile['key'] )
1916 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
1917 self.tooltips.set_tip(self.key_entry, "WEP key: Plain language or hex string to use for encrypted communication with the network.")
1919 self.security_combo = gtk.combo_box_new_text()
1920 for security in self.WIFI_SECURITY:
1921 self.security_combo.append_text( security )
1922 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
1923 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
1924 self.tooltips.set_tip(self.security_combo, "Use Open to allow unencrypted communication and Restricted to force encrypted-only communication.")
1925 # Add the wifi table to the expander
1926 self.wifi_expander.add( wifi_table )
1927 # Add the expander to the dialog
1928 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
1930 # create the wpa expander
1931 self.wpa_expander = gtk.Expander( NO_WPA_LABEL )
1932 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
1933 wpa_table = gtk.Table( 1, 2, False )
1934 wpa_table.set_row_spacings( 3 )
1935 wpa_table.set_col_spacings( 3 )
1936 # The labels
1937 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1938 # The text boxes
1939 self.wpa_driver_entry = gtk.Entry()
1940 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
1941 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
1942 # Add the wpa table to the expander
1943 self.wpa_expander.add( wpa_table )
1944 # Add the expander to the dialog
1945 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
1947 # create the dhcp expander
1948 self.dhcp_expander = gtk.Expander( USE_DHCP_LABEL )
1949 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1950 ip_table = gtk.Table( 6, 2, False )
1951 ip_table.set_row_spacings( 3 )
1952 ip_table.set_col_spacings( 3 )
1953 # The IP labels
1954 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
1955 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
1956 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
1957 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
1958 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
1959 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
1960 # The IP text boxes
1961 self.ip_entry = gtk.Entry( 15 )
1962 self.ip_entry.set_text( self.profile['ip'] )
1963 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
1964 self.netmask_entry = gtk.Entry( 15 )
1965 self.netmask_entry.set_text( self.profile['netmask'] )
1966 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
1967 self.gw_entry = gtk.Entry( 15 )
1968 self.gw_entry.set_text( self.profile['gateway'] )
1969 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
1970 self.domain_entry = gtk.Entry( 32 )
1971 self.domain_entry.set_text( self.profile['domain'] )
1972 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
1973 self.dns1_entry = gtk.Entry( 15 )
1974 self.dns1_entry.set_text( self.profile['dns1'] )
1975 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
1976 self.dns2_entry = gtk.Entry( 15 )
1977 self.dns2_entry.set_text( self.profile['dns2'] )
1978 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
1979 # Add the ip table to the expander
1980 self.dhcp_expander.add( ip_table )
1981 # Add the expander to the dialog
1982 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
1984 # create the connection-building postpre expander
1985 self.con_pp_expander = gtk.Expander( CON_PP_LABEL )
1986 con_pp_table = gtk.Table( 2, 2, False )
1987 con_pp_table.set_row_spacings( 3 )
1988 con_pp_table.set_col_spacings( 3 )
1989 # The labels
1990 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1991 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1992 # The text boxes
1993 self.con_prescript_entry = gtk.Entry()
1994 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
1995 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
1996 self.tooltips.set_tip(self.con_prescript_entry, "The local command to execute before trying to connect to the network.")
1997 self.con_postscript_entry = gtk.Entry()
1998 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
1999 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
2000 self.tooltips.set_tip(self.con_postscript_entry, "The local command to execute after connecting to the network.")
2001 # Add the pp table to the expander
2002 self.con_pp_expander.add( con_pp_table )
2003 # Add the expander to the dialog
2004 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
2006 # create the disconnection postpre expander
2007 self.dis_pp_expander = gtk.Expander( DIS_PP_LABEL )
2008 dis_pp_table = gtk.Table( 2, 2, False )
2009 dis_pp_table.set_row_spacings( 3 )
2010 dis_pp_table.set_col_spacings( 3 )
2011 # The labels
2012 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2013 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2014 # The text boxes
2015 self.dis_prescript_entry = gtk.Entry()
2016 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
2017 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
2018 self.tooltips.set_tip(self.dis_prescript_entry, "The local command to execute before disconnecting from the network.")
2019 self.dis_postscript_entry = gtk.Entry()
2020 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
2021 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
2022 self.tooltips.set_tip(self.dis_postscript_entry, "The local command to execute after disconnecting from the network.")
2023 # Add the pp table to the expander
2024 self.dis_pp_expander.add( dis_pp_table )
2025 # Add the expander to the dialog
2026 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
2028 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
2030 #Parameters:
2032 # nothing
2034 #Returns:
2036 # dictionary or None -- a profile, or None on cancel
2038 #NOTES:
2040 # Raises ValueError if an attempt is made to save an ESSID with no name.
2041 def run( self ):
2042 self.dialog.show_all()
2043 if self.dialog.run():
2044 if self.essid_entry.get_text().strip() == "":
2045 raise ValueError
2046 self.profile['known'] = True
2047 self.profile['essid'] = self.essid_entry.get_text().strip()
2048 if self.roaming_cb.get_active():
2049 self.profile['bssid'] = ''
2050 else:
2051 self.profile['bssid'] = self.bssid_entry.get_text().strip()
2052 self.profile['roaming'] = self.roaming_cb.get_active()
2053 self.profile['key'] = self.key_entry.get_text().strip()
2054 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
2055 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
2056 self.profile['encrypted'] = ( self.profile['security'] != '' )
2057 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
2058 self.profile['protocol'] = 'g'
2059 self.profile['available'] = ( self.profile['signal'] > 0 )
2060 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
2061 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
2062 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
2063 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
2064 # wpa
2065 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
2066 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
2067 # dhcp
2068 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
2069 self.profile['ip'] = self.ip_entry.get_text().strip()
2070 self.profile['netmask'] = self.netmask_entry.get_text().strip()
2071 self.profile['gateway'] = self.gw_entry.get_text().strip()
2072 self.profile['domain'] = self.domain_entry.get_text().strip()
2073 self.profile['dns1'] = self.dns1_entry.get_text().strip()
2074 self.profile['dns2'] = self.dns2_entry.get_text().strip()
2075 return self.profile
2076 return None
2078 # Remove profile dialog.
2080 #Parameters:
2082 # nothing
2084 #Returns:
2086 # nothing
2087 def destroy( self ):
2088 self.dialog.destroy()
2089 del self.dialog
2091 # Respond to roaming checkbox toggle by activating/de-activating the BSSID text entry.
2093 #Parameters:
2095 # 'roaming_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2097 # 'data' -- tuple - list of arbitrary arguments (not used)
2099 #Returns:
2101 # nothing
2102 def toggle_roaming(self, roaming_toggle, data=None):
2103 self.bssid_entry.set_sensitive(not roaming_toggle.get_active())
2105 # Respond to expanding/hiding IP segment.
2107 #Parameters:
2109 # 'widget' -- gtk.Widget - The widget sending the event.
2111 # 'data' -- tuple - List of arbitrary arguments (not used)
2113 #Returns:
2115 # nothing
2116 def toggle_use_dhcp( self, widget, data = None ):
2117 expanded = self.dhcp_expander.get_expanded()
2118 if expanded:
2119 self.dhcp_expander.set_label( USE_IP_LABEL )
2120 else:
2121 self.dhcp_expander.set_label( USE_DHCP_LABEL )
2123 # Respond to expanding/hiding WPA segment.
2125 #Parameters:
2127 # 'widget' -- gtk.Widget - The widget sending the event.
2129 # 'data' -- tuple - List of arbitrary arguments (not used)
2131 #Returns:
2133 # nothing
2134 def toggle_use_wpa( self, widget, data = None ):
2135 expanded = self.wpa_expander.get_expanded()
2136 if expanded:
2137 self.wpa_expander.set_label( USE_WPA_LABEL )
2138 else:
2139 self.wpa_expander.set_label( NO_WPA_LABEL )
2141 # Return the index where item matches a cell in array.
2143 #Parameters:
2145 # 'item' -- string - Item to find in array
2147 # 'array' -- list - List in which to find match.
2149 #Returns:
2151 # integer - 0 (no match) or higher (index of match)
2152 def get_array_index( self, item, array ):
2153 try:
2154 return array.index( item.strip() )
2155 except:
2156 pass
2157 return 0
2159 # Return the value in array[ index ]
2161 #Parameters:
2163 # 'index' -- integer - The index to look up.
2165 # 'array' -- list - List in which to look up value.
2167 #Returns:
2169 # string -- empty string (no match) or looked up value
2170 def get_array_item( self, index, array ):
2171 try:
2172 return array[ index ]
2173 except:
2174 pass
2175 return ''
2178 # A simple class for putting up a "Please wait" dialog so the user
2179 # doesn't think we've forgotten about them. Implements the status interface.
2180 class StatusWindow:
2181 # Create a new StatusWindow.
2183 #Parameters:
2185 # 'parent' -- gtk.Object - Usually, the calling window.
2187 #Returns:
2189 # StatusWindow instance
2191 #NOTE:
2193 # Sample implementation of status interface. Status interface
2194 #requires .show(), .update_message(message), and .hide() methods.
2195 def __init__( self, parent ):
2196 global wifi_radar_icon
2197 self.parent = parent
2198 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2199 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2200 self.dialog.set_icon( icon )
2201 self.lbl = gtk.Label("Please wait...")
2202 self.bar = gtk.ProgressBar()
2203 self.dialog.vbox.pack_start(self.lbl)
2204 self.dialog.vbox.pack_start(self.bar)
2205 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2206 self.timer = None
2208 # Change the message displayed to the user.
2210 #Parameters:
2212 # 'message' -- string - The message to show to the user.
2214 #Returns:
2216 # nothing
2217 def update_message( self, message ):
2218 self.lbl.set_text(message)
2220 # Update the StatusWindow progress bar.
2222 #Parameters:
2224 # nothing
2226 #Returns:
2228 # True -- always return True
2229 def update_window( self ):
2230 self.bar.pulse()
2231 return True
2233 # Display and operate the StatusWindow.
2235 #Parameters:
2237 # nothing
2239 #Returns:
2241 # nothing
2242 def run( self ):
2243 pass
2245 # Show all the widgets of the StatusWindow.
2247 #Parameters:
2249 # nothing
2251 #Returns:
2253 # nothing
2254 def show( self ):
2255 self.dialog.show_all()
2256 self.timer = gobject.timeout_add(250, self.update_window)
2257 return False
2259 # Hide all the widgets of the StatusWindow.
2261 #Parameters:
2263 # nothing
2265 #Returns:
2267 # nothing
2268 def hide( self ):
2269 if self.timer:
2270 gobject.source_remove(self.timer)
2271 self.timer = None
2272 self.dialog.hide_all()
2273 return False
2275 # Remove the StatusWindow.
2277 #Parameters:
2279 # nothing
2281 #Returns:
2283 # nothing
2284 def destroy( self ):
2285 if self.timer:
2286 gobject.source_remove(self.timer)
2287 self.dialog.destroy()
2288 del self.dialog
2291 # Manage a GTK About Dialog
2292 class about_dialog(gtk.AboutDialog):
2293 # Subclass GTK AboutDialog
2295 #Parameters:
2297 # nothing
2299 #Returns:
2301 # nothing
2302 def __init__( self ):
2303 global wifi_radar_icon
2305 gtk.AboutDialog.__init__(self)
2306 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"])
2307 self.set_comments("WiFi connection manager")
2308 self.set_copyright("Copyright 2004-2009 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2309 self.set_documenters(["Gary Case"])
2310 license = """
2311 This program is free software; you can redistribute it and/or modify
2312 it under the terms of the GNU General Public License as published by
2313 the Free Software Foundation; either version 2 of the License, or
2314 (at your option) any later version.
2316 This program is distributed in the hope that it will be useful,
2317 but WITHOUT ANY WARRANTY; without even the implied warranty of
2318 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2319 GNU General Public License for more details.
2321 You should have received a copy of the GNU General Public License
2322 along with this program; if not, write to the Free Software
2323 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2324 self.set_license(license)
2325 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2326 self.set_logo(logo)
2327 self.set_name("WiFi Radar")
2328 self.set_version(WIFI_RADAR_VERSION)
2329 self.set_website("http://wifi-radar.berlios.de")
2333 # Manage the configuration for the application, including reading and writing the config from/to a file.
2334 class ConfigFile(ConfigParser.SafeConfigParser):
2335 # Create a new ConfigFile.
2337 #Parameters:
2339 # 'filename' -- string - The configuration file's name.
2341 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2343 #Returns:
2345 # ConfigFile instance
2346 def __init__( self, filename, defaults, raw=False ):
2347 self.filename = filename
2348 self.raw = raw
2349 self.auto_profile_order = []
2350 ConfigParser.SafeConfigParser.__init__(self, defaults)
2352 # Set the contents of a section to values from a dictionary.
2354 #Parameters:
2356 # 'section_name' -- string - Configuration file section.
2358 # 'section_dict' -- dictionary - Values to add to section.
2360 #Returns:
2362 # nothing
2363 def set_section( self, section_name, section_dict ):
2364 try:
2365 self.add_section(section_name)
2366 except ConfigParser.DuplicateSectionError:
2367 pass
2368 for key in section_dict.keys():
2369 if type(section_dict[key]) == BooleanType:
2370 self.set_bool_opt(section_name + "." + key, section_dict[key])
2371 elif type(section_dict[key]) == IntType:
2372 self.set_int_opt(section_name + "." + key, section_dict[key])
2373 elif type(section_dict[key]) == FloatType:
2374 self.set_float_opt(section_name + "." + key, section_dict[key])
2375 else:
2376 self.set_opt(section_name + "." + key, section_dict[key])
2378 # Return the profile recorded in the specified section.
2380 #Parameters:
2382 # 'section_name' -- string - Configuration file section.
2384 #Returns:
2386 # dictionary or None - The specified profile or None if not found
2387 def get_profile( self, section_name ):
2388 if section_name in self.profiles():
2389 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' ]
2390 bool_types = [ 'known', 'available', 'roaming', 'encrypted', 'use_wpa', 'use_dhcp' ]
2391 int_types = [ 'signal' ]
2392 profile = get_new_profile()
2393 for option in bool_types:
2394 option_tmp = self.get_opt_as_bool(section_name + "." + option)
2395 if option_tmp:
2396 profile[option] = option_tmp
2397 for option in int_types:
2398 option_tmp = self.get_opt_as_int(section_name + "." + option)
2399 if option_tmp:
2400 profile[option] = option_tmp
2401 for option in str_types:
2402 option_tmp = self.get_opt(section_name + "." + option)
2403 if option_tmp:
2404 profile[option] = option_tmp
2405 return profile
2406 return None
2408 # Get a config option and handle exceptions.
2410 #Parameters:
2412 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2413 # period and the option key. (E.g. "DEFAULT.interface")
2415 #Returns:
2417 # string or None - option value as string or None on failure
2418 def get_opt( self, option_path ):
2419 #print "ConfigFile.get_opt: ", option_path
2420 (section, option) = option_path.split('.')
2421 try:
2422 return self.get(section, option, self.raw)
2423 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2424 return None
2426 # Get a config option and return as a boolean type.
2428 #Parameters:
2430 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2431 # period and the option key. (E.g. "DEFAULT.interface")
2433 #Returns:
2435 # boolean - option value as boolean
2436 def get_opt_as_bool( self, option_path ):
2437 option = self.get_opt(option_path)
2438 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2439 return option
2440 if option == 'True':
2441 return True
2442 if option == 'False':
2443 return False
2444 raise ValueError, 'boolean option was not True or False'
2446 # Get a config option and return as an integer type.
2448 #Parameters:
2450 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2451 # period and the option key. (E.g. "DEFAULT.interface")
2453 #Returns:
2455 # integer- option value as integer
2456 def get_opt_as_int( self, option_path ):
2457 return int(float(self.get_opt(option_path)))
2459 # Convert boolean type to string and set config option.
2461 #Parameters:
2463 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2464 # period and the option key. (E.g. "DEFAULT.interface")
2466 # 'value' -- boolean - Value to set.
2468 #Returns:
2470 # nothing
2471 def set_bool_opt( self, option_path, value ):
2472 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2473 value == 'True'
2474 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2475 value == 'False'
2476 else:
2477 raise ValueError, 'cannot convert value to string'
2478 self.set_opt(option_path, repr(value))
2480 # Convert integer type to string and set config option.
2482 #Parameters:
2484 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2485 # period and the option key. (E.g. "DEFAULT.interface")
2487 # 'value' -- integer - Value to set.
2489 #Returns:
2491 # nothing
2492 def set_int_opt( self, option_path, value ):
2493 if not isinstance(value, IntType):
2494 raise ValueError, 'value is not an integer'
2495 self.set_opt(option_path, repr(value))
2497 # Convert float type to string and set config option.
2499 #Parameters:
2501 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2502 # period and the option key. (E.g. "DEFAULT.interface")
2504 # 'value' -- float - Value to set.
2506 #Returns:
2508 # nothing
2509 def set_float_opt( self, option_path, value ):
2510 if not isinstance(value, FloatType):
2511 raise ValueError, 'value is not a float'
2512 self.set_opt(option_path, repr(int(value)))
2514 # Set a config option while handling exceptions.
2516 #Parameters:
2518 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2519 # period and the option key. (E.g. "DEFAULT.interface")
2521 # 'value' -- string - Value to set.
2523 #Returns:
2525 # nothing
2526 def set_opt( self, option_path, value ):
2527 (section, option) = option_path.split('.')
2528 try:
2529 self.set(section, option, value)
2530 except ConfigParser.NoSectionError:
2531 self.add_section(section)
2532 self.set_opt(option_path, value)
2534 # Return a list of the section names which denote AP profiles.
2536 #Parameters:
2538 # nothing
2540 #Returns:
2542 # list - profile names
2543 def profiles( self ):
2544 profile_list = []
2545 for section in self.sections():
2546 if ':' in section:
2547 profile_list.append(section)
2548 return profile_list
2550 # Read configuration file from disk into instance variables.
2552 #Parameters:
2554 # nothing
2556 #Returns:
2558 # nothing
2559 def read( self ):
2560 fp = open( self.filename, "r" )
2561 self.readfp(fp)
2562 # convert the auto_profile_order to a list for ordering
2563 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2564 for ap in self.profiles():
2565 self.set_bool_opt( ap + '.known', True)
2566 if ap in self.auto_profile_order: continue
2567 self.auto_profile_order.append( ap )
2568 fp.close()
2570 # Write configuration file to disk from instance variables. Copied from
2571 # ConfigParser and modified to write options in alphabetical order.
2573 #Parameters:
2575 # nothing
2577 #Returns:
2579 # nothing
2580 def write( self ):
2581 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2582 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2583 fp = open( self.filename, "w" )
2584 # write DEFAULT section first
2585 if self._defaults:
2586 fp.write("[DEFAULT]\n")
2587 for key in sorted(self._defaults.keys()):
2588 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2589 fp.write("\n")
2590 # write non-profile sections first
2591 for section in self._sections:
2592 if section not in self.profiles():
2593 fp.write("[%s]\n" % section)
2594 for key in sorted(self._sections[section].keys()):
2595 if key != "__name__":
2596 fp.write("%s = %s\n" %
2597 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2598 fp.write("\n")
2599 # write profile sections
2600 for section in self._sections:
2601 if section in self.profiles():
2602 fp.write("[%s]\n" % section)
2603 for key in sorted(self._sections[section].keys()):
2604 if key != "__name__":
2605 fp.write("%s = %s\n" %
2606 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2607 fp.write("\n")
2608 fp.close()
2612 # Load our conf file and known profiles
2613 # Defaults, these may get overridden by values found in the conf file.
2614 config_defaults = { # The network interface you use.
2615 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2616 'interface': "auto_detect",
2617 # How long should the scan for access points last?
2618 #'scan_timeout': '5',
2619 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2620 # Set the speak_up option to false if you do not have or want this.
2621 'speak_command': '/usr/bin/say',
2622 # Should I speak up when connecting to a network? (If you have a speech command)
2623 'speak_up': 'False',
2624 # You may set this to true for cards that require a "commit" command with iwconfig
2625 'commit_required': 'False',
2626 # You may set this to true for cards that require the interface to be brought up first
2627 'ifup_required': 'False',
2628 # set the location of the log file
2629 'logfile': './wifi-radar.log',
2630 # Set the location of several important programs
2631 'iwlist_command': '/sbin/iwlist',
2632 'iwconfig_command': '/sbin/iwconfig',
2633 'ifconfig_command': '/sbin/ifconfig',
2634 'route_command': '/sbin/route',
2635 'auto_profile_order': '[]',
2636 'version': WIFI_RADAR_VERSION }
2638 config_dhcp = { # DHCP client
2639 'command': 'dhcpcd',
2640 # How long to wait for an IP addr from DHCP server
2641 'timeout': '30',
2642 # Arguments to use with DHCP client on connect
2643 'args': '-D -o -i dhcp_client -t %(timeout)s',
2644 # Argument to use with DHCP client on disconnect
2645 'kill_args': '-k',
2646 # The file where DHCP client PID is written
2647 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2649 config_wpa = { # WPA Supplicant
2650 'command': '/usr/sbin/wpa_supplicant',
2651 # Arguments to use with WPA Supplicant on connect
2652 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2653 # Arguments to use with WPA Supplicant on disconnect
2654 'kill_command': '',
2655 # Where the WPA Supplicant config file can be found
2656 'configuration': '/etc/wpa_supplicant.conf',
2657 # Driver to use with WPA Supplicant
2658 'driver': 'wext',
2659 # The file where WPA Supplicant PID is written
2660 'pidfile': '/var/run/wpa_supplicant.pid' }
2662 # initialize config, with defaults
2663 confFile = ConfigFile(CONF_FILE, config_defaults)
2664 confFile.set_section("DHCP", config_dhcp)
2665 confFile.set_section("WPA", config_wpa)
2667 if not os.path.isfile( CONF_FILE ):
2668 confFile.set_bool_opt('DEFAULT.new_file', True)
2669 else:
2670 if not os.access(CONF_FILE, os.R_OK):
2671 print "Can't open " + CONF_FILE + "."
2672 print "Are you root?"
2673 sys.exit()
2674 confFile.read()
2677 ####################################################################################################
2678 # Embedded Images
2679 wifi_radar_icon = [ ""
2680 "GdkP"
2681 "\0\0\22""7"
2682 "\2\1\0\2"
2683 "\0\0\1\214"
2684 "\0\0\0c"
2685 "\0\0\0O"
2686 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2687 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2688 "\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"
2689 "\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"
2690 "\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"
2691 "\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"
2692 "\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"
2693 "\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"
2694 "\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"
2695 "\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"
2696 "\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"
2697 "\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"
2698 "\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"
2699 "\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"
2700 "\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"
2701 "\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"
2702 "\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"
2703 "\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"
2704 "\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"
2705 "\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"
2706 "\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"
2707 "\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"
2708 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2709 "\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"
2710 "\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"
2711 "\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"
2712 "\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"
2713 "\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"
2714 "\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"
2715 "\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"
2716 "\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"
2717 "\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"
2718 "\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"
2719 "\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"
2720 "\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"
2721 "\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"
2722 "\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"
2723 "\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"
2724 "\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"
2725 "\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"
2726 "\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"
2727 "\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"
2728 "\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"
2729 "\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"
2730 "\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"
2731 "\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"
2732 "\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"
2733 "\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"
2734 "\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"
2735 "\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"
2736 "\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"
2737 "\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"
2738 "\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"
2739 "\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"
2740 "\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"
2741 "\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"
2742 "\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"
2743 "\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"
2744 "\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"
2745 "\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"
2746 "\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"
2747 "\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"
2748 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2749 "\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"
2750 "\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"
2751 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2752 "\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"
2753 "\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"
2754 "\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"
2755 "\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"
2756 "\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"
2757 "\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"
2758 "\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"
2759 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2760 "\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"
2761 "\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"
2762 "\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"
2763 "\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"
2764 "\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"
2765 "\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"
2766 "|\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"
2767 "\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"
2768 "\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"
2769 "\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"
2770 "\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"
2771 "\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"
2772 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2773 "\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"
2774 "\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"
2775 "\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"
2776 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
2777 "\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"
2778 "\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"
2779 "\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"
2780 "\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"
2781 "\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"
2782 "\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"
2783 "\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"
2784 "\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"
2785 "\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"
2786 "\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"
2787 "\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"
2788 "\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"
2789 "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"
2790 "\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"
2791 "\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"
2792 "\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"
2793 "\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"
2794 "\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"
2795 "\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"
2796 "\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"
2797 "\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"
2798 "\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"
2799 "\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"
2800 "\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"
2801 "\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"
2802 "\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"
2803 "\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"
2804 "\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|"
2805 "\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"
2806 "\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"
2807 "\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"
2808 "\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"
2809 "\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"
2810 "\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"
2811 "\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"
2812 "\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"
2813 "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"
2814 "\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"
2815 "\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"
2816 "\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"
2817 "\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"
2818 "\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"
2819 "\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"
2820 "\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"
2821 "\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"
2822 "\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"
2823 "\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"
2824 "\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"
2825 "\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"
2826 "\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"
2827 "\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"
2828 "\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"
2829 "\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"
2830 "\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"
2831 "\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"
2832 "\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"
2833 "\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"
2834 "\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"
2835 "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"
2836 "\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"
2837 "\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"
2838 "\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"
2839 "\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"
2840 "\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"
2841 "\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"
2842 "\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"
2843 "\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"
2844 "\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"
2845 "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"
2846 "\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"
2847 "\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"
2848 "\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"
2849 "\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"
2850 "\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"
2851 "\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"
2852 "\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"
2853 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2854 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2855 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2856 "\0"]
2858 known_profile_icon = [ ""
2859 "GdkP"
2860 "\0\0\5""0"
2861 "\2\1\0\2"
2862 "\0\0\0P"
2863 "\0\0\0\24"
2864 "\0\0\0\24"
2865 "\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"
2866 "\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"
2867 "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"
2868 "\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"
2869 "\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"
2870 "\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"
2871 "\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"
2872 "\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"
2873 "\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"
2874 "\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"
2875 "\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"
2876 "\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"
2877 "\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"
2878 "\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"
2879 "\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"
2880 "\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"
2881 "\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"
2882 "\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"
2883 "\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"
2884 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2885 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2886 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2887 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2888 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2889 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2890 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2891 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2892 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2893 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2894 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2895 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2896 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2897 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2898 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2899 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2900 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2901 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2902 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2903 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2904 "\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"
2905 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2906 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2907 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2908 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2909 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2910 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2911 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2912 "\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"
2913 "\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"
2914 "\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"
2915 "\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"
2916 "\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"]
2918 unknown_profile_icon = [ ""
2919 "GdkP"
2920 "\0\0\5\22"
2921 "\2\1\0\2"
2922 "\0\0\0P"
2923 "\0\0\0\24"
2924 "\0\0\0\24"
2925 "\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"
2926 "\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"
2927 "\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"
2928 "\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"
2929 "(\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"
2930 "\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"
2931 "#\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"
2932 "\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"
2933 "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"
2934 "\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"
2935 "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"
2936 "\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"
2937 "\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"
2938 "\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"
2939 "\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"
2940 "\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"
2941 "\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"
2942 "\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"
2943 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2944 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2945 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2946 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2947 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2948 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2949 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2950 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2951 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2952 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2953 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2954 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2955 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2956 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2957 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2958 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2959 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2960 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2961 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2962 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2963 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2964 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2965 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2966 "\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"
2967 "\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"
2968 "\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"
2969 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2971 signal_xpm_barely = [
2972 "20 20 10 1",
2973 " c None",
2974 ". c #C6C6C6",
2975 "+ c #CCCCCC",
2976 "@ c #DBDBDB",
2977 "# c #D3D3D3",
2978 "$ c #A9B099",
2979 "% c #95A173",
2980 "& c #6B8428",
2981 "* c #B4B7AC",
2982 "= c #80924D",
2983 " .+++.",
2984 " +@@@+",
2985 " +@@@+",
2986 " +@@@+",
2987 " +@@@+",
2988 " .++++#@@@+",
2989 " +@@@@@@@@+",
2990 " +@@@@@@@@+",
2991 " +@@@@@@@@+",
2992 " +@@@@@@@@+",
2993 " $%%%%#@@@@@@@@+",
2994 " %&&&&@@@@@@@@@+",
2995 " %&&&&@@@@@@@@@+",
2996 " %&&&&@@@@@@@@@+",
2997 " %&&&&@@@@@@@@@+",
2998 "*%%%%=&&&&@@@@@@@@@+",
2999 "%&&&&&&&&&@@@@@@@@@+",
3000 "%&&&&&&&&&@@@@@@@@@+",
3001 "%&&&&&&&&&@@@@@@@@@+",
3002 "*%%%%%%%%%+++++++++."
3006 signal_xpm_best = [
3007 "20 20 6 1",
3008 " c None",
3009 ". c #9DAABF",
3010 "+ c #7B96BF",
3011 "@ c #386EBF",
3012 "# c #5982BF",
3013 "$ c #AEB4BF",
3014 " .+++.",
3015 " +@@@+",
3016 " +@@@+",
3017 " +@@@+",
3018 " +@@@+",
3019 " .++++#@@@+",
3020 " +@@@@@@@@+",
3021 " +@@@@@@@@+",
3022 " +@@@@@@@@+",
3023 " +@@@@@@@@+",
3024 " .++++#@@@@@@@@+",
3025 " +@@@@@@@@@@@@@+",
3026 " +@@@@@@@@@@@@@+",
3027 " +@@@@@@@@@@@@@+",
3028 " +@@@@@@@@@@@@@+",
3029 "$++++#@@@@@@@@@@@@@+",
3030 "+@@@@@@@@@@@@@@@@@@+",
3031 "+@@@@@@@@@@@@@@@@@@+",
3032 "+@@@@@@@@@@@@@@@@@@+",
3033 "$++++++++++++++++++."
3036 signal_xpm_none = [
3037 "20 20 6 1",
3038 " c None",
3039 ". c #C6C6C6",
3040 "+ c #CCCCCC",
3041 "@ c #DBDBDB",
3042 "# c #D3D3D3",
3043 "$ c #C2C2C2",
3044 " .+++.",
3045 " +@@@+",
3046 " +@@@+",
3047 " +@@@+",
3048 " +@@@+",
3049 " .++++#@@@+",
3050 " +@@@@@@@@+",
3051 " +@@@@@@@@+",
3052 " +@@@@@@@@+",
3053 " +@@@@@@@@+",
3054 " .++++#@@@@@@@@+",
3055 " +@@@@@@@@@@@@@+",
3056 " +@@@@@@@@@@@@@+",
3057 " +@@@@@@@@@@@@@+",
3058 " +@@@@@@@@@@@@@+",
3059 "$++++#@@@@@@@@@@@@@+",
3060 "+@@@@@@@@@@@@@@@@@@+",
3061 "+@@@@@@@@@@@@@@@@@@+",
3062 "+@@@@@@@@@@@@@@@@@@+",
3063 "$++++++++++++++++++."
3066 signal_xpm_ok = [
3067 "20 20 10 1",
3068 " c None",
3069 ". c #C6C6C6",
3070 "+ c #CCCCCC",
3071 "@ c #DBDBDB",
3072 "# c #A1A5B2",
3073 "$ c #848DA5",
3074 "% c #D3D3D3",
3075 "& c #4A5B8C",
3076 "* c #677498",
3077 "= c #B0B2B8",
3078 " .+++.",
3079 " +@@@+",
3080 " +@@@+",
3081 " +@@@+",
3082 " +@@@+",
3083 " #$$$$%@@@+",
3084 " $&&&&@@@@+",
3085 " $&&&&@@@@+",
3086 " $&&&&@@@@+",
3087 " $&&&&@@@@+",
3088 " #$$$$*&&&&@@@@+",
3089 " $&&&&&&&&&@@@@+",
3090 " $&&&&&&&&&@@@@+",
3091 " $&&&&&&&&&@@@@+",
3092 " $&&&&&&&&&@@@@+",
3093 "=$$$$*&&&&&&&&&@@@@+",
3094 "$&&&&&&&&&&&&&&@@@@+",
3095 "$&&&&&&&&&&&&&&@@@@+",
3096 "$&&&&&&&&&&&&&&@@@@+",
3097 "=$$$$$$$$$$$$$$++++."
3101 signal_xpm_low = [
3102 "20 20 8 1",
3103 " c None",
3104 ". c #C6C6C6",
3105 "+ c #CCCCCC",
3106 "@ c #DBDBDB",
3107 "# c #D3D3D3",
3108 "$ c #BFB0B5",
3109 "% c #C18799",
3110 "& c #C54F74",
3111 " .+++.",
3112 " +@@@+",
3113 " +@@@+",
3114 " +@@@+",
3115 " +@@@+",
3116 " .++++#@@@+",
3117 " +@@@@@@@@+",
3118 " +@@@@@@@@+",
3119 " +@@@@@@@@+",
3120 " +@@@@@@@@+",
3121 " .++++#@@@@@@@@+",
3122 " +@@@@@@@@@@@@@+",
3123 " +@@@@@@@@@@@@@+",
3124 " +@@@@@@@@@@@@@+",
3125 " +@@@@@@@@@@@@@+",
3126 "$%%%%#@@@@@@@@@@@@@+",
3127 "%&&&&@@@@@@@@@@@@@@+",
3128 "%&&&&@@@@@@@@@@@@@@+",
3129 "%&&&&@@@@@@@@@@@@@@+",
3130 "$%%%%++++++++++++++."
3134 ####################################################################################################
3135 # Make so we can be imported
3136 if __name__ == "__main__":
3137 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
3138 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
3139 else:
3140 import gtk, gobject
3141 gtk.gdk.threads_init()
3142 apQueue = Queue.Queue(100)
3143 commQueue = Queue.Queue(2)
3145 logger = logging.getLogger("wrlog")
3146 logger.setLevel(logging.WARNING)
3147 fileLogHandler = logging.handlers.RotatingFileHandler(confFile.get_opt('DEFAULT.logfile'), maxBytes=64*1024, backupCount=5)
3148 fileLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(funcName)s: %(message)s'))
3149 logger.addHandler(fileLogHandler)
3150 if __debug__:
3151 logger.setLevel(logging.DEBUG)
3152 consoleLogHandler = logging.StreamHandler()
3153 consoleLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(funcName)s: %(message)s'))
3154 logger.addHandler(consoleLogHandler)
3156 main_radar_window = radar_window(confFile, apQueue, commQueue, logger)
3157 threading.Thread(None, scanning_thread, None, (confFile, apQueue, commQueue, logger)).start()
3158 threading.Thread(None, update_profiles, None, (main_radar_window, confFile, apQueue, commQueue, logger)).start()
3159 main_radar_window.main()