Bump version number for release (v2.0.s06)
[wifi-radar.git] / wifi-radar
blob12a7803944eab95e842813a1060cade3fb85b314
1 #!/usr/bin/python -OO
3 # A utility for managing WiFi profiles on GNU/Linux.
5 # Copyright (C) 2004-2005 Ahmad Baitalmal <ahmad@baitalmal.com>
6 # Copyright (C) 2005 Nicolas Brouard <nicolas.brouard@mandrake.org>
7 # Copyright (C) 2005-2009 Brian Elliott Finley <brian@thefinleys.com>
8 # Copyright (C) 2006 David Decotigny <com.d2@free.fr>
9 # Copyright (C) 2006 Simon Gerber <gesimu@gmail.com>
10 # Copyright (C) 2006-2007 Joey Hurst <jhurst@lucubrate.org>
11 # Copyright (C) 2006, 2009 Ante Karamatic <ivoks@ubuntu.com>
12 # Copyright (C) 2009 Sean Robinson <seankrobinson@gmail.com>
14 # This program is free software; you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; version 2 of the License.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License in LICENSE.GPL for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 # http://wifi-radar.berlios.de
29 # See CREDITS file for more contributors.
30 # See HISTORY file for, well, changes.
32 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
33 # turn on console debugging.
35 import ConfigParser
36 import errno
37 import gtk
38 import logging
39 import logging.handlers
40 import os
41 import Queue
42 import re
43 import string
44 import sys
45 import tempfile
46 import threading
47 from shutil import move
48 from signal import SIGTERM
49 from subprocess import call, Popen, PIPE, STDOUT
50 from time import sleep
51 from types import *
53 WIFI_RADAR_VERSION = "0.0.0"
56 # Where the conf file should live could be different for your distro. Please change
57 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
59 CONF_FILE = "/etc/wifi-radar/wifi-radar.conf"
61 os.environ['LC_MESSAGES'] = 'C'
64 ####################################################################################################
65 ####################################################################################################
67 # Gets the network interface device
69 #Parameters:
71 # 'device' -- string - The proposed network device to use
73 #Returns:
75 # string -- The actual network device to use
76 def get_network_device(device):
77 #print "get_network_device: %s" % (device, )
78 if device != "auto_detect":
79 return confFile.get_opt('DEFAULT.interface')
80 else:
81 # auto detect network device
82 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
83 # If no devices are found, default to eth1.
84 # call iwconfig command and read output
85 iwconfig_info = Popen(confFile.get_opt('DEFAULT.iwconfig_command'), shell=True, stdout=PIPE, stderr=STDOUT).stdout
86 wireless_devices = [(x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
87 if len(wireless_devices) > 0:
88 return wireless_devices[0]
89 logger.critical("No WiFi device found, please set this in the preferences.")
90 return ""
92 # Return a blank profile
94 #Parameters:
96 # none
98 #Returns:
100 # dictionary -- An AP profile with defaults set.
101 def get_new_profile():
102 return { 'known': False,
103 'available': False,
104 'encrypted': False,
105 'essid': '',
106 'bssid': '',
107 'roaming': False,
108 'protocol': 'g',
109 'signal': -193,
110 'channel': 'auto',
111 'con_prescript': '',
112 'con_postscript': '',
113 'dis_prescript': '',
114 'dis_postscript': '',
115 'key': '',
116 'mode': 'auto',
117 'security': '',
118 'use_wpa': False,
119 'wpa_driver': '',
120 'use_dhcp': True,
121 'ip': '',
122 'netmask': '',
123 'gateway': '',
124 'domain': '',
125 'dns1': '',
126 'dns2': ''
129 # Combine essid and bssid to make a config file section name
131 #Parameters:
133 # 'essid' -- string - AP ESSID
135 # 'bssid' -- string - AP BSSID
137 #Returns:
139 # string -- the bssid concatenated to a colon, concatenated to the essid
140 def make_section_name( essid, bssid ):
141 return essid + ':' + bssid
143 # Split a config file section name into an essid and a bssid
145 #Parameters:
147 # 'section' -- string - Config file section name
149 #Returns:
151 # list -- the essid and bssid
152 def split_section_name( section ):
153 parts = re.split(':', section)
154 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
156 # Run commands through the shell
158 #Parameters:
160 # 'command' -- tuple - The command and arguments to run.
162 # 'environment' -- dictionary - Environment variables (as keys) and their values.
164 #Returns:
166 # boolean -- True on success, otherwise, False
167 def shellcmd( command, environment = None ):
168 try:
169 env_tmp = os.environ
170 env_tmp.update(environment)
171 command = ' '.join(command)
172 return_code = call(command, shell=True, env=env_tmp)
173 if return_code >= 0:
174 return True
175 else:
176 print >>sys.stderr, "Child was terminated by signal", -return_code
177 except OSError, exception:
178 print >>sys.stderr, "Execution failed:", exception
179 return False
181 # Speak feedback message to user
183 #Parameters:
185 # 'words' -- string - Message to speak to user
187 #Returns:
189 # nothing
190 def say( words ):
191 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
192 words = words.replace( "\"", "\\\"" )
193 shellcmd([confFile.get_opt('DEFAULT.speak_command'), words])
195 # Scan for a limited time and return AP names and bssid found.
196 # Access points we find will be put on the outgoing Queue, apQueue.
198 #Parameters:
200 # 'confFile' -- ConfigFile - Config file object
202 # 'apQueue' -- Queue - Queue on which to put AP profiles
204 # 'commandQueue' -- Queue - Queue from which to read commands
206 # 'logger' -- Logger - Python's logging facility
208 #Returns:
210 # nothing
211 def scanning_thread(confFile, apQueue, commandQueue, logger, exit_event):
212 logger.info("Begin thread.")
213 # Setup our essid pattern matcher
214 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
215 bssid_pattern = re.compile( "Address\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
216 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abgn]+)", re.I | re.M | re.S )
217 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
218 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
219 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
220 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
222 access_points = {}
223 command = "scan"
224 while True:
225 try:
226 command = commandQueue.get_nowait()
227 logger.info("received command: %s" % (command, ))
228 command_read = True
229 except Queue.Empty:
230 command_read = False
231 device = get_network_device(confFile.get_opt('DEFAULT.interface'))
232 if command == "scan":
233 logger.debug("Beginning scan pass")
234 # Some cards need to have the interface up to scan
235 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
236 # call ifconfig command and wait for return
237 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), device, 'up'])
238 # update the signal strengths
239 try:
240 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), device, 'scan'], stdout=PIPE).stdout.read()
241 except OSError, (errno, strerror):
242 if errno == 2:
243 logger.critical("iwlist command not found, please set this in the preferences.")
244 scandata = ""
245 # zero out the signal levels for all access points
246 for bssid in access_points:
247 access_points[bssid]['signal'] = 0
248 # split the scan data based on the address line
249 hits = scandata.split(' - ')
250 for hit in hits:
251 # set the defaults for profile template
252 profile = get_new_profile()
253 m = essid_pattern.search( hit )
254 if m:
255 # we found an essid
256 profile['essid'] = m.groups()[1]
257 m = bssid_pattern.search( hit ) # get BSSID from scan
258 if m: profile['bssid'] = m.groups()[1]
259 m = protocol_pattern.search( hit ) # get protocol from scan
260 if m: profile['protocol'] = m.groups()[1]
261 m = mode_pattern.search( hit ) # get mode from scan
262 if m: profile['mode'] = m.groups()[1]
263 m = channel_pattern.search( hit ) # get channel from scan
264 if m: profile['channel'] = m.groups()[1]
265 m = enckey_pattern.search( hit ) # get encryption key from scan
266 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
267 m = signal_pattern.search( hit ) # get signal strength from scan
268 if m: profile['signal'] = m.groups()[1]
269 access_points[ profile['bssid'] ] = profile
270 for bssid in access_points:
271 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
272 # Put all, now or previously, sensed access_points into apQueue
273 try:
274 logger.debug("Scanned profile: %s" % (access_points[ bssid ], ))
275 apQueue.put_nowait( access_points[bssid] )
276 except Queue.Full:
277 pass
278 if command_read:
279 commandQueue.task_done()
280 if exit_event.isSet():
281 logger.info("Exiting.")
282 return
283 if device.find('ath') == 0:
284 sleep( 3 )
285 else:
286 sleep( 1 )
289 # Manage a connection; including reporting connection state,
290 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
291 class ConnectionManager():
292 # Create a new connection manager which can read a config file and send to scanning thread
293 # command Queue. A new manager checks for a pre-existing connection and takes
294 # its AP profile from the ESSID and BSSID to which it is currently attached.
296 #Parameters:
298 # 'confFile' -- ConfigFile - Config file object
300 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
302 # 'logger' -- Logger - Python's logging facility
304 #Returns:
306 # ConnectionManager instance
307 def __init__( self, confFile, commandQueue, logger ):
308 self.confFile = confFile
309 self.commQueue = commandQueue
310 self.logger = logger
311 # is connection running?
312 self.state = False
313 if self.get_current_ip():
314 self.state = True
315 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
317 # Change the interface state: up or down.
319 #Parameters:
321 # 'state' -- string - The state to which to change the interface.
323 #Returns:
325 # nothing
326 def if_change( self, state ):
327 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
328 self.logger.info("changing interface state to %s" % (state, ))
329 # call ifconfig command and wait for return
330 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface')), state])
332 # Return the WiFi encryption mode from evidence in the profile.
334 #Parameters:
336 # 'use_wpa' -- boolean - The use_wpa from the profile.
338 # 'key' -- string - The WEP key or empty string.
340 #Returns:
342 # string - none, wep, or wpa; indicates WiFi encryption mode
343 def _get_enc_mode(self, use_wpa, key):
344 if use_wpa:
345 return 'wpa'
346 elif key == '':
347 return 'none'
348 else:
349 return 'wep'
351 # Connect to the specified AP.
353 #Parameters:
355 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
357 # 'status' -- status implementer - Object which implements status interface.
359 #Returns:
361 # nothing
362 def connect_to_network( self, profile, status ):
363 self.profile = profile
364 if self.profile['bssid'] == '':
365 raise TypeError("Empty AP address")
366 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
367 say( msg )
368 self.logger.info(msg)
369 # Make a temporary copy of the DEFAULT.interface option.
370 default_interface = self.confFile.get_opt('DEFAULT.interface')
371 device = get_network_device(self.confFile.get_opt('DEFAULT.interface'))
372 # Temporarily set the configured interface to a real one.
373 self.confFile.set_opt('DEFAULT.interface', device)
374 # ready to dance
375 # Let's run the connection prescript
376 if self.profile['con_prescript'].strip() != '':
377 # got something to execute
378 # run connection prescript through shell and wait for return
379 self.logger.info("executing connection prescript: %s" % (self.profile['con_prescript'], ))
380 shellcmd([self.profile['con_prescript']], environment = {
381 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
382 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
383 "WIFIRADAR_SECMODE": self.profile['security'],
384 "WIFIRADAR_IF": device or ''
387 status.show()
388 # Some cards need to have the interface up
389 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
390 self.if_change('up')
391 # Start building iwconfig command line, command
392 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
393 iwconfig_command.append(device)
394 # Setting essid
395 iwconfig_command.append( 'essid' )
396 iwconfig_command.append( "'" + self.profile['essid'] + "'" )
397 # Setting nick
398 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
399 # Setting key
400 iwconfig_command.append( 'key' )
401 if self.profile['key'] == '':
402 iwconfig_command.append( 'off' )
403 else:
404 # Setting this stops association from working, so remove it for now
405 #if self.profile['security'] != '':
406 #iwconfig_command.append(self.profile['security'])
407 iwconfig_command.append( "'" + self.profile['key'] + "'" )
408 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
409 # Setting mode
410 if self.profile['mode'].lower() == 'master' or self.profile['mode'].lower() == 'auto':
411 self.profile['mode'] = 'Managed'
412 iwconfig_command.append( 'mode' )
413 iwconfig_command.append( self.profile['mode'] )
414 # Setting channel
415 if self.profile['channel'] != '':
416 iwconfig_command.append( 'channel' )
417 iwconfig_command.append( self.profile['channel'] )
418 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
419 iwconfig_command.append( 'ap' )
420 iwconfig_command.append( self.profile['bssid'] )
421 # Some cards require a commit
422 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
423 iwconfig_command.append( 'commit' )
424 self.logger.info("iwconfig_command: %s" % (iwconfig_command, ))
425 # call iwconfig command and wait for return
426 if not shellcmd(iwconfig_command): return
427 # Now normal network stuff
428 # Kill off any existing DHCP clients running
429 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
430 self.logger.info("Killing existing DHCP...")
431 try:
432 if self.confFile.get_opt('DHCP.kill_args') != '':
433 # call DHCP client kill command and wait for return
434 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
435 else:
436 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
437 except OSError:
438 print "failed to kill DHCP client"
439 sys.exit()
440 finally:
441 print "Stale pid file. Removing..."
442 os.remove(self.confFile.get_opt('DHCP.pidfile'))
443 # Kill off any existing WPA supplicants running
444 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
445 self.logger.info("Killing existing WPA supplicant...")
446 try:
447 if not self.confFile.get_opt('WPA.kill_command') != '':
448 # call WPA supplicant kill command and wait for return
449 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
450 else:
451 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
452 except OSError:
453 print "failed to kill WPA supplicant"
454 sys.exit()
455 finally:
456 print "Stale pid file. Removing..."
457 os.remove(self.confFile.get_opt('WPA.pidfile'))
458 self.logger.debug("Disable scan while connection attempt in progress...")
459 try:
460 self.commQueue.put("pause")
461 except Queue.Full:
462 pass
463 # Begin WPA supplicant
464 if self.profile['use_wpa'] :
465 self.logger.info("WPA args: %s" % (self.confFile.get_opt('WPA.args'), ))
466 status.update_message("WPA supplicant starting")
467 if sys.modules.has_key("gtk"):
468 while gtk.events_pending():
469 gtk.main_iteration(False)
470 # call WPA supplicant command and do not wait for return
471 try:
472 wpa_proc = shellcmd([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args')])
473 if sys.modules.has_key("gtk"):
474 while gtk.events_pending():
475 gtk.main_iteration(False)
476 sleep(2)
477 except OSError, (errno, strerror):
478 if errno == 2:
479 logger.critical("WPA supplicant not found, please set this in the preferences.")
480 if self.profile['use_dhcp'] :
481 status.update_message("Acquiring IP Address (DHCP)")
482 if sys.modules.has_key("gtk"):
483 while gtk.events_pending():
484 gtk.main_iteration(False)
485 # call DHCP client command and do not wait for return
486 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
487 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
488 dhcp_command.append(device)
489 self.logger.info("dhcp_command: %s" % (dhcp_command, ))
490 try:
491 dhcp_proc = Popen(dhcp_command, stdout=None)
492 except OSError, (errno, strerror):
493 if errno == 2:
494 logger.critical("DHCP client not found, please set this in the preferences.")
495 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
496 tick = 0.25
497 waiting = dhcp_proc.poll()
498 while waiting == None:
499 waiting = dhcp_proc.poll()
500 if timer < 0:
501 os.kill(dhcp_proc.pid, SIGTERM)
502 break
503 if sys.modules.has_key("gtk"):
504 while gtk.events_pending():
505 gtk.main_iteration(False)
506 timer -= tick
507 sleep(tick)
508 if not self.get_current_ip():
509 status.update_message("Could not get IP address!")
510 if sys.modules.has_key("gtk"):
511 while gtk.events_pending():
512 gtk.main_iteration(False)
513 sleep(1)
514 if self.state:
515 self.disconnect_interface()
516 status.hide()
517 return
518 else:
519 status.update_message("Got IP address. Done.")
520 self.state = True
521 if sys.modules.has_key("gtk"):
522 while gtk.events_pending():
523 gtk.main_iteration(False)
524 sleep(2)
525 else:
526 ifconfig_command= "%s %s down; %s %s %s netmask %s" % ( self.confFile.get_opt('DEFAULT.ifconfig_command'), device, self.confFile.get_opt('DEFAULT.ifconfig_command'), device, self.profile['ip'], self.profile['netmask'] )
527 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
528 resolv_contents = ''
529 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
530 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
531 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
532 if ( resolv_contents != '' ):
533 resolv_file=open('/etc/resolv.conf', 'w')
534 resolv_file.write(s)
535 resolv_file.close
536 if not shellcmd([ifconfig_command]): return
537 if not shellcmd([route_command]): return
538 self.state = True
539 # Re-enable iwlist
540 try:
541 self.commQueue.put("scan")
542 except Queue.Full:
543 pass
544 # Let's run the connection postscript
545 con_postscript = self.profile['con_postscript']
546 if self.profile['con_postscript'].strip() != '':
547 self.logger.info("executing connection postscript: %s" % (self.profile['con_postscript'], ))
548 shellcmd([self.profile['con_postscript']], environment = {
549 "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
550 "WIFIRADAR_ESSID": self.get_current_essid() or '',
551 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
552 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
553 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
554 "WIFIRADAR_SECMODE": self.profile['security'],
555 "WIFIRADAR_IF": device or ''
558 # Set the configured interface back to original value.
559 self.confFile.set_opt('DEFAULT.interface', default_interface)
560 status.hide()
562 # Disconnect from the AP with which a connection has been established/attempted.
564 #Parameters:
566 # nothing
568 #Returns:
570 # nothing
571 def disconnect_interface( self ):
572 msg = "Disconnecting"
573 say( msg )
574 self.logger.info(msg)
575 # Make a temporary copy of the DEFAULT.interface option.
576 default_interface = self.confFile.get_opt('DEFAULT.interface')
577 device = get_network_device(self.confFile.get_opt('DEFAULT.interface'))
578 # Temporarily set the configured interface to a real one.
579 self.confFile.set_opt('DEFAULT.interface', device)
580 # Pause scanning while manipulating card
581 try:
582 self.commQueue.put("pause")
583 self.commQueue.join()
584 except Queue.Full:
585 pass
586 if self.state:
587 self.profile = self.confFile.get_profile(make_section_name(self.get_current_essid(), self.get_current_bssid()))
588 if not self.profile:
589 self.profile = self.confFile.get_profile(make_section_name(self.get_current_essid(), ''))
590 if not self.profile:
591 raise KeyError
592 # Let's run the disconnection prescript
593 if self.profile['dis_prescript'].strip() != '':
594 self.logger.info("executing disconnection prescript: %s" % (self.profile['dis_prescript'], ))
595 shellcmd([self.profile['dis_prescript']], environment = {
596 "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
597 "WIFIRADAR_ESSID": self.get_current_essid() or '',
598 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
599 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
600 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
601 "WIFIRADAR_SECMODE": self.profile['security'],
602 "WIFIRADAR_IF": device or ''
605 self.logger.info("Kill off any existing DHCP clients running...")
606 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
607 self.logger.info("Killing existing DHCP...")
608 try:
609 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
610 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
611 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
612 dhcp_command.append(device)
613 self.logger.info("DHCP command: %s" % (dhcp_command, ))
614 # call DHCP client command and wait for return
615 if not shellcmd(dhcp_command): return
616 else:
617 self.logger.info("Killing DHCP manually...")
618 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
619 except OSError:
620 print "failed to kill DHCP client"
621 self.logger.info("Kill off any existing WPA supplicants running...")
622 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
623 self.logger.info("Killing existing WPA supplicant...")
624 try:
625 if self.confFile.get_opt('WPA.kill_command') != '':
626 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
627 if not shellcmd(wpa_command): return
628 else:
629 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
630 except OSError:
631 print "failed to kill WPA supplicant"
632 self.logger.info("Let's clear out the wireless stuff")
633 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), device, 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
634 self.logger.info("Now take the interface down")
635 self.logger.info("Since it may be brought back up by the next scan, lets unset its IP")
636 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), device, '0.0.0.0'])
637 # taking down the interface too quickly can crash my system, so pause a moment
638 sleep(1)
639 self.if_change('down')
640 # Let's run the disconnection postscript
641 if self.profile['dis_postscript'].strip() != '':
642 self.logger.info("executing disconnection postscript: %s" % (self.profile['dis_postscript'], ))
643 shellcmd([self.profile['dis_postscript']], environment = {
644 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
645 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
646 "WIFIRADAR_SECMODE": self.profile['security'],
647 "WIFIRADAR_IF": device or ''
650 self.state = False
651 self.logger.info("Disconnect complete.")
652 # Begin scanning again
653 try:
654 self.commQueue.put("scan")
655 except Queue.Full:
656 pass
657 # Set the configured interface back to original value.
658 self.confFile.set_opt('DEFAULT.interface', default_interface)
660 # Returns the current IP, if any, by calling ifconfig.
662 #Parameters:
664 # nothing
666 #Returns:
668 # string or None -- the IP address or None (if no there is no current connection)
669 def get_current_ip( self ):
670 ifconfig_command = [confFile.get_opt('DEFAULT.ifconfig_command'), get_network_device(confFile.get_opt('DEFAULT.interface'))]
671 try:
672 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
673 except OSError, (errno, strerror):
674 if errno == 2:
675 logger.critical("ifconfig command not found, please set this in the preferences.")
676 ifconfig_info = open("/dev/null", "r")
677 # Be careful to the language (inet adr: in French for example)
679 # Hi Brian
681 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
682 # There the string in ifconfig is inet Adresse for the IP which isn't
683 # found by the current get_current_ip function in wifi-radar. I changed
684 # the according line (#289; gentoo, v1.9.6-r1) to
685 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
686 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
688 # I'd be happy if you could incorporate this small change because as now
689 # I've got to change the file every time it is updated.
691 # Best wishes
693 # Simon
694 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
695 line = ifconfig_info.read()
696 if ip_re.search( line ):
697 return ip_re.search( line ).group(1)
698 return None
700 # Returns the current ESSID, if any, by calling iwconfig.
702 #Parameters:
704 # nothing
706 #Returns:
708 # string or None -- the ESSID or None (if no there is no current association)
709 def get_current_essid( self ):
710 """Returns the current ESSID if any by calling iwconfig"""
711 try:
712 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface'))], stdout=PIPE, stderr=STDOUT).stdout
713 except OSError, (errno, strerror):
714 if errno == 2:
715 logger.critical("iwconfig command not found, please set this in the preferences.")
716 iwconfig_info = open("/dev/null", "r")
717 # Be careful to the language (inet adr: in French for example)
718 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
719 line = iwconfig_info.read()
720 if essid_re.search( line ):
721 return essid_re.search( line ).group(2)
722 return None
724 # Returns the current BSSID, if any, by calling iwconfig.
726 #Parameters:
728 # nothing
730 #Returns:
732 # string or None -- the BSSID or None (if no there is no current association)
733 def get_current_bssid( self ):
734 """Returns the current BSSID if any by calling iwconfig"""
735 try:
736 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface'))], stdout=PIPE, stderr=STDOUT).stdout
737 except OSError, (errno, strerror):
738 if errno == 2:
739 logger.critical("iwconfig command not found, please set this in the preferences.")
740 iwconfig_info = open("/dev/null", "r")
741 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
742 line = iwconfig_info.read()
743 if bssid_re.search( line ):
744 return bssid_re.search( line ).group(2)
745 return None
749 # The main user interface window for WiFi Radar. This class also is the control
750 # center for most of the rest of the operations.
751 class radar_window:
752 # Create a new radar_window.
754 #Parameters:
756 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
758 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
760 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
762 # 'logger' -- Logger - Python's logging facility
764 #Returns:
766 # radar_window instance
767 def __init__(self, confFile, apQueue, commQueue, logger, exit_event):
768 global signal_xpm_none
769 global signal_xpm_low
770 global signal_xpm_barely
771 global signal_xpm_ok
772 global signal_xpm_best
773 global known_profile_icon
774 global unknown_profile_icon
775 global wifi_radar_icon
777 self.confFile = confFile
778 self.apQueue = apQueue
779 self.commandQueue = commQueue
780 self.logger = logger
781 self.access_points = {}
782 self.exit_event = exit_event
783 self.connection = None
785 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
786 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
787 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
788 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
789 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
790 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
791 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
792 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
793 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
794 self.window.set_icon( icon )
795 self.window.set_border_width( 10 )
796 self.window.set_size_request( 550, 300 )
797 self.window.set_title( "WiFi Radar" )
798 self.window.connect( 'delete_event', self.delete_event )
799 self.window.connect( 'destroy', self.destroy )
800 # let's create all our widgets
801 self.current_network = gtk.Label()
802 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
803 self.current_network.show()
804 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
805 self.close_button.show()
806 self.close_button.connect( 'clicked', self.delete_event, None )
807 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
808 self.about_button.show()
809 self.about_button.connect( 'clicked', self.show_about_info, None )
810 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
811 self.preferences_button.show()
812 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
813 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
814 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
815 self.plist = gtk.TreeView( self.pstore )
816 # The icons column, known and encryption
817 self.pix_cell = gtk.CellRendererPixbuf()
818 self.wep_cell = gtk.CellRendererPixbuf()
819 self.icons_cell = gtk.CellRendererText()
820 self.icons_col = gtk.TreeViewColumn()
821 self.icons_col.pack_start( self.pix_cell, False )
822 self.icons_col.pack_start( self.wep_cell, False )
823 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
824 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
825 self.plist.append_column( self.icons_col )
826 # The AP column
827 self.ap_cell = gtk.CellRendererText()
828 self.ap_col = gtk.TreeViewColumn( "Access Point" )
829 self.ap_col.pack_start( self.ap_cell, True )
830 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
831 self.plist.append_column( self.ap_col )
832 # The signal column
833 self.sig_cell = gtk.CellRendererPixbuf()
834 self.signal_col = gtk.TreeViewColumn( "Signal" )
835 self.signal_col.pack_start( self.sig_cell, True )
836 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
837 self.plist.append_column( self.signal_col )
838 # The mode column
839 self.mode_cell = gtk.CellRendererText()
840 self.mode_col = gtk.TreeViewColumn( "Mode" )
841 self.mode_col.pack_start( self.mode_cell, True )
842 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
843 self.plist.append_column( self.mode_col )
844 # The protocol column
845 self.prot_cell = gtk.CellRendererText()
846 self.protocol_col = gtk.TreeViewColumn( "802.11" )
847 self.protocol_col.pack_start( self.prot_cell, True )
848 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
849 self.plist.append_column( self.protocol_col )
850 # The channel column
851 self.channel_cell = gtk.CellRendererText()
852 self.channel_col = gtk.TreeViewColumn( "Channel" )
853 self.channel_col.pack_start( self.channel_cell, True )
854 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
855 self.plist.append_column( self.channel_col )
856 # DnD Ordering
857 self.plist.set_reorderable( True )
858 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
859 self.pstore.connect( 'row-deleted', self.update_auto_profile_order )
860 # enable/disable buttons based on the selected network
861 self.selected_network = self.plist.get_selection()
862 self.selected_network.connect( 'changed', self.on_network_selection, None )
863 # the list scroll bar
864 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
865 sb.show()
866 self.plist.show()
867 # Add New button
868 self.new_button = gtk.Button( "_New" )
869 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
870 self.new_button.show()
871 # Add Configure button
872 self.edit_button = gtk.Button( "C_onfigure" )
873 self.edit_button.connect( 'clicked', self.edit_profile, None )
874 self.edit_button.show()
875 self.edit_button.set_sensitive(False)
876 # Add Delete button
877 self.delete_button = gtk.Button( "_Delete" )
878 self.delete_button.connect('clicked', self.delete_profile_with_check, None)
879 self.delete_button.show()
880 self.delete_button.set_sensitive(False)
881 # Add Connect button
882 self.connect_button = gtk.Button( "Co_nnect" )
883 self.connect_button.connect( 'clicked', self.connect_profile, None )
884 # Add Disconnect button
885 self.disconnect_button = gtk.Button( "D_isconnect" )
886 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
887 # lets add our widgets
888 rows = gtk.VBox( False, 3 )
889 net_list = gtk.HBox( False, 0 )
890 listcols = gtk.HBox( False, 0 )
891 prows = gtk.VBox( False, 0 )
892 # lets start packing
893 # the network list
894 net_list.pack_start( self.plist, True, True, 0 )
895 net_list.pack_start( sb, False, False, 0 )
896 # the rows level
897 rows.pack_start( net_list , True, True, 0 )
898 rows.pack_start( self.current_network, False, True, 0 )
899 # the list columns
900 listcols.pack_start( rows, True, True, 0 )
901 listcols.pack_start( prows, False, False, 5 )
902 # the list buttons
903 prows.pack_start( self.new_button, False, False, 2 )
904 prows.pack_start( self.edit_button, False, False, 2 )
905 prows.pack_start( self.delete_button, False, False, 2 )
906 prows.pack_end( self.connect_button, False, False, 2 )
907 prows.pack_end( self.disconnect_button, False, False, 2 )
909 self.window.action_area.pack_start( self.about_button )
910 self.window.action_area.pack_start( self.preferences_button )
911 self.window.action_area.pack_start( self.close_button )
913 rows.show()
914 prows.show()
915 listcols.show()
916 self.window.vbox.add( listcols )
917 self.window.vbox.set_spacing( 3 )
918 self.window.show_all()
920 # Now, immediately hide these two. The proper one will be
921 # displayed later, based on interface state. -BEF-
922 self.disconnect_button.hide()
923 self.connect_button.hide()
924 self.connect_button.set_sensitive(False)
926 # set up connection manager for later use
927 self.connection = ConnectionManager( self.confFile, self.commandQueue, self.logger )
928 # set up status window for later use
929 self.status_window = StatusWindow( self )
930 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
932 # Add our known profiles in order
933 for profile_name in self.confFile.auto_profile_order:
934 profile_name = profile_name.strip()
935 self.access_points[profile_name] = self.confFile.get_profile(profile_name)
936 wep = None
937 if self.access_points[profile_name]['encrypted']:
938 wep = gtk.STOCK_DIALOG_AUTHENTICATION
939 if self.access_points[profile_name]['roaming']:
940 ap_name = self.access_points[profile_name]['essid'] + "\n" + ' Multiple APs'
941 else:
942 ap_name = self.access_points[profile_name]['essid'] + "\n" + self.access_points[profile_name]['bssid']
943 self.pstore.append([ap_name, self.known_profile_icon, self.access_points[profile_name]['known'], self.access_points[profile_name]['available'], wep, self.signal_none_pb, self.access_points[profile_name]['mode'], self.access_points[profile_name]['protocol'], self.access_points[profile_name]['channel'] ] )
944 # This is the first run (or, at least, no config file was present), so pop up the preferences window
945 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
946 self.confFile.remove_option('DEFAULT', 'new_file')
947 self.edit_preferences(self.preferences_button)
949 # Begin running radar_window in Gtk event loop.
951 #Parameters:
953 # nothing
955 #Returns:
957 # nothing
958 def main( self ):
959 gtk.main()
961 # Quit application.
963 #Parameters:
965 # 'widget' -- gtk.Widget - The widget sending the event.
967 #Returns:
969 # nothing
970 def destroy( self, widget = None):
971 if self.status_window:
972 self.status_window.destroy()
973 gtk.main_quit()
975 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
977 #Parameters:
979 # 'widget' -- gtk.Widget - The widget sending the event.
981 # 'data' -- tuple - list of arbitrary arguments (not used)
983 #Returns:
985 # boolean -- always return False (i.e. do not propigate the signal which called)
986 def delete_event( self, widget, data = None ):
987 # Let other threads know it is time to exit
988 self.exit_event.set()
989 # Wait for all other threads to exit before continuing
990 while threading.activeCount() > 1:
991 sleep(0.25)
992 self.destroy()
993 return False
995 # Update the current ip and essid shown to user.
997 #Parameters:
999 # nothing
1001 #Returns:
1003 # nothing
1004 def update_network_info(self):
1005 if self.connection and self.connection.state:
1006 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()))
1007 else:
1008 self.current_network.set_text("Not Connected.")
1010 # Set the state of connect/disconnect buttons based on whether we have a connection.
1012 #Parameters:
1014 # nothing
1016 #Returns:
1018 # nothing
1019 def update_connect_buttons(self):
1020 if self.connection and self.connection.state:
1021 self.connect_button.hide()
1022 self.disconnect_button.show()
1023 else:
1024 self.disconnect_button.hide()
1025 self.connect_button.show()
1027 # Updates the on-screen profiles list.
1029 #Parameters:
1031 # 'ap' -- dictionary -- The AP found by scanning_thread.
1033 #Returns:
1035 # nothing
1036 def update_plist_items(self, ap):
1037 # Check for roaming profile.
1038 ap_name = make_section_name(ap['essid'], '')
1039 profile = self.confFile.get_profile(ap_name)
1040 prow_iter = self.get_row_by_ap(ap['essid'], ' Multiple APs')
1041 ap_display = ap['essid'] + "\n" + ' Multiple APs'
1042 if not profile:
1043 # Check for normal profile.
1044 ap_name = make_section_name(ap['essid'], ap['bssid'])
1045 profile = self.confFile.get_profile(ap_name)
1046 prow_iter = self.get_row_by_ap(ap['essid'], ap['bssid'])
1047 ap_display = ap['essid'] + "\n" + ap['bssid']
1048 if not profile:
1049 # No profile, so make a new one.
1050 profile = get_new_profile()
1051 wep = None
1052 if prow_iter != None:
1053 # the AP is in the list of APs on the screen
1054 # Set the 'known' values; False is default, overridden to True by self.access_points
1055 ap['known'] = self.access_points[ap_name]['known']
1056 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known(ap['known']))
1057 self.pstore.set_value(prow_iter, 2, ap['known'])
1058 self.pstore.set_value(prow_iter, 3, ap['available'])
1059 if ap['encrypted']:
1060 wep = gtk.STOCK_DIALOG_AUTHENTICATION
1061 self.pstore.set_value(prow_iter, 4, wep)
1062 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal(self.access_points[ap_name]['signal']))
1063 self.pstore.set_value(prow_iter, 6, ap['mode'])
1064 self.pstore.set_value(prow_iter, 7, ap['protocol'])
1065 self.pstore.set_value(prow_iter, 8, self.access_points[ap_name]['channel'])
1066 else:
1067 # the AP is not in the list of APs on the screen
1068 self.pstore.append([ap_display, self.pixbuf_from_known(ap['known']), ap['known'], ap['available'], wep, self.pixbuf_from_signal(ap['signal']), ap['mode'], ap['protocol'], ap['channel']])
1069 #print "update_plist_items: new ap", ap[ 'essid' ], ap['bssid']
1071 # Updates the record-keeping profiles list.
1073 #Parameters:
1075 # 'ap' -- dictionary -- The AP found by scanning_thread.
1077 #Returns:
1079 # nothing
1080 def update_ap_list(self, ap):
1081 # Check for roaming profile.
1082 ap_name = make_section_name(ap['essid'], '')
1083 profile = self.confFile.get_profile(ap_name)
1084 if not profile:
1085 # Check for normal profile.
1086 ap_name = make_section_name(ap['essid'], ap['bssid'])
1087 profile = self.confFile.get_profile(ap_name)
1088 if not profile:
1089 # No profile, so make a new one.
1090 profile = get_new_profile()
1091 if self.access_points.has_key(ap_name):
1092 # This AP has been configured and should be updated
1093 self.access_points[ap_name]['known'] = profile['known']
1094 self.access_points[ap_name]['available'] = ap['available']
1095 self.access_points[ap_name]['encrypted'] = ap['encrypted']
1096 self.access_points[ap_name]['mode'] = ap['mode']
1097 self.access_points[ap_name]['protocol'] = ap['protocol']
1098 if self.access_points[ap_name]['roaming']:
1099 # Roaming
1100 if self.access_points[ap_name]['bssid'] == ap['bssid']:
1101 # Same AP for this roaming profile
1102 self.access_points[ap_name]['signal'] = ap['signal']
1103 else:
1104 # Different AP
1105 if int(self.access_points[ap_name]['signal']) < int(ap['signal']):
1106 # Stronger signal with this AP, so promote it to preferred
1107 self.access_points[ap_name]['signal'] = ap['signal']
1108 self.access_points[ap_name]['channel'] = ap['channel']
1109 self.access_points[ap_name]['bssid'] = ap['bssid']
1110 else:
1111 # Not roaming
1112 self.access_points[ap_name]['signal'] = ap['signal']
1113 self.access_points[ap_name]['channel'] = ap['channel']
1114 else:
1115 # Not seen before, begin tracking it.
1116 self.access_points[ap_name] = profile
1118 # Updates the main user interface.
1120 #Parameters:
1122 # nothing
1124 #Returns:
1126 # boolean -- always return True
1127 def update_window(self):
1128 # Indicate to PyGtk that only one Gtk thread should run here
1129 gtk.gdk.threads_enter()
1130 self.update_network_info()
1131 self.update_connect_buttons()
1132 while True:
1133 # Get APs scanned by iwlist
1134 try:
1135 ap = self.apQueue.get_nowait()
1136 except Queue.Empty:
1137 break
1138 else:
1139 self.update_ap_list(ap)
1140 self.update_plist_items(ap)
1141 # Allow other Gtk threads to run
1142 gtk.gdk.threads_leave()
1143 return True
1145 # Return the proper icon for a value of known.
1147 #Parameters:
1149 # 'known' -- boolean - Whether the AP is known (i.e. configured)
1151 #Returns:
1153 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
1154 def pixbuf_from_known( self, known ):
1155 """ return the proper icon for value of known """
1156 if known:
1157 return self.known_profile_icon
1158 else:
1159 return self.unknown_profile_icon
1161 # Return an icon indicating the signal level.
1163 #Parameters:
1165 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
1167 #Returns:
1169 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
1170 def pixbuf_from_signal( self, signal ):
1171 signal = int( signal )
1172 # shift signal up by 80 to convert dBm scale to arbitrary scale
1173 if signal < 0: signal = signal + 80
1174 #print "signal level:", signal
1175 if signal < 3:
1176 return self.signal_none_pb
1177 elif signal < 12:
1178 return self.signal_low_pb
1179 elif signal < 20:
1180 return self.signal_barely_pb
1181 elif signal < 35:
1182 return self.signal_ok_pb
1183 elif signal >= 35:
1184 return self.signal_best_pb
1185 else:
1186 return None
1188 # Return row which holds specified ESSID and BSSID.
1190 #Parameters:
1192 # 'essid' -- string - ESSID to match
1194 # 'bssid' -- string - BSSID to match
1196 #Returns:
1198 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
1199 def get_row_by_ap( self, essid, bssid ):
1200 if bssid == "roaming":
1201 for row in self.pstore:
1202 if (row[0][:row[0].find("\n")] == essid):
1203 #print "roaming match:", row.iter, essid, bssid
1204 return row.iter
1205 else:
1206 for row in self.pstore:
1207 if (row[0] == essid + "\n" + bssid):
1208 #print "normal match:", row.iter, essid, bssid
1209 return row.iter
1210 return None
1212 # Enable/disable buttons based on the selected network.
1214 #Parameters:
1216 # 'widget' -- gtk.Widget - The widget sending the event.
1218 # 'data' -- tuple - list of arbitrary arguments (not used)
1220 #Returns:
1222 # nothing
1223 def on_network_selection( self, widget, data = None ):
1224 ( store, selected_iter ) = self.selected_network.get_selected()
1225 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
1226 # if no networks are selected, disable all buttons except New
1227 # (this occurs after a drag-and-drop)
1228 if selected_iter == None:
1229 self.edit_button.set_sensitive(False)
1230 self.delete_button.set_sensitive(False)
1231 self.connect_button.set_sensitive(False)
1232 return
1233 # enable/disable buttons
1234 self.connect_button.set_sensitive(True)
1235 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1236 self.edit_button.set_sensitive(True)
1237 self.delete_button.set_sensitive(True)
1238 else:
1239 self.edit_button.set_sensitive(True)
1240 self.delete_button.set_sensitive(False)
1242 # Init and run the about dialog
1244 #Parameters:
1246 # 'widget' -- gtk.Widget - The widget sending the event.
1248 # 'data' -- tuple - list of arbitrary arguments (not used)
1250 #Returns:
1252 # nothing
1253 def show_about_info( self, widget, data=None ):
1254 about = about_dialog()
1255 about.run()
1256 about.destroy()
1258 # Init and run the preferences dialog
1260 #Parameters:
1262 # 'widget' -- gtk.Widget - The widget sending the event.
1264 # 'data' -- tuple - list of arbitrary arguments (not used)
1266 #Returns:
1268 # nothing
1269 def edit_preferences( self, widget, data=None ):
1270 # get raw strings from config file
1271 self.confFile.raw = True
1272 prefs = preferences_dialog( self, self.confFile )
1273 response = prefs.run()
1274 if response == int(gtk.RESPONSE_ACCEPT):
1275 prefs.save()
1276 prefs.destroy()
1277 # get cooked strings from config file
1278 self.confFile.raw = False
1280 # Respond to a request to create a new AP profile
1282 #Parameters:
1284 # 'widget' -- gtk.Widget - The widget sending the event.
1286 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1288 # 'data' -- tuple - list of arbitrary arguments (not used)
1290 #Returns:
1292 # boolean -- True if a profile was created and False if profile creation was canceled.
1293 def create_new_profile( self, widget, profile, data=None ):
1294 profile_editor = profile_dialog( self, profile )
1295 try:
1296 profile = profile_editor.run()
1297 except ValueError:
1298 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1299 del error_dlg
1300 return False
1301 finally:
1302 profile_editor.destroy()
1303 if profile:
1304 (store, selected_iter) = self.plist.get_selection().get_selected()
1305 if selected_iter is not None:
1306 store.remove(selected_iter)
1307 if profile['roaming']:
1308 apname = make_section_name(profile['essid'], '')
1309 else:
1310 apname = make_section_name(profile['essid'], profile['bssid'])
1311 # Check that the ap does not exist already
1312 if apname in self.confFile.profiles():
1313 error_dlg = ErrorDialog(None, "A profile for %s already exists" % (apname))
1314 del error_dlg
1315 # try again
1316 self.access_points[ apname ] = profile
1317 self.confFile.set_section( apname, profile )
1318 # if it is not in the auto_profile_order add it
1319 if apname not in self.confFile.auto_profile_order:
1320 self.confFile.auto_profile_order.insert(0, apname)
1321 # add to the store
1322 wep = None
1323 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1324 try:
1325 self.confFile.write()
1326 except IOError, (error_number, error_str):
1327 if error_number == errno.ENOENT:
1328 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1329 del error_dlg
1330 else:
1331 raise IOError(error_number, error_str)
1332 # Add AP to the list displayed to user
1333 if profile['roaming']:
1334 ap_name = profile['essid'] + "\n" + ' Multiple APs'
1335 while True:
1336 prow_iter = self.get_row_by_ap(profile['essid'], 'roaming')
1337 if prow_iter:
1338 self.pstore.remove(prow_iter)
1339 else:
1340 break
1341 else:
1342 ap_name = profile['essid'] + "\n" + profile['bssid']
1343 self.pstore.prepend([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']])
1344 return True
1345 else:
1346 # Did not create new profile
1347 return False
1349 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1350 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1352 #Parameters:
1354 # 'widget' -- gtk.Widget - The widget sending the event.
1356 # 'data' -- tuple - list of arbitrary arguments (not used)
1358 #Returns:
1360 # nothing
1361 def edit_profile(self, widget, data=None):
1362 (store, selected_iter) = self.plist.get_selection().get_selected()
1363 if not selected_iter:
1364 # No AP is selected
1365 return
1366 (essid, bssid) = str(self.pstore.get_value(selected_iter, 0)).split("\n")
1367 if bssid == ' Multiple APs':
1368 # AP list says this is a roaming profile
1369 apname = make_section_name(essid, '')
1370 else:
1371 # AP list says this is NOT a roaming profile
1372 apname = make_section_name(essid, bssid)
1373 profile = self.confFile.get_profile(apname)
1374 if profile:
1375 # A profile was found in the config file
1376 profile['bssid'] = self.access_points[apname]['bssid']
1377 profile_editor = profile_dialog(self, profile)
1378 try:
1379 # try editing the profile
1380 edited_profile = profile_editor.run()
1381 except ValueError:
1382 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1383 del error_dlg
1384 return False
1385 finally:
1386 # Always remove profile editor window from screen
1387 profile_editor.destroy()
1388 if edited_profile:
1389 # A profile was returned by the editor
1390 old_index = None
1391 row = None
1392 if edited_profile['essid'] != profile['essid'] or edited_profile['bssid'] != profile['bssid'] or edited_profile['roaming'] != profile['roaming']:
1393 # ESSID, BSSID, or roaming was changed in profile editor
1394 try:
1395 self.commandQueue.put('pause')
1396 self.commandQueue.join()
1397 except Queue.Full:
1398 pass
1399 if profile['roaming']:
1400 # The old profile was a roaming profile
1401 old_ap = make_section_name(profile['essid'], '')
1402 else:
1403 # The old profile was NOT a roaming profile
1404 old_ap = make_section_name(profile['essid'], profile['bssid'])
1405 # Find where old profile was in auto order
1406 old_index = self.confFile.auto_profile_order.index(old_ap)
1407 # Remove old profile and get its place in AP list
1408 row = self.delete_profile(selected_iter, old_ap)
1409 self.confFile.remove_section(old_ap)
1410 try:
1411 # Add AP to the list displayed to user
1412 self.apQueue.put_nowait(edited_profile)
1413 self.commandQueue.put('scan')
1414 except Queue.Full:
1415 pass
1416 if edited_profile['roaming']:
1417 # New profile is a roaming profile
1418 apname = make_section_name(edited_profile['essid'], '')
1419 ap_display = edited_profile['essid'] + "\n" + ' Multiple APs'
1420 # Remove all other profiles that match the new profile ESSID
1421 while True:
1422 prow_iter = self.get_row_by_ap(edited_profile['essid'], 'roaming')
1423 if prow_iter:
1424 self.pstore.remove(prow_iter)
1425 else:
1426 break
1427 else:
1428 # New profile is NOT a roaming profile
1429 apname = make_section_name(edited_profile['essid'], edited_profile['bssid'])
1430 ap_display = edited_profile['essid'] + "\n" + edited_profile['bssid']
1431 # Insert the new profile in the same position as the one being replaced
1432 if old_index != None:
1433 # Old profile was in auto order list
1434 self.confFile.auto_profile_order.insert(old_index, apname)
1435 if ((row is not None) and (self.pstore.iter_is_valid(row))):
1436 self.pstore.insert_before(row, [ap_display, None, None, None, None, None, None, None, None])
1437 self.access_points[apname] = edited_profile
1438 self.confFile.set_section(apname, edited_profile)
1439 try:
1440 # Save updated profile to config file
1441 self.confFile.write()
1442 except IOError, (error_number, error_str):
1443 if error_number == errno.ENOENT:
1444 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1445 del error_dlg
1446 else:
1447 raise IOError(error_number, error_str)
1448 else:
1449 # The AP does not already have a profile
1450 profile = get_new_profile()
1451 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1452 self.create_new_profile( widget, profile, data )
1454 # Delete an AP profile (i.e. make profile unknown)
1456 #Parameters:
1458 # 'selected_iter' -- gtk.TreeIter - The selected row.
1460 # 'apname' -- string - The configuration file section to remove
1462 #Returns:
1464 # gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
1465 def delete_profile(self, selected_iter, apname):
1466 # Remove it
1467 del self.access_points[apname]
1468 self.confFile.remove_section(apname)
1469 self.logger.info(apname)
1470 if apname in self.confFile.auto_profile_order:
1471 self.confFile.auto_profile_order.remove(apname)
1472 self.pstore.remove(selected_iter)
1473 # Let's save our current state
1474 self.update_auto_profile_order()
1475 try:
1476 self.confFile.write()
1477 except IOError, (error_number, error_str):
1478 if error_number == errno.ENOENT:
1479 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1480 del error_dlg
1481 else:
1482 raise IOError(error_number, error_str)
1483 return selected_iter
1485 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1486 # Check with user first.
1488 #Parameters:
1490 # 'widget' -- gtk.Widget - The widget sending the event.
1492 # 'data' -- tuple - list of arbitrary arguments (not used)
1494 #Returns:
1496 # nothing
1497 def delete_profile_with_check(self, widget, data=None):
1498 (store, selected_iter) = self.plist.get_selection().get_selected()
1499 if not selected_iter: return
1500 (essid, bssid) = store.get_value(selected_iter, 0).split("\n")
1501 if bssid == ' Multiple APs':
1502 apname = make_section_name(essid, '')
1503 else:
1504 apname = make_section_name(essid, bssid)
1505 profile = self.confFile.get_profile(apname)
1506 if profile['roaming']:
1507 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, ))
1508 else:
1509 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))
1510 known = store.get_value( selected_iter, 1 )
1511 if not known: return
1512 res = dlg.run()
1513 dlg.destroy()
1514 del dlg
1515 if res == gtk.RESPONSE_NO:
1516 return
1517 self.delete_profile(selected_iter, apname)
1519 # Respond to a request to connect to an AP.
1521 #Parameters:
1523 # 'widget' -- gtk.Widget - The widget sending the event.
1525 # 'profile' -- dictionary - The AP profile to which to connect.
1527 # 'data' -- tuple - list of arbitrary arguments (not used)
1529 #Returns:
1531 # nothing
1532 def connect_profile( self, widget, profile, data=None ):
1533 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1534 if not selected_iter: return
1535 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1536 known = store.get_value( selected_iter, 2 )
1537 if not known:
1538 dlg = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "This network does not have a profile configured.\n\nWould you like to create one now?")
1539 res = dlg.run()
1540 dlg.destroy()
1541 del dlg
1542 if res == gtk.RESPONSE_NO:
1543 return
1544 profile = get_new_profile()
1545 profile['essid'] = essid
1546 profile['bssid'] = bssid
1547 if not self.create_new_profile( widget, profile, data ):
1548 return
1549 else:
1550 # Check for roaming profile.
1551 ap_name = make_section_name(essid, '')
1552 profile = self.confFile.get_profile(ap_name)
1553 if not profile:
1554 # Check for normal profile.
1555 ap_name = make_section_name(essid, bssid)
1556 profile = self.confFile.get_profile(ap_name)
1557 if not profile:
1558 # No configured profile
1559 return
1560 profile['bssid'] = self.access_points[ap_name]['bssid']
1561 profile['channel'] = self.access_points[ap_name]['channel']
1562 self.connection.connect_to_network(profile, self.status_window)
1564 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1566 #Parameters:
1568 # 'widget' -- gtk.Widget - The widget sending the event.
1570 # 'data' -- tuple - list of arbitrary arguments (not used)
1572 #Returns:
1574 # nothing
1575 def disconnect_profile( self, widget, data=None ):
1576 if data == "cancel":
1577 self.status_window.update_message("Canceling connection...")
1578 if sys.modules.has_key("gtk"):
1579 while gtk.events_pending():
1580 gtk.main_iteration(False)
1581 sleep(1)
1582 self.connection.disconnect_interface()
1584 # Update the config file auto profile order from the on-screen order
1586 #Parameters:
1588 # 'widget' -- gtk.Widget - The widget sending the event.
1590 # 'data' -- tuple - list of arbitrary arguments (not used)
1592 # 'data2' -- tuple - list of arbitrary arguments (not used)
1594 #Returns:
1596 # nothing
1597 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1598 # recreate the auto_profile_order
1599 auto_profile_order = []
1600 piter = self.pstore.get_iter_first()
1601 while piter:
1602 # only if it's known
1603 if self.pstore.get_value(piter, 2) == True:
1604 (essid, bssid) = self.pstore.get_value(piter, 0).split("\n")
1605 if bssid == ' Multiple APs':
1606 apname = make_section_name(essid, '')
1607 else:
1608 apname = make_section_name(essid, bssid)
1609 auto_profile_order.append(apname)
1610 piter = self.pstore.iter_next(piter)
1611 self.confFile.auto_profile_order = auto_profile_order
1612 try:
1613 self.confFile.write()
1614 except IOError, (error_number, error_str):
1615 if error_number == errno.ENOENT:
1616 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1617 del error_dlg
1618 else:
1619 raise IOError(error_number, error_str)
1622 # Button to allow user to choose a file and put value into specified gtk.Entry
1623 class file_browse_button(gtk.Button):
1624 # Create a button to simulate a File/Open
1626 #Parameters:
1628 # 'parent' -- gtk.Object -- Usually, the calling window.
1630 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1632 #Returns:
1634 # file_browse_button instance
1635 def __init__( self, parent, entry ):
1636 self.parent_window = parent
1637 self.entry = entry
1638 gtk.Button.__init__(self, "Browse", None)
1639 #self.
1640 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)
1641 self.connect("clicked", self.browse_files)
1643 # Show filechooser dialog and get user selection
1645 #Parameters:
1647 # 'widget' -- gtk.Widget -- The widget sending the event.
1649 #Returns:
1651 # nothing
1653 #NOTES:
1655 # updates entry value
1657 def browse_files( self, widget ):
1658 self.browser_dialog.set_filename(self.entry.get_text())
1659 self.browser_dialog.run()
1660 filename = self.browser_dialog.get_filename()
1661 if filename:
1662 self.entry.set_text(filename)
1663 self.browser_dialog.destroy()
1666 # Simple dialog to report an error to the user.
1667 class ErrorDialog:
1668 # Create a new ErrorDialog.
1670 #Parameters:
1672 # 'parent' -- gtk.Object - Usually, the calling window.
1674 # 'message' -- string - The message to display to the user.
1676 #Returns:
1678 # ErrorDialog instance
1679 def __init__( self, parent, message ):
1680 dialog = gtk.MessageDialog( parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message )
1681 dialog.run()
1682 dialog.destroy()
1683 del dialog
1686 # The preferences dialog. Edits non-profile sections of the config file.
1687 class preferences_dialog:
1688 # Create a new preferences_dialog.
1690 #Parameters:
1692 # 'parent' -- gtk.Object - Usually, the calling window.
1694 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1696 #Returns:
1698 # preferences_dialog instance
1699 def __init__( self, parent, confFile ):
1700 global wifi_radar_icon
1701 self.parent = parent
1702 self.confFile = confFile
1703 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1704 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1705 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1706 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1707 self.dialog.set_icon( icon )
1708 self.dialog.set_resizable( True )
1709 self.dialog.set_transient_for( self.parent.window )
1710 self.tooltips = gtk.Tooltips()
1712 # set up preferences widgets
1714 # build everything in a tabbed notebook
1715 self.prefs_notebook = gtk.Notebook()
1717 ### General tab
1718 self.general_page = gtk.VBox()
1719 # auto detect wireless device
1720 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1722 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1723 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1724 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1725 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1727 # network interface selecter
1728 self.w_interface = gtk.combo_box_entry_new_text()
1729 try:
1730 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE, stderr=STDOUT).stdout
1731 except OSError, (errno, strerror):
1732 if errno == 2:
1733 logger.critical("iwconfig command not found, please set this in the preferences.")
1734 iwconfig_info = ""
1735 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1736 for device in wireless_devices:
1737 if device != self.confFile.get_opt('DEFAULT.interface'):
1738 self.w_interface.append_text(device)
1739 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1740 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1741 self.w_interface.set_active(0)
1742 self.w_interface_label = gtk.Label("Wireless device")
1743 self.w_hbox1 = gtk.HBox(False, 0)
1744 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1745 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1746 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1747 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1749 # scan timeout (spin button of integers from 1 to 100)
1750 #self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,0 )
1751 #self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1752 #self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1753 #self.w_scan_timeout.set_numeric(True)
1754 #self.w_scan_timeout.set_snap_to_ticks(True)
1755 #self.w_scan_timeout.set_wrap(False)
1756 #self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1757 #self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1758 #self.w_hbox2 = gtk.HBox(False, 0)
1759 #self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1760 #self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1761 #self.general_page.pack_start(self.w_hbox2, False, False, 5)
1763 # speak up
1764 self.w_speak_up = gtk.CheckButton("Use speak-up")
1765 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1766 self.w_speak_up.connect("toggled", self.toggle_speak)
1767 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1768 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1770 # speak up command
1771 self.w_speak_cmd = gtk.Entry()
1772 self.w_speak_cmd.set_width_chars(16)
1773 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1774 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1775 self.w_speak_cmd_label = gtk.Label("Speak Command")
1776 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1777 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1778 self.w_hbox3 = gtk.HBox(False, 0)
1779 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1780 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1781 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1782 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1783 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1785 # commit required
1786 self.w_commit_required = gtk.CheckButton("Commit required")
1787 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1788 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1789 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1791 # ifup required
1792 self.w_ifup_required = gtk.CheckButton("Ifup required")
1793 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1794 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1795 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1797 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1798 ### End of General tab
1800 ### Advanced tab
1801 # table to use for layout of following command configurations
1802 self.cmds_table = gtk.Table()
1804 # ifconfig command
1805 self.ifconfig_cmd = gtk.Entry()
1806 self.ifconfig_cmd.set_width_chars(32)
1807 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1808 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1809 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1810 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1811 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1812 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1813 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1814 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1816 # iwconfig command
1817 self.iwconfig_cmd = gtk.Entry()
1818 self.iwconfig_cmd.set_width_chars(32)
1819 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1820 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1821 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1822 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1823 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1824 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1825 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1826 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1828 # iwlist command
1829 self.iwlist_cmd = gtk.Entry()
1830 self.iwlist_cmd.set_width_chars(32)
1831 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1832 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1833 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1834 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1835 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1836 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1837 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1838 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1840 # route command
1841 self.route_cmd = gtk.Entry()
1842 self.route_cmd.set_width_chars(32)
1843 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1844 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1845 self.route_cmd_label = gtk.Label("Network route configure command")
1846 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1847 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1848 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1849 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1850 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1852 # log file
1853 self.logfile_entry = gtk.Entry()
1854 self.logfile_entry.set_width_chars(32)
1855 self.tooltips.set_tip(self.logfile_entry, "The file in which to save logging info")
1856 self.logfile_entry.set_text(self.confFile.get_opt('DEFAULT.logfile'))
1857 self.logfile_label = gtk.Label("Log file")
1858 self.logfile_button = file_browse_button(self.dialog, self.logfile_entry)
1859 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1860 self.cmds_table.attach(self.logfile_label, 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1861 self.cmds_table.attach(self.logfile_entry, 2, 3, 5, 6, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1862 self.cmds_table.attach(self.logfile_button, 3, 4, 5, 6, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1864 # log level (spin button of integers from 0 to 50 by 5's)
1865 self.loglevel = gtk.SpinButton(gtk.Adjustment(self.confFile.get_opt_as_int('DEFAULT.loglevel'), 1, 50, 5, 5, 0), 1, 0)
1866 self.loglevel.set_update_policy(gtk.UPDATE_IF_VALID)
1867 self.loglevel.set_numeric(True)
1868 self.loglevel.set_snap_to_ticks(True)
1869 self.loglevel.set_wrap(False)
1870 self.tooltips.set_tip(self.loglevel, "How much detail to save in log file. Larger numbers provide less detail and smaller numbers, more detail.")
1871 self.loglevel.set_text(self.confFile.get_opt('DEFAULT.loglevel'))
1872 self.loglevel_label = gtk.Label("Log level")
1873 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1874 self.cmds_table.attach(self.loglevel_label, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1875 self.cmds_table.attach(self.loglevel, 2, 3, 6, 7, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1877 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1878 ### End of Advanced tab
1880 ### DHCP tab
1881 # table to use for layout of DHCP prefs
1882 self.dhcp_table = gtk.Table()
1884 self.dhcp_cmd = gtk.Entry()
1885 self.dhcp_cmd.set_width_chars(32)
1886 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1887 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1888 self.dhcp_cmd_label = gtk.Label("Command")
1889 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1890 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1891 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1892 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1894 self.dhcp_args = gtk.Entry()
1895 self.dhcp_args.set_width_chars(32)
1896 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1897 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1898 self.dhcp_args_label = gtk.Label("Arguments")
1899 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1900 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1902 self.dhcp_kill_args = gtk.Entry()
1903 self.dhcp_kill_args.set_width_chars(32)
1904 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1905 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1906 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1907 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1908 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1910 self.dhcp_timeout = gtk.Entry()
1911 self.dhcp_timeout.set_width_chars(32)
1912 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1913 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1914 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1915 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1916 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1918 self.dhcp_pidfile = gtk.Entry()
1919 self.dhcp_pidfile.set_width_chars(32)
1920 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1921 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1922 self.dhcp_pidfile_label = gtk.Label("PID file")
1923 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1924 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1926 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1927 ### End of DHCP tab
1929 ### WPA tab
1930 # table to use for layout of DHCP prefs
1931 self.wpa_table = gtk.Table()
1933 self.wpa_cmd = gtk.Entry()
1934 self.wpa_cmd.set_width_chars(32)
1935 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1936 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1937 self.wpa_cmd_label = gtk.Label("Command")
1938 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1939 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1940 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1941 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1943 self.wpa_args = gtk.Entry()
1944 self.wpa_args.set_width_chars(32)
1945 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1946 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1947 self.wpa_args_label = gtk.Label("Arguments")
1948 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1949 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1951 self.wpa_kill_args = gtk.Entry()
1952 self.wpa_kill_args.set_width_chars(32)
1953 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1954 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1955 self.wpa_kill_args_label = gtk.Label("Kill command")
1956 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1957 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1959 self.wpa_config = gtk.Entry()
1960 self.wpa_config.set_width_chars(32)
1961 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1962 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1963 self.wpa_config_label = gtk.Label("Configuration file")
1964 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1965 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1967 self.wpa_driver = gtk.Entry()
1968 self.wpa_driver.set_width_chars(32)
1969 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1970 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1971 self.wpa_driver_label = gtk.Label("Driver")
1972 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1973 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1975 self.wpa_pidfile = gtk.Entry()
1976 self.wpa_pidfile.set_width_chars(32)
1977 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1978 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1979 self.wpa_pidfile_label = gtk.Label("PID file")
1980 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1981 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1983 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1984 ### End of WPA tab
1986 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1988 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1990 #Parameters:
1992 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1994 # 'data' -- tuple - list of arbitrary arguments (not used)
1996 #Returns:
1998 # nothing
1999 def toggle_auto_detect(self, auto_detect_toggle, data=None):
2000 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
2002 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
2004 #Parameters:
2006 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2008 # 'data' -- tuple - list of arbitrary arguments (not used)
2010 #Returns:
2012 # nothing
2013 def toggle_speak(self, speak_toggle, data=None):
2014 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
2015 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
2017 # Display preferences dialog and operate until canceled or okayed.
2019 #Parameters:
2021 # nothing
2023 #Returns:
2025 # integer -- gtk response ID
2026 def run(self):
2027 self.dialog.show_all()
2028 return self.dialog.run()
2030 # Write updated values to config file.
2032 #Parameters:
2034 # nothing
2036 #Returns:
2038 # nothing
2039 def save(self):
2040 if self.w_auto_detect.get_active():
2041 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
2042 else:
2043 interface = "auto_detect"
2044 if self.w_interface.get_active_text() != "":
2045 interface = self.w_interface.get_active_text()
2046 self.confFile.set_opt('DEFAULT.interface', interface)
2047 #self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
2048 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
2049 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
2050 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
2051 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
2052 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
2053 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
2054 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
2055 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
2056 self.confFile.set_opt('DEFAULT.logfile', self.logfile_entry.get_text())
2057 self.confFile.set_int_opt('DEFAULT.loglevel', int(self.loglevel.get_value()))
2058 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
2059 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
2060 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
2061 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
2062 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
2063 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
2064 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
2065 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
2066 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
2067 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
2068 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
2069 try:
2070 self.confFile.write()
2071 except IOError, (error_number, error_str):
2072 if error_number == errno.ENOENT:
2073 error_dlg = ErrorDialog( self.dialog, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
2074 del error_dlg
2075 else:
2076 raise IOError(error_number, error_str)
2078 # Remove preferences window.
2080 #Parameters:
2082 # nothing
2084 #Returns:
2086 # nothing
2087 def destroy(self):
2088 self.dialog.destroy()
2089 del self.dialog
2092 # Edit and return an AP profile.
2093 class profile_dialog:
2094 # Create a new profile_dialog.
2096 #Parameters:
2098 # 'parent' -- gtk.Object - Usually, the calling window.
2100 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
2102 #Returns:
2104 # profile_dialog instance
2105 def __init__( self, parent, profile ):
2106 global wifi_radar_icon
2108 # Labels
2109 self.WIFI_SET_LABEL = "WiFi Options"
2110 self.USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
2111 self.USE_IP_LABEL = "Manual network configuration"
2112 self.USE_WPA_LABEL = "Use WPA"
2113 self.NO_WPA_LABEL = "No WPA"
2114 self.CON_PP_LABEL = "Connection Commands"
2115 self.DIS_PP_LABEL = "Disconnection Commands"
2117 self.parent = parent
2118 self.profile = profile.copy()
2119 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
2120 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
2121 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
2122 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
2123 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
2124 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
2125 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2126 self.dialog.set_icon( icon )
2127 self.dialog.set_resizable( False )
2128 self.dialog.set_transient_for( self.parent.window )
2129 #self.dialog.set_size_request( 400, 400 )
2130 #################
2131 self.tooltips = gtk.Tooltips()
2133 general_table = gtk.Table()
2134 general_table.set_row_spacings(3)
2135 general_table.set_col_spacings(3)
2136 # The essid labels
2137 general_table.attach(gtk.Label('Network Name'), 0, 1, 0, 1)
2138 # The essid textboxes
2139 self.essid_entry = gtk.Entry(32)
2140 self.essid_entry.set_text(self.profile['essid'])
2141 general_table.attach(self.essid_entry, 1, 2, 0, 1)
2142 # Add the essid table to the dialog
2143 self.dialog.vbox.pack_start(general_table, True, True, 5)
2144 self.tooltips.set_tip(self.essid_entry, "The name of the network with which to connect.")
2146 # The bssid labels
2147 general_table.attach(gtk.Label('Network Address'), 0, 1, 1, 2)
2148 # The bssid textboxes
2149 self.bssid_entry = gtk.Entry(32)
2150 self.bssid_entry.set_text(self.profile['bssid'])
2151 self.bssid_entry.set_sensitive(not self.profile['roaming'])
2152 # Add the bssid table to the dialog
2153 general_table.attach(self.bssid_entry, 1, 2, 1, 2)
2154 self.tooltips.set_tip(self.bssid_entry, "The address of the network with which to connect.")
2155 # Add the roaming checkbox
2156 self.roaming_cb = gtk.CheckButton('Roaming')
2157 self.roaming_cb.set_active(self.profile['roaming'])
2158 self.roaming_cb.connect("toggled", self.toggle_roaming)
2159 general_table.attach(self.roaming_cb, 1, 2, 2, 3)
2160 self.tooltips.set_tip(self.roaming_cb, "Use the AP in this network which provides strongest signal?")
2161 # create the WiFi expander
2162 self.wifi_expander = gtk.Expander( self.WIFI_SET_LABEL )
2163 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2164 wifi_table = gtk.Table( 4, 2, False )
2165 wifi_table.set_row_spacings( 3 )
2166 wifi_table.set_col_spacings( 3 )
2167 # The WiFi labels
2168 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
2169 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
2170 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
2171 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
2172 # The WiFi text boxes
2173 self.mode_combo = gtk.combo_box_new_text()
2174 for mode in self.WIFI_MODES:
2175 self.mode_combo.append_text( mode )
2176 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
2177 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
2178 self.tooltips.set_tip(self.mode_combo, "Method to use for connection. You probably want auto mode.")
2179 self.channel_combo = gtk.combo_box_new_text()
2180 for channel in self.WIFI_CHANNELS:
2181 self.channel_combo.append_text( channel )
2182 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
2183 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
2184 self.tooltips.set_tip(self.channel_combo, "Channel the network uses. You probably want auto mode.")
2186 self.key_entry = gtk.Entry( 64 )
2187 self.key_entry.set_text( self.profile['key'] )
2188 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
2189 self.tooltips.set_tip(self.key_entry, "WEP key: Plain language or hex string to use for encrypted communication with the network.")
2191 self.security_combo = gtk.combo_box_new_text()
2192 for security in self.WIFI_SECURITY:
2193 self.security_combo.append_text( security )
2194 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
2195 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
2196 self.tooltips.set_tip(self.security_combo, "Use Open to allow unencrypted communication and Restricted to force encrypted-only communication.")
2197 # Add the wifi table to the expander
2198 self.wifi_expander.add( wifi_table )
2199 # Add the expander to the dialog
2200 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
2202 # create the wpa expander
2203 self.wpa_expander = gtk.Expander( self.NO_WPA_LABEL )
2204 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
2205 wpa_table = gtk.Table( 1, 2, False )
2206 wpa_table.set_row_spacings( 3 )
2207 wpa_table.set_col_spacings( 3 )
2208 # The labels
2209 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
2210 # The text boxes
2211 self.wpa_driver_entry = gtk.Entry()
2212 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
2213 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
2214 # Add the wpa table to the expander
2215 self.wpa_expander.add( wpa_table )
2216 # Add the expander to the dialog
2217 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
2219 # create the dhcp expander
2220 self.dhcp_expander = gtk.Expander( self.USE_DHCP_LABEL )
2221 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2222 ip_table = gtk.Table( 6, 2, False )
2223 ip_table.set_row_spacings( 3 )
2224 ip_table.set_col_spacings( 3 )
2225 # The IP labels
2226 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
2227 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
2228 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
2229 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
2230 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
2231 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
2232 # The IP text boxes
2233 self.ip_entry = gtk.Entry( 15 )
2234 self.ip_entry.set_text( self.profile['ip'] )
2235 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
2236 self.netmask_entry = gtk.Entry( 15 )
2237 self.netmask_entry.set_text( self.profile['netmask'] )
2238 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
2239 self.gw_entry = gtk.Entry( 15 )
2240 self.gw_entry.set_text( self.profile['gateway'] )
2241 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
2242 self.domain_entry = gtk.Entry( 32 )
2243 self.domain_entry.set_text( self.profile['domain'] )
2244 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
2245 self.dns1_entry = gtk.Entry( 15 )
2246 self.dns1_entry.set_text( self.profile['dns1'] )
2247 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
2248 self.dns2_entry = gtk.Entry( 15 )
2249 self.dns2_entry.set_text( self.profile['dns2'] )
2250 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
2251 # Add the ip table to the expander
2252 self.dhcp_expander.add( ip_table )
2253 # Add the expander to the dialog
2254 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
2256 # create the connection-building postpre expander
2257 self.con_pp_expander = gtk.Expander( self.CON_PP_LABEL )
2258 con_pp_table = gtk.Table( 2, 2, False )
2259 con_pp_table.set_row_spacings( 3 )
2260 con_pp_table.set_col_spacings( 3 )
2261 # The labels
2262 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2263 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2264 # The text boxes
2265 self.con_prescript_entry = gtk.Entry()
2266 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
2267 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
2268 self.tooltips.set_tip(self.con_prescript_entry, "The local command to execute before trying to connect to the network.")
2269 self.con_postscript_entry = gtk.Entry()
2270 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
2271 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
2272 self.tooltips.set_tip(self.con_postscript_entry, "The local command to execute after connecting to the network.")
2273 # Add the pp table to the expander
2274 self.con_pp_expander.add( con_pp_table )
2275 # Add the expander to the dialog
2276 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
2278 # create the disconnection postpre expander
2279 self.dis_pp_expander = gtk.Expander( self.DIS_PP_LABEL )
2280 dis_pp_table = gtk.Table( 2, 2, False )
2281 dis_pp_table.set_row_spacings( 3 )
2282 dis_pp_table.set_col_spacings( 3 )
2283 # The labels
2284 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2285 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2286 # The text boxes
2287 self.dis_prescript_entry = gtk.Entry()
2288 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
2289 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
2290 self.tooltips.set_tip(self.dis_prescript_entry, "The local command to execute before disconnecting from the network.")
2291 self.dis_postscript_entry = gtk.Entry()
2292 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
2293 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
2294 self.tooltips.set_tip(self.dis_postscript_entry, "The local command to execute after disconnecting from the network.")
2295 # Add the pp table to the expander
2296 self.dis_pp_expander.add( dis_pp_table )
2297 # Add the expander to the dialog
2298 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
2300 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
2302 #Parameters:
2304 # nothing
2306 #Returns:
2308 # dictionary or None -- a profile, or None on cancel
2310 #NOTES:
2312 # Raises ValueError if an attempt is made to save an ESSID with no name.
2313 def run( self ):
2314 self.dialog.show_all()
2315 if self.dialog.run():
2316 if self.essid_entry.get_text().strip() == "":
2317 raise ValueError
2318 self.profile['known'] = True
2319 self.profile['essid'] = self.essid_entry.get_text().strip()
2320 if self.roaming_cb.get_active():
2321 self.profile['bssid'] = ''
2322 else:
2323 self.profile['bssid'] = self.bssid_entry.get_text().strip()
2324 self.profile['roaming'] = self.roaming_cb.get_active()
2325 self.profile['key'] = self.key_entry.get_text().strip()
2326 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
2327 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
2328 self.profile['encrypted'] = ( self.profile['security'] != '' )
2329 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
2330 self.profile['protocol'] = 'g'
2331 self.profile['available'] = ( self.profile['signal'] > 0 )
2332 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
2333 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
2334 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
2335 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
2336 # wpa
2337 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
2338 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
2339 # dhcp
2340 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
2341 self.profile['ip'] = self.ip_entry.get_text().strip()
2342 self.profile['netmask'] = self.netmask_entry.get_text().strip()
2343 self.profile['gateway'] = self.gw_entry.get_text().strip()
2344 self.profile['domain'] = self.domain_entry.get_text().strip()
2345 self.profile['dns1'] = self.dns1_entry.get_text().strip()
2346 self.profile['dns2'] = self.dns2_entry.get_text().strip()
2347 return self.profile
2348 return None
2350 # Remove profile dialog.
2352 #Parameters:
2354 # nothing
2356 #Returns:
2358 # nothing
2359 def destroy( self ):
2360 self.dialog.destroy()
2361 del self.dialog
2363 # Respond to roaming checkbox toggle by activating/de-activating the BSSID text entry.
2365 #Parameters:
2367 # 'roaming_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2369 # 'data' -- tuple - list of arbitrary arguments (not used)
2371 #Returns:
2373 # nothing
2374 def toggle_roaming(self, roaming_toggle, data=None):
2375 self.bssid_entry.set_sensitive(not roaming_toggle.get_active())
2377 # Respond to expanding/hiding IP segment.
2379 #Parameters:
2381 # 'widget' -- gtk.Widget - The widget sending the event.
2383 # 'data' -- tuple - List of arbitrary arguments (not used)
2385 #Returns:
2387 # nothing
2388 def toggle_use_dhcp( self, widget, data = None ):
2389 expanded = self.dhcp_expander.get_expanded()
2390 if expanded:
2391 self.dhcp_expander.set_label( self.USE_IP_LABEL )
2392 else:
2393 self.dhcp_expander.set_label( self.USE_DHCP_LABEL )
2395 # Respond to expanding/hiding WPA segment.
2397 #Parameters:
2399 # 'widget' -- gtk.Widget - The widget sending the event.
2401 # 'data' -- tuple - List of arbitrary arguments (not used)
2403 #Returns:
2405 # nothing
2406 def toggle_use_wpa( self, widget, data = None ):
2407 expanded = self.wpa_expander.get_expanded()
2408 if expanded:
2409 self.wpa_expander.set_label( self.USE_WPA_LABEL )
2410 else:
2411 self.wpa_expander.set_label( self.NO_WPA_LABEL )
2413 # Return the index where item matches a cell in array.
2415 #Parameters:
2417 # 'item' -- string - Item to find in array
2419 # 'array' -- list - List in which to find match.
2421 #Returns:
2423 # integer - 0 (no match) or higher (index of match)
2424 def get_array_index( self, item, array ):
2425 try:
2426 return array.index( item.strip() )
2427 except:
2428 pass
2429 return 0
2431 # Return the value in array[ index ]
2433 #Parameters:
2435 # 'index' -- integer - The index to look up.
2437 # 'array' -- list - List in which to look up value.
2439 #Returns:
2441 # string -- empty string (no match) or looked up value
2442 def get_array_item( self, index, array ):
2443 try:
2444 return array[ index ]
2445 except:
2446 pass
2447 return ''
2450 # A simple class for putting up a "Please wait" dialog so the user
2451 # doesn't think we've forgotten about them. Implements the status interface.
2452 class StatusWindow:
2453 # Create a new StatusWindow.
2455 #Parameters:
2457 # 'parent' -- gtk.Object - Usually, the calling window.
2459 #Returns:
2461 # StatusWindow instance
2463 #NOTE:
2465 # Sample implementation of status interface. Status interface
2466 #requires .show(), .update_message(message), and .hide() methods.
2467 def __init__( self, parent ):
2468 global wifi_radar_icon
2469 self.parent = parent
2470 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2471 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2472 self.dialog.set_icon( icon )
2473 self.lbl = gtk.Label("Please wait...")
2474 self.bar = gtk.ProgressBar()
2475 self.dialog.vbox.pack_start(self.lbl)
2476 self.dialog.vbox.pack_start(self.bar)
2477 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2478 self.timer = None
2480 # Change the message displayed to the user.
2482 #Parameters:
2484 # 'message' -- string - The message to show to the user.
2486 #Returns:
2488 # nothing
2489 def update_message( self, message ):
2490 self.lbl.set_text(message)
2492 # Update the StatusWindow progress bar.
2494 #Parameters:
2496 # nothing
2498 #Returns:
2500 # True -- always return True
2501 def update_window( self ):
2502 self.bar.pulse()
2503 return True
2505 # Display and operate the StatusWindow.
2507 #Parameters:
2509 # nothing
2511 #Returns:
2513 # nothing
2514 def run( self ):
2515 pass
2517 # Show all the widgets of the StatusWindow.
2519 #Parameters:
2521 # nothing
2523 #Returns:
2525 # nothing
2526 def show( self ):
2527 self.dialog.show_all()
2528 self.timer = gobject.timeout_add(250, self.update_window)
2529 return False
2531 # Hide all the widgets of the StatusWindow.
2533 #Parameters:
2535 # nothing
2537 #Returns:
2539 # nothing
2540 def hide( self ):
2541 if self.timer:
2542 gobject.source_remove(self.timer)
2543 self.timer = None
2544 self.dialog.hide_all()
2545 return False
2547 # Remove the StatusWindow.
2549 #Parameters:
2551 # nothing
2553 #Returns:
2555 # nothing
2556 def destroy( self ):
2557 if self.timer:
2558 gobject.source_remove(self.timer)
2559 self.dialog.destroy()
2560 del self.dialog
2563 # Manage a GTK About Dialog
2564 class about_dialog(gtk.AboutDialog):
2565 # Subclass GTK AboutDialog
2567 #Parameters:
2569 # nothing
2571 #Returns:
2573 # nothing
2574 def __init__( self ):
2575 global wifi_radar_icon
2577 gtk.AboutDialog.__init__(self)
2578 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", "Ante Karamatic", "Richard Monk", "Nicolas Brouard", "Kevin Otte", "Nathanael Rebsch", "Andrea Scarpino", "Patrick Winnertz"])
2579 self.set_comments("WiFi connection manager")
2580 self.set_copyright("Copyright 2004-2009 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2581 self.set_documenters(["Gary Case"])
2582 license = """
2583 This program is free software; you can redistribute it and/or modify
2584 it under the terms of the GNU General Public License as published by
2585 the Free Software Foundation; either version 2 of the License, or
2586 (at your option) any later version.
2588 This program is distributed in the hope that it will be useful,
2589 but WITHOUT ANY WARRANTY; without even the implied warranty of
2590 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2591 GNU General Public License for more details.
2593 You should have received a copy of the GNU General Public License
2594 along with this program; if not, write to the Free Software
2595 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2596 self.set_license(license)
2597 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2598 self.set_logo(logo)
2599 self.set_name("WiFi Radar")
2600 self.set_version(WIFI_RADAR_VERSION)
2601 self.set_website("http://wifi-radar.berlios.de")
2605 # Manage the configuration for the application, including reading and writing the config from/to a file.
2606 class ConfigFile(ConfigParser.SafeConfigParser):
2607 # Create a new ConfigFile.
2609 #Parameters:
2611 # 'filename' -- string - The configuration file's name.
2613 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2615 #Returns:
2617 # ConfigFile instance
2618 def __init__( self, filename, defaults, raw=False ):
2619 self.filename = filename
2620 self.raw = raw
2621 self.auto_profile_order = []
2622 ConfigParser.SafeConfigParser.__init__(self, defaults)
2624 # Set the contents of a section to values from a dictionary.
2626 #Parameters:
2628 # 'section_name' -- string - Configuration file section.
2630 # 'section_dict' -- dictionary - Values to add to section.
2632 #Returns:
2634 # nothing
2635 def set_section( self, section_name, section_dict ):
2636 try:
2637 self.add_section(section_name)
2638 except ConfigParser.DuplicateSectionError:
2639 pass
2640 for key in section_dict.keys():
2641 if type(section_dict[key]) == BooleanType:
2642 self.set_bool_opt(section_name + "." + key, section_dict[key])
2643 elif type(section_dict[key]) == IntType:
2644 self.set_int_opt(section_name + "." + key, section_dict[key])
2645 elif type(section_dict[key]) == FloatType:
2646 self.set_float_opt(section_name + "." + key, section_dict[key])
2647 else:
2648 self.set_opt(section_name + "." + key, section_dict[key])
2650 # Return the profile recorded in the specified section.
2652 #Parameters:
2654 # 'section_name' -- string - Configuration file section.
2656 #Returns:
2658 # dictionary or None - The specified profile or None if not found
2659 def get_profile( self, section_name ):
2660 if section_name in self.profiles():
2661 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' ]
2662 bool_types = [ 'known', 'available', 'roaming', 'encrypted', 'use_wpa', 'use_dhcp' ]
2663 int_types = [ 'signal' ]
2664 profile = get_new_profile()
2665 for option in bool_types:
2666 option_tmp = self.get_opt_as_bool(section_name + "." + option)
2667 if option_tmp:
2668 profile[option] = option_tmp
2669 for option in int_types:
2670 option_tmp = self.get_opt_as_int(section_name + "." + option)
2671 if option_tmp:
2672 profile[option] = option_tmp
2673 for option in str_types:
2674 option_tmp = self.get_opt(section_name + "." + option)
2675 if option_tmp:
2676 profile[option] = option_tmp
2677 return profile
2678 return None
2680 # Get a config option and handle exceptions.
2682 #Parameters:
2684 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2685 # period and the option key. (E.g. "DEFAULT.interface")
2687 #Returns:
2689 # string or None - option value as string or None on failure
2690 def get_opt( self, option_path ):
2691 #print "ConfigFile.get_opt: ", option_path
2692 (section, option) = option_path.split('.')
2693 try:
2694 return self.get(section, option, self.raw)
2695 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2696 return None
2698 # Get a config option and return as a boolean type.
2700 #Parameters:
2702 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2703 # period and the option key. (E.g. "DEFAULT.interface")
2705 #Returns:
2707 # boolean - option value as boolean
2708 def get_opt_as_bool( self, option_path ):
2709 option = self.get_opt(option_path)
2710 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2711 return option
2712 if option == 'True':
2713 return True
2714 if option == 'False':
2715 return False
2716 raise ValueError, 'boolean option was not True or False'
2718 # Get a config option and return as an integer type.
2720 #Parameters:
2722 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2723 # period and the option key. (E.g. "DEFAULT.interface")
2725 #Returns:
2727 # integer- option value as integer
2728 def get_opt_as_int( self, option_path ):
2729 return int(float(self.get_opt(option_path)))
2731 # Convert boolean type to string and set config option.
2733 #Parameters:
2735 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2736 # period and the option key. (E.g. "DEFAULT.interface")
2738 # 'value' -- boolean - Value to set.
2740 #Returns:
2742 # nothing
2743 def set_bool_opt( self, option_path, value ):
2744 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2745 value == 'True'
2746 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2747 value == 'False'
2748 else:
2749 raise ValueError, 'cannot convert value to string'
2750 self.set_opt(option_path, repr(value))
2752 # Convert integer type to string and set config option.
2754 #Parameters:
2756 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2757 # period and the option key. (E.g. "DEFAULT.interface")
2759 # 'value' -- integer - Value to set.
2761 #Returns:
2763 # nothing
2764 def set_int_opt( self, option_path, value ):
2765 if not isinstance(value, IntType):
2766 raise ValueError, 'value is not an integer'
2767 self.set_opt(option_path, repr(value))
2769 # Convert float type to string and set config option.
2771 #Parameters:
2773 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2774 # period and the option key. (E.g. "DEFAULT.interface")
2776 # 'value' -- float - Value to set.
2778 #Returns:
2780 # nothing
2781 def set_float_opt( self, option_path, value ):
2782 if not isinstance(value, FloatType):
2783 raise ValueError, 'value is not a float'
2784 self.set_opt(option_path, repr(int(value)))
2786 # Set a config option while handling exceptions.
2788 #Parameters:
2790 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2791 # period and the option key. (E.g. "DEFAULT.interface")
2793 # 'value' -- string - Value to set.
2795 #Returns:
2797 # nothing
2798 def set_opt( self, option_path, value ):
2799 (section, option) = option_path.split('.')
2800 try:
2801 self.set(section, option, value)
2802 except ConfigParser.NoSectionError:
2803 self.add_section(section)
2804 self.set_opt(option_path, value)
2806 # Return a list of the section names which denote AP profiles.
2808 #Parameters:
2810 # nothing
2812 #Returns:
2814 # list - profile names
2815 def profiles( self ):
2816 profile_list = []
2817 for section in self.sections():
2818 if ':' in section:
2819 profile_list.append(section)
2820 return profile_list
2822 # Read configuration file from disk into instance variables.
2824 #Parameters:
2826 # nothing
2828 #Returns:
2830 # nothing
2831 def read( self ):
2832 fp = open( self.filename, "r" )
2833 self.readfp(fp)
2834 # convert the auto_profile_order to a list for ordering
2835 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2836 for ap in self.profiles():
2837 self.set_bool_opt( ap + '.known', True)
2838 if ap in self.auto_profile_order: continue
2839 self.auto_profile_order.append( ap )
2840 fp.close()
2841 # Remove any auto_profile_order AP without a matching section.
2842 auto_profile_order_copy = self.auto_profile_order[:]
2843 for ap in auto_profile_order_copy:
2844 if ap not in self.profiles():
2845 self.auto_profile_order.remove(ap)
2847 # Write configuration file to disk from instance variables. Copied from
2848 # ConfigParser and modified to write options in alphabetical order.
2850 #Parameters:
2852 # nothing
2854 #Returns:
2856 # nothing
2857 def write( self ):
2858 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2859 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2860 (fd, tempfilename) = tempfile.mkstemp(prefix="wifi-radar.conf.")
2861 fp = os.fdopen(fd, "w")
2862 # write DEFAULT section first
2863 if self._defaults:
2864 fp.write("[DEFAULT]\n")
2865 for key in sorted(self._defaults.keys()):
2866 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2867 fp.write("\n")
2868 # write other non-profile sections next
2869 for section in self._sections:
2870 if section not in self.profiles():
2871 fp.write("[%s]\n" % section)
2872 for key in sorted(self._sections[section].keys()):
2873 if key != "__name__":
2874 fp.write("%s = %s\n" %
2875 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2876 fp.write("\n")
2877 # write profile sections
2878 for section in self._sections:
2879 if section in self.profiles():
2880 fp.write("[%s]\n" % section)
2881 for key in sorted(self._sections[section].keys()):
2882 if key != "__name__":
2883 fp.write("%s = %s\n" %
2884 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2885 fp.write("\n")
2886 fp.close()
2887 move(tempfilename, self.filename)
2889 # Load our conf file and known profiles
2890 # Defaults, these may get overridden by values found in the conf file.
2891 config_defaults = { # The network interface you use.
2892 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2893 'interface': "auto_detect",
2894 # How long should the scan for access points last?
2895 #'scan_timeout': '5',
2896 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2897 # Set the speak_up option to false if you do not have or want this.
2898 'speak_command': '/usr/bin/say',
2899 # Should I speak up when connecting to a network? (If you have a speech command)
2900 'speak_up': 'False',
2901 # You may set this to true for cards that require a "commit" command with iwconfig
2902 'commit_required': 'False',
2903 # You may set this to true for cards that require the interface to be brought up first
2904 'ifup_required': 'False',
2905 # set the location and verbosity of the log file
2906 'logfile': '/var/log/wifi-radar.log',
2907 'loglevel': '50',
2908 # Set the location of several important programs
2909 'iwlist_command': '/sbin/iwlist',
2910 'iwconfig_command': '/sbin/iwconfig',
2911 'ifconfig_command': '/sbin/ifconfig',
2912 'route_command': '/sbin/route',
2913 'auto_profile_order': '[]',
2914 'version': WIFI_RADAR_VERSION }
2916 config_dhcp = { # DHCP client
2917 'command': '/sbin/dhcpcd',
2918 # How long to wait for an IP addr from DHCP server
2919 'timeout': '30',
2920 # Arguments to use with DHCP client on connect
2921 'args': '-D -o -i dhcp_client -t %(timeout)s',
2922 # Argument to use with DHCP client on disconnect
2923 'kill_args': '-k',
2924 # The file where DHCP client PID is written
2925 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2927 config_wpa = { # WPA Supplicant
2928 'command': '/usr/sbin/wpa_supplicant',
2929 # Arguments to use with WPA Supplicant on connect
2930 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2931 # Arguments to use with WPA Supplicant on disconnect
2932 'kill_command': '',
2933 # Where the WPA Supplicant config file can be found
2934 'configuration': '/etc/wpa_supplicant.conf',
2935 # Driver to use with WPA Supplicant
2936 'driver': 'wext',
2937 # The file where WPA Supplicant PID is written
2938 'pidfile': '/var/run/wpa_supplicant.pid' }
2940 # initialize config, with defaults
2941 confFile = ConfigFile(CONF_FILE, config_defaults)
2942 confFile.set_section("DHCP", config_dhcp)
2943 confFile.set_section("WPA", config_wpa)
2945 if not os.path.isfile( CONF_FILE ):
2946 confFile.set_bool_opt('DEFAULT.new_file', True)
2947 else:
2948 if not os.access(CONF_FILE, os.R_OK):
2949 print "Can't open " + CONF_FILE + "."
2950 print "Are you root?"
2951 sys.exit()
2952 try:
2953 confFile.read()
2954 except NameError:
2955 error_dlg = ErrorDialog(None, "A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. Because %s may contain information that you might wish to use when configuring WiFi Radar %s, rename this file and run the program again.\n" % (CONF_FILE, CONF_FILE, WIFI_RADAR_VERSION))
2956 del error_dlg
2957 print "ERROR: A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. Because %s may contain information that you might wish to use when configuring WiFi Radar %s, rename this file and run the program again.\n" % (CONF_FILE, CONF_FILE, WIFI_RADAR_VERSION)
2958 sys.exit()
2959 except SyntaxError:
2960 error_dlg = ErrorDialog(None, "A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. The old configuration file is probably empty and can be removed. Rename %s if you want to be very careful. After removing or renaming %s, run this program again.\n" % (CONF_FILE, CONF_FILE, CONF_FILE))
2961 del error_dlg
2962 print "ERROR: A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. The old configuration file is probably empty and can be removed. Rename %s if you want to be very careful. After removing or renaming %s, run this program again.\n" % (CONF_FILE, CONF_FILE, CONF_FILE)
2963 sys.exit()
2966 ####################################################################################################
2967 # Embedded Images
2968 wifi_radar_icon = [ ""
2969 "GdkP"
2970 "\0\0\22""7"
2971 "\2\1\0\2"
2972 "\0\0\1\214"
2973 "\0\0\0c"
2974 "\0\0\0O"
2975 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2976 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2977 "\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"
2978 "\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"
2979 "\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"
2980 "\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"
2981 "\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"
2982 "\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"
2983 "\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"
2984 "\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"
2985 "\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"
2986 "\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"
2987 "\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"
2988 "\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"
2989 "\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"
2990 "\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"
2991 "\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"
2992 "\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"
2993 "\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"
2994 "\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"
2995 "\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"
2996 "\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"
2997 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2998 "\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"
2999 "\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"
3000 "\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"
3001 "\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"
3002 "\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"
3003 "\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"
3004 "\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"
3005 "\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"
3006 "\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"
3007 "\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"
3008 "\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"
3009 "\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"
3010 "\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"
3011 "\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"
3012 "\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"
3013 "\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"
3014 "\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"
3015 "\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"
3016 "\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"
3017 "\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"
3018 "\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"
3019 "\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"
3020 "\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"
3021 "\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"
3022 "\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"
3023 "\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"
3024 "\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"
3025 "\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"
3026 "\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"
3027 "\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"
3028 "\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"
3029 "\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"
3030 "\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"
3031 "\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"
3032 "\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"
3033 "\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"
3034 "\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"
3035 "\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"
3036 "\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"
3037 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
3038 "\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"
3039 "\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"
3040 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
3041 "\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"
3042 "\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"
3043 "\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"
3044 "\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"
3045 "\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"
3046 "\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"
3047 "\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"
3048 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
3049 "\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"
3050 "\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"
3051 "\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"
3052 "\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"
3053 "\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"
3054 "\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"
3055 "|\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"
3056 "\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"
3057 "\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"
3058 "\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"
3059 "\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"
3060 "\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"
3061 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
3062 "\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"
3063 "\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"
3064 "\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"
3065 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
3066 "\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"
3067 "\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"
3068 "\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"
3069 "\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"
3070 "\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"
3071 "\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"
3072 "\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"
3073 "\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"
3074 "\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"
3075 "\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"
3076 "\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"
3077 "\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"
3078 "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"
3079 "\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"
3080 "\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"
3081 "\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"
3082 "\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"
3083 "\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"
3084 "\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"
3085 "\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"
3086 "\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"
3087 "\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"
3088 "\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"
3089 "\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"
3090 "\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"
3091 "\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"
3092 "\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"
3093 "\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|"
3094 "\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"
3095 "\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"
3096 "\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"
3097 "\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"
3098 "\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"
3099 "\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"
3100 "\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"
3101 "\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"
3102 "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"
3103 "\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"
3104 "\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"
3105 "\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"
3106 "\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"
3107 "\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"
3108 "\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"
3109 "\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"
3110 "\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"
3111 "\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"
3112 "\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"
3113 "\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"
3114 "\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"
3115 "\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"
3116 "\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"
3117 "\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"
3118 "\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"
3119 "\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"
3120 "\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"
3121 "\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"
3122 "\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"
3123 "\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"
3124 "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"
3125 "\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"
3126 "\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"
3127 "\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"
3128 "\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"
3129 "\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"
3130 "\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"
3131 "\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"
3132 "\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"
3133 "\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"
3134 "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"
3135 "\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"
3136 "\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"
3137 "\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"
3138 "\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"
3139 "\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"
3140 "\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"
3141 "\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"
3142 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
3143 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
3144 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
3145 "\0"]
3147 known_profile_icon = [ ""
3148 "GdkP"
3149 "\0\0\5""0"
3150 "\2\1\0\2"
3151 "\0\0\0P"
3152 "\0\0\0\24"
3153 "\0\0\0\24"
3154 "\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"
3155 "\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"
3156 "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"
3157 "\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"
3158 "\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"
3159 "\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"
3160 "\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"
3161 "\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"
3162 "\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"
3163 "\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"
3164 "\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"
3165 "\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"
3166 "\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"
3167 "\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"
3168 "\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"
3169 "\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"
3170 "\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"
3171 "\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"
3172 "\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"
3173 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
3174 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
3175 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
3176 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
3177 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
3178 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
3179 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
3180 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
3181 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
3182 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
3183 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
3184 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
3185 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
3186 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
3187 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
3188 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
3189 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
3190 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
3191 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
3192 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
3193 "\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"
3194 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
3195 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
3196 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
3197 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
3198 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
3199 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
3200 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
3201 "\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"
3202 "\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"
3203 "\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"
3204 "\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"
3205 "\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"]
3207 unknown_profile_icon = [ ""
3208 "GdkP"
3209 "\0\0\5\22"
3210 "\2\1\0\2"
3211 "\0\0\0P"
3212 "\0\0\0\24"
3213 "\0\0\0\24"
3214 "\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"
3215 "\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"
3216 "\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"
3217 "\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"
3218 "(\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"
3219 "\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"
3220 "#\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"
3221 "\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"
3222 "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"
3223 "\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"
3224 "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"
3225 "\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"
3226 "\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"
3227 "\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"
3228 "\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"
3229 "\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"
3230 "\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"
3231 "\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"
3232 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
3233 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
3234 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
3235 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
3236 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
3237 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
3238 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
3239 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
3240 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
3241 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
3242 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
3243 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
3244 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
3245 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
3246 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
3247 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
3248 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
3249 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
3250 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
3251 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
3252 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
3253 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
3254 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
3255 "\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"
3256 "\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"
3257 "\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"
3258 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
3260 signal_xpm_barely = [
3261 "20 20 10 1",
3262 " c None",
3263 ". c #C6C6C6",
3264 "+ c #CCCCCC",
3265 "@ c #DBDBDB",
3266 "# c #D3D3D3",
3267 "$ c #A9B099",
3268 "% c #95A173",
3269 "& c #6B8428",
3270 "* c #B4B7AC",
3271 "= c #80924D",
3272 " .+++.",
3273 " +@@@+",
3274 " +@@@+",
3275 " +@@@+",
3276 " +@@@+",
3277 " .++++#@@@+",
3278 " +@@@@@@@@+",
3279 " +@@@@@@@@+",
3280 " +@@@@@@@@+",
3281 " +@@@@@@@@+",
3282 " $%%%%#@@@@@@@@+",
3283 " %&&&&@@@@@@@@@+",
3284 " %&&&&@@@@@@@@@+",
3285 " %&&&&@@@@@@@@@+",
3286 " %&&&&@@@@@@@@@+",
3287 "*%%%%=&&&&@@@@@@@@@+",
3288 "%&&&&&&&&&@@@@@@@@@+",
3289 "%&&&&&&&&&@@@@@@@@@+",
3290 "%&&&&&&&&&@@@@@@@@@+",
3291 "*%%%%%%%%%+++++++++."
3295 signal_xpm_best = [
3296 "20 20 6 1",
3297 " c None",
3298 ". c #9DAABF",
3299 "+ c #7B96BF",
3300 "@ c #386EBF",
3301 "# c #5982BF",
3302 "$ c #AEB4BF",
3303 " .+++.",
3304 " +@@@+",
3305 " +@@@+",
3306 " +@@@+",
3307 " +@@@+",
3308 " .++++#@@@+",
3309 " +@@@@@@@@+",
3310 " +@@@@@@@@+",
3311 " +@@@@@@@@+",
3312 " +@@@@@@@@+",
3313 " .++++#@@@@@@@@+",
3314 " +@@@@@@@@@@@@@+",
3315 " +@@@@@@@@@@@@@+",
3316 " +@@@@@@@@@@@@@+",
3317 " +@@@@@@@@@@@@@+",
3318 "$++++#@@@@@@@@@@@@@+",
3319 "+@@@@@@@@@@@@@@@@@@+",
3320 "+@@@@@@@@@@@@@@@@@@+",
3321 "+@@@@@@@@@@@@@@@@@@+",
3322 "$++++++++++++++++++."
3325 signal_xpm_none = [
3326 "20 20 6 1",
3327 " c None",
3328 ". c #C6C6C6",
3329 "+ c #CCCCCC",
3330 "@ c #DBDBDB",
3331 "# c #D3D3D3",
3332 "$ c #C2C2C2",
3333 " .+++.",
3334 " +@@@+",
3335 " +@@@+",
3336 " +@@@+",
3337 " +@@@+",
3338 " .++++#@@@+",
3339 " +@@@@@@@@+",
3340 " +@@@@@@@@+",
3341 " +@@@@@@@@+",
3342 " +@@@@@@@@+",
3343 " .++++#@@@@@@@@+",
3344 " +@@@@@@@@@@@@@+",
3345 " +@@@@@@@@@@@@@+",
3346 " +@@@@@@@@@@@@@+",
3347 " +@@@@@@@@@@@@@+",
3348 "$++++#@@@@@@@@@@@@@+",
3349 "+@@@@@@@@@@@@@@@@@@+",
3350 "+@@@@@@@@@@@@@@@@@@+",
3351 "+@@@@@@@@@@@@@@@@@@+",
3352 "$++++++++++++++++++."
3355 signal_xpm_ok = [
3356 "20 20 10 1",
3357 " c None",
3358 ". c #C6C6C6",
3359 "+ c #CCCCCC",
3360 "@ c #DBDBDB",
3361 "# c #A1A5B2",
3362 "$ c #848DA5",
3363 "% c #D3D3D3",
3364 "& c #4A5B8C",
3365 "* c #677498",
3366 "= c #B0B2B8",
3367 " .+++.",
3368 " +@@@+",
3369 " +@@@+",
3370 " +@@@+",
3371 " +@@@+",
3372 " #$$$$%@@@+",
3373 " $&&&&@@@@+",
3374 " $&&&&@@@@+",
3375 " $&&&&@@@@+",
3376 " $&&&&@@@@+",
3377 " #$$$$*&&&&@@@@+",
3378 " $&&&&&&&&&@@@@+",
3379 " $&&&&&&&&&@@@@+",
3380 " $&&&&&&&&&@@@@+",
3381 " $&&&&&&&&&@@@@+",
3382 "=$$$$*&&&&&&&&&@@@@+",
3383 "$&&&&&&&&&&&&&&@@@@+",
3384 "$&&&&&&&&&&&&&&@@@@+",
3385 "$&&&&&&&&&&&&&&@@@@+",
3386 "=$$$$$$$$$$$$$$++++."
3390 signal_xpm_low = [
3391 "20 20 8 1",
3392 " c None",
3393 ". c #C6C6C6",
3394 "+ c #CCCCCC",
3395 "@ c #DBDBDB",
3396 "# c #D3D3D3",
3397 "$ c #BFB0B5",
3398 "% c #C18799",
3399 "& c #C54F74",
3400 " .+++.",
3401 " +@@@+",
3402 " +@@@+",
3403 " +@@@+",
3404 " +@@@+",
3405 " .++++#@@@+",
3406 " +@@@@@@@@+",
3407 " +@@@@@@@@+",
3408 " +@@@@@@@@+",
3409 " +@@@@@@@@+",
3410 " .++++#@@@@@@@@+",
3411 " +@@@@@@@@@@@@@+",
3412 " +@@@@@@@@@@@@@+",
3413 " +@@@@@@@@@@@@@+",
3414 " +@@@@@@@@@@@@@+",
3415 "$%%%%#@@@@@@@@@@@@@+",
3416 "%&&&&@@@@@@@@@@@@@@+",
3417 "%&&&&@@@@@@@@@@@@@@+",
3418 "%&&&&@@@@@@@@@@@@@@+",
3419 "$%%%%++++++++++++++."
3423 ####################################################################################################
3424 # Make so we can be imported
3425 if __name__ == "__main__":
3426 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
3427 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3428 elif len( sys.argv ) > 1 and ( sys.argv[1] == '--help' or sys.argv[1] == '-h' ):
3429 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3430 print "For help, check man pages for wifi-radar and wifi-radar.conf,"
3431 print "or visit http://wifi-radar.berlios.de"
3432 else:
3433 import gtk, gobject
3434 gtk.gdk.threads_init()
3435 apQueue = Queue.Queue(100)
3436 commQueue = Queue.Queue(2)
3438 logger = logging.getLogger("wrlog")
3439 logger.setLevel(confFile.get_opt_as_int('DEFAULT.loglevel'))
3440 fileLogHandler = logging.handlers.RotatingFileHandler(confFile.get_opt('DEFAULT.logfile'), maxBytes=64*1024, backupCount=5)
3441 fileLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(funcName)s: %(message)s'))
3442 logger.addHandler(fileLogHandler)
3443 consoleLogHandler = logging.StreamHandler()
3444 consoleLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(funcName)s: %(message)s'))
3445 logger.addHandler(consoleLogHandler)
3446 if __debug__:
3447 logger.setLevel(logging.INFO)
3449 exit_event = threading.Event()
3450 exit_event.clear()
3451 threading.Thread(None, scanning_thread, None, (confFile, apQueue, commQueue, logger, exit_event)).start()
3452 main_radar_window = radar_window(confFile, apQueue, commQueue, logger, exit_event)
3453 gobject.timeout_add( 500, main_radar_window.update_window )
3454 main_radar_window.main()