Remove embedded images
[wifi-radar.git] / wifi-radar
blobf87d08c5c6626c0ca1f85808a63e49983a2a374e
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'] == '') or (self.profile['key'] == 's:'):
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 # ToggleButton to select ASCII/hex WEP key type
2169 self.wep_key_button = gtk.ToggleButton( 'Hex Key' )
2170 if self.profile['key'].startswith('s:'):
2171 self.wep_key_button.set_active(False)
2172 self.wep_key_button.set_label('ASCII Key')
2173 self.profile['key'] = self.profile['key'][2:]
2174 else:
2175 self.wep_key_button.set_active(True)
2176 self.wep_key_button.connect( 'toggled', self.toggle_wep_key_type )
2177 # The Wifi labels
2178 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
2179 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
2180 wifi_table.attach( self.wep_key_button, 0, 1, 2, 3 )
2181 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
2182 # The WiFi text boxes
2183 self.mode_combo = gtk.combo_box_new_text()
2184 for mode in self.WIFI_MODES:
2185 self.mode_combo.append_text( mode )
2186 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
2187 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
2188 self.tooltips.set_tip(self.mode_combo, "Method to use for connection. You probably want auto mode.")
2189 self.channel_combo = gtk.combo_box_new_text()
2190 for channel in self.WIFI_CHANNELS:
2191 self.channel_combo.append_text( channel )
2192 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
2193 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
2194 self.tooltips.set_tip(self.channel_combo, "Channel the network uses. You probably want auto mode.")
2196 self.key_entry = gtk.Entry( 64 )
2197 self.key_entry.set_text( self.profile['key'] )
2198 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
2199 self.tooltips.set_tip(self.key_entry, "WEP key: Plain text or hex string to use for encrypted communication with the network.")
2201 self.security_combo = gtk.combo_box_new_text()
2202 for security in self.WIFI_SECURITY:
2203 self.security_combo.append_text( security )
2204 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
2205 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
2206 self.tooltips.set_tip(self.security_combo, "Use Open to allow unencrypted communication and Restricted to force encrypted-only communication.")
2207 # Add the wifi table to the expander
2208 self.wifi_expander.add( wifi_table )
2209 # Add the expander to the dialog
2210 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
2212 # create the wpa expander
2213 self.wpa_expander = gtk.Expander( self.NO_WPA_LABEL )
2214 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
2215 wpa_table = gtk.Table( 1, 2, False )
2216 wpa_table.set_row_spacings( 3 )
2217 wpa_table.set_col_spacings( 3 )
2218 # The labels
2219 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
2220 # The text boxes
2221 self.wpa_driver_entry = gtk.Entry()
2222 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
2223 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
2224 # Add the wpa table to the expander
2225 self.wpa_expander.add( wpa_table )
2226 self.wpa_expander.set_expanded(self.profile['use_wpa'])
2227 # Add the expander to the dialog
2228 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
2230 # create the dhcp expander
2231 self.dhcp_expander = gtk.Expander( self.USE_DHCP_LABEL )
2232 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
2233 self.dhcp_expander.set_expanded(not self.profile['use_dhcp'])
2234 ip_table = gtk.Table( 6, 2, False )
2235 ip_table.set_row_spacings( 3 )
2236 ip_table.set_col_spacings( 3 )
2237 # The IP labels
2238 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
2239 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
2240 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
2241 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
2242 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
2243 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
2244 # The IP text boxes
2245 self.ip_entry = gtk.Entry( 15 )
2246 self.ip_entry.set_text( self.profile['ip'] )
2247 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
2248 self.netmask_entry = gtk.Entry( 15 )
2249 self.netmask_entry.set_text( self.profile['netmask'] )
2250 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
2251 self.gw_entry = gtk.Entry( 15 )
2252 self.gw_entry.set_text( self.profile['gateway'] )
2253 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
2254 self.domain_entry = gtk.Entry( 32 )
2255 self.domain_entry.set_text( self.profile['domain'] )
2256 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
2257 self.dns1_entry = gtk.Entry( 15 )
2258 self.dns1_entry.set_text( self.profile['dns1'] )
2259 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
2260 self.dns2_entry = gtk.Entry( 15 )
2261 self.dns2_entry.set_text( self.profile['dns2'] )
2262 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
2263 # Add the ip table to the expander
2264 self.dhcp_expander.add( ip_table )
2265 # Add the expander to the dialog
2266 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
2268 # create the connection-building postpre expander
2269 self.con_pp_expander = gtk.Expander( self.CON_PP_LABEL )
2270 con_pp_table = gtk.Table( 2, 2, False )
2271 con_pp_table.set_row_spacings( 3 )
2272 con_pp_table.set_col_spacings( 3 )
2273 # The labels
2274 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2275 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2276 # The text boxes
2277 self.con_prescript_entry = gtk.Entry()
2278 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
2279 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
2280 self.tooltips.set_tip(self.con_prescript_entry, "The local command to execute before trying to connect to the network.")
2281 self.con_postscript_entry = gtk.Entry()
2282 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
2283 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
2284 self.tooltips.set_tip(self.con_postscript_entry, "The local command to execute after connecting to the network.")
2285 # Add the pp table to the expander
2286 self.con_pp_expander.add( con_pp_table )
2287 # Add the expander to the dialog
2288 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
2290 # create the disconnection postpre expander
2291 self.dis_pp_expander = gtk.Expander( self.DIS_PP_LABEL )
2292 dis_pp_table = gtk.Table( 2, 2, False )
2293 dis_pp_table.set_row_spacings( 3 )
2294 dis_pp_table.set_col_spacings( 3 )
2295 # The labels
2296 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
2297 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
2298 # The text boxes
2299 self.dis_prescript_entry = gtk.Entry()
2300 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
2301 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
2302 self.tooltips.set_tip(self.dis_prescript_entry, "The local command to execute before disconnecting from the network.")
2303 self.dis_postscript_entry = gtk.Entry()
2304 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
2305 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
2306 self.tooltips.set_tip(self.dis_postscript_entry, "The local command to execute after disconnecting from the network.")
2307 # Add the pp table to the expander
2308 self.dis_pp_expander.add( dis_pp_table )
2309 # Add the expander to the dialog
2310 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
2312 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
2314 #Parameters:
2316 # nothing
2318 #Returns:
2320 # dictionary or None -- a profile, or None on cancel
2322 #NOTES:
2324 # Raises ValueError if an attempt is made to save an ESSID with no name.
2325 def run( self ):
2326 self.dialog.show_all()
2327 error = True
2328 while error:
2329 error = False
2330 if self.dialog.run():
2331 if self.essid_entry.get_text().strip() == "":
2332 raise ValueError
2333 self.profile['known'] = True
2334 self.profile['essid'] = self.essid_entry.get_text().strip()
2335 if self.roaming_cb.get_active():
2336 self.profile['bssid'] = ''
2337 else:
2338 self.profile['bssid'] = self.bssid_entry.get_text().strip()
2339 self.profile['roaming'] = self.roaming_cb.get_active()
2340 if self.wep_key_button.get_active():
2341 self.profile['key'] = '' # hex WEP key
2342 if re.compile("[^0-9a-fA-F]").search(self.key_entry.get_text().strip()):
2343 dlg = gtk.MessageDialog( self.dialog, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Invalid hex WEP key." )
2344 dlg.run()
2345 dlg.destroy()
2346 del dlg
2347 error = True
2348 else:
2349 self.profile['key'] = 's:' # ASCII WEP key
2350 self.profile['key'] = self.profile['key'] + self.key_entry.get_text().strip()
2351 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
2352 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
2353 self.profile['encrypted'] = ( self.profile['security'] != '' )
2354 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
2355 self.profile['protocol'] = 'g'
2356 self.profile['available'] = ( self.profile['signal'] > 0 )
2357 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
2358 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
2359 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
2360 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
2361 # wpa
2362 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
2363 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
2364 # dhcp
2365 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
2366 self.profile['ip'] = self.ip_entry.get_text().strip()
2367 self.profile['netmask'] = self.netmask_entry.get_text().strip()
2368 self.profile['gateway'] = self.gw_entry.get_text().strip()
2369 self.profile['domain'] = self.domain_entry.get_text().strip()
2370 self.profile['dns1'] = self.dns1_entry.get_text().strip()
2371 self.profile['dns2'] = self.dns2_entry.get_text().strip()
2372 else:
2373 return None
2374 return self.profile
2376 # Remove profile dialog.
2378 #Parameters:
2380 # nothing
2382 #Returns:
2384 # nothing
2385 def destroy( self ):
2386 self.dialog.destroy()
2387 del self.dialog
2389 # Respond to roaming checkbox toggle by activating/de-activating the BSSID text entry.
2391 #Parameters:
2393 # 'roaming_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2395 # 'data' -- tuple - list of arbitrary arguments (not used)
2397 #Returns:
2399 # nothing
2400 def toggle_roaming(self, roaming_toggle, data=None):
2401 self.bssid_entry.set_sensitive(not roaming_toggle.get_active())
2403 # Respond to WEP key-type toggle by changing the button label.
2405 #Parameters:
2407 # 'wep_key_type_toggle' -- gtk.ToggleButton - The statful button sending the signal.
2409 # 'data' -- tuple - list of arbitrary arguments (not used)
2411 #Returns:
2413 # nothing
2414 def toggle_wep_key_type(self, wep_key_type_toggle, data=None):
2415 if wep_key_type_toggle.get_active():
2416 wep_key_type_toggle.set_label('Hex Key')
2417 else:
2418 wep_key_type_toggle.set_label('ASCII Key')
2420 # Respond to expanding/hiding IP segment.
2422 #Parameters:
2424 # 'widget' -- gtk.Widget - The widget sending the event.
2426 # 'data' -- tuple - List of arbitrary arguments (not used)
2428 #Returns:
2430 # nothing
2431 def toggle_use_dhcp( self, widget, data = None ):
2432 expanded = self.dhcp_expander.get_expanded()
2433 if expanded:
2434 self.dhcp_expander.set_label( self.USE_IP_LABEL )
2435 else:
2436 self.dhcp_expander.set_label( self.USE_DHCP_LABEL )
2438 # Respond to expanding/hiding WPA segment.
2440 #Parameters:
2442 # 'widget' -- gtk.Widget - The widget sending the event.
2444 # 'data' -- tuple - List of arbitrary arguments (not used)
2446 #Returns:
2448 # nothing
2449 def toggle_use_wpa( self, widget, data = None ):
2450 expanded = self.wpa_expander.get_expanded()
2451 if expanded:
2452 self.wpa_expander.set_label( self.USE_WPA_LABEL )
2453 else:
2454 self.wpa_expander.set_label( self.NO_WPA_LABEL )
2456 # Return the index where item matches a cell in array.
2458 #Parameters:
2460 # 'item' -- string - Item to find in array
2462 # 'array' -- list - List in which to find match.
2464 #Returns:
2466 # integer - 0 (no match) or higher (index of match)
2467 def get_array_index( self, item, array ):
2468 try:
2469 return array.index( item.strip() )
2470 except:
2471 pass
2472 return 0
2474 # Return the value in array[ index ]
2476 #Parameters:
2478 # 'index' -- integer - The index to look up.
2480 # 'array' -- list - List in which to look up value.
2482 #Returns:
2484 # string -- empty string (no match) or looked up value
2485 def get_array_item( self, index, array ):
2486 try:
2487 return array[ index ]
2488 except:
2489 pass
2490 return ''
2493 # A simple class for putting up a "Please wait" dialog so the user
2494 # doesn't think we've forgotten about them. Implements the status interface.
2495 class StatusWindow:
2496 # Create a new StatusWindow.
2498 #Parameters:
2500 # 'parent' -- gtk.Object - Usually, the calling window.
2502 #Returns:
2504 # StatusWindow instance
2506 #NOTE:
2508 # Sample implementation of status interface. Status interface
2509 #requires .show(), .update_message(message), and .hide() methods.
2510 def __init__( self, parent ):
2511 global wifi_radar_icon
2512 self.parent = parent
2513 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
2514 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2515 self.dialog.set_icon( icon )
2516 self.lbl = gtk.Label("Please wait...")
2517 self.bar = gtk.ProgressBar()
2518 self.dialog.vbox.pack_start(self.lbl)
2519 self.dialog.vbox.pack_start(self.bar)
2520 self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.BUTTONS_CANCEL)
2521 self.timer = None
2523 # Change the message displayed to the user.
2525 #Parameters:
2527 # 'message' -- string - The message to show to the user.
2529 #Returns:
2531 # nothing
2532 def update_message( self, message ):
2533 self.lbl.set_text(message)
2535 # Update the StatusWindow progress bar.
2537 #Parameters:
2539 # nothing
2541 #Returns:
2543 # True -- always return True
2544 def update_window( self ):
2545 self.bar.pulse()
2546 return True
2548 # Display and operate the StatusWindow.
2550 #Parameters:
2552 # nothing
2554 #Returns:
2556 # nothing
2557 def run( self ):
2558 pass
2560 # Show all the widgets of the StatusWindow.
2562 #Parameters:
2564 # nothing
2566 #Returns:
2568 # nothing
2569 def show( self ):
2570 self.dialog.show_all()
2571 self.timer = gobject.timeout_add(250, self.update_window)
2572 return False
2574 # Hide all the widgets of the StatusWindow.
2576 #Parameters:
2578 # nothing
2580 #Returns:
2582 # nothing
2583 def hide( self ):
2584 if self.timer:
2585 gobject.source_remove(self.timer)
2586 self.timer = None
2587 self.dialog.hide_all()
2588 return False
2590 # Remove the StatusWindow.
2592 #Parameters:
2594 # nothing
2596 #Returns:
2598 # nothing
2599 def destroy( self ):
2600 if self.timer:
2601 gobject.source_remove(self.timer)
2602 self.dialog.destroy()
2603 del self.dialog
2606 # Manage a GTK About Dialog
2607 class about_dialog(gtk.AboutDialog):
2608 # Subclass GTK AboutDialog
2610 #Parameters:
2612 # nothing
2614 #Returns:
2616 # nothing
2617 def __init__( self ):
2618 global wifi_radar_icon
2620 gtk.AboutDialog.__init__(self)
2621 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"])
2622 self.set_comments("WiFi connection manager")
2623 self.set_copyright("Copyright 2004-2010 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2624 self.set_documenters(["Gary Case"])
2625 license = """
2626 This program is free software; you can redistribute it and/or modify
2627 it under the terms of the GNU General Public License as published by
2628 the Free Software Foundation; either version 2 of the License, or
2629 (at your option) any later version.
2631 This program is distributed in the hope that it will be useful,
2632 but WITHOUT ANY WARRANTY; without even the implied warranty of
2633 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2634 GNU General Public License for more details.
2636 You should have received a copy of the GNU General Public License
2637 along with this program; if not, write to the Free Software
2638 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2639 self.set_license(license)
2640 logo = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
2641 self.set_logo(logo)
2642 self.set_name("WiFi Radar")
2643 self.set_version(WIFI_RADAR_VERSION)
2644 self.set_website("http://wifi-radar.berlios.de")
2648 # Manage the configuration for the application, including reading and writing the config from/to a file.
2649 class ConfigFile(ConfigParser.SafeConfigParser):
2650 # Create a new ConfigFile.
2652 #Parameters:
2654 # 'filename' -- string - The configuration file's name.
2656 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2658 #Returns:
2660 # ConfigFile instance
2661 def __init__( self, filename, defaults, raw=False ):
2662 self.filename = filename
2663 self.raw = raw
2664 self.auto_profile_order = []
2665 ConfigParser.SafeConfigParser.__init__(self, defaults)
2667 # Set the contents of a section to values from a dictionary.
2669 #Parameters:
2671 # 'section_name' -- string - Configuration file section.
2673 # 'section_dict' -- dictionary - Values to add to section.
2675 #Returns:
2677 # nothing
2678 def set_section( self, section_name, section_dict ):
2679 try:
2680 self.add_section(section_name)
2681 except ConfigParser.DuplicateSectionError:
2682 pass
2683 for key in section_dict.keys():
2684 if type(section_dict[key]) == BooleanType:
2685 self.set_bool_opt(section_name + "." + key, section_dict[key])
2686 elif type(section_dict[key]) == IntType:
2687 self.set_int_opt(section_name + "." + key, section_dict[key])
2688 elif type(section_dict[key]) == FloatType:
2689 self.set_float_opt(section_name + "." + key, section_dict[key])
2690 else:
2691 self.set_opt(section_name + "." + key, section_dict[key])
2693 # Return the profile recorded in the specified section.
2695 #Parameters:
2697 # 'section_name' -- string - Configuration file section.
2699 #Returns:
2701 # dictionary or None - The specified profile or None if not found
2702 def get_profile( self, section_name ):
2703 if section_name in self.profiles():
2704 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' ]
2705 bool_types = [ 'known', 'available', 'roaming', 'encrypted', 'use_wpa', 'use_dhcp' ]
2706 int_types = [ 'signal' ]
2707 profile = get_new_profile()
2708 for option in bool_types:
2709 profile[option] = self.get_opt_as_bool(section_name + "." + option)
2710 for option in int_types:
2711 option_tmp = self.get_opt_as_int(section_name + "." + option)
2712 if option_tmp:
2713 profile[option] = option_tmp
2714 for option in str_types:
2715 option_tmp = self.get_opt(section_name + "." + option)
2716 if option_tmp:
2717 profile[option] = option_tmp
2718 return profile
2719 return None
2721 # Get a config option and handle exceptions.
2723 #Parameters:
2725 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2726 # period and the option key. (E.g. "DEFAULT.interface")
2728 #Returns:
2730 # string or None - option value as string or None on failure
2731 def get_opt( self, option_path ):
2732 #print "ConfigFile.get_opt: ", option_path
2733 (section, option) = option_path.split('.')
2734 try:
2735 return self.get(section, option, self.raw)
2736 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
2737 return None
2739 # Get a config option and return as a boolean type.
2741 #Parameters:
2743 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2744 # period and the option key. (E.g. "DEFAULT.interface")
2746 #Returns:
2748 # boolean - option value as boolean
2749 def get_opt_as_bool( self, option_path ):
2750 option = self.get_opt(option_path)
2751 if isinstance(option, BooleanType) or isinstance(option, NoneType):
2752 return option
2753 if option == 'True':
2754 return True
2755 if option == 'False':
2756 return False
2757 raise ValueError, 'boolean option was not True or False'
2759 # Get a config option and return as an integer type.
2761 #Parameters:
2763 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2764 # period and the option key. (E.g. "DEFAULT.interface")
2766 #Returns:
2768 # integer- option value as integer
2769 def get_opt_as_int( self, option_path ):
2770 return int(float(self.get_opt(option_path)))
2772 # Convert boolean type to string and set config option.
2774 #Parameters:
2776 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2777 # period and the option key. (E.g. "DEFAULT.interface")
2779 # 'value' -- boolean - Value to set.
2781 #Returns:
2783 # nothing
2784 def set_bool_opt( self, option_path, value ):
2785 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
2786 value == 'True'
2787 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
2788 value == 'False'
2789 else:
2790 raise ValueError, 'cannot convert value to string'
2791 self.set_opt(option_path, repr(value))
2793 # Convert integer type to string and set config option.
2795 #Parameters:
2797 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2798 # period and the option key. (E.g. "DEFAULT.interface")
2800 # 'value' -- integer - Value to set.
2802 #Returns:
2804 # nothing
2805 def set_int_opt( self, option_path, value ):
2806 if not isinstance(value, IntType):
2807 raise ValueError, 'value is not an integer'
2808 self.set_opt(option_path, repr(value))
2810 # Convert float type to string and set config option.
2812 #Parameters:
2814 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2815 # period and the option key. (E.g. "DEFAULT.interface")
2817 # 'value' -- float - Value to set.
2819 #Returns:
2821 # nothing
2822 def set_float_opt( self, option_path, value ):
2823 if not isinstance(value, FloatType):
2824 raise ValueError, 'value is not a float'
2825 self.set_opt(option_path, repr(int(value)))
2827 # Set a config option while handling exceptions.
2829 #Parameters:
2831 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2832 # period and the option key. (E.g. "DEFAULT.interface")
2834 # 'value' -- string - Value to set.
2836 #Returns:
2838 # nothing
2839 def set_opt( self, option_path, value ):
2840 (section, option) = option_path.split('.')
2841 try:
2842 self.set(section, option, value)
2843 except ConfigParser.NoSectionError:
2844 self.add_section(section)
2845 self.set_opt(option_path, value)
2847 # Return a list of the section names which denote AP profiles.
2849 #Parameters:
2851 # nothing
2853 #Returns:
2855 # list - profile names
2856 def profiles( self ):
2857 profile_list = []
2858 for section in self.sections():
2859 if ':' in section:
2860 profile_list.append(section)
2861 return profile_list
2863 # Read configuration file from disk into instance variables.
2865 #Parameters:
2867 # nothing
2869 #Returns:
2871 # nothing
2872 def read( self ):
2873 fp = open( self.filename, "r" )
2874 self.readfp(fp)
2875 # convert the auto_profile_order to a list for ordering
2876 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
2877 for ap in self.profiles():
2878 self.set_bool_opt( ap + '.known', True)
2879 if ap in self.auto_profile_order: continue
2880 self.auto_profile_order.append( ap )
2881 fp.close()
2882 # Remove any auto_profile_order AP without a matching section.
2883 auto_profile_order_copy = self.auto_profile_order[:]
2884 for ap in auto_profile_order_copy:
2885 if ap not in self.profiles():
2886 self.auto_profile_order.remove(ap)
2888 # Write configuration file to disk from instance variables. Copied from
2889 # ConfigParser and modified to write options in alphabetical order.
2891 #Parameters:
2893 # nothing
2895 #Returns:
2897 # nothing
2898 def write( self ):
2899 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
2900 self.set_opt('DEFAULT.version', WIFI_RADAR_VERSION)
2901 (fd, tempfilename) = tempfile.mkstemp(prefix="wifi-radar.conf.")
2902 fp = os.fdopen(fd, "w")
2903 # write DEFAULT section first
2904 if self._defaults:
2905 fp.write("[DEFAULT]\n")
2906 for key in sorted(self._defaults.keys()):
2907 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
2908 fp.write("\n")
2909 # write other non-profile sections next
2910 for section in self._sections:
2911 if section not in self.profiles():
2912 fp.write("[%s]\n" % section)
2913 for key in sorted(self._sections[section].keys()):
2914 if key != "__name__":
2915 fp.write("%s = %s\n" %
2916 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2917 fp.write("\n")
2918 # write profile sections
2919 for section in self._sections:
2920 if section in self.profiles():
2921 fp.write("[%s]\n" % section)
2922 for key in sorted(self._sections[section].keys()):
2923 if key != "__name__":
2924 fp.write("%s = %s\n" %
2925 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
2926 fp.write("\n")
2927 fp.close()
2928 move(tempfilename, self.filename)
2930 # Load our conf file and known profiles
2931 # Defaults, these may get overridden by values found in the conf file.
2932 config_defaults = { # The network interface you use.
2933 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2934 'interface': "auto_detect",
2935 # How long should the scan for access points last?
2936 #'scan_timeout': '5',
2937 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2938 # Set the speak_up option to false if you do not have or want this.
2939 'speak_command': '/usr/bin/say',
2940 # Should I speak up when connecting to a network? (If you have a speech command)
2941 'speak_up': 'False',
2942 # You may set this to true for cards that require a "commit" command with iwconfig
2943 'commit_required': 'False',
2944 # You may set this to true for cards that require the interface to be brought up first
2945 'ifup_required': 'False',
2946 # set the location and verbosity of the log file
2947 'logfile': '/var/log/wifi-radar.log',
2948 'loglevel': '50',
2949 # Set the location of several important programs
2950 'iwlist_command': '/sbin/iwlist',
2951 'iwconfig_command': '/sbin/iwconfig',
2952 'ifconfig_command': '/sbin/ifconfig',
2953 'route_command': '/sbin/route',
2954 'auto_profile_order': '[]',
2955 'version': WIFI_RADAR_VERSION }
2957 config_dhcp = { # DHCP client
2958 'command': '/sbin/dhcpcd',
2959 # How long to wait for an IP addr from DHCP server
2960 'timeout': '30',
2961 # Arguments to use with DHCP client on connect
2962 'args': '-D -o -i dhcp_client -t %(timeout)s',
2963 # Argument to use with DHCP client on disconnect
2964 'kill_args': '-k',
2965 # The file where DHCP client PID is written
2966 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2968 config_wpa = { # WPA Supplicant
2969 'command': '/usr/sbin/wpa_supplicant',
2970 # Arguments to use with WPA Supplicant on connect
2971 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2972 # Arguments to use with WPA Supplicant on disconnect
2973 'kill_command': '',
2974 # Where the WPA Supplicant config file can be found
2975 'configuration': '/etc/wpa_supplicant.conf',
2976 # Driver to use with WPA Supplicant
2977 'driver': 'wext',
2978 # The file where WPA Supplicant PID is written
2979 'pidfile': '/var/run/wpa_supplicant.pid' }
2981 # initialize config, with defaults
2982 confFile = ConfigFile(CONF_FILE, config_defaults)
2983 confFile.set_section("DHCP", config_dhcp)
2984 confFile.set_section("WPA", config_wpa)
2986 if not os.path.isfile( CONF_FILE ):
2987 confFile.set_bool_opt('DEFAULT.new_file', True)
2988 else:
2989 if not os.access(CONF_FILE, os.R_OK):
2990 print "Can't open " + CONF_FILE + "."
2991 print "Are you root?"
2992 sys.exit()
2993 try:
2994 confFile.read()
2995 except NameError:
2996 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))
2997 del error_dlg
2998 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)
2999 sys.exit()
3000 except SyntaxError:
3001 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))
3002 del error_dlg
3003 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)
3004 sys.exit()
3008 ####################################################################################################
3009 # Make so we can be imported
3010 if __name__ == "__main__":
3011 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
3012 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3013 elif len( sys.argv ) > 1 and ( sys.argv[1] == '--help' or sys.argv[1] == '-h' ):
3014 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3015 print "For help, check man pages for wifi-radar and wifi-radar.conf,"
3016 print "or visit http://wifi-radar.berlios.de"
3017 else:
3018 import gtk, gobject
3019 gtk.gdk.threads_init()
3020 apQueue = Queue.Queue(100)
3021 commQueue = Queue.Queue(2)
3023 logger = logging.getLogger("wrlog")
3024 logger.setLevel(confFile.get_opt_as_int('DEFAULT.loglevel'))
3025 fileLogHandler = logging.handlers.RotatingFileHandler(confFile.get_opt('DEFAULT.logfile'), maxBytes=64*1024, backupCount=5)
3026 fileLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(levelname)s: %(funcName)s: %(message)s'))
3027 logger.addHandler(fileLogHandler)
3028 consoleLogHandler = logging.StreamHandler()
3029 consoleLogHandler.setFormatter(logging.Formatter('%(asctime)s: %(funcName)s: %(message)s'))
3030 logger.addHandler(consoleLogHandler)
3031 if __debug__:
3032 logger.setLevel(logging.INFO)
3034 exit_event = threading.Event()
3035 exit_event.clear()
3036 threading.Thread(None, scanning_thread, None, (confFile, apQueue, commQueue, logger, exit_event)).start()
3037 main_radar_window = radar_window(confFile, apQueue, commQueue, logger, exit_event)
3038 gobject.timeout_add( 500, main_radar_window.update_window )
3039 main_radar_window.main()