Add 2.0.s08 to HISTORY
[wifi-radar.git] / wifi-radar
blob905a782b6942db18fdffd2febf88bfbb579ae375
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-2010 Sean Robinson <seankrobinson@gmail.com>
13 # Copyright (C) 2010 Prokhor Shuchalov <p@shuchalov.ru>
15 # This program is free software; you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation; version 2 of the License.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License in LICENSE.GPL for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 # http://wifi-radar.berlios.de
30 # See CREDITS file for more contributors.
31 # See HISTORY file for, well, changes.
33 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
34 # turn on console debugging.
36 import ConfigParser
37 import errno
38 import gtk
39 import logging
40 import logging.handlers
41 import os
42 import Queue
43 import re
44 import string
45 import sys
46 import tempfile
47 import threading
48 from shutil import move
49 from signal import SIGTERM
50 from subprocess import call, Popen, PIPE, STDOUT
51 from time import sleep
52 from types import *
54 WIFI_RADAR_VERSION = "0.0.0"
57 # Where the conf file should live could be different for your distro. Please change
58 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
60 CONF_FILE = "/etc/wifi-radar/wifi-radar.conf"
62 os.environ['LC_MESSAGES'] = 'C'
65 ####################################################################################################
66 ####################################################################################################
68 # Gets the network interface device
70 #Parameters:
72 # 'device' -- string - The proposed network device to use
74 #Returns:
76 # string -- The actual network device to use
77 def get_network_device(device):
78 #print "get_network_device: %s" % (device, )
79 if device != "auto_detect":
80 return confFile.get_opt('DEFAULT.interface')
81 else:
82 # auto detect network device
83 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
84 # If no devices are found, default to eth1.
85 # call iwconfig command and read output
86 iwconfig_info = Popen(confFile.get_opt('DEFAULT.iwconfig_command'), shell=True, stdout=PIPE, stderr=STDOUT).stdout
87 wireless_devices = [(x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
88 if len(wireless_devices) > 0:
89 return wireless_devices[0]
90 logger.critical("No WiFi device found, please set this in the preferences.")
91 return ""
93 # Return a blank profile
95 #Parameters:
97 # none
99 #Returns:
101 # dictionary -- An AP profile with defaults set.
102 def get_new_profile():
103 return { 'known': False,
104 'available': False,
105 'encrypted': False,
106 'essid': '',
107 'bssid': '',
108 'roaming': False,
109 'protocol': 'g',
110 'signal': -193,
111 'channel': 'auto',
112 'con_prescript': '',
113 'con_postscript': '',
114 'dis_prescript': '',
115 'dis_postscript': '',
116 'key': '',
117 'mode': 'auto',
118 'security': '',
119 'use_wpa': False,
120 'wpa_driver': '',
121 'use_dhcp': True,
122 'ip': '',
123 'netmask': '',
124 'gateway': '',
125 'domain': '',
126 'dns1': '',
127 'dns2': ''
130 # Combine essid and bssid to make a config file section name
132 #Parameters:
134 # 'essid' -- string - AP ESSID
136 # 'bssid' -- string - AP BSSID
138 #Returns:
140 # string -- the bssid concatenated to a colon, concatenated to the essid
141 def make_section_name( essid, bssid ):
142 return essid + ':' + bssid
144 # Split a config file section name into an essid and a bssid
146 #Parameters:
148 # 'section' -- string - Config file section name
150 #Returns:
152 # list -- the essid and bssid
153 def split_section_name( section ):
154 parts = re.split(':', section)
155 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
157 # Run commands through the shell
159 #Parameters:
161 # 'command' -- tuple - The command and arguments to run.
163 # 'environment' -- dictionary - Environment variables (as keys) and their values.
165 #Returns:
167 # boolean -- True on success, otherwise, False
168 def shellcmd( command, environment = None ):
169 try:
170 env_tmp = os.environ
171 env_tmp.update(environment)
172 command = ' '.join(command)
173 return_code = call(command, shell=True, env=env_tmp)
174 if return_code >= 0:
175 return True
176 else:
177 print >>sys.stderr, "Child was terminated by signal", -return_code
178 except OSError, exception:
179 print >>sys.stderr, "Execution failed:", exception
180 return False
182 # Speak feedback message to user
184 #Parameters:
186 # 'words' -- string - Message to speak to user
188 #Returns:
190 # nothing
191 def say( words ):
192 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
193 words = words.replace( "\"", "\\\"" )
194 shellcmd([confFile.get_opt('DEFAULT.speak_command'), words])
196 # Scan for a limited time and return AP names and bssid found.
197 # Access points we find will be put on the outgoing Queue, apQueue.
199 #Parameters:
201 # 'confFile' -- ConfigFile - Config file object
203 # 'apQueue' -- Queue - Queue on which to put AP profiles
205 # 'commandQueue' -- Queue - Queue from which to read commands
207 # 'logger' -- Logger - Python's logging facility
209 #Returns:
211 # nothing
212 def scanning_thread(confFile, apQueue, commandQueue, logger, exit_event):
213 logger.info("Begin thread.")
214 # Setup our essid pattern matcher
215 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
216 bssid_pattern = re.compile( "Address\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
217 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abgn]+)", re.I | re.M | re.S )
218 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
219 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
220 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
221 signal_pattern = re.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S )
223 access_points = {}
224 command = "scan"
225 while True:
226 try:
227 command = commandQueue.get_nowait()
228 logger.info("received command: %s" % (command, ))
229 command_read = True
230 except Queue.Empty:
231 command_read = False
232 device = get_network_device(confFile.get_opt('DEFAULT.interface'))
233 if command == "scan":
234 logger.debug("Beginning scan pass")
235 # Some cards need to have the interface up to scan
236 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
237 # call ifconfig command and wait for return
238 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), device, 'up'])
239 # update the signal strengths
240 try:
241 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), device, 'scan'], stdout=PIPE).stdout.read()
242 except OSError, (errno, strerror):
243 if errno == 2:
244 logger.critical("iwlist command not found, please set this in the preferences.")
245 scandata = ""
246 # zero out the signal levels for all access points
247 for bssid in access_points:
248 access_points[bssid]['signal'] = 0
249 # split the scan data based on the address line
250 hits = scandata.split(' - ')
251 for hit in hits:
252 # set the defaults for profile template
253 profile = get_new_profile()
254 m = essid_pattern.search( hit )
255 if m:
256 # we found an essid
257 profile['essid'] = m.groups()[1]
258 m = bssid_pattern.search( hit ) # get BSSID from scan
259 if m: profile['bssid'] = m.groups()[1]
260 m = protocol_pattern.search( hit ) # get protocol from scan
261 if m: profile['protocol'] = m.groups()[1]
262 m = mode_pattern.search( hit ) # get mode from scan
263 if m: profile['mode'] = m.groups()[1]
264 m = channel_pattern.search( hit ) # get channel from scan
265 if m: profile['channel'] = m.groups()[1]
266 m = enckey_pattern.search( hit ) # get encryption key from scan
267 if m: profile['encrypted'] = ( m.groups()[1] == 'on' )
268 m = signal_pattern.search( hit ) # get signal strength from scan
269 if m: profile['signal'] = m.groups()[1]
270 access_points[ profile['bssid'] ] = profile
271 for bssid in access_points:
272 access_points[bssid]['available'] = ( access_points[bssid]['signal'] > 0 )
273 # Put all, now or previously, sensed access_points into apQueue
274 try:
275 logger.debug("Scanned profile: %s" % (access_points[ bssid ], ))
276 apQueue.put_nowait( access_points[bssid] )
277 except Queue.Full:
278 pass
279 if command_read:
280 commandQueue.task_done()
281 if exit_event.isSet():
282 logger.info("Exiting.")
283 return
284 if device.find('ath') == 0:
285 sleep( 3 )
286 else:
287 sleep( 1 )
290 # Manage a connection; including reporting connection state,
291 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
292 class ConnectionManager():
293 # Create a new connection manager which can read a config file and send to scanning thread
294 # command Queue. A new manager checks for a pre-existing connection and takes
295 # its AP profile from the ESSID and BSSID to which it is currently attached.
297 #Parameters:
299 # 'confFile' -- ConfigFile - Config file object
301 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
303 # 'logger' -- Logger - Python's logging facility
305 #Returns:
307 # ConnectionManager instance
308 def __init__( self, confFile, commandQueue, logger ):
309 self.confFile = confFile
310 self.commQueue = commandQueue
311 self.logger = logger
312 # is connection running?
313 self.state = False
314 if self.get_current_ip():
315 self.state = True
316 self.profile = self.confFile.get_profile( make_section_name(self.get_current_essid(), self.get_current_bssid()) )
318 # Change the interface state: up or down.
320 #Parameters:
322 # 'state' -- string - The state to which to change the interface.
324 #Returns:
326 # nothing
327 def if_change( self, state ):
328 if ( (state.lower() == 'up') or (state.lower() == 'down') ):
329 self.logger.info("changing interface state to %s" % (state, ))
330 # call ifconfig command and wait for return
331 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface')), state])
333 # Return the WiFi encryption mode from evidence in the profile.
335 #Parameters:
337 # 'use_wpa' -- boolean - The use_wpa from the profile.
339 # 'key' -- string - The WEP key or empty string.
341 #Returns:
343 # string - none, wep, or wpa; indicates WiFi encryption mode
344 def _get_enc_mode(self, use_wpa, key):
345 if use_wpa:
346 return 'wpa'
347 elif key == '':
348 return 'none'
349 else:
350 return 'wep'
352 # Connect to the specified AP.
354 #Parameters:
356 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
358 # 'status' -- status implementer - Object which implements status interface.
360 #Returns:
362 # nothing
363 def connect_to_network( self, profile, status ):
364 self.profile = profile
365 if self.profile['bssid'] == '':
366 raise TypeError("Empty AP address")
367 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
368 say( msg )
369 self.logger.info(msg)
370 # Make a temporary copy of the DEFAULT.interface option.
371 default_interface = self.confFile.get_opt('DEFAULT.interface')
372 device = get_network_device(self.confFile.get_opt('DEFAULT.interface'))
373 # Temporarily set the configured interface to a real one.
374 self.confFile.set_opt('DEFAULT.interface', device)
375 # ready to dance
376 # Let's run the connection prescript
377 if self.profile['con_prescript'].strip() != '':
378 # got something to execute
379 # run connection prescript through shell and wait for return
380 self.logger.info("executing connection prescript: %s" % (self.profile['con_prescript'], ))
381 shellcmd([self.profile['con_prescript']], environment = {
382 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
383 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
384 "WIFIRADAR_SECMODE": self.profile['security'],
385 "WIFIRADAR_IF": device or ''
388 status.show()
389 # Some cards need to have the interface up
390 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
391 self.if_change('up')
392 # Start building iwconfig command line, command
393 iwconfig_command = [ self.confFile.get_opt('DEFAULT.iwconfig_command') ]
394 iwconfig_command.append(device)
395 # Setting essid
396 iwconfig_command.append( 'essid' )
397 iwconfig_command.append( "'" + self.profile['essid'] + "'" )
398 # Setting nick
399 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
400 # Setting key
401 iwconfig_command.append( 'key' )
402 if self.profile['key'] == '':
403 iwconfig_command.append( 'off' )
404 else:
405 # Setting this stops association from working, so remove it for now
406 #if self.profile['security'] != '':
407 #iwconfig_command.append(self.profile['security'])
408 iwconfig_command.append( "'" + self.profile['key'] + "'" )
409 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
410 # Setting mode
411 if self.profile['mode'].lower() == 'master' or self.profile['mode'].lower() == 'auto':
412 self.profile['mode'] = 'Managed'
413 iwconfig_command.append( 'mode' )
414 iwconfig_command.append( self.profile['mode'] )
415 # Setting channel
416 if self.profile['channel'] != '':
417 iwconfig_command.append( 'channel' )
418 iwconfig_command.append( self.profile['channel'] )
419 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
420 iwconfig_command.append( 'ap' )
421 iwconfig_command.append( self.profile['bssid'] )
422 # Some cards require a commit
423 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
424 iwconfig_command.append( 'commit' )
425 self.logger.info("iwconfig_command: %s" % (iwconfig_command, ))
426 # call iwconfig command and wait for return
427 if not shellcmd(iwconfig_command): return
428 # Now normal network stuff
429 # Kill off any existing DHCP clients running
430 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
431 self.logger.info("Killing existing DHCP...")
432 try:
433 if self.confFile.get_opt('DHCP.kill_args') != '':
434 # call DHCP client kill command and wait for return
435 shellcmd([self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args')])
436 else:
437 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
438 except OSError:
439 print "failed to kill DHCP client"
440 sys.exit()
441 finally:
442 print "Stale pid file. Removing..."
443 os.remove(self.confFile.get_opt('DHCP.pidfile'))
444 # Kill off any existing WPA supplicants running
445 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
446 self.logger.info("Killing existing WPA supplicant...")
447 try:
448 if not self.confFile.get_opt('WPA.kill_command') != '':
449 # call WPA supplicant kill command and wait for return
450 shellcmd([self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command')])
451 else:
452 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
453 except OSError:
454 print "failed to kill WPA supplicant"
455 sys.exit()
456 finally:
457 print "Stale pid file. Removing..."
458 os.remove(self.confFile.get_opt('WPA.pidfile'))
459 self.logger.debug("Disable scan while connection attempt in progress...")
460 try:
461 self.commQueue.put("pause")
462 except Queue.Full:
463 pass
464 # Begin WPA supplicant
465 if self.profile['use_wpa'] :
466 self.logger.info("WPA args: %s" % (self.confFile.get_opt('WPA.args'), ))
467 status.update_message("WPA supplicant starting")
468 if sys.modules.has_key("gtk"):
469 while gtk.events_pending():
470 gtk.main_iteration(False)
471 # call WPA supplicant command and do not wait for return
472 try:
473 wpa_proc = shellcmd([self.confFile.get_opt('WPA.command'), self.confFile.get_opt('WPA.args')])
474 if sys.modules.has_key("gtk"):
475 while gtk.events_pending():
476 gtk.main_iteration(False)
477 sleep(2)
478 except OSError, (errno, strerror):
479 if errno == 2:
480 logger.critical("WPA supplicant not found, please set this in the preferences.")
481 if self.profile['use_dhcp'] :
482 status.update_message("Acquiring IP Address (DHCP)")
483 if sys.modules.has_key("gtk"):
484 while gtk.events_pending():
485 gtk.main_iteration(False)
486 # call DHCP client command and do not wait for return
487 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
488 dhcp_command.extend( self.confFile.get_opt('DHCP.args').split(' ') )
489 dhcp_command.append(device)
490 self.logger.info("dhcp_command: %s" % (dhcp_command, ))
491 try:
492 dhcp_proc = Popen(dhcp_command, stdout=None)
493 except OSError, (errno, strerror):
494 if errno == 2:
495 logger.critical("DHCP client not found, please set this in the preferences.")
496 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 3
497 tick = 0.25
498 waiting = dhcp_proc.poll()
499 while waiting == None:
500 waiting = dhcp_proc.poll()
501 if timer < 0:
502 os.kill(dhcp_proc.pid, SIGTERM)
503 break
504 if sys.modules.has_key("gtk"):
505 while gtk.events_pending():
506 gtk.main_iteration(False)
507 timer -= tick
508 sleep(tick)
509 if not self.get_current_ip():
510 status.update_message("Could not get IP address!")
511 if sys.modules.has_key("gtk"):
512 while gtk.events_pending():
513 gtk.main_iteration(False)
514 sleep(1)
515 if self.state:
516 self.disconnect_interface()
517 status.hide()
518 return
519 else:
520 status.update_message("Got IP address. Done.")
521 self.state = True
522 if sys.modules.has_key("gtk"):
523 while gtk.events_pending():
524 gtk.main_iteration(False)
525 sleep(2)
526 else:
527 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'] )
528 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
529 resolv_contents = ''
530 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % self.profile['domain']
531 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % self.profile['dns1']
532 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % self.profile['dns2']
533 if ( resolv_contents != '' ):
534 resolv_file=open('/etc/resolv.conf', 'w')
535 resolv_file.write(resolv_contents)
536 resolv_file.close
537 if not shellcmd([ifconfig_command]): return
538 if not shellcmd([route_command]): return
539 self.state = True
540 # Re-enable iwlist
541 try:
542 self.commQueue.put("scan")
543 except Queue.Full:
544 pass
545 # Let's run the connection postscript
546 con_postscript = self.profile['con_postscript']
547 if self.profile['con_postscript'].strip() != '':
548 self.logger.info("executing connection postscript: %s" % (self.profile['con_postscript'], ))
549 shellcmd([self.profile['con_postscript']], environment = {
550 "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
551 "WIFIRADAR_ESSID": self.get_current_essid() or '',
552 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
553 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
554 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
555 "WIFIRADAR_SECMODE": self.profile['security'],
556 "WIFIRADAR_IF": device or ''
559 # Set the configured interface back to original value.
560 self.confFile.set_opt('DEFAULT.interface', default_interface)
561 status.hide()
563 # Disconnect from the AP with which a connection has been established/attempted.
565 #Parameters:
567 # nothing
569 #Returns:
571 # nothing
572 def disconnect_interface( self ):
573 msg = "Disconnecting"
574 say( msg )
575 self.logger.info(msg)
576 # Make a temporary copy of the DEFAULT.interface option.
577 default_interface = self.confFile.get_opt('DEFAULT.interface')
578 device = get_network_device(self.confFile.get_opt('DEFAULT.interface'))
579 # Temporarily set the configured interface to a real one.
580 self.confFile.set_opt('DEFAULT.interface', device)
581 # Pause scanning while manipulating card
582 try:
583 self.commQueue.put("pause")
584 self.commQueue.join()
585 except Queue.Full:
586 pass
587 if self.state:
588 self.profile = self.confFile.get_profile(make_section_name(self.get_current_essid(), self.get_current_bssid()))
589 if not self.profile:
590 self.profile = self.confFile.get_profile(make_section_name(self.get_current_essid(), ''))
591 if not self.profile:
592 raise KeyError
593 # Let's run the disconnection prescript
594 if self.profile['dis_prescript'].strip() != '':
595 self.logger.info("executing disconnection prescript: %s" % (self.profile['dis_prescript'], ))
596 shellcmd([self.profile['dis_prescript']], environment = {
597 "WIFIRADAR_IP": self.get_current_ip() or '0.0.0.0',
598 "WIFIRADAR_ESSID": self.get_current_essid() or '',
599 "WIFIRADAR_BSSID": self.get_current_bssid() or '00:00:00:00:00:00',
600 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
601 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
602 "WIFIRADAR_SECMODE": self.profile['security'],
603 "WIFIRADAR_IF": device or ''
606 self.logger.info("Kill off any existing DHCP clients running...")
607 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
608 self.logger.info("Killing existing DHCP...")
609 try:
610 if self.confFile.get_opt('DHCP.kill_args').strip() != '':
611 dhcp_command = [ self.confFile.get_opt('DHCP.command') ]
612 dhcp_command.extend( self.confFile.get_opt('DHCP.kill_args').split(' ') )
613 dhcp_command.append(device)
614 self.logger.info("DHCP command: %s" % (dhcp_command, ))
615 # call DHCP client command and wait for return
616 if not shellcmd(dhcp_command): return
617 else:
618 self.logger.info("Killing DHCP manually...")
619 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
620 except OSError:
621 print "failed to kill DHCP client"
622 self.logger.info("Kill off any existing WPA supplicants running...")
623 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
624 self.logger.info("Killing existing WPA supplicant...")
625 try:
626 if self.confFile.get_opt('WPA.kill_command') != '':
627 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
628 if not shellcmd(wpa_command): return
629 else:
630 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
631 except OSError:
632 print "failed to kill WPA supplicant"
633 self.logger.info("Let's clear out the wireless stuff")
634 shellcmd([self.confFile.get_opt('DEFAULT.iwconfig_command'), device, 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
635 self.logger.info("Now take the interface down")
636 self.logger.info("Since it may be brought back up by the next scan, lets unset its IP")
637 shellcmd([self.confFile.get_opt('DEFAULT.ifconfig_command'), device, '0.0.0.0'])
638 # taking down the interface too quickly can crash my system, so pause a moment
639 sleep(1)
640 self.if_change('down')
641 # Let's run the disconnection postscript
642 if self.profile['dis_postscript'].strip() != '':
643 self.logger.info("executing disconnection postscript: %s" % (self.profile['dis_postscript'], ))
644 shellcmd([self.profile['dis_postscript']], environment = {
645 "WIFIRADAR_PROFILE": make_section_name(self.profile['essid'], self.profile['bssid']),
646 "WIFIRADAR_ENCMODE": self._get_enc_mode(self.profile['use_wpa'], self.profile['key']),
647 "WIFIRADAR_SECMODE": self.profile['security'],
648 "WIFIRADAR_IF": device or ''
651 self.state = False
652 self.logger.info("Disconnect complete.")
653 # Begin scanning again
654 try:
655 self.commQueue.put("scan")
656 except Queue.Full:
657 pass
658 # Set the configured interface back to original value.
659 self.confFile.set_opt('DEFAULT.interface', default_interface)
661 # Returns the current IP, if any, by calling ifconfig.
663 #Parameters:
665 # nothing
667 #Returns:
669 # string or None -- the IP address or None (if no there is no current connection)
670 def get_current_ip( self ):
671 ifconfig_command = [confFile.get_opt('DEFAULT.ifconfig_command'), get_network_device(confFile.get_opt('DEFAULT.interface'))]
672 try:
673 ifconfig_info = Popen(ifconfig_command, stdout=PIPE).stdout
674 except OSError, (errno, strerror):
675 if errno == 2:
676 logger.critical("ifconfig command not found, please set this in the preferences.")
677 ifconfig_info = open("/dev/null", "r")
678 # Be careful to the language (inet adr: in French for example)
680 # Hi Brian
682 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
683 # There the string in ifconfig is inet Adresse for the IP which isn't
684 # found by the current get_current_ip function in wifi-radar. I changed
685 # the according line (#289; gentoo, v1.9.6-r1) to
686 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
687 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
689 # I'd be happy if you could incorporate this small change because as now
690 # I've got to change the file every time it is updated.
692 # Best wishes
694 # Simon
695 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
696 line = ifconfig_info.read()
697 if ip_re.search( line ):
698 return ip_re.search( line ).group(1)
699 return None
701 # Returns the current ESSID, if any, by calling iwconfig.
703 #Parameters:
705 # nothing
707 #Returns:
709 # string or None -- the ESSID or None (if no there is no current association)
710 def get_current_essid( self ):
711 """Returns the current ESSID if any by calling iwconfig"""
712 try:
713 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface'))], stdout=PIPE, stderr=STDOUT).stdout
714 except OSError, (errno, strerror):
715 if errno == 2:
716 logger.critical("iwconfig command not found, please set this in the preferences.")
717 iwconfig_info = open("/dev/null", "r")
718 # Be careful to the language (inet adr: in French for example)
719 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
720 line = iwconfig_info.read()
721 if essid_re.search( line ):
722 return essid_re.search( line ).group(2)
723 return None
725 # Returns the current BSSID, if any, by calling iwconfig.
727 #Parameters:
729 # nothing
731 #Returns:
733 # string or None -- the BSSID or None (if no there is no current association)
734 def get_current_bssid( self ):
735 """Returns the current BSSID if any by calling iwconfig"""
736 try:
737 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command'), get_network_device(self.confFile.get_opt('DEFAULT.interface'))], stdout=PIPE, stderr=STDOUT).stdout
738 except OSError, (errno, strerror):
739 if errno == 2:
740 logger.critical("iwconfig command not found, please set this in the preferences.")
741 iwconfig_info = open("/dev/null", "r")
742 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
743 line = iwconfig_info.read()
744 if bssid_re.search( line ):
745 return bssid_re.search( line ).group(2)
746 return None
750 # The main user interface window for WiFi Radar. This class also is the control
751 # center for most of the rest of the operations.
752 class radar_window:
753 # Create a new radar_window.
755 #Parameters:
757 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
759 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
761 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
763 # 'logger' -- Logger - Python's logging facility
765 #Returns:
767 # radar_window instance
768 def __init__(self, confFile, apQueue, commQueue, logger, exit_event):
769 global signal_xpm_none
770 global signal_xpm_low
771 global signal_xpm_barely
772 global signal_xpm_ok
773 global signal_xpm_best
774 global known_profile_icon
775 global unknown_profile_icon
776 global wifi_radar_icon
778 self.confFile = confFile
779 self.apQueue = apQueue
780 self.commandQueue = commQueue
781 self.logger = logger
782 self.access_points = {}
783 self.exit_event = exit_event
784 self.connection = None
786 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
787 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
788 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
789 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
790 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
791 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
792 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
793 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
794 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
795 self.window.set_icon( icon )
796 self.window.set_border_width( 10 )
797 self.window.set_size_request( 550, 300 )
798 self.window.set_title( "WiFi Radar" )
799 self.window.connect( 'delete_event', self.delete_event )
800 self.window.connect( 'destroy', self.destroy )
801 # let's create all our widgets
802 self.current_network = gtk.Label()
803 self.current_network.set_property('justify', gtk.JUSTIFY_CENTER)
804 self.current_network.show()
805 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
806 self.close_button.show()
807 self.close_button.connect( 'clicked', self.delete_event, None )
808 self.about_button = gtk.Button( "About", gtk.STOCK_ABOUT )
809 self.about_button.show()
810 self.about_button.connect( 'clicked', self.show_about_info, None )
811 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
812 self.preferences_button.show()
813 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
814 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
815 self.pstore = gtk.ListStore( str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
816 self.plist = gtk.TreeView( self.pstore )
817 # The icons column, known and encryption
818 self.pix_cell = gtk.CellRendererPixbuf()
819 self.wep_cell = gtk.CellRendererPixbuf()
820 self.icons_cell = gtk.CellRendererText()
821 self.icons_col = gtk.TreeViewColumn()
822 self.icons_col.pack_start( self.pix_cell, False )
823 self.icons_col.pack_start( self.wep_cell, False )
824 self.icons_col.add_attribute( self.pix_cell, 'pixbuf', 1 )
825 self.icons_col.add_attribute( self.wep_cell, 'stock-id', 4 )
826 self.plist.append_column( self.icons_col )
827 # The AP column
828 self.ap_cell = gtk.CellRendererText()
829 self.ap_col = gtk.TreeViewColumn( "Access Point" )
830 self.ap_col.pack_start( self.ap_cell, True )
831 self.ap_col.add_attribute( self.ap_cell, 'text', 0 )
832 self.plist.append_column( self.ap_col )
833 # The signal column
834 self.sig_cell = gtk.CellRendererPixbuf()
835 self.signal_col = gtk.TreeViewColumn( "Signal" )
836 self.signal_col.pack_start( self.sig_cell, True )
837 self.signal_col.add_attribute( self.sig_cell, 'pixbuf', 5 )
838 self.plist.append_column( self.signal_col )
839 # The mode column
840 self.mode_cell = gtk.CellRendererText()
841 self.mode_col = gtk.TreeViewColumn( "Mode" )
842 self.mode_col.pack_start( self.mode_cell, True )
843 self.mode_col.add_attribute( self.mode_cell, 'text', 6 )
844 self.plist.append_column( self.mode_col )
845 # The protocol column
846 self.prot_cell = gtk.CellRendererText()
847 self.protocol_col = gtk.TreeViewColumn( "802.11" )
848 self.protocol_col.pack_start( self.prot_cell, True )
849 self.protocol_col.add_attribute( self.prot_cell, 'text', 7 )
850 self.plist.append_column( self.protocol_col )
851 # The channel column
852 self.channel_cell = gtk.CellRendererText()
853 self.channel_col = gtk.TreeViewColumn( "Channel" )
854 self.channel_col.pack_start( self.channel_cell, True )
855 self.channel_col.add_attribute( self.channel_cell, 'text', 8 )
856 self.plist.append_column( self.channel_col )
857 # DnD Ordering
858 self.plist.set_reorderable( True )
859 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
860 self.pstore.connect( 'row-deleted', self.update_auto_profile_order )
861 # enable/disable buttons based on the selected network
862 self.selected_network = self.plist.get_selection()
863 self.selected_network.connect( 'changed', self.on_network_selection, None )
864 # the list scroll bar
865 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
866 sb.show()
867 self.plist.show()
868 # Add New button
869 self.new_button = gtk.Button( "_New" )
870 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
871 self.new_button.show()
872 # Add Configure button
873 self.edit_button = gtk.Button( "C_onfigure" )
874 self.edit_button.connect( 'clicked', self.edit_profile, None )
875 self.edit_button.show()
876 self.edit_button.set_sensitive(False)
877 # Add Delete button
878 self.delete_button = gtk.Button( "_Delete" )
879 self.delete_button.connect('clicked', self.delete_profile_with_check, None)
880 self.delete_button.show()
881 self.delete_button.set_sensitive(False)
882 # Add Connect button
883 self.connect_button = gtk.Button( "Co_nnect" )
884 self.connect_button.connect( 'clicked', self.connect_profile, None )
885 # Add Disconnect button
886 self.disconnect_button = gtk.Button( "D_isconnect" )
887 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
888 # lets add our widgets
889 rows = gtk.VBox( False, 3 )
890 net_list = gtk.HBox( False, 0 )
891 listcols = gtk.HBox( False, 0 )
892 prows = gtk.VBox( False, 0 )
893 # lets start packing
894 # the network list
895 net_list.pack_start( self.plist, True, True, 0 )
896 net_list.pack_start( sb, False, False, 0 )
897 # the rows level
898 rows.pack_start( net_list , True, True, 0 )
899 rows.pack_start( self.current_network, False, True, 0 )
900 # the list columns
901 listcols.pack_start( rows, True, True, 0 )
902 listcols.pack_start( prows, False, False, 5 )
903 # the list buttons
904 prows.pack_start( self.new_button, False, False, 2 )
905 prows.pack_start( self.edit_button, False, False, 2 )
906 prows.pack_start( self.delete_button, False, False, 2 )
907 prows.pack_end( self.connect_button, False, False, 2 )
908 prows.pack_end( self.disconnect_button, False, False, 2 )
910 self.window.action_area.pack_start( self.about_button )
911 self.window.action_area.pack_start( self.preferences_button )
912 self.window.action_area.pack_start( self.close_button )
914 rows.show()
915 prows.show()
916 listcols.show()
917 self.window.vbox.add( listcols )
918 self.window.vbox.set_spacing( 3 )
919 self.window.show_all()
921 # Now, immediately hide these two. The proper one will be
922 # displayed later, based on interface state. -BEF-
923 self.disconnect_button.hide()
924 self.connect_button.hide()
925 self.connect_button.set_sensitive(False)
927 # set up connection manager for later use
928 self.connection = ConnectionManager( self.confFile, self.commandQueue, self.logger )
929 # set up status window for later use
930 self.status_window = StatusWindow( self )
931 self.status_window.cancel_button.connect('clicked', self.disconnect_profile, "cancel")
933 # Add our known profiles in order
934 for profile_name in self.confFile.auto_profile_order:
935 profile_name = profile_name.strip()
936 self.access_points[profile_name] = self.confFile.get_profile(profile_name)
937 wep = None
938 if self.access_points[profile_name]['encrypted']:
939 wep = gtk.STOCK_DIALOG_AUTHENTICATION
940 if self.access_points[profile_name]['roaming']:
941 ap_name = self.access_points[profile_name]['essid'] + "\n" + ' Multiple APs'
942 else:
943 ap_name = self.access_points[profile_name]['essid'] + "\n" + self.access_points[profile_name]['bssid']
944 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'] ] )
945 # This is the first run (or, at least, no config file was present), so pop up the preferences window
946 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
947 self.confFile.remove_option('DEFAULT', 'new_file')
948 self.edit_preferences(self.preferences_button)
950 # Begin running radar_window in Gtk event loop.
952 #Parameters:
954 # nothing
956 #Returns:
958 # nothing
959 def main( self ):
960 gtk.main()
962 # Quit application.
964 #Parameters:
966 # 'widget' -- gtk.Widget - The widget sending the event.
968 #Returns:
970 # nothing
971 def destroy( self, widget = None):
972 if self.status_window:
973 self.status_window.destroy()
974 gtk.main_quit()
976 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
978 #Parameters:
980 # 'widget' -- gtk.Widget - The widget sending the event.
982 # 'data' -- tuple - list of arbitrary arguments (not used)
984 #Returns:
986 # boolean -- always return False (i.e. do not propigate the signal which called)
987 def delete_event( self, widget, data = None ):
988 # Let other threads know it is time to exit
989 self.exit_event.set()
990 # Wait for all other threads to exit before continuing
991 while threading.activeCount() > 1:
992 sleep(0.25)
993 self.destroy()
994 return False
996 # Update the current ip and essid shown to user.
998 #Parameters:
1000 # nothing
1002 #Returns:
1004 # nothing
1005 def update_network_info(self):
1006 if self.connection and self.connection.state:
1007 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()))
1008 else:
1009 self.current_network.set_text("Not Connected.")
1011 # Set the state of connect/disconnect buttons based on whether we have a connection.
1013 #Parameters:
1015 # nothing
1017 #Returns:
1019 # nothing
1020 def update_connect_buttons(self):
1021 if self.connection and self.connection.state:
1022 self.connect_button.hide()
1023 self.disconnect_button.show()
1024 else:
1025 self.disconnect_button.hide()
1026 self.connect_button.show()
1028 # Updates the on-screen profiles list.
1030 #Parameters:
1032 # 'ap' -- dictionary -- The AP found by scanning_thread.
1034 #Returns:
1036 # nothing
1037 def update_plist_items(self, ap):
1038 # Check for roaming profile.
1039 ap_name = make_section_name(ap['essid'], '')
1040 profile = self.confFile.get_profile(ap_name)
1041 prow_iter = self.get_row_by_ap(ap['essid'], ' Multiple APs')
1042 ap_display = ap['essid'] + "\n" + ' Multiple APs'
1043 if not profile:
1044 # Check for normal profile.
1045 ap_name = make_section_name(ap['essid'], ap['bssid'])
1046 profile = self.confFile.get_profile(ap_name)
1047 prow_iter = self.get_row_by_ap(ap['essid'], ap['bssid'])
1048 ap_display = ap['essid'] + "\n" + ap['bssid']
1049 if not profile:
1050 # No profile, so make a new one.
1051 profile = get_new_profile()
1052 wep = None
1053 if prow_iter != None:
1054 # the AP is in the list of APs on the screen
1055 # Set the 'known' values; False is default, overridden to True by self.access_points
1056 ap['known'] = self.access_points[ap_name]['known']
1057 self.pstore.set_value(prow_iter, 1, self.pixbuf_from_known(ap['known']))
1058 self.pstore.set_value(prow_iter, 2, ap['known'])
1059 self.pstore.set_value(prow_iter, 3, ap['available'])
1060 if ap['encrypted']:
1061 wep = gtk.STOCK_DIALOG_AUTHENTICATION
1062 self.pstore.set_value(prow_iter, 4, wep)
1063 self.pstore.set_value(prow_iter, 5, self.pixbuf_from_signal(self.access_points[ap_name]['signal']))
1064 self.pstore.set_value(prow_iter, 6, ap['mode'])
1065 self.pstore.set_value(prow_iter, 7, ap['protocol'])
1066 self.pstore.set_value(prow_iter, 8, self.access_points[ap_name]['channel'])
1067 else:
1068 # the AP is not in the list of APs on the screen
1069 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']])
1070 #print "update_plist_items: new ap", ap[ 'essid' ], ap['bssid']
1072 # Updates the record-keeping profiles list.
1074 #Parameters:
1076 # 'ap' -- dictionary -- The AP found by scanning_thread.
1078 #Returns:
1080 # nothing
1081 def update_ap_list(self, ap):
1082 # Check for roaming profile.
1083 ap_name = make_section_name(ap['essid'], '')
1084 profile = self.confFile.get_profile(ap_name)
1085 if not profile:
1086 # Check for normal profile.
1087 ap_name = make_section_name(ap['essid'], ap['bssid'])
1088 profile = self.confFile.get_profile(ap_name)
1089 if not profile:
1090 # No profile, so make a new one.
1091 profile = get_new_profile()
1092 if self.access_points.has_key(ap_name):
1093 # This AP has been configured and should be updated
1094 self.access_points[ap_name]['known'] = profile['known']
1095 self.access_points[ap_name]['available'] = ap['available']
1096 self.access_points[ap_name]['encrypted'] = ap['encrypted']
1097 self.access_points[ap_name]['mode'] = ap['mode']
1098 self.access_points[ap_name]['protocol'] = ap['protocol']
1099 if self.access_points[ap_name]['roaming']:
1100 # Roaming
1101 if self.access_points[ap_name]['bssid'] == ap['bssid']:
1102 # Same AP for this roaming profile
1103 self.access_points[ap_name]['signal'] = ap['signal']
1104 else:
1105 # Different AP
1106 if int(self.access_points[ap_name]['signal']) < int(ap['signal']):
1107 # Stronger signal with this AP, so promote it to preferred
1108 self.access_points[ap_name]['signal'] = ap['signal']
1109 self.access_points[ap_name]['channel'] = ap['channel']
1110 self.access_points[ap_name]['bssid'] = ap['bssid']
1111 else:
1112 # Not roaming
1113 self.access_points[ap_name]['signal'] = ap['signal']
1114 self.access_points[ap_name]['channel'] = ap['channel']
1115 else:
1116 # Not seen before, begin tracking it.
1117 self.access_points[ap_name] = profile
1119 # Updates the main user interface.
1121 #Parameters:
1123 # nothing
1125 #Returns:
1127 # boolean -- always return True
1128 def update_window(self):
1129 # Indicate to PyGtk that only one Gtk thread should run here
1130 gtk.gdk.threads_enter()
1131 self.update_network_info()
1132 self.update_connect_buttons()
1133 while True:
1134 # Get APs scanned by iwlist
1135 try:
1136 ap = self.apQueue.get_nowait()
1137 except Queue.Empty:
1138 break
1139 else:
1140 self.update_ap_list(ap)
1141 self.update_plist_items(ap)
1142 # Allow other Gtk threads to run
1143 gtk.gdk.threads_leave()
1144 return True
1146 # Return the proper icon for a value of known.
1148 #Parameters:
1150 # 'known' -- boolean - Whether the AP is known (i.e. configured)
1152 #Returns:
1154 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
1155 def pixbuf_from_known( self, known ):
1156 """ return the proper icon for value of known """
1157 if known:
1158 return self.known_profile_icon
1159 else:
1160 return self.unknown_profile_icon
1162 # Return an icon indicating the signal level.
1164 #Parameters:
1166 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
1168 #Returns:
1170 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
1171 def pixbuf_from_signal( self, signal ):
1172 signal = int( signal )
1173 # shift signal up by 80 to convert dBm scale to arbitrary scale
1174 if signal < 0: signal = signal + 80
1175 #print "signal level:", signal
1176 if signal < 3:
1177 return self.signal_none_pb
1178 elif signal < 12:
1179 return self.signal_low_pb
1180 elif signal < 20:
1181 return self.signal_barely_pb
1182 elif signal < 35:
1183 return self.signal_ok_pb
1184 elif signal >= 35:
1185 return self.signal_best_pb
1186 else:
1187 return None
1189 # Return row which holds specified ESSID and BSSID.
1191 #Parameters:
1193 # 'essid' -- string - ESSID to match
1195 # 'bssid' -- string - BSSID to match
1197 #Returns:
1199 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
1200 def get_row_by_ap( self, essid, bssid ):
1201 if bssid == "roaming":
1202 for row in self.pstore:
1203 if (row[0][:row[0].find("\n")] == essid):
1204 #print "roaming match:", row.iter, essid, bssid
1205 return row.iter
1206 else:
1207 for row in self.pstore:
1208 if (row[0] == essid + "\n" + bssid):
1209 #print "normal match:", row.iter, essid, bssid
1210 return row.iter
1211 return None
1213 # Enable/disable buttons based on the selected network.
1215 #Parameters:
1217 # 'widget' -- gtk.Widget - The widget sending the event.
1219 # 'data' -- tuple - list of arbitrary arguments (not used)
1221 #Returns:
1223 # nothing
1224 def on_network_selection( self, widget, data = None ):
1225 ( store, selected_iter ) = self.selected_network.get_selected()
1226 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
1227 # if no networks are selected, disable all buttons except New
1228 # (this occurs after a drag-and-drop)
1229 if selected_iter == None:
1230 self.edit_button.set_sensitive(False)
1231 self.delete_button.set_sensitive(False)
1232 self.connect_button.set_sensitive(False)
1233 return
1234 # enable/disable buttons
1235 self.connect_button.set_sensitive(True)
1236 if (store.get_value(selected_iter, 2) == True): # is selected network known?
1237 self.edit_button.set_sensitive(True)
1238 self.delete_button.set_sensitive(True)
1239 else:
1240 self.edit_button.set_sensitive(True)
1241 self.delete_button.set_sensitive(False)
1243 # Init and run the about dialog
1245 #Parameters:
1247 # 'widget' -- gtk.Widget - The widget sending the event.
1249 # 'data' -- tuple - list of arbitrary arguments (not used)
1251 #Returns:
1253 # nothing
1254 def show_about_info( self, widget, data=None ):
1255 about = about_dialog()
1256 about.run()
1257 about.destroy()
1259 # Init and run the preferences dialog
1261 #Parameters:
1263 # 'widget' -- gtk.Widget - The widget sending the event.
1265 # 'data' -- tuple - list of arbitrary arguments (not used)
1267 #Returns:
1269 # nothing
1270 def edit_preferences( self, widget, data=None ):
1271 # get raw strings from config file
1272 self.confFile.raw = True
1273 prefs = preferences_dialog( self, self.confFile )
1274 response = prefs.run()
1275 if response == int(gtk.RESPONSE_ACCEPT):
1276 prefs.save()
1277 prefs.destroy()
1278 # get cooked strings from config file
1279 self.confFile.raw = False
1281 # Respond to a request to create a new AP profile
1283 #Parameters:
1285 # 'widget' -- gtk.Widget - The widget sending the event.
1287 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1289 # 'data' -- tuple - list of arbitrary arguments (not used)
1291 #Returns:
1293 # boolean -- True if a profile was created and False if profile creation was canceled.
1294 def create_new_profile( self, widget, profile, data=None ):
1295 profile_editor = profile_dialog( self, profile )
1296 try:
1297 profile = profile_editor.run()
1298 except ValueError:
1299 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1300 del error_dlg
1301 return False
1302 finally:
1303 profile_editor.destroy()
1304 if profile:
1305 (store, selected_iter) = self.plist.get_selection().get_selected()
1306 if selected_iter is not None:
1307 store.remove(selected_iter)
1308 if profile['roaming']:
1309 apname = make_section_name(profile['essid'], '')
1310 else:
1311 apname = make_section_name(profile['essid'], profile['bssid'])
1312 # Check that the ap does not exist already
1313 if apname in self.confFile.profiles():
1314 error_dlg = ErrorDialog(None, "A profile for %s already exists" % (apname))
1315 del error_dlg
1316 # try again
1317 self.access_points[ apname ] = profile
1318 self.confFile.set_section( apname, profile )
1319 # if it is not in the auto_profile_order add it
1320 if apname not in self.confFile.auto_profile_order:
1321 self.confFile.auto_profile_order.insert(0, apname)
1322 # add to the store
1323 wep = None
1324 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
1325 try:
1326 self.confFile.write()
1327 except IOError, (error_number, error_str):
1328 if error_number == errno.ENOENT:
1329 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1330 del error_dlg
1331 else:
1332 raise IOError(error_number, error_str)
1333 # Add AP to the list displayed to user
1334 if profile['roaming']:
1335 ap_name = profile['essid'] + "\n" + ' Multiple APs'
1336 while True:
1337 prow_iter = self.get_row_by_ap(profile['essid'], 'roaming')
1338 if prow_iter:
1339 self.pstore.remove(prow_iter)
1340 else:
1341 break
1342 else:
1343 ap_name = profile['essid'] + "\n" + profile['bssid']
1344 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']])
1345 return True
1346 else:
1347 # Did not create new profile
1348 return False
1350 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1351 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1353 #Parameters:
1355 # 'widget' -- gtk.Widget - The widget sending the event.
1357 # 'data' -- tuple - list of arbitrary arguments (not used)
1359 #Returns:
1361 # nothing
1362 def edit_profile(self, widget, data=None):
1363 (store, selected_iter) = self.plist.get_selection().get_selected()
1364 if not selected_iter:
1365 # No AP is selected
1366 return
1367 (essid, bssid) = str(self.pstore.get_value(selected_iter, 0)).split("\n")
1368 if bssid == ' Multiple APs':
1369 # AP list says this is a roaming profile
1370 apname = make_section_name(essid, '')
1371 else:
1372 # AP list says this is NOT a roaming profile
1373 apname = make_section_name(essid, bssid)
1374 profile = self.confFile.get_profile(apname)
1375 if profile:
1376 # A profile was found in the config file
1377 profile['bssid'] = self.access_points[apname]['bssid']
1378 profile_editor = profile_dialog(self, profile)
1379 try:
1380 # try editing the profile
1381 edited_profile = profile_editor.run()
1382 except ValueError:
1383 error_dlg = ErrorDialog(None, "Cannot save empty ESSID")
1384 del error_dlg
1385 return False
1386 finally:
1387 # Always remove profile editor window from screen
1388 profile_editor.destroy()
1389 if edited_profile:
1390 # A profile was returned by the editor
1391 old_index = None
1392 row = None
1393 if edited_profile['essid'] != profile['essid'] or edited_profile['bssid'] != profile['bssid'] or edited_profile['roaming'] != profile['roaming']:
1394 # ESSID, BSSID, or roaming was changed in profile editor
1395 try:
1396 self.commandQueue.put('pause')
1397 self.commandQueue.join()
1398 except Queue.Full:
1399 pass
1400 if profile['roaming']:
1401 # The old profile was a roaming profile
1402 old_ap = make_section_name(profile['essid'], '')
1403 else:
1404 # The old profile was NOT a roaming profile
1405 old_ap = make_section_name(profile['essid'], profile['bssid'])
1406 # Find where old profile was in auto order
1407 old_index = self.confFile.auto_profile_order.index(old_ap)
1408 # Remove old profile and get its place in AP list
1409 row = self.delete_profile(selected_iter, old_ap)
1410 self.confFile.remove_section(old_ap)
1411 try:
1412 # Add AP to the list displayed to user
1413 self.apQueue.put_nowait(edited_profile)
1414 self.commandQueue.put('scan')
1415 except Queue.Full:
1416 pass
1417 if edited_profile['roaming']:
1418 # New profile is a roaming profile
1419 apname = make_section_name(edited_profile['essid'], '')
1420 ap_display = edited_profile['essid'] + "\n" + ' Multiple APs'
1421 # Remove all other profiles that match the new profile ESSID
1422 while True:
1423 prow_iter = self.get_row_by_ap(edited_profile['essid'], 'roaming')
1424 if prow_iter:
1425 self.pstore.remove(prow_iter)
1426 else:
1427 break
1428 else:
1429 # New profile is NOT a roaming profile
1430 apname = make_section_name(edited_profile['essid'], edited_profile['bssid'])
1431 ap_display = edited_profile['essid'] + "\n" + edited_profile['bssid']
1432 # Insert the new profile in the same position as the one being replaced
1433 if old_index != None:
1434 # Old profile was in auto order list
1435 self.confFile.auto_profile_order.insert(old_index, apname)
1436 if ((row is not None) and (self.pstore.iter_is_valid(row))):
1437 self.pstore.insert_before(row, [ap_display, None, None, None, None, None, None, None, None])
1438 self.access_points[apname] = edited_profile
1439 self.confFile.set_section(apname, edited_profile)
1440 try:
1441 # Save updated profile to config file
1442 self.confFile.write()
1443 except IOError, (error_number, error_str):
1444 if error_number == errno.ENOENT:
1445 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1446 del error_dlg
1447 else:
1448 raise IOError(error_number, error_str)
1449 else:
1450 # The AP does not already have a profile
1451 profile = get_new_profile()
1452 ( profile['essid'], profile['bssid'] ) = store.get_value( selected_iter, 0 ).split("\n")
1453 self.create_new_profile( widget, profile, data )
1455 # Delete an AP profile (i.e. make profile unknown)
1457 #Parameters:
1459 # 'selected_iter' -- gtk.TreeIter - The selected row.
1461 # 'apname' -- string - The configuration file section to remove
1463 #Returns:
1465 # gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
1466 def delete_profile(self, selected_iter, apname):
1467 # Remove it
1468 del self.access_points[apname]
1469 self.confFile.remove_section(apname)
1470 self.logger.info(apname)
1471 if apname in self.confFile.auto_profile_order:
1472 self.confFile.auto_profile_order.remove(apname)
1473 self.pstore.remove(selected_iter)
1474 # Let's save our current state
1475 self.update_auto_profile_order()
1476 try:
1477 self.confFile.write()
1478 except IOError, (error_number, error_str):
1479 if error_number == errno.ENOENT:
1480 error_dlg = ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str))
1481 del error_dlg
1482 else:
1483 raise IOError(error_number, error_str)
1484 return selected_iter
1486 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1487 # Check with user first.
1489 #Parameters:
1491 # 'widget' -- gtk.Widget - The widget sending the event.
1493 # 'data' -- tuple - list of arbitrary arguments (not used)
1495 #Returns:
1497 # nothing
1498 def delete_profile_with_check(self, widget, data=None):
1499 (store, selected_iter) = self.plist.get_selection().get_selected()
1500 if not selected_iter: return
1501 (essid, bssid) = store.get_value(selected_iter, 0).split("\n")
1502 if bssid == ' Multiple APs':
1503 apname = make_section_name(essid, '')
1504 else:
1505 apname = make_section_name(essid, bssid)
1506 profile = self.confFile.get_profile(apname)
1507 if profile['roaming']:
1508 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, ))
1509 else:
1510 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))
1511 known = store.get_value( selected_iter, 1 )
1512 if not known: return
1513 res = dlg.run()
1514 dlg.destroy()
1515 del dlg
1516 if res == gtk.RESPONSE_NO:
1517 return
1518 self.delete_profile(selected_iter, apname)
1520 # Respond to a request to connect to an AP.
1522 #Parameters:
1524 # 'widget' -- gtk.Widget - The widget sending the event.
1526 # 'profile' -- dictionary - The AP profile to which to connect.
1528 # 'data' -- tuple - list of arbitrary arguments (not used)
1530 #Returns:
1532 # nothing
1533 def connect_profile( self, widget, profile, data=None ):
1534 ( store, selected_iter ) = self.plist.get_selection().get_selected()
1535 if not selected_iter: return
1536 ( essid, bssid ) = store.get_value( selected_iter, 0 ).split("\n")
1537 known = store.get_value( selected_iter, 2 )
1538 if not known:
1539 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?")
1540 res = dlg.run()
1541 dlg.destroy()
1542 del dlg
1543 if res == gtk.RESPONSE_NO:
1544 return
1545 profile = get_new_profile()
1546 profile['essid'] = essid
1547 profile['bssid'] = bssid
1548 if not self.create_new_profile( widget, profile, data ):
1549 return
1550 else:
1551 # Check for roaming profile.
1552 ap_name = make_section_name(essid, '')
1553 profile = self.confFile.get_profile(ap_name)
1554 if not profile:
1555 # Check for normal profile.
1556 ap_name = make_section_name(essid, bssid)
1557 profile = self.confFile.get_profile(ap_name)
1558 if not profile:
1559 # No configured profile
1560 return
1561 profile['bssid'] = self.access_points[ap_name]['bssid']
1562 profile['channel'] = self.access_points[ap_name]['channel']
1563 self.connection.connect_to_network(profile, self.status_window)
1565 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1567 #Parameters:
1569 # 'widget' -- gtk.Widget - The widget sending the event.
1571 # 'data' -- tuple - list of arbitrary arguments (not used)
1573 #Returns:
1575 # nothing
1576 def disconnect_profile( self, widget, data=None ):
1577 if data == "cancel":
1578 self.status_window.update_message("Canceling connection...")
1579 if sys.modules.has_key("gtk"):
1580 while gtk.events_pending():
1581 gtk.main_iteration(False)
1582 sleep(1)
1583 self.connection.disconnect_interface()
1585 # Update the config file auto profile order from the on-screen order
1587 #Parameters:
1589 # 'widget' -- gtk.Widget - The widget sending the event.
1591 # 'data' -- tuple - list of arbitrary arguments (not used)
1593 # 'data2' -- tuple - list of arbitrary arguments (not used)
1595 #Returns:
1597 # nothing
1598 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
1599 # recreate the auto_profile_order
1600 auto_profile_order = []
1601 piter = self.pstore.get_iter_first()
1602 while piter:
1603 # only if it's known
1604 if self.pstore.get_value(piter, 2) == True:
1605 (essid, bssid) = self.pstore.get_value(piter, 0).split("\n")
1606 if bssid == ' Multiple APs':
1607 apname = make_section_name(essid, '')
1608 else:
1609 apname = make_section_name(essid, bssid)
1610 auto_profile_order.append(apname)
1611 piter = self.pstore.iter_next(piter)
1612 self.confFile.auto_profile_order = auto_profile_order
1613 try:
1614 self.confFile.write()
1615 except IOError, (error_number, error_str):
1616 if error_number == errno.ENOENT:
1617 error_dlg = ErrorDialog( self.window, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
1618 del error_dlg
1619 else:
1620 raise IOError(error_number, error_str)
1623 # Button to allow user to choose a file and put value into specified gtk.Entry
1624 class file_browse_button(gtk.Button):
1625 # Create a button to simulate a File/Open
1627 #Parameters:
1629 # 'parent' -- gtk.Object -- Usually, the calling window.
1631 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1633 #Returns:
1635 # file_browse_button instance
1636 def __init__( self, parent, entry ):
1637 self.parent_window = parent
1638 self.entry = entry
1639 gtk.Button.__init__(self, "Browse", None)
1640 #self.
1641 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)
1642 self.connect("clicked", self.browse_files)
1644 # Show filechooser dialog and get user selection
1646 #Parameters:
1648 # 'widget' -- gtk.Widget -- The widget sending the event.
1650 #Returns:
1652 # nothing
1654 #NOTES:
1656 # updates entry value
1658 def browse_files( self, widget ):
1659 self.browser_dialog.set_filename(self.entry.get_text())
1660 self.browser_dialog.run()
1661 filename = self.browser_dialog.get_filename()
1662 if filename:
1663 self.entry.set_text(filename)
1664 self.browser_dialog.destroy()
1667 # Simple dialog to report an error to the user.
1668 class ErrorDialog:
1669 # Create a new ErrorDialog.
1671 #Parameters:
1673 # 'parent' -- gtk.Object - Usually, the calling window.
1675 # 'message' -- string - The message to display to the user.
1677 #Returns:
1679 # ErrorDialog instance
1680 def __init__( self, parent, message ):
1681 dialog = gtk.MessageDialog( parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message )
1682 dialog.run()
1683 dialog.destroy()
1684 del dialog
1687 # The preferences dialog. Edits non-profile sections of the config file.
1688 class preferences_dialog:
1689 # Create a new preferences_dialog.
1691 #Parameters:
1693 # 'parent' -- gtk.Object - Usually, the calling window.
1695 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1697 #Returns:
1699 # preferences_dialog instance
1700 def __init__( self, parent, confFile ):
1701 global wifi_radar_icon
1702 self.parent = parent
1703 self.confFile = confFile
1704 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
1705 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1706 ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT ) )
1707 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1708 self.dialog.set_icon( icon )
1709 self.dialog.set_resizable( True )
1710 self.dialog.set_transient_for( self.parent.window )
1711 self.tooltips = gtk.Tooltips()
1713 # set up preferences widgets
1715 # build everything in a tabbed notebook
1716 self.prefs_notebook = gtk.Notebook()
1718 ### General tab
1719 self.general_page = gtk.VBox()
1720 # auto detect wireless device
1721 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
1723 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
1724 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
1725 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
1726 self.general_page.pack_start(self.w_auto_detect, False, False, 5)
1728 # network interface selecter
1729 self.w_interface = gtk.combo_box_entry_new_text()
1730 try:
1731 iwconfig_info = Popen([self.confFile.get_opt('DEFAULT.iwconfig_command')], stdout=PIPE, stderr=STDOUT).stdout
1732 except OSError, (errno, strerror):
1733 if errno == 2:
1734 logger.critical("iwconfig command not found, please set this in the preferences.")
1735 iwconfig_info = ""
1736 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
1737 for device in wireless_devices:
1738 if device != self.confFile.get_opt('DEFAULT.interface'):
1739 self.w_interface.append_text(device)
1740 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
1741 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
1742 self.w_interface.set_active(0)
1743 self.w_interface_label = gtk.Label("Wireless device")
1744 self.w_hbox1 = gtk.HBox(False, 0)
1745 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
1746 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
1747 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
1748 self.general_page.pack_start(self.w_hbox1, False, False, 5)
1750 # scan timeout (spin button of integers from 1 to 100)
1751 #self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,0 )
1752 #self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
1753 #self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
1754 #self.w_scan_timeout.set_numeric(True)
1755 #self.w_scan_timeout.set_snap_to_ticks(True)
1756 #self.w_scan_timeout.set_wrap(False)
1757 #self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
1758 #self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
1759 #self.w_hbox2 = gtk.HBox(False, 0)
1760 #self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
1761 #self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
1762 #self.general_page.pack_start(self.w_hbox2, False, False, 5)
1764 # speak up
1765 self.w_speak_up = gtk.CheckButton("Use speak-up")
1766 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
1767 self.w_speak_up.connect("toggled", self.toggle_speak)
1768 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
1769 self.general_page.pack_start(self.w_speak_up, False, False, 5)
1771 # speak up command
1772 self.w_speak_cmd = gtk.Entry()
1773 self.w_speak_cmd.set_width_chars(16)
1774 self.tooltips.set_tip(self.w_speak_cmd, "The command to use to speak feedback")
1775 self.w_speak_cmd.set_text(self.confFile.get_opt('DEFAULT.speak_command'))
1776 self.w_speak_cmd_label = gtk.Label("Speak Command")
1777 self.w_speak_cmd_button = file_browse_button(self.dialog, self.w_speak_cmd)
1778 self.w_speak_cmd_button.set_sensitive(self.w_speak_up.get_active())
1779 self.w_hbox3 = gtk.HBox(False, 0)
1780 self.w_hbox3.pack_start(self.w_speak_cmd_label, True, False, 5)
1781 self.w_hbox3.pack_start(self.w_speak_cmd, False, False, 0)
1782 self.w_hbox3.pack_start(self.w_speak_cmd_button, False, False, 0)
1783 self.w_speak_cmd.set_sensitive(self.w_speak_up.get_active())
1784 self.general_page.pack_start(self.w_hbox3, False, False, 5)
1786 # commit required
1787 self.w_commit_required = gtk.CheckButton("Commit required")
1788 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
1789 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
1790 self.general_page.pack_start(self.w_commit_required, False, False, 5)
1792 # ifup required
1793 self.w_ifup_required = gtk.CheckButton("Ifup required")
1794 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
1795 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
1796 self.general_page.pack_start(self.w_ifup_required, False, False, 5)
1798 self.prefs_notebook.append_page(self.general_page, gtk.Label("General"))
1799 ### End of General tab
1801 ### Advanced tab
1802 # table to use for layout of following command configurations
1803 self.cmds_table = gtk.Table()
1805 # ifconfig command
1806 self.ifconfig_cmd = gtk.Entry()
1807 self.ifconfig_cmd.set_width_chars(32)
1808 self.tooltips.set_tip(self.ifconfig_cmd, "The command to use to configure the network card")
1809 self.ifconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.ifconfig_command'))
1810 self.ifconfig_cmd_label = gtk.Label("Network interface configure command")
1811 self.ifconfig_cmd_button = file_browse_button(self.dialog, self.ifconfig_cmd)
1812 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1813 self.cmds_table.attach(self.ifconfig_cmd_label, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1814 self.cmds_table.attach(self.ifconfig_cmd, 2, 3, 1, 2, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1815 self.cmds_table.attach(self.ifconfig_cmd_button, 3, 4, 1, 2, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1817 # iwconfig command
1818 self.iwconfig_cmd = gtk.Entry()
1819 self.iwconfig_cmd.set_width_chars(32)
1820 self.tooltips.set_tip(self.iwconfig_cmd, "The command to use to configure the wireless connection")
1821 self.iwconfig_cmd.set_text(self.confFile.get_opt('DEFAULT.iwconfig_command'))
1822 self.iwconfig_cmd_label = gtk.Label("Wireless connection configure command")
1823 self.iwconfig_cmd_button = file_browse_button(self.dialog, self.iwconfig_cmd)
1824 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1825 self.cmds_table.attach(self.iwconfig_cmd_label, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1826 self.cmds_table.attach(self.iwconfig_cmd, 2, 3, 2, 3, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1827 self.cmds_table.attach(self.iwconfig_cmd_button, 3, 4, 2, 3, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1829 # iwlist command
1830 self.iwlist_cmd = gtk.Entry()
1831 self.iwlist_cmd.set_width_chars(32)
1832 self.tooltips.set_tip(self.iwlist_cmd, "The command to use to scan for access points")
1833 self.iwlist_cmd.set_text(self.confFile.get_opt('DEFAULT.iwlist_command'))
1834 self.iwlist_cmd_label = gtk.Label("Wireless scanning command")
1835 self.iwlist_cmd_button = file_browse_button(self.dialog, self.iwlist_cmd)
1836 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1837 self.cmds_table.attach(self.iwlist_cmd_label, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1838 self.cmds_table.attach(self.iwlist_cmd, 2, 3, 3, 4, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1839 self.cmds_table.attach(self.iwlist_cmd_button, 3, 4, 3, 4, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1841 # route command
1842 self.route_cmd = gtk.Entry()
1843 self.route_cmd.set_width_chars(32)
1844 self.tooltips.set_tip(self.route_cmd, "The command to use to configure the network routing")
1845 self.route_cmd.set_text(self.confFile.get_opt('DEFAULT.route_command'))
1846 self.route_cmd_label = gtk.Label("Network route configure command")
1847 self.route_cmd_button = file_browse_button(self.dialog, self.route_cmd)
1848 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1849 self.cmds_table.attach(self.route_cmd_label, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1850 self.cmds_table.attach(self.route_cmd, 2, 3, 4, 5, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1851 self.cmds_table.attach(self.route_cmd_button, 3, 4, 4, 5, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1853 # log file
1854 self.logfile_entry = gtk.Entry()
1855 self.logfile_entry.set_width_chars(32)
1856 self.tooltips.set_tip(self.logfile_entry, "The file in which to save logging info")
1857 self.logfile_entry.set_text(self.confFile.get_opt('DEFAULT.logfile'))
1858 self.logfile_label = gtk.Label("Log file")
1859 self.logfile_button = file_browse_button(self.dialog, self.logfile_entry)
1860 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1861 self.cmds_table.attach(self.logfile_label, 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1862 self.cmds_table.attach(self.logfile_entry, 2, 3, 5, 6, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1863 self.cmds_table.attach(self.logfile_button, 3, 4, 5, 6, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1865 # log level (spin button of integers from 0 to 50 by 5's)
1866 self.loglevel = gtk.SpinButton(gtk.Adjustment(self.confFile.get_opt_as_int('DEFAULT.loglevel'), 1, 50, 5, 5, 0), 1, 0)
1867 self.loglevel.set_update_policy(gtk.UPDATE_IF_VALID)
1868 self.loglevel.set_numeric(True)
1869 self.loglevel.set_snap_to_ticks(True)
1870 self.loglevel.set_wrap(False)
1871 self.tooltips.set_tip(self.loglevel, "How much detail to save in log file. Larger numbers provide less detail and smaller numbers, more detail.")
1872 self.loglevel.set_text(self.confFile.get_opt('DEFAULT.loglevel'))
1873 self.loglevel_label = gtk.Label("Log level")
1874 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
1875 self.cmds_table.attach(self.loglevel_label, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
1876 self.cmds_table.attach(self.loglevel, 2, 3, 6, 7, gtk.FILL|gtk.EXPAND, 0, 0, 0)
1878 self.prefs_notebook.append_page(self.cmds_table, gtk.Label("Advanced"))
1879 ### End of Advanced tab
1881 ### DHCP tab
1882 # table to use for layout of DHCP prefs
1883 self.dhcp_table = gtk.Table()
1885 self.dhcp_cmd = gtk.Entry()
1886 self.dhcp_cmd.set_width_chars(32)
1887 self.tooltips.set_tip(self.dhcp_cmd, "The command to use for automatic network configuration")
1888 self.dhcp_cmd.set_text(self.confFile.get_opt('DHCP.command'))
1889 self.dhcp_cmd_label = gtk.Label("Command")
1890 self.dhcp_cmd_button = file_browse_button(self.dialog, self.dhcp_cmd)
1891 self.dhcp_table.attach(self.dhcp_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1892 self.dhcp_table.attach(self.dhcp_cmd, 2, 3, 1, 2, True, False, 0, 0)
1893 self.dhcp_table.attach(self.dhcp_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1895 self.dhcp_args = gtk.Entry()
1896 self.dhcp_args.set_width_chars(32)
1897 self.tooltips.set_tip(self.dhcp_args, "The start-up arguments to the DHCP command")
1898 self.dhcp_args.set_text(self.confFile.get_opt('DHCP.args'))
1899 self.dhcp_args_label = gtk.Label("Arguments")
1900 self.dhcp_table.attach(self.dhcp_args_label, 1, 2, 2, 3, True, False, 5, 0)
1901 self.dhcp_table.attach(self.dhcp_args, 2, 3, 2, 3, True, False, 0, 0)
1903 self.dhcp_kill_args = gtk.Entry()
1904 self.dhcp_kill_args.set_width_chars(32)
1905 self.tooltips.set_tip(self.dhcp_kill_args, "The shutdown arguments to the DHCP command")
1906 self.dhcp_kill_args.set_text(self.confFile.get_opt('DHCP.kill_args'))
1907 self.dhcp_kill_args_label = gtk.Label("Kill arguments")
1908 self.dhcp_table.attach(self.dhcp_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1909 self.dhcp_table.attach(self.dhcp_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1911 self.dhcp_timeout = gtk.Entry()
1912 self.dhcp_timeout.set_width_chars(32)
1913 self.tooltips.set_tip(self.dhcp_timeout, "The amount of time DHCP will spend trying to connect")
1914 self.dhcp_timeout.set_text(self.confFile.get_opt('DHCP.timeout'))
1915 self.dhcp_timeout_label = gtk.Label("DHCP connect timeout (seconds)")
1916 self.dhcp_table.attach(self.dhcp_timeout_label, 1, 2, 4, 5, True, False, 5, 0)
1917 self.dhcp_table.attach(self.dhcp_timeout, 2, 3, 4, 5, True, False, 0, 0)
1919 self.dhcp_pidfile = gtk.Entry()
1920 self.dhcp_pidfile.set_width_chars(32)
1921 self.tooltips.set_tip(self.dhcp_pidfile, "The file DHCP uses to store its PID")
1922 self.dhcp_pidfile.set_text(self.confFile.get_opt('DHCP.pidfile'))
1923 self.dhcp_pidfile_label = gtk.Label("PID file")
1924 self.dhcp_table.attach(self.dhcp_pidfile_label, 1, 2, 5, 6, True, False, 5, 0)
1925 self.dhcp_table.attach(self.dhcp_pidfile, 2, 3, 5, 6, True, False, 0, 0)
1927 self.prefs_notebook.append_page(self.dhcp_table, gtk.Label("DHCP"))
1928 ### End of DHCP tab
1930 ### WPA tab
1931 # table to use for layout of DHCP prefs
1932 self.wpa_table = gtk.Table()
1934 self.wpa_cmd = gtk.Entry()
1935 self.wpa_cmd.set_width_chars(32)
1936 self.tooltips.set_tip(self.wpa_cmd, "The command to use for WPA encrypted connections")
1937 self.wpa_cmd.set_text(self.confFile.get_opt('WPA.command'))
1938 self.wpa_cmd_label = gtk.Label("Command")
1939 self.wpa_cmd_button = file_browse_button(self.dialog, self.wpa_cmd)
1940 self.wpa_table.attach(self.wpa_cmd_label, 1, 2, 1, 2, True, False, 5, 0)
1941 self.wpa_table.attach(self.wpa_cmd, 2, 3, 1, 2, True, False, 0, 0)
1942 self.wpa_table.attach(self.wpa_cmd_button, 3, 4, 1, 2, False, False, 0, 0)
1944 self.wpa_args = gtk.Entry()
1945 self.wpa_args.set_width_chars(32)
1946 self.tooltips.set_tip(self.wpa_args, "The start-up arguments to the WPA command")
1947 self.wpa_args.set_text(self.confFile.get_opt('WPA.args'))
1948 self.wpa_args_label = gtk.Label("Arguments")
1949 self.wpa_table.attach(self.wpa_args_label, 1, 2, 2, 3, True, False, 5, 0)
1950 self.wpa_table.attach(self.wpa_args, 2, 3, 2, 3, True, False, 0, 0)
1952 self.wpa_kill_args = gtk.Entry()
1953 self.wpa_kill_args.set_width_chars(32)
1954 self.tooltips.set_tip(self.wpa_kill_args, "The shutdown arguments to the DHCP command")
1955 self.wpa_kill_args.set_text(self.confFile.get_opt('WPA.kill_command'))
1956 self.wpa_kill_args_label = gtk.Label("Kill command")
1957 self.wpa_table.attach(self.wpa_kill_args_label, 1, 2, 3, 4, True, False, 5, 0)
1958 self.wpa_table.attach(self.wpa_kill_args, 2, 3, 3, 4, True, False, 0, 0)
1960 self.wpa_config = gtk.Entry()
1961 self.wpa_config.set_width_chars(32)
1962 self.tooltips.set_tip(self.wpa_config, "The WPA configuration file to use")
1963 self.wpa_config.set_text(self.confFile.get_opt('WPA.configuration'))
1964 self.wpa_config_label = gtk.Label("Configuration file")
1965 self.wpa_table.attach(self.wpa_config_label, 1, 2, 4, 5, True, False, 5, 0)
1966 self.wpa_table.attach(self.wpa_config, 2, 3, 4, 5, True, False, 0, 0)
1968 self.wpa_driver = gtk.Entry()
1969 self.wpa_driver.set_width_chars(32)
1970 self.tooltips.set_tip(self.wpa_driver, "The WPA driver to use")
1971 self.wpa_driver.set_text(self.confFile.get_opt('WPA.driver'))
1972 self.wpa_driver_label = gtk.Label("Driver")
1973 self.wpa_table.attach(self.wpa_driver_label, 1, 2, 5, 6, True, False, 5, 0)
1974 self.wpa_table.attach(self.wpa_driver, 2, 3, 5, 6, True, False, 0, 0)
1976 self.wpa_pidfile = gtk.Entry()
1977 self.wpa_pidfile.set_width_chars(32)
1978 self.tooltips.set_tip(self.wpa_pidfile, "The file WPA uses to store its PID")
1979 self.wpa_pidfile.set_text(self.confFile.get_opt('WPA.pidfile'))
1980 self.wpa_pidfile_label = gtk.Label("PID file")
1981 self.wpa_table.attach(self.wpa_pidfile_label, 1, 2, 6, 7, True, False, 5, 0)
1982 self.wpa_table.attach(self.wpa_pidfile, 2, 3, 6, 7, True, False, 0, 0)
1984 self.prefs_notebook.append_page(self.wpa_table, gtk.Label("WPA"))
1985 ### End of WPA tab
1987 self.dialog.vbox.pack_start(self.prefs_notebook, False, False, 5)
1989 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1991 #Parameters:
1993 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1995 # 'data' -- tuple - list of arbitrary arguments (not used)
1997 #Returns:
1999 # nothing
2000 def toggle_auto_detect(self, auto_detect_toggle, data=None):
2001 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
2003 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
2005 #Parameters:
2007 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2009 # 'data' -- tuple - list of arbitrary arguments (not used)
2011 #Returns:
2013 # nothing
2014 def toggle_speak(self, speak_toggle, data=None):
2015 self.w_speak_cmd.set_sensitive(speak_toggle.get_active())
2016 self.w_speak_cmd_button.set_sensitive(speak_toggle.get_active())
2018 # Display preferences dialog and operate until canceled or okayed.
2020 #Parameters:
2022 # nothing
2024 #Returns:
2026 # integer -- gtk response ID
2027 def run(self):
2028 self.dialog.show_all()
2029 return self.dialog.run()
2031 # Write updated values to config file.
2033 #Parameters:
2035 # nothing
2037 #Returns:
2039 # nothing
2040 def save(self):
2041 if self.w_auto_detect.get_active():
2042 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
2043 else:
2044 interface = "auto_detect"
2045 if self.w_interface.get_active_text() != "":
2046 interface = self.w_interface.get_active_text()
2047 self.confFile.set_opt('DEFAULT.interface', interface)
2048 #self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
2049 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
2050 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
2051 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
2052 self.confFile.set_opt('DEFAULT.speak_command', self.w_speak_cmd.get_text())
2053 self.confFile.set_opt('DEFAULT.ifconfig_command', self.ifconfig_cmd.get_text())
2054 self.confFile.set_opt('DEFAULT.iwconfig_command', self.iwconfig_cmd.get_text())
2055 self.confFile.set_opt('DEFAULT.iwlist_command', self.iwlist_cmd.get_text())
2056 self.confFile.set_opt('DEFAULT.route_command', self.route_cmd.get_text())
2057 self.confFile.set_opt('DEFAULT.logfile', self.logfile_entry.get_text())
2058 self.confFile.set_int_opt('DEFAULT.loglevel', int(self.loglevel.get_value()))
2059 self.confFile.set_opt('DHCP.command', self.dhcp_cmd.get_text())
2060 self.confFile.set_opt('DHCP.args', self.dhcp_args.get_text())
2061 self.confFile.set_opt('DHCP.kill_args', self.dhcp_kill_args.get_text())
2062 self.confFile.set_opt('DHCP.timeout', self.dhcp_timeout.get_text())
2063 self.confFile.set_opt('DHCP.pidfile', self.dhcp_pidfile.get_text())
2064 self.confFile.set_opt('WPA.command', self.wpa_cmd.get_text())
2065 self.confFile.set_opt('WPA.args', self.wpa_args.get_text())
2066 self.confFile.set_opt('WPA.kill_command', self.wpa_kill_args.get_text())
2067 self.confFile.set_opt('WPA.configuration', self.wpa_config.get_text())
2068 self.confFile.set_opt('WPA.driver', self.wpa_driver.get_text())
2069 self.confFile.set_opt('WPA.pidfile', self.wpa_pidfile.get_text())
2070 try:
2071 self.confFile.write()
2072 except IOError, (error_number, error_str):
2073 if error_number == errno.ENOENT:
2074 error_dlg = ErrorDialog( self.dialog, "Could not save configuration file:\n%s\n\n%s" % (self.confFile.filename, error_str) )
2075 del error_dlg
2076 else:
2077 raise IOError(error_number, error_str)
2079 # Remove preferences window.
2081 #Parameters:
2083 # nothing
2085 #Returns:
2087 # nothing
2088 def destroy(self):
2089 self.dialog.destroy()
2090 del self.dialog
2093 # Edit and return an AP profile.
2094 class profile_dialog:
2095 # Create a new profile_dialog.
2097 #Parameters:
2099 # 'parent' -- gtk.Object - Usually, the calling window.
2101 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
2103 #Returns:
2105 # profile_dialog instance
2106 def __init__( self, parent, profile ):
2107 global wifi_radar_icon
2109 # Labels
2110 self.WIFI_SET_LABEL = "WiFi Options"
2111 self.USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
2112 self.USE_IP_LABEL = "Manual network configuration"
2113 self.USE_WPA_LABEL = "Use WPA"
2114 self.NO_WPA_LABEL = "No WPA"
2115 self.CON_PP_LABEL = "Connection Commands"
2116 self.DIS_PP_LABEL = "Disconnection Commands"
2118 self.parent = parent
2119 self.profile = profile.copy()
2120 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
2121 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
2122 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
2123 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
2124 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
2125 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
2126 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2127 self.dialog.set_icon( icon )
2128 self.dialog.set_resizable( False )
2129 self.dialog.set_transient_for( self.parent.window )
2130 #self.dialog.set_size_request( 400, 400 )
2131 #################
2132 self.tooltips = gtk.Tooltips()
2134 general_table = gtk.Table()
2135 general_table.set_row_spacings(3)
2136 general_table.set_col_spacings(3)
2137 # The essid labels
2138 general_table.attach(gtk.Label('Network Name'), 0, 1, 0, 1)
2139 # The essid textboxes
2140 self.essid_entry = gtk.Entry(32)
2141 self.essid_entry.set_text(self.profile['essid'])
2142 general_table.attach(self.essid_entry, 1, 2, 0, 1)
2143 # Add the essid table to the dialog
2144 self.dialog.vbox.pack_start(general_table, True, True, 5)
2145 self.tooltips.set_tip(self.essid_entry, "The name of the network with which to connect.")
2147 # The bssid labels
2148 general_table.attach(gtk.Label('Network Address'), 0, 1, 1, 2)
2149 # The bssid textboxes
2150 self.bssid_entry = gtk.Entry(32)
2151 self.bssid_entry.set_text(self.profile['bssid'])
2152 self.bssid_entry.set_sensitive(not self.profile['roaming'])
2153 # Add the bssid table to the dialog
2154 general_table.attach(self.bssid_entry, 1, 2, 1, 2)
2155 self.tooltips.set_tip(self.bssid_entry, "The address of the network with which to connect.")
2156 # Add the roaming checkbox
2157 self.roaming_cb = gtk.CheckButton('Roaming')
2158 self.roaming_cb.set_active(self.profile['roaming'])
2159 self.roaming_cb.connect("toggled", self.toggle_roaming)
2160 general_table.attach(self.roaming_cb, 1, 2, 2, 3)
2161 self.tooltips.set_tip(self.roaming_cb, "Use the AP in this network which provides strongest signal?")
2162 # create the WiFi expander
2163 self.wifi_expander = gtk.Expander( self.WIFI_SET_LABEL )
2164 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2165 wifi_table = gtk.Table( 4, 2, False )
2166 wifi_table.set_row_spacings( 3 )
2167 wifi_table.set_col_spacings( 3 )
2168 # The WiFi labels
2169 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
2170 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
2171 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
2172 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
2173 # The WiFi text boxes
2174 self.mode_combo = gtk.combo_box_new_text()
2175 for mode in self.WIFI_MODES:
2176 self.mode_combo.append_text( mode )
2177 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
2178 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
2179 self.tooltips.set_tip(self.mode_combo, "Method to use for connection. You probably want auto mode.")
2180 self.channel_combo = gtk.combo_box_new_text()
2181 for channel in self.WIFI_CHANNELS:
2182 self.channel_combo.append_text( channel )
2183 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
2184 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
2185 self.tooltips.set_tip(self.channel_combo, "Channel the network uses. You probably want auto mode.")
2187 self.key_entry = gtk.Entry( 64 )
2188 self.key_entry.set_text( self.profile['key'] )
2189 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
2190 self.tooltips.set_tip(self.key_entry, "WEP key: Plain language or hex string to use for encrypted communication with the network.")
2192 self.security_combo = gtk.combo_box_new_text()
2193 for security in self.WIFI_SECURITY:
2194 self.security_combo.append_text( security )
2195 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
2196 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
2197 self.tooltips.set_tip(self.security_combo, "Use Open to allow unencrypted communication and Restricted to force encrypted-only communication.")
2198 # Add the wifi table to the expander
2199 self.wifi_expander.add( wifi_table )
2200 # Add the expander to the dialog
2201 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
2203 # create the wpa expander
2204 self.wpa_expander = gtk.Expander( self.NO_WPA_LABEL )
2205 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
2206 wpa_table = gtk.Table( 1, 2, False )
2207 wpa_table.set_row_spacings( 3 )
2208 wpa_table.set_col_spacings( 3 )
2209 # The labels
2210 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
2211 # The text boxes
2212 self.wpa_driver_entry = gtk.Entry()
2213 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
2214 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
2215 # Add the wpa table to the expander
2216 self.wpa_expander.add( wpa_table )
2217 self.wpa_expander.set_expanded(self.profile['use_wpa'])
2218 # Add the expander to the dialog
2219 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
2221 # create the dhcp expander
2222 self.dhcp_expander = gtk.Expander( self.USE_DHCP_LABEL )
2223 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2224 self.dhcp_expander.set_expanded(not self.profile['use_dhcp'])
2225 ip_table = gtk.Table( 6, 2, False )
2226 ip_table.set_row_spacings( 3 )
2227 ip_table.set_col_spacings( 3 )
2228 # The IP labels
2229 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
2230 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
2231 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
2232 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
2233 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
2234 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
2235 # The IP text boxes
2236 self.ip_entry = gtk.Entry( 15 )
2237 self.ip_entry.set_text( self.profile['ip'] )
2238 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
2239 self.netmask_entry = gtk.Entry( 15 )
2240 self.netmask_entry.set_text( self.profile['netmask'] )
2241 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
2242 self.gw_entry = gtk.Entry( 15 )
2243 self.gw_entry.set_text( self.profile['gateway'] )
2244 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
2245 self.domain_entry = gtk.Entry( 32 )
2246 self.domain_entry.set_text( self.profile['domain'] )
2247 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
2248 self.dns1_entry = gtk.Entry( 15 )
2249 self.dns1_entry.set_text( self.profile['dns1'] )
2250 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
2251 self.dns2_entry = gtk.Entry( 15 )
2252 self.dns2_entry.set_text( self.profile['dns2'] )
2253 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
2254 # Add the ip table to the expander
2255 self.dhcp_expander.add( ip_table )
2256 # Add the expander to the dialog
2257 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
2259 # create the connection-building postpre expander
2260 self.con_pp_expander = gtk.Expander( self.CON_PP_LABEL )
2261 con_pp_table = gtk.Table( 2, 2, False )
2262 con_pp_table.set_row_spacings( 3 )
2263 con_pp_table.set_col_spacings( 3 )
2264 # The labels
2265 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2266 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2267 # The text boxes
2268 self.con_prescript_entry = gtk.Entry()
2269 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
2270 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
2271 self.tooltips.set_tip(self.con_prescript_entry, "The local command to execute before trying to connect to the network.")
2272 self.con_postscript_entry = gtk.Entry()
2273 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
2274 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
2275 self.tooltips.set_tip(self.con_postscript_entry, "The local command to execute after connecting to the network.")
2276 # Add the pp table to the expander
2277 self.con_pp_expander.add( con_pp_table )
2278 # Add the expander to the dialog
2279 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
2281 # create the disconnection postpre expander
2282 self.dis_pp_expander = gtk.Expander( self.DIS_PP_LABEL )
2283 dis_pp_table = gtk.Table( 2, 2, False )
2284 dis_pp_table.set_row_spacings( 3 )
2285 dis_pp_table.set_col_spacings( 3 )
2286 # The labels
2287 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2288 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2289 # The text boxes
2290 self.dis_prescript_entry = gtk.Entry()
2291 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
2292 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
2293 self.tooltips.set_tip(self.dis_prescript_entry, "The local command to execute before disconnecting from the network.")
2294 self.dis_postscript_entry = gtk.Entry()
2295 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
2296 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
2297 self.tooltips.set_tip(self.dis_postscript_entry, "The local command to execute after disconnecting from the network.")
2298 # Add the pp table to the expander
2299 self.dis_pp_expander.add( dis_pp_table )
2300 # Add the expander to the dialog
2301 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
2303 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
2305 #Parameters:
2307 # nothing
2309 #Returns:
2311 # dictionary or None -- a profile, or None on cancel
2313 #NOTES:
2315 # Raises ValueError if an attempt is made to save an ESSID with no name.
2316 def run( self ):
2317 self.dialog.show_all()
2318 if self.dialog.run():
2319 if self.essid_entry.get_text().strip() == "":
2320 raise ValueError
2321 self.profile['known'] = True
2322 self.profile['essid'] = self.essid_entry.get_text().strip()
2323 if self.roaming_cb.get_active():
2324 self.profile['bssid'] = ''
2325 else:
2326 self.profile['bssid'] = self.bssid_entry.get_text().strip()
2327 self.profile['roaming'] = self.roaming_cb.get_active()
2328 self.profile['key'] = self.key_entry.get_text().strip()
2329 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
2330 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
2331 self.profile['encrypted'] = ( self.profile['security'] != '' )
2332 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
2333 self.profile['protocol'] = 'g'
2334 self.profile['available'] = ( self.profile['signal'] > 0 )
2335 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
2336 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
2337 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
2338 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
2339 # wpa
2340 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
2341 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
2342 # dhcp
2343 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
2344 self.profile['ip'] = self.ip_entry.get_text().strip()
2345 self.profile['netmask'] = self.netmask_entry.get_text().strip()
2346 self.profile['gateway'] = self.gw_entry.get_text().strip()
2347 self.profile['domain'] = self.domain_entry.get_text().strip()
2348 self.profile['dns1'] = self.dns1_entry.get_text().strip()
2349 self.profile['dns2'] = self.dns2_entry.get_text().strip()
2350 return self.profile
2351 return None
2353 # Remove profile dialog.
2355 #Parameters:
2357 # nothing
2359 #Returns:
2361 # nothing
2362 def destroy( self ):
2363 self.dialog.destroy()
2364 del self.dialog
2366 # Respond to roaming checkbox toggle by activating/de-activating the BSSID text entry.
2368 #Parameters:
2370 # 'roaming_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2372 # 'data' -- tuple - list of arbitrary arguments (not used)
2374 #Returns:
2376 # nothing
2377 def toggle_roaming(self, roaming_toggle, data=None):
2378 self.bssid_entry.set_sensitive(not roaming_toggle.get_active())
2380 # Respond to expanding/hiding IP segment.
2382 #Parameters:
2384 # 'widget' -- gtk.Widget - The widget sending the event.
2386 # 'data' -- tuple - List of arbitrary arguments (not used)
2388 #Returns:
2390 # nothing
2391 def toggle_use_dhcp( self, widget, data = None ):
2392 expanded = self.dhcp_expander.get_expanded()
2393 if expanded:
2394 self.dhcp_expander.set_label( self.USE_IP_LABEL )
2395 else:
2396 self.dhcp_expander.set_label( self.USE_DHCP_LABEL )
2398 # Respond to expanding/hiding WPA segment.
2400 #Parameters:
2402 # 'widget' -- gtk.Widget - The widget sending the event.
2404 # 'data' -- tuple - List of arbitrary arguments (not used)
2406 #Returns:
2408 # nothing
2409 def toggle_use_wpa( self, widget, data = None ):
2410 expanded = self.wpa_expander.get_expanded()
2411 if expanded:
2412 self.wpa_expander.set_label( self.USE_WPA_LABEL )
2413 else:
2414 self.wpa_expander.set_label( self.NO_WPA_LABEL )
2416 # Return the index where item matches a cell in array.
2418 #Parameters:
2420 # 'item' -- string - Item to find in array
2422 # 'array' -- list - List in which to find match.
2424 #Returns:
2426 # integer - 0 (no match) or higher (index of match)
2427 def get_array_index( self, item, array ):
2428 try:
2429 return array.index( item.strip() )
2430 except:
2431 pass
2432 return 0
2434 # Return the value in array[ index ]
2436 #Parameters:
2438 # 'index' -- integer - The index to look up.
2440 # 'array' -- list - List in which to look up value.
2442 #Returns:
2444 # string -- empty string (no match) or looked up value
2445 def get_array_item( self, index, array ):
2446 try:
2447 return array[ index ]
2448 except:
2449 pass
2450 return ''
2453 # A simple class for putting up a "Please wait" dialog so the user
2454 # doesn't think we've forgotten about them. Implements the status interface.
2455 class StatusWindow:
2456 # Create a new StatusWindow.
2458 #Parameters:
2460 # 'parent' -- gtk.Object - Usually, the calling window.
2462 #Returns:
2464 # StatusWindow instance
2466 #NOTE:
2468 # Sample implementation of status interface. Status interface
2469 #requires .show(), .update_message(message), and .hide() methods.
2470 def __init__( self, parent ):
2471 global wifi_radar_icon
2472 self.parent = parent
2473 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2474 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2475 self.dialog.set_icon( icon )
2476 self.lbl = gtk.Label("Please wait...")
2477 self.bar = gtk.ProgressBar()
2478 self.dialog.vbox.pack_start(self.lbl)
2479 self.dialog.vbox.pack_start(self.bar)
2480 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2481 self.timer = None
2483 # Change the message displayed to the user.
2485 #Parameters:
2487 # 'message' -- string - The message to show to the user.
2489 #Returns:
2491 # nothing
2492 def update_message( self, message ):
2493 self.lbl.set_text(message)
2495 # Update the StatusWindow progress bar.
2497 #Parameters:
2499 # nothing
2501 #Returns:
2503 # True -- always return True
2504 def update_window( self ):
2505 self.bar.pulse()
2506 return True
2508 # Display and operate the StatusWindow.
2510 #Parameters:
2512 # nothing
2514 #Returns:
2516 # nothing
2517 def run( self ):
2518 pass
2520 # Show all the widgets of the StatusWindow.
2522 #Parameters:
2524 # nothing
2526 #Returns:
2528 # nothing
2529 def show( self ):
2530 self.dialog.show_all()
2531 self.timer = gobject.timeout_add(250, self.update_window)
2532 return False
2534 # Hide all the widgets of the StatusWindow.
2536 #Parameters:
2538 # nothing
2540 #Returns:
2542 # nothing
2543 def hide( self ):
2544 if self.timer:
2545 gobject.source_remove(self.timer)
2546 self.timer = None
2547 self.dialog.hide_all()
2548 return False
2550 # Remove the StatusWindow.
2552 #Parameters:
2554 # nothing
2556 #Returns:
2558 # nothing
2559 def destroy( self ):
2560 if self.timer:
2561 gobject.source_remove(self.timer)
2562 self.dialog.destroy()
2563 del self.dialog
2566 # Manage a GTK About Dialog
2567 class about_dialog(gtk.AboutDialog):
2568 # Subclass GTK AboutDialog
2570 #Parameters:
2572 # nothing
2574 #Returns:
2576 # nothing
2577 def __init__( self ):
2578 global wifi_radar_icon
2580 gtk.AboutDialog.__init__(self)
2581 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", "Prokhor Shuchalov", "Patrick Winnertz"])
2582 self.set_comments("WiFi connection manager")
2583 self.set_copyright("Copyright 2004-2010 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2584 self.set_documenters(["Gary Case"])
2585 license = """
2586 This program is free software; you can redistribute it and/or modify
2587 it under the terms of the GNU General Public License as published by
2588 the Free Software Foundation; either version 2 of the License, or
2589 (at your option) any later version.
2591 This program is distributed in the hope that it will be useful,
2592 but WITHOUT ANY WARRANTY; without even the implied warranty of
2593 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2594 GNU General Public License for more details.
2596 You should have received a copy of the GNU General Public License
2597 along with this program; if not, write to the Free Software
2598 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2599 self.set_license(license)
2600 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2601 self.set_logo(logo)
2602 self.set_name("WiFi Radar")
2603 self.set_version(WIFI_RADAR_VERSION)
2604 self.set_website("http://wifi-radar.berlios.de")
2608 # Manage the configuration for the application, including reading and writing the config from/to a file.
2609 class ConfigFile(ConfigParser.SafeConfigParser):
2610 # Create a new ConfigFile.
2612 #Parameters:
2614 # 'filename' -- string - The configuration file's name.
2616 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2618 #Returns:
2620 # ConfigFile instance
2621 def __init__( self, filename, defaults, raw=False ):
2622 self.filename = filename
2623 self.raw = raw
2624 self.auto_profile_order = []
2625 ConfigParser.SafeConfigParser.__init__(self, defaults)
2627 # Set the contents of a section to values from a dictionary.
2629 #Parameters:
2631 # 'section_name' -- string - Configuration file section.
2633 # 'section_dict' -- dictionary - Values to add to section.
2635 #Returns:
2637 # nothing
2638 def set_section( self, section_name, section_dict ):
2639 try:
2640 self.add_section(section_name)
2641 except ConfigParser.DuplicateSectionError:
2642 pass
2643 for key in section_dict.keys():
2644 if type(section_dict[key]) == BooleanType:
2645 self.set_bool_opt(section_name + "." + key, section_dict[key])
2646 elif type(section_dict[key]) == IntType:
2647 self.set_int_opt(section_name + "." + key, section_dict[key])
2648 elif type(section_dict[key]) == FloatType:
2649 self.set_float_opt(section_name + "." + key, section_dict[key])
2650 else:
2651 self.set_opt(section_name + "." + key, section_dict[key])
2653 # Return the profile recorded in the specified section.
2655 #Parameters:
2657 # 'section_name' -- string - Configuration file section.
2659 #Returns:
2661 # dictionary or None - The specified profile or None if not found
2662 def get_profile( self, section_name ):
2663 if section_name in self.profiles():
2664 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' ]
2665 bool_types = [ 'known', 'available', 'roaming', 'encrypted', 'use_wpa', 'use_dhcp' ]
2666 int_types = [ 'signal' ]
2667 profile = get_new_profile()
2668 for option in bool_types:
2669 profile[option] = self.get_opt_as_bool(section_name + "." + option)
2670 for option in int_types:
2671 option_tmp = self.get_opt_as_int(section_name + "." + option)
2672 if option_tmp:
2673 profile[option] = option_tmp
2674 for option in str_types:
2675 option_tmp = self.get_opt(section_name + "." + option)
2676 if option_tmp:
2677 profile[option] = option_tmp
2678 return profile
2679 return None
2681 # Get a config option and handle exceptions.
2683 #Parameters:
2685 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2686 # period and the option key. (E.g. "DEFAULT.interface")
2688 #Returns:
2690 # string or None - option value as string or None on failure
2691 def get_opt( self, option_path ):
2692 #print "ConfigFile.get_opt: ", option_path
2693 (section, option) = option_path.split('.')
2694 try:
2695 return self.get(section, option, self.raw)
2696 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2697 return None
2699 # Get a config option and return as a boolean type.
2701 #Parameters:
2703 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2704 # period and the option key. (E.g. "DEFAULT.interface")
2706 #Returns:
2708 # boolean - option value as boolean
2709 def get_opt_as_bool( self, option_path ):
2710 option = self.get_opt(option_path)
2711 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2712 return option
2713 if option == 'True':
2714 return True
2715 if option == 'False':
2716 return False
2717 raise ValueError, 'boolean option was not True or False'
2719 # Get a config option and return as an integer type.
2721 #Parameters:
2723 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2724 # period and the option key. (E.g. "DEFAULT.interface")
2726 #Returns:
2728 # integer- option value as integer
2729 def get_opt_as_int( self, option_path ):
2730 return int(float(self.get_opt(option_path)))
2732 # Convert boolean type to string and set config option.
2734 #Parameters:
2736 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2737 # period and the option key. (E.g. "DEFAULT.interface")
2739 # 'value' -- boolean - Value to set.
2741 #Returns:
2743 # nothing
2744 def set_bool_opt( self, option_path, value ):
2745 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2746 value == 'True'
2747 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2748 value == 'False'
2749 else:
2750 raise ValueError, 'cannot convert value to string'
2751 self.set_opt(option_path, repr(value))
2753 # Convert integer type to string and set config option.
2755 #Parameters:
2757 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2758 # period and the option key. (E.g. "DEFAULT.interface")
2760 # 'value' -- integer - Value to set.
2762 #Returns:
2764 # nothing
2765 def set_int_opt( self, option_path, value ):
2766 if not isinstance(value, IntType):
2767 raise ValueError, 'value is not an integer'
2768 self.set_opt(option_path, repr(value))
2770 # Convert float type to string and set config option.
2772 #Parameters:
2774 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2775 # period and the option key. (E.g. "DEFAULT.interface")
2777 # 'value' -- float - Value to set.
2779 #Returns:
2781 # nothing
2782 def set_float_opt( self, option_path, value ):
2783 if not isinstance(value, FloatType):
2784 raise ValueError, 'value is not a float'
2785 self.set_opt(option_path, repr(int(value)))
2787 # Set a config option while handling exceptions.
2789 #Parameters:
2791 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2792 # period and the option key. (E.g. "DEFAULT.interface")
2794 # 'value' -- string - Value to set.
2796 #Returns:
2798 # nothing
2799 def set_opt( self, option_path, value ):
2800 (section, option) = option_path.split('.')
2801 try:
2802 self.set(section, option, value)
2803 except ConfigParser.NoSectionError:
2804 self.add_section(section)
2805 self.set_opt(option_path, value)
2807 # Return a list of the section names which denote AP profiles.
2809 #Parameters:
2811 # nothing
2813 #Returns:
2815 # list - profile names
2816 def profiles( self ):
2817 profile_list = []
2818 for section in self.sections():
2819 if ':' in section:
2820 profile_list.append(section)
2821 return profile_list
2823 # Read configuration file from disk into instance variables.
2825 #Parameters:
2827 # nothing
2829 #Returns:
2831 # nothing
2832 def read( self ):
2833 fp = open( self.filename, "r" )
2834 self.readfp(fp)
2835 # convert the auto_profile_order to a list for ordering
2836 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2837 for ap in self.profiles():
2838 self.set_bool_opt( ap + '.known', True)
2839 if ap in self.auto_profile_order: continue
2840 self.auto_profile_order.append( ap )
2841 fp.close()
2842 # Remove any auto_profile_order AP without a matching section.
2843 auto_profile_order_copy = self.auto_profile_order[:]
2844 for ap in auto_profile_order_copy:
2845 if ap not in self.profiles():
2846 self.auto_profile_order.remove(ap)
2848 # Write configuration file to disk from instance variables. Copied from
2849 # ConfigParser and modified to write options in alphabetical order.
2851 #Parameters:
2853 # nothing
2855 #Returns:
2857 # nothing
2858 def write( self ):
2859 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2860 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2861 (fd, tempfilename) = tempfile.mkstemp(prefix="wifi-radar.conf.")
2862 fp = os.fdopen(fd, "w")
2863 # write DEFAULT section first
2864 if self._defaults:
2865 fp.write("[DEFAULT]\n")
2866 for key in sorted(self._defaults.keys()):
2867 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2868 fp.write("\n")
2869 # write other non-profile sections next
2870 for section in self._sections:
2871 if section not in self.profiles():
2872 fp.write("[%s]\n" % section)
2873 for key in sorted(self._sections[section].keys()):
2874 if key != "__name__":
2875 fp.write("%s = %s\n" %
2876 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2877 fp.write("\n")
2878 # write profile sections
2879 for section in self._sections:
2880 if section in self.profiles():
2881 fp.write("[%s]\n" % section)
2882 for key in sorted(self._sections[section].keys()):
2883 if key != "__name__":
2884 fp.write("%s = %s\n" %
2885 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2886 fp.write("\n")
2887 fp.close()
2888 move(tempfilename, self.filename)
2890 # Load our conf file and known profiles
2891 # Defaults, these may get overridden by values found in the conf file.
2892 config_defaults = { # The network interface you use.
2893 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2894 'interface': "auto_detect",
2895 # How long should the scan for access points last?
2896 #'scan_timeout': '5',
2897 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2898 # Set the speak_up option to false if you do not have or want this.
2899 'speak_command': '/usr/bin/say',
2900 # Should I speak up when connecting to a network? (If you have a speech command)
2901 'speak_up': 'False',
2902 # You may set this to true for cards that require a "commit" command with iwconfig
2903 'commit_required': 'False',
2904 # You may set this to true for cards that require the interface to be brought up first
2905 'ifup_required': 'False',
2906 # set the location and verbosity of the log file
2907 'logfile': '/var/log/wifi-radar.log',
2908 'loglevel': '50',
2909 # Set the location of several important programs
2910 'iwlist_command': '/sbin/iwlist',
2911 'iwconfig_command': '/sbin/iwconfig',
2912 'ifconfig_command': '/sbin/ifconfig',
2913 'route_command': '/sbin/route',
2914 'auto_profile_order': '[]',
2915 'version': WIFI_RADAR_VERSION }
2917 config_dhcp = { # DHCP client
2918 'command': '/sbin/dhcpcd',
2919 # How long to wait for an IP addr from DHCP server
2920 'timeout': '30',
2921 # Arguments to use with DHCP client on connect
2922 'args': '-D -o -i dhcp_client -t %(timeout)s',
2923 # Argument to use with DHCP client on disconnect
2924 'kill_args': '-k',
2925 # The file where DHCP client PID is written
2926 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2928 config_wpa = { # WPA Supplicant
2929 'command': '/usr/sbin/wpa_supplicant',
2930 # Arguments to use with WPA Supplicant on connect
2931 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2932 # Arguments to use with WPA Supplicant on disconnect
2933 'kill_command': '',
2934 # Where the WPA Supplicant config file can be found
2935 'configuration': '/etc/wpa_supplicant.conf',
2936 # Driver to use with WPA Supplicant
2937 'driver': 'wext',
2938 # The file where WPA Supplicant PID is written
2939 'pidfile': '/var/run/wpa_supplicant.pid' }
2941 # initialize config, with defaults
2942 confFile = ConfigFile(CONF_FILE, config_defaults)
2943 confFile.set_section("DHCP", config_dhcp)
2944 confFile.set_section("WPA", config_wpa)
2946 if not os.path.isfile( CONF_FILE ):
2947 confFile.set_bool_opt('DEFAULT.new_file', True)
2948 else:
2949 if not os.access(CONF_FILE, os.R_OK):
2950 print "Can't open " + CONF_FILE + "."
2951 print "Are you root?"
2952 sys.exit()
2953 try:
2954 confFile.read()
2955 except NameError:
2956 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))
2957 del error_dlg
2958 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)
2959 sys.exit()
2960 except SyntaxError:
2961 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))
2962 del error_dlg
2963 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)
2964 sys.exit()
2967 ####################################################################################################
2968 # Embedded Images
2969 wifi_radar_icon = [ ""
2970 "GdkP"
2971 "\0\0\22""7"
2972 "\2\1\0\2"
2973 "\0\0\1\214"
2974 "\0\0\0c"
2975 "\0\0\0O"
2976 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2977 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2978 "\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"
2979 "\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"
2980 "\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"
2981 "\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"
2982 "\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"
2983 "\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"
2984 "\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"
2985 "\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"
2986 "\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"
2987 "\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"
2988 "\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"
2989 "\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"
2990 "\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"
2991 "\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"
2992 "\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"
2993 "\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"
2994 "\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"
2995 "\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"
2996 "\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"
2997 "\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"
2998 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2999 "\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"
3000 "\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"
3001 "\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"
3002 "\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"
3003 "\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"
3004 "\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"
3005 "\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"
3006 "\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"
3007 "\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"
3008 "\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"
3009 "\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"
3010 "\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"
3011 "\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"
3012 "\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"
3013 "\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"
3014 "\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"
3015 "\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"
3016 "\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"
3017 "\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"
3018 "\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"
3019 "\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"
3020 "\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"
3021 "\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"
3022 "\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"
3023 "\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"
3024 "\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"
3025 "\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"
3026 "\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"
3027 "\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"
3028 "\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"
3029 "\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"
3030 "\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"
3031 "\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"
3032 "\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"
3033 "\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"
3034 "\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"
3035 "\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"
3036 "\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"
3037 "\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"
3038 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
3039 "\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"
3040 "\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"
3041 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
3042 "\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"
3043 "\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"
3044 "\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"
3045 "\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"
3046 "\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"
3047 "\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"
3048 "\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"
3049 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
3050 "\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"
3051 "\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"
3052 "\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"
3053 "\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"
3054 "\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"
3055 "\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"
3056 "|\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"
3057 "\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"
3058 "\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"
3059 "\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"
3060 "\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"
3061 "\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"
3062 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
3063 "\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"
3064 "\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"
3065 "\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"
3066 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
3067 "\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"
3068 "\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"
3069 "\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"
3070 "\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"
3071 "\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"
3072 "\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"
3073 "\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"
3074 "\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"
3075 "\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"
3076 "\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"
3077 "\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"
3078 "\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"
3079 "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"
3080 "\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"
3081 "\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"
3082 "\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"
3083 "\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"
3084 "\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"
3085 "\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"
3086 "\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"
3087 "\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"
3088 "\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"
3089 "\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"
3090 "\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"
3091 "\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"
3092 "\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"
3093 "\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"
3094 "\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|"
3095 "\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"
3096 "\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"
3097 "\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"
3098 "\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"
3099 "\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"
3100 "\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"
3101 "\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"
3102 "\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"
3103 "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"
3104 "\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"
3105 "\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"
3106 "\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"
3107 "\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"
3108 "\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"
3109 "\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"
3110 "\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"
3111 "\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"
3112 "\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"
3113 "\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"
3114 "\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"
3115 "\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"
3116 "\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"
3117 "\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"
3118 "\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"
3119 "\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"
3120 "\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"
3121 "\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"
3122 "\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"
3123 "\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"
3124 "\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"
3125 "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"
3126 "\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"
3127 "\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"
3128 "\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"
3129 "\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"
3130 "\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"
3131 "\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"
3132 "\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"
3133 "\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"
3134 "\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"
3135 "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"
3136 "\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"
3137 "\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"
3138 "\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"
3139 "\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"
3140 "\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"
3141 "\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"
3142 "\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"
3143 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
3144 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
3145 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
3146 "\0"]
3148 known_profile_icon = [ ""
3149 "GdkP"
3150 "\0\0\5""0"
3151 "\2\1\0\2"
3152 "\0\0\0P"
3153 "\0\0\0\24"
3154 "\0\0\0\24"
3155 "\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"
3156 "\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"
3157 "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"
3158 "\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"
3159 "\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"
3160 "\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"
3161 "\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"
3162 "\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"
3163 "\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"
3164 "\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"
3165 "\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"
3166 "\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"
3167 "\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"
3168 "\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"
3169 "\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"
3170 "\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"
3171 "\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"
3172 "\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"
3173 "\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"
3174 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
3175 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
3176 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
3177 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
3178 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
3179 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
3180 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
3181 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
3182 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
3183 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
3184 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
3185 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
3186 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
3187 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
3188 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
3189 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
3190 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
3191 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
3192 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
3193 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
3194 "\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"
3195 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
3196 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
3197 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
3198 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
3199 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
3200 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
3201 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
3202 "\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"
3203 "\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"
3204 "\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"
3205 "\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"
3206 "\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"]
3208 unknown_profile_icon = [ ""
3209 "GdkP"
3210 "\0\0\5\22"
3211 "\2\1\0\2"
3212 "\0\0\0P"
3213 "\0\0\0\24"
3214 "\0\0\0\24"
3215 "\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"
3216 "\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"
3217 "\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"
3218 "\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"
3219 "(\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"
3220 "\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"
3221 "#\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"
3222 "\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"
3223 "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"
3224 "\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"
3225 "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"
3226 "\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"
3227 "\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"
3228 "\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"
3229 "\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"
3230 "\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"
3231 "\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"
3232 "\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"
3233 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
3234 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
3235 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
3236 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
3237 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
3238 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
3239 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
3240 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
3241 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
3242 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
3243 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
3244 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
3245 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
3246 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
3247 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
3248 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
3249 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
3250 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
3251 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
3252 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
3253 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
3254 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
3255 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
3256 "\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"
3257 "\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"
3258 "\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"
3259 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
3261 signal_xpm_barely = [
3262 "20 20 10 1",
3263 " c None",
3264 ". c #C6C6C6",
3265 "+ c #CCCCCC",
3266 "@ c #DBDBDB",
3267 "# c #D3D3D3",
3268 "$ c #A9B099",
3269 "% c #95A173",
3270 "& c #6B8428",
3271 "* c #B4B7AC",
3272 "= c #80924D",
3273 " .+++.",
3274 " +@@@+",
3275 " +@@@+",
3276 " +@@@+",
3277 " +@@@+",
3278 " .++++#@@@+",
3279 " +@@@@@@@@+",
3280 " +@@@@@@@@+",
3281 " +@@@@@@@@+",
3282 " +@@@@@@@@+",
3283 " $%%%%#@@@@@@@@+",
3284 " %&&&&@@@@@@@@@+",
3285 " %&&&&@@@@@@@@@+",
3286 " %&&&&@@@@@@@@@+",
3287 " %&&&&@@@@@@@@@+",
3288 "*%%%%=&&&&@@@@@@@@@+",
3289 "%&&&&&&&&&@@@@@@@@@+",
3290 "%&&&&&&&&&@@@@@@@@@+",
3291 "%&&&&&&&&&@@@@@@@@@+",
3292 "*%%%%%%%%%+++++++++."
3296 signal_xpm_best = [
3297 "20 20 6 1",
3298 " c None",
3299 ". c #9DAABF",
3300 "+ c #7B96BF",
3301 "@ c #386EBF",
3302 "# c #5982BF",
3303 "$ c #AEB4BF",
3304 " .+++.",
3305 " +@@@+",
3306 " +@@@+",
3307 " +@@@+",
3308 " +@@@+",
3309 " .++++#@@@+",
3310 " +@@@@@@@@+",
3311 " +@@@@@@@@+",
3312 " +@@@@@@@@+",
3313 " +@@@@@@@@+",
3314 " .++++#@@@@@@@@+",
3315 " +@@@@@@@@@@@@@+",
3316 " +@@@@@@@@@@@@@+",
3317 " +@@@@@@@@@@@@@+",
3318 " +@@@@@@@@@@@@@+",
3319 "$++++#@@@@@@@@@@@@@+",
3320 "+@@@@@@@@@@@@@@@@@@+",
3321 "+@@@@@@@@@@@@@@@@@@+",
3322 "+@@@@@@@@@@@@@@@@@@+",
3323 "$++++++++++++++++++."
3326 signal_xpm_none = [
3327 "20 20 6 1",
3328 " c None",
3329 ". c #C6C6C6",
3330 "+ c #CCCCCC",
3331 "@ c #DBDBDB",
3332 "# c #D3D3D3",
3333 "$ c #C2C2C2",
3334 " .+++.",
3335 " +@@@+",
3336 " +@@@+",
3337 " +@@@+",
3338 " +@@@+",
3339 " .++++#@@@+",
3340 " +@@@@@@@@+",
3341 " +@@@@@@@@+",
3342 " +@@@@@@@@+",
3343 " +@@@@@@@@+",
3344 " .++++#@@@@@@@@+",
3345 " +@@@@@@@@@@@@@+",
3346 " +@@@@@@@@@@@@@+",
3347 " +@@@@@@@@@@@@@+",
3348 " +@@@@@@@@@@@@@+",
3349 "$++++#@@@@@@@@@@@@@+",
3350 "+@@@@@@@@@@@@@@@@@@+",
3351 "+@@@@@@@@@@@@@@@@@@+",
3352 "+@@@@@@@@@@@@@@@@@@+",
3353 "$++++++++++++++++++."
3356 signal_xpm_ok = [
3357 "20 20 10 1",
3358 " c None",
3359 ". c #C6C6C6",
3360 "+ c #CCCCCC",
3361 "@ c #DBDBDB",
3362 "# c #A1A5B2",
3363 "$ c #848DA5",
3364 "% c #D3D3D3",
3365 "& c #4A5B8C",
3366 "* c #677498",
3367 "= c #B0B2B8",
3368 " .+++.",
3369 " +@@@+",
3370 " +@@@+",
3371 " +@@@+",
3372 " +@@@+",
3373 " #$$$$%@@@+",
3374 " $&&&&@@@@+",
3375 " $&&&&@@@@+",
3376 " $&&&&@@@@+",
3377 " $&&&&@@@@+",
3378 " #$$$$*&&&&@@@@+",
3379 " $&&&&&&&&&@@@@+",
3380 " $&&&&&&&&&@@@@+",
3381 " $&&&&&&&&&@@@@+",
3382 " $&&&&&&&&&@@@@+",
3383 "=$$$$*&&&&&&&&&@@@@+",
3384 "$&&&&&&&&&&&&&&@@@@+",
3385 "$&&&&&&&&&&&&&&@@@@+",
3386 "$&&&&&&&&&&&&&&@@@@+",
3387 "=$$$$$$$$$$$$$$++++."
3391 signal_xpm_low = [
3392 "20 20 8 1",
3393 " c None",
3394 ". c #C6C6C6",
3395 "+ c #CCCCCC",
3396 "@ c #DBDBDB",
3397 "# c #D3D3D3",
3398 "$ c #BFB0B5",
3399 "% c #C18799",
3400 "& c #C54F74",
3401 " .+++.",
3402 " +@@@+",
3403 " +@@@+",
3404 " +@@@+",
3405 " +@@@+",
3406 " .++++#@@@+",
3407 " +@@@@@@@@+",
3408 " +@@@@@@@@+",
3409 " +@@@@@@@@+",
3410 " +@@@@@@@@+",
3411 " .++++#@@@@@@@@+",
3412 " +@@@@@@@@@@@@@+",
3413 " +@@@@@@@@@@@@@+",
3414 " +@@@@@@@@@@@@@+",
3415 " +@@@@@@@@@@@@@+",
3416 "$%%%%#@@@@@@@@@@@@@+",
3417 "%&&&&@@@@@@@@@@@@@@+",
3418 "%&&&&@@@@@@@@@@@@@@+",
3419 "%&&&&@@@@@@@@@@@@@@+",
3420 "$%%%%++++++++++++++."
3424 ####################################################################################################
3425 # Make so we can be imported
3426 if __name__ == "__main__":
3427 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
3428 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3429 elif len( sys.argv ) > 1 and ( sys.argv[1] == '--help' or sys.argv[1] == '-h' ):
3430 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3431 print "For help, check man pages for wifi-radar and wifi-radar.conf,"
3432 print "or visit http://wifi-radar.berlios.de"
3433 else:
3434 import gtk, gobject
3435 gtk.gdk.threads_init()
3436 apQueue = Queue.Queue(100)
3437 commQueue = Queue.Queue(2)
3439 logger = logging.getLogger("wrlog")
3440 logger.setLevel(confFile.get_opt_as_int('DEFAULT.loglevel'))
3441 try:
3442 fileLogHandler = logging.handlers.RotatingFileHandler(confFile.get_opt('DEFAULT.logfile'), maxBytes=64*1024, backupCount=5)
3443 except IOError, (error_number, error_str):
3444 error_dlg = ErrorDialog(None, "Cannot open log file for writing: %s.\n\nWiFi Radar will work, but a log file will not be recorded." % (error_str))
3445 del error_dlg
3446 else:
3447 fileLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(funcName)s: %(message)s'))
3448 logger.addHandler(fileLogHandler)
3449 consoleLogHandler = logging.StreamHandler()
3450 consoleLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(funcName)s: %(message)s'))
3451 logger.addHandler(consoleLogHandler)
3452 if __debug__:
3453 logger.setLevel(logging.INFO)
3455 exit_event = threading.Event()
3456 exit_event.clear()
3457 threading.Thread(None, scanning_thread, None, (confFile, apQueue, commQueue, logger, exit_event)).start()
3458 main_radar_window = radar_window(confFile, apQueue, commQueue, logger, exit_event)
3459 gobject.timeout_add( 500, main_radar_window.update_window )
3460 main_radar_window.main()