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.
40 import logging
.handlers
48 from shutil
import move
49 from signal
import SIGTERM
50 from subprocess
import call
, Popen
, PIPE
, STDOUT
51 from time
import sleep
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
72 # 'device' -- string - The proposed network device to use
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')
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.")
93 # Return a blank profile
101 # dictionary -- An AP profile with defaults set.
102 def get_new_profile():
103 return { 'known': False,
113 'con_postscript': '',
115 'dis_postscript': '',
130 # Combine essid and bssid to make a config file section name
134 # 'essid' -- string - AP ESSID
136 # 'bssid' -- string - AP BSSID
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
148 # 'section' -- string - Config file section name
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
161 # 'command' -- tuple - The command and arguments to run.
163 # 'environment' -- dictionary - Environment variables (as keys) and their values.
167 # boolean -- True on success, otherwise, False
168 def shellcmd( command
, environment
= None ):
171 env_tmp
.update(environment
)
172 command
= ' '.join(command
)
173 return_code
= call(command
, shell
=True, env
=env_tmp
)
177 print >>sys
.stderr
, "Child was terminated by signal", -return_code
178 except OSError, exception
:
179 print >>sys
.stderr
, "Execution failed:", exception
182 # Speak feedback message to user
186 # 'words' -- string - Message to speak to user
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.
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
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
)
227 command
= commandQueue
.get_nowait()
228 logger
.info("received command: %s" % (command
, ))
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
241 scandata
= Popen([confFile
.get_opt('DEFAULT.iwlist_command'), device
, 'scan'], stdout
=PIPE
).stdout
.read()
242 except OSError, (errno
, strerror
):
244 logger
.critical("iwlist command not found, please set this in the preferences.")
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(' - ')
252 # set the defaults for profile template
253 profile
= get_new_profile()
254 m
= essid_pattern
.search( hit
)
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
275 logger
.debug("Scanned profile: %s" % (access_points
[ bssid
], ))
276 apQueue
.put_nowait( access_points
[bssid
] )
280 commandQueue
.task_done()
281 if exit_event
.isSet():
282 logger
.info("Exiting.")
284 if device
.find('ath') == 0:
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.
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
307 # ConnectionManager instance
308 def __init__( self
, confFile
, commandQueue
, logger
):
309 self
.confFile
= confFile
310 self
.commQueue
= commandQueue
312 # is connection running?
314 if self
.get_current_ip():
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.
322 # 'state' -- string - The state to which to change the interface.
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.
337 # 'use_wpa' -- boolean - The use_wpa from the profile.
339 # 'key' -- string - The WEP key or empty string.
343 # string - none, wep, or wpa; indicates WiFi encryption mode
344 def _get_enc_mode(self
, use_wpa
, key
):
352 # Connect to the specified AP.
356 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
358 # 'status' -- status implementer - Object which implements status interface.
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'] )
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
)
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 ''
389 # Some cards need to have the interface up
390 if self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
392 # Start building iwconfig command line, command
393 iwconfig_command
= [ self
.confFile
.get_opt('DEFAULT.iwconfig_command') ]
394 iwconfig_command
.append(device
)
396 iwconfig_command
.append( 'essid' )
397 iwconfig_command
.append( "'" + self
.profile
['essid'] + "'" )
399 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
401 iwconfig_command
.append( 'key' )
402 if self
.profile
['key'] == '':
403 iwconfig_command
.append( 'off' )
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'] ) )
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'] )
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...")
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')])
437 os
.kill(int(open(self
.confFile
.get_opt('DHCP.pidfile'), mode
='r').readline()), SIGTERM
)
439 print "failed to kill DHCP client"
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...")
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')])
452 os
.kill(int(open(self
.confFile
.get_opt('WPA.pidfile'), mode
='r').readline()), SIGTERM
)
454 print "failed to kill WPA supplicant"
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...")
461 self
.commQueue
.put("pause")
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
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)
478 except OSError, (errno
, strerror
):
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
, ))
492 dhcp_proc
= Popen(dhcp_command
, stdout
=None)
493 except OSError, (errno
, strerror
):
495 logger
.critical("DHCP client not found, please set this in the preferences.")
496 timer
= self
.confFile
.get_opt_as_int('DHCP.timeout') + 3
498 waiting
= dhcp_proc
.poll()
499 while waiting
== None:
500 waiting
= dhcp_proc
.poll()
502 os
.kill(dhcp_proc
.pid
, SIGTERM
)
504 if sys
.modules
.has_key("gtk"):
505 while gtk
.events_pending():
506 gtk
.main_iteration(False)
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)
516 self
.disconnect_interface()
520 status
.update_message("Got IP address. Done.")
522 if sys
.modules
.has_key("gtk"):
523 while gtk
.events_pending():
524 gtk
.main_iteration(False)
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'] )
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
)
537 if not shellcmd([ifconfig_command
]): return
538 if not shellcmd([route_command
]): return
542 self
.commQueue
.put("scan")
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
)
563 # Disconnect from the AP with which a connection has been established/attempted.
572 def disconnect_interface( self
):
573 msg
= "Disconnecting"
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
583 self
.commQueue
.put("pause")
584 self
.commQueue
.join()
588 self
.profile
= self
.confFile
.get_profile(make_section_name(self
.get_current_essid(), self
.get_current_bssid()))
590 self
.profile
= self
.confFile
.get_profile(make_section_name(self
.get_current_essid(), ''))
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...")
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
618 self
.logger
.info("Killing DHCP manually...")
619 os
.kill(int(open(self
.confFile
.get_opt('DHCP.pidfile'), mode
='r').readline()), SIGTERM
)
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...")
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
630 os
.kill(int(open(self
.confFile
.get_opt('WPA.pidfile'), mode
='r').readline()), SIGTERM
)
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
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 ''
652 self
.logger
.info("Disconnect complete.")
653 # Begin scanning again
655 self
.commQueue
.put("scan")
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.
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'))]
673 ifconfig_info
= Popen(ifconfig_command
, stdout
=PIPE
).stdout
674 except OSError, (errno
, strerror
):
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)
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.
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)
701 # Returns the current ESSID, if any, by calling iwconfig.
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"""
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
):
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)
725 # Returns the current BSSID, if any, by calling iwconfig.
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"""
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
):
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)
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.
753 # Create a new radar_window.
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
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
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
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
)
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
)
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
)
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
)
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
)
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() )
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)
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)
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 )
895 net_list
.pack_start( self
.plist
, True, True, 0 )
896 net_list
.pack_start( sb
, False, False, 0 )
898 rows
.pack_start( net_list
, True, True, 0 )
899 rows
.pack_start( self
.current_network
, False, True, 0 )
901 listcols
.pack_start( rows
, True, True, 0 )
902 listcols
.pack_start( prows
, False, False, 5 )
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
)
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
)
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'
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.
966 # 'widget' -- gtk.Widget - The widget sending the event.
971 def destroy( self
, widget
= None):
972 if self
.status_window
:
973 self
.status_window
.destroy()
976 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
980 # 'widget' -- gtk.Widget - The widget sending the event.
982 # 'data' -- tuple - list of arbitrary arguments (not used)
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:
996 # Update the current ip and essid shown to user.
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()))
1009 self
.current_network
.set_text("Not Connected.")
1011 # Set the state of connect/disconnect buttons based on whether we have a connection.
1020 def update_connect_buttons(self
):
1021 if self
.connection
and self
.connection
.state
:
1022 self
.connect_button
.hide()
1023 self
.disconnect_button
.show()
1025 self
.disconnect_button
.hide()
1026 self
.connect_button
.show()
1028 # Updates the on-screen profiles list.
1032 # 'ap' -- dictionary -- The AP found by scanning_thread.
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'
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']
1050 # No profile, so make a new one.
1051 profile
= get_new_profile()
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'])
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'])
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.
1076 # 'ap' -- dictionary -- The AP found by scanning_thread.
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
)
1086 # Check for normal profile.
1087 ap_name
= make_section_name(ap
['essid'], ap
['bssid'])
1088 profile
= self
.confFile
.get_profile(ap_name
)
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']:
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']
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']
1113 self
.access_points
[ap_name
]['signal'] = ap
['signal']
1114 self
.access_points
[ap_name
]['channel'] = ap
['channel']
1116 # Not seen before, begin tracking it.
1117 self
.access_points
[ap_name
] = profile
1119 # Updates the main user interface.
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()
1134 # Get APs scanned by iwlist
1136 ap
= self
.apQueue
.get_nowait()
1140 self
.update_ap_list(ap
)
1141 self
.update_plist_items(ap
)
1142 # Allow other Gtk threads to run
1143 gtk
.gdk
.threads_leave()
1146 # Return the proper icon for a value of known.
1150 # 'known' -- boolean - Whether the AP is known (i.e. configured)
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 """
1158 return self
.known_profile_icon
1160 return self
.unknown_profile_icon
1162 # Return an icon indicating the signal level.
1166 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
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
1177 return self
.signal_none_pb
1179 return self
.signal_low_pb
1181 return self
.signal_barely_pb
1183 return self
.signal_ok_pb
1185 return self
.signal_best_pb
1189 # Return row which holds specified ESSID and BSSID.
1193 # 'essid' -- string - ESSID to match
1195 # 'bssid' -- string - BSSID to match
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
1207 for row
in self
.pstore
:
1208 if (row
[0] == essid
+ "\n" + bssid
):
1209 #print "normal match:", row.iter, essid, bssid
1213 # Enable/disable buttons based on the selected network.
1217 # 'widget' -- gtk.Widget - The widget sending the event.
1219 # 'data' -- tuple - list of arbitrary arguments (not used)
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)
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)
1240 self
.edit_button
.set_sensitive(True)
1241 self
.delete_button
.set_sensitive(False)
1243 # Init and run the about dialog
1247 # 'widget' -- gtk.Widget - The widget sending the event.
1249 # 'data' -- tuple - list of arbitrary arguments (not used)
1254 def show_about_info( self
, widget
, data
=None ):
1255 about
= about_dialog()
1259 # Init and run the preferences dialog
1263 # 'widget' -- gtk.Widget - The widget sending the event.
1265 # 'data' -- tuple - list of arbitrary arguments (not used)
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
):
1278 # get cooked strings from config file
1279 self
.confFile
.raw
= False
1281 # Respond to a request to create a new AP profile
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)
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
)
1297 profile
= profile_editor
.run()
1299 error_dlg
= ErrorDialog(None, "Cannot save empty ESSID")
1303 profile_editor
.destroy()
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'], '')
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
))
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
)
1324 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
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
) )
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'
1337 prow_iter
= self
.get_row_by_ap(profile
['essid'], 'roaming')
1339 self
.pstore
.remove(prow_iter
)
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']])
1347 # Did not create new profile
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.
1355 # 'widget' -- gtk.Widget - The widget sending the event.
1357 # 'data' -- tuple - list of arbitrary arguments (not used)
1362 def edit_profile(self
, widget
, data
=None):
1363 (store
, selected_iter
) = self
.plist
.get_selection().get_selected()
1364 if not selected_iter
:
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
, '')
1372 # AP list says this is NOT a roaming profile
1373 apname
= make_section_name(essid
, bssid
)
1374 profile
= self
.confFile
.get_profile(apname
)
1376 # A profile was found in the config file
1377 profile
['bssid'] = self
.access_points
[apname
]['bssid']
1378 profile_editor
= profile_dialog(self
, profile
)
1380 # try editing the profile
1381 edited_profile
= profile_editor
.run()
1383 error_dlg
= ErrorDialog(None, "Cannot save empty ESSID")
1387 # Always remove profile editor window from screen
1388 profile_editor
.destroy()
1390 # A profile was returned by the editor
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
1396 self
.commandQueue
.put('pause')
1397 self
.commandQueue
.join()
1400 if profile
['roaming']:
1401 # The old profile was a roaming profile
1402 old_ap
= make_section_name(profile
['essid'], '')
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
)
1412 # Add AP to the list displayed to user
1413 self
.apQueue
.put_nowait(edited_profile
)
1414 self
.commandQueue
.put('scan')
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
1423 prow_iter
= self
.get_row_by_ap(edited_profile
['essid'], 'roaming')
1425 self
.pstore
.remove(prow_iter
)
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
)
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
))
1448 raise IOError(error_number
, error_str
)
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)
1459 # 'selected_iter' -- gtk.TreeIter - The selected row.
1461 # 'apname' -- string - The configuration file section to remove
1465 # gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
1466 def delete_profile(self
, selected_iter
, apname
):
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()
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
))
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.
1491 # 'widget' -- gtk.Widget - The widget sending the event.
1493 # 'data' -- tuple - list of arbitrary arguments (not used)
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
, '')
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
, ))
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
1516 if res
== gtk
.RESPONSE_NO
:
1518 self
.delete_profile(selected_iter
, apname
)
1520 # Respond to a request to connect to an AP.
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)
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 )
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?")
1543 if res
== gtk
.RESPONSE_NO
:
1545 profile
= get_new_profile()
1546 profile
['essid'] = essid
1547 profile
['bssid'] = bssid
1548 if not self
.create_new_profile( widget
, profile
, data
):
1551 # Check for roaming profile.
1552 ap_name
= make_section_name(essid
, '')
1553 profile
= self
.confFile
.get_profile(ap_name
)
1555 # Check for normal profile.
1556 ap_name
= make_section_name(essid
, bssid
)
1557 profile
= self
.confFile
.get_profile(ap_name
)
1559 # No configured profile
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().
1569 # 'widget' -- gtk.Widget - The widget sending the event.
1571 # 'data' -- tuple - list of arbitrary arguments (not used)
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)
1583 self
.connection
.disconnect_interface()
1585 # Update the config file auto profile order from the on-screen order
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)
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()
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
, '')
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
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
) )
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
1629 # 'parent' -- gtk.Object -- Usually, the calling window.
1631 # 'entry' -- gtk.Entry -- The text entry to update with user selection.
1635 # file_browse_button instance
1636 def __init__( self
, parent
, entry
):
1637 self
.parent_window
= parent
1639 gtk
.Button
.__init
__(self
, "Browse", None)
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
1648 # 'widget' -- gtk.Widget -- The widget sending the event.
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()
1663 self
.entry
.set_text(filename
)
1664 self
.browser_dialog
.destroy()
1667 # Simple dialog to report an error to the user.
1669 # Create a new ErrorDialog.
1673 # 'parent' -- gtk.Object - Usually, the calling window.
1675 # 'message' -- string - The message to display to the user.
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
)
1687 # The preferences dialog. Edits non-profile sections of the config file.
1688 class preferences_dialog
:
1689 # Create a new preferences_dialog.
1693 # 'parent' -- gtk.Object - Usually, the calling window.
1695 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
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()
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()
1731 iwconfig_info
= Popen([self
.confFile
.get_opt('DEFAULT.iwconfig_command')], stdout
=PIPE
, stderr
=STDOUT
).stdout
1732 except OSError, (errno
, strerror
):
1734 logger
.critical("iwconfig command not found, please set this in the preferences.")
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)
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)
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)
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)
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
1802 # table to use for layout of following command configurations
1803 self
.cmds_table
= gtk
.Table()
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)
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)
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)
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)
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
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"))
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"))
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.
1993 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1995 # 'data' -- tuple - list of arbitrary arguments (not used)
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.
2007 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2009 # 'data' -- tuple - list of arbitrary arguments (not used)
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.
2026 # integer -- gtk response ID
2028 self
.dialog
.show_all()
2029 return self
.dialog
.run()
2031 # Write updated values to config file.
2041 if self
.w_auto_detect
.get_active():
2042 self
.confFile
.set_opt('DEFAULT.interface', "auto_detect")
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())
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
) )
2077 raise IOError(error_number
, error_str
)
2079 # Remove preferences window.
2089 self
.dialog
.destroy()
2093 # Edit and return an AP profile.
2094 class profile_dialog
:
2095 # Create a new profile_dialog.
2099 # 'parent' -- gtk.Object - Usually, the calling window.
2101 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
2105 # profile_dialog instance
2106 def __init__( self
, parent
, profile
):
2107 global wifi_radar_icon
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 )
2132 self
.tooltips
= gtk
.Tooltips()
2134 general_table
= gtk
.Table()
2135 general_table
.set_row_spacings(3)
2136 general_table
.set_col_spacings(3)
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.")
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 )
2169 wifi_table
.attach( gtk
.Label( 'Mode' ), 0, 1, 0, 1 )
2170 wifi_table
.attach( gtk
.Label( 'Channel' ), 0, 1, 1, 2 )
2171 wifi_table
.attach( gtk
.Label( 'Key' ), 0, 1, 2, 3 )
2172 wifi_table
.attach( gtk
.Label( 'Security' ), 0, 1, 3, 4 )
2173 # The WiFi text boxes
2174 self
.mode_combo
= gtk
.combo_box_new_text()
2175 for mode
in self
.WIFI_MODES
:
2176 self
.mode_combo
.append_text( mode
)
2177 self
.mode_combo
.set_active( self
.get_array_index( self
.profile
['mode'], self
.WIFI_MODES
) )
2178 wifi_table
.attach( self
.mode_combo
, 1, 2, 0, 1 )
2179 self
.tooltips
.set_tip(self
.mode_combo
, "Method to use for connection. You probably want auto mode.")
2180 self
.channel_combo
= gtk
.combo_box_new_text()
2181 for channel
in self
.WIFI_CHANNELS
:
2182 self
.channel_combo
.append_text( channel
)
2183 self
.channel_combo
.set_active( self
.get_array_index( self
.profile
['channel'], self
.WIFI_CHANNELS
) )
2184 wifi_table
.attach( self
.channel_combo
, 1, 2, 1, 2 )
2185 self
.tooltips
.set_tip(self
.channel_combo
, "Channel the network uses. You probably want auto mode.")
2187 self
.key_entry
= gtk
.Entry( 64 )
2188 self
.key_entry
.set_text( self
.profile
['key'] )
2189 wifi_table
.attach( self
.key_entry
, 1, 2, 2, 3 )
2190 self
.tooltips
.set_tip(self
.key_entry
, "WEP key: Plain language or hex string to use for encrypted communication with the network.")
2192 self
.security_combo
= gtk
.combo_box_new_text()
2193 for security
in self
.WIFI_SECURITY
:
2194 self
.security_combo
.append_text( security
)
2195 self
.security_combo
.set_active( self
.get_array_index( self
.profile
['security'], self
.WIFI_SECURITY
) )
2196 wifi_table
.attach( self
.security_combo
, 1, 2, 3, 4 )
2197 self
.tooltips
.set_tip(self
.security_combo
, "Use Open to allow unencrypted communication and Restricted to force encrypted-only communication.")
2198 # Add the wifi table to the expander
2199 self
.wifi_expander
.add( wifi_table
)
2200 # Add the expander to the dialog
2201 self
.dialog
.vbox
.pack_start( self
.wifi_expander
, False, False, 5 )
2203 # create the wpa expander
2204 self
.wpa_expander
= gtk
.Expander( self
.NO_WPA_LABEL
)
2205 self
.wpa_expander
.connect( 'notify::expanded', self
.toggle_use_wpa
)
2206 wpa_table
= gtk
.Table( 1, 2, False )
2207 wpa_table
.set_row_spacings( 3 )
2208 wpa_table
.set_col_spacings( 3 )
2210 wpa_table
.attach( gtk
.Label( 'Driver' ), 0, 1, 0, 1 )
2212 self
.wpa_driver_entry
= gtk
.Entry()
2213 self
.wpa_driver_entry
.set_text( self
.profile
['wpa_driver'] )
2214 wpa_table
.attach( self
.wpa_driver_entry
, 1, 2, 0, 1 )
2215 # Add the wpa table to the expander
2216 self
.wpa_expander
.add( wpa_table
)
2217 self
.wpa_expander
.set_expanded(self
.profile
['use_wpa'])
2218 # Add the expander to the dialog
2219 self
.dialog
.vbox
.pack_start( self
.wpa_expander
, False, False, 5 )
2221 # create the dhcp expander
2222 self
.dhcp_expander
= gtk
.Expander( self
.USE_DHCP_LABEL
)
2223 self
.dhcp_expander
.connect( 'notify::expanded', self
.toggle_use_dhcp
)
2224 self
.dhcp_expander
.set_expanded(not self
.profile
['use_dhcp'])
2225 ip_table
= gtk
.Table( 6, 2, False )
2226 ip_table
.set_row_spacings( 3 )
2227 ip_table
.set_col_spacings( 3 )
2229 ip_table
.attach( gtk
.Label( 'IP' ), 0, 1, 0, 1 )
2230 ip_table
.attach( gtk
.Label( 'Netmask' ), 0, 1, 1, 2 )
2231 ip_table
.attach( gtk
.Label( 'Gateway' ), 0, 1, 2, 3 )
2232 ip_table
.attach( gtk
.Label( 'Domain' ), 0, 1, 3, 4 )
2233 ip_table
.attach( gtk
.Label( 'DNS' ), 0, 1, 4, 5 )
2234 ip_table
.attach( gtk
.Label( 'DNS' ), 0, 1, 5, 6 )
2236 self
.ip_entry
= gtk
.Entry( 15 )
2237 self
.ip_entry
.set_text( self
.profile
['ip'] )
2238 ip_table
.attach( self
.ip_entry
, 1, 2, 0, 1 )
2239 self
.netmask_entry
= gtk
.Entry( 15 )
2240 self
.netmask_entry
.set_text( self
.profile
['netmask'] )
2241 ip_table
.attach( self
.netmask_entry
, 1, 2, 1, 2 )
2242 self
.gw_entry
= gtk
.Entry( 15 )
2243 self
.gw_entry
.set_text( self
.profile
['gateway'] )
2244 ip_table
.attach( self
.gw_entry
, 1, 2, 2, 3 )
2245 self
.domain_entry
= gtk
.Entry( 32 )
2246 self
.domain_entry
.set_text( self
.profile
['domain'] )
2247 ip_table
.attach( self
.domain_entry
, 1, 2, 3, 4 )
2248 self
.dns1_entry
= gtk
.Entry( 15 )
2249 self
.dns1_entry
.set_text( self
.profile
['dns1'] )
2250 ip_table
.attach( self
.dns1_entry
, 1, 2, 4, 5 )
2251 self
.dns2_entry
= gtk
.Entry( 15 )
2252 self
.dns2_entry
.set_text( self
.profile
['dns2'] )
2253 ip_table
.attach( self
.dns2_entry
, 1, 2, 5, 6 )
2254 # Add the ip table to the expander
2255 self
.dhcp_expander
.add( ip_table
)
2256 # Add the expander to the dialog
2257 self
.dialog
.vbox
.pack_start( self
.dhcp_expander
, False, False, 5 )
2259 # create the connection-building postpre expander
2260 self
.con_pp_expander
= gtk
.Expander( self
.CON_PP_LABEL
)
2261 con_pp_table
= gtk
.Table( 2, 2, False )
2262 con_pp_table
.set_row_spacings( 3 )
2263 con_pp_table
.set_col_spacings( 3 )
2265 con_pp_table
.attach( gtk
.Label( 'Before' ), 0, 1, 0, 1 )
2266 con_pp_table
.attach( gtk
.Label( 'After' ), 0, 1, 1, 2 )
2268 self
.con_prescript_entry
= gtk
.Entry()
2269 self
.con_prescript_entry
.set_text( self
.profile
['con_prescript'] )
2270 con_pp_table
.attach( self
.con_prescript_entry
, 1, 2, 0, 1 )
2271 self
.tooltips
.set_tip(self
.con_prescript_entry
, "The local command to execute before trying to connect to the network.")
2272 self
.con_postscript_entry
= gtk
.Entry()
2273 self
.con_postscript_entry
.set_text( self
.profile
['con_postscript'] )
2274 con_pp_table
.attach( self
.con_postscript_entry
, 1, 2, 1, 2 )
2275 self
.tooltips
.set_tip(self
.con_postscript_entry
, "The local command to execute after connecting to the network.")
2276 # Add the pp table to the expander
2277 self
.con_pp_expander
.add( con_pp_table
)
2278 # Add the expander to the dialog
2279 self
.dialog
.vbox
.pack_start( self
.con_pp_expander
, False, False, 5 )
2281 # create the disconnection postpre expander
2282 self
.dis_pp_expander
= gtk
.Expander( self
.DIS_PP_LABEL
)
2283 dis_pp_table
= gtk
.Table( 2, 2, False )
2284 dis_pp_table
.set_row_spacings( 3 )
2285 dis_pp_table
.set_col_spacings( 3 )
2287 dis_pp_table
.attach( gtk
.Label( 'Before' ), 0, 1, 0, 1 )
2288 dis_pp_table
.attach( gtk
.Label( 'After' ), 0, 1, 1, 2 )
2290 self
.dis_prescript_entry
= gtk
.Entry()
2291 self
.dis_prescript_entry
.set_text( self
.profile
['dis_prescript'] )
2292 dis_pp_table
.attach( self
.dis_prescript_entry
, 1, 2, 0, 1 )
2293 self
.tooltips
.set_tip(self
.dis_prescript_entry
, "The local command to execute before disconnecting from the network.")
2294 self
.dis_postscript_entry
= gtk
.Entry()
2295 self
.dis_postscript_entry
.set_text( self
.profile
['dis_postscript'] )
2296 dis_pp_table
.attach( self
.dis_postscript_entry
, 1, 2, 1, 2 )
2297 self
.tooltips
.set_tip(self
.dis_postscript_entry
, "The local command to execute after disconnecting from the network.")
2298 # Add the pp table to the expander
2299 self
.dis_pp_expander
.add( dis_pp_table
)
2300 # Add the expander to the dialog
2301 self
.dialog
.vbox
.pack_start( self
.dis_pp_expander
, False, False, 5 )
2303 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
2311 # dictionary or None -- a profile, or None on cancel
2315 # Raises ValueError if an attempt is made to save an ESSID with no name.
2317 self
.dialog
.show_all()
2318 if self
.dialog
.run():
2319 if self
.essid_entry
.get_text().strip() == "":
2321 self
.profile
['known'] = True
2322 self
.profile
['essid'] = self
.essid_entry
.get_text().strip()
2323 if self
.roaming_cb
.get_active():
2324 self
.profile
['bssid'] = ''
2326 self
.profile
['bssid'] = self
.bssid_entry
.get_text().strip()
2327 self
.profile
['roaming'] = self
.roaming_cb
.get_active()
2328 self
.profile
['key'] = self
.key_entry
.get_text().strip()
2329 self
.profile
['mode'] = self
.get_array_item( self
.mode_combo
.get_active(), self
.WIFI_MODES
)
2330 self
.profile
['security'] = self
.get_array_item( self
.security_combo
.get_active(), self
.WIFI_SECURITY
)
2331 self
.profile
['encrypted'] = ( self
.profile
['security'] != '' )
2332 self
.profile
['channel'] = self
.get_array_item( self
.channel_combo
.get_active(), self
.WIFI_CHANNELS
)
2333 self
.profile
['protocol'] = 'g'
2334 self
.profile
['available'] = ( self
.profile
['signal'] > 0 )
2335 self
.profile
['con_prescript'] = self
.con_prescript_entry
.get_text().strip()
2336 self
.profile
['con_postscript'] = self
.con_postscript_entry
.get_text().strip()
2337 self
.profile
['dis_prescript'] = self
.dis_prescript_entry
.get_text().strip()
2338 self
.profile
['dis_postscript'] = self
.dis_postscript_entry
.get_text().strip()
2340 self
.profile
['use_wpa'] = self
.wpa_expander
.get_expanded()
2341 self
.profile
['wpa_driver'] = self
.wpa_driver_entry
.get_text().strip()
2343 self
.profile
['use_dhcp'] = not self
.dhcp_expander
.get_expanded()
2344 self
.profile
['ip'] = self
.ip_entry
.get_text().strip()
2345 self
.profile
['netmask'] = self
.netmask_entry
.get_text().strip()
2346 self
.profile
['gateway'] = self
.gw_entry
.get_text().strip()
2347 self
.profile
['domain'] = self
.domain_entry
.get_text().strip()
2348 self
.profile
['dns1'] = self
.dns1_entry
.get_text().strip()
2349 self
.profile
['dns2'] = self
.dns2_entry
.get_text().strip()
2353 # Remove profile dialog.
2362 def destroy( self
):
2363 self
.dialog
.destroy()
2366 # Respond to roaming checkbox toggle by activating/de-activating the BSSID text entry.
2370 # 'roaming_toggle' -- gtk.CheckButton - The checkbox sending the signal.
2372 # 'data' -- tuple - list of arbitrary arguments (not used)
2377 def toggle_roaming(self
, roaming_toggle
, data
=None):
2378 self
.bssid_entry
.set_sensitive(not roaming_toggle
.get_active())
2380 # Respond to expanding/hiding IP segment.
2384 # 'widget' -- gtk.Widget - The widget sending the event.
2386 # 'data' -- tuple - List of arbitrary arguments (not used)
2391 def toggle_use_dhcp( self
, widget
, data
= None ):
2392 expanded
= self
.dhcp_expander
.get_expanded()
2394 self
.dhcp_expander
.set_label( self
.USE_IP_LABEL
)
2396 self
.dhcp_expander
.set_label( self
.USE_DHCP_LABEL
)
2398 # Respond to expanding/hiding WPA segment.
2402 # 'widget' -- gtk.Widget - The widget sending the event.
2404 # 'data' -- tuple - List of arbitrary arguments (not used)
2409 def toggle_use_wpa( self
, widget
, data
= None ):
2410 expanded
= self
.wpa_expander
.get_expanded()
2412 self
.wpa_expander
.set_label( self
.USE_WPA_LABEL
)
2414 self
.wpa_expander
.set_label( self
.NO_WPA_LABEL
)
2416 # Return the index where item matches a cell in array.
2420 # 'item' -- string - Item to find in array
2422 # 'array' -- list - List in which to find match.
2426 # integer - 0 (no match) or higher (index of match)
2427 def get_array_index( self
, item
, array
):
2429 return array
.index( item
.strip() )
2434 # Return the value in array[ index ]
2438 # 'index' -- integer - The index to look up.
2440 # 'array' -- list - List in which to look up value.
2444 # string -- empty string (no match) or looked up value
2445 def get_array_item( self
, index
, array
):
2447 return array
[ index
]
2453 # A simple class for putting up a "Please wait" dialog so the user
2454 # doesn't think we've forgotten about them. Implements the status interface.
2456 # Create a new StatusWindow.
2460 # 'parent' -- gtk.Object - Usually, the calling window.
2464 # StatusWindow instance
2468 # Sample implementation of status interface. Status interface
2469 #requires .show(), .update_message(message), and .hide() methods.
2470 def __init__( self
, parent
):
2471 global wifi_radar_icon
2472 self
.parent
= parent
2473 self
.dialog
= gtk
.Dialog("Working", self
.parent
.window
, gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
)
2474 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
2475 self
.dialog
.set_icon( icon
)
2476 self
.lbl
= gtk
.Label("Please wait...")
2477 self
.bar
= gtk
.ProgressBar()
2478 self
.dialog
.vbox
.pack_start(self
.lbl
)
2479 self
.dialog
.vbox
.pack_start(self
.bar
)
2480 self
.cancel_button
= self
.dialog
.add_button(gtk
.STOCK_CANCEL
, gtk
.BUTTONS_CANCEL
)
2483 # Change the message displayed to the user.
2487 # 'message' -- string - The message to show to the user.
2492 def update_message( self
, message
):
2493 self
.lbl
.set_text(message
)
2495 # Update the StatusWindow progress bar.
2503 # True -- always return True
2504 def update_window( self
):
2508 # Display and operate the StatusWindow.
2520 # Show all the widgets of the StatusWindow.
2530 self
.dialog
.show_all()
2531 self
.timer
= gobject
.timeout_add(250, self
.update_window
)
2534 # Hide all the widgets of the StatusWindow.
2545 gobject
.source_remove(self
.timer
)
2547 self
.dialog
.hide_all()
2550 # Remove the StatusWindow.
2559 def destroy( self
):
2561 gobject
.source_remove(self
.timer
)
2562 self
.dialog
.destroy()
2566 # Manage a GTK About Dialog
2567 class about_dialog(gtk
.AboutDialog
):
2568 # Subclass GTK AboutDialog
2577 def __init__( self
):
2578 global wifi_radar_icon
2580 gtk
.AboutDialog
.__init
__(self
)
2581 self
.set_authors(["Ahmad Baitalmal <ahmad@baitalmal.com>", "Brian Elliott Finley <brian@thefinleys.com>", "Sean Robinson <seankrobinson@gmail.com>", "", "Contributors", "Douglas Breault", "Jon Collette", "David Decotigny", "Simon Gerber", "Joey Hurst", "Ante Karamatic", "Richard Monk", "Nicolas Brouard", "Kevin Otte", "Nathanael Rebsch", "Andrea Scarpino", "Prokhor Shuchalov", "Patrick Winnertz"])
2582 self
.set_comments("WiFi connection manager")
2583 self
.set_copyright("Copyright 2004-2010 by various authors and contributors\nCurrent Maintainer: Sean Robinson <seankrobinson@gmail.com>")
2584 self
.set_documenters(["Gary Case"])
2586 This program is free software; you can redistribute it and/or modify
2587 it under the terms of the GNU General Public License as published by
2588 the Free Software Foundation; either version 2 of the License, or
2589 (at your option) any later version.
2591 This program is distributed in the hope that it will be useful,
2592 but WITHOUT ANY WARRANTY; without even the implied warranty of
2593 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2594 GNU General Public License for more details.
2596 You should have received a copy of the GNU General Public License
2597 along with this program; if not, write to the Free Software
2598 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
2599 self
.set_license(license
)
2600 logo
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
2602 self
.set_name("WiFi Radar")
2603 self
.set_version(WIFI_RADAR_VERSION
)
2604 self
.set_website("http://wifi-radar.berlios.de")
2608 # Manage the configuration for the application, including reading and writing the config from/to a file.
2609 class ConfigFile(ConfigParser
.SafeConfigParser
):
2610 # Create a new ConfigFile.
2614 # 'filename' -- string - The configuration file's name.
2616 # 'defaults' -- dictionary - Default values for the DEFAULT section.
2620 # ConfigFile instance
2621 def __init__( self
, filename
, defaults
, raw
=False ):
2622 self
.filename
= filename
2624 self
.auto_profile_order
= []
2625 ConfigParser
.SafeConfigParser
.__init
__(self
, defaults
)
2627 # Set the contents of a section to values from a dictionary.
2631 # 'section_name' -- string - Configuration file section.
2633 # 'section_dict' -- dictionary - Values to add to section.
2638 def set_section( self
, section_name
, section_dict
):
2640 self
.add_section(section_name
)
2641 except ConfigParser
.DuplicateSectionError
:
2643 for key
in section_dict
.keys():
2644 if type(section_dict
[key
]) == BooleanType
:
2645 self
.set_bool_opt(section_name
+ "." + key
, section_dict
[key
])
2646 elif type(section_dict
[key
]) == IntType
:
2647 self
.set_int_opt(section_name
+ "." + key
, section_dict
[key
])
2648 elif type(section_dict
[key
]) == FloatType
:
2649 self
.set_float_opt(section_name
+ "." + key
, section_dict
[key
])
2651 self
.set_opt(section_name
+ "." + key
, section_dict
[key
])
2653 # Return the profile recorded in the specified section.
2657 # 'section_name' -- string - Configuration file section.
2661 # dictionary or None - The specified profile or None if not found
2662 def get_profile( self
, section_name
):
2663 if section_name
in self
.profiles():
2664 str_types
= [ 'bssid', 'channel', 'essid', 'protocol', 'con_prescript', 'con_postscript', 'dis_prescript', 'dis_postscript', 'key', 'mode', 'security', 'wpa_driver', 'ip', 'netmask', 'gateway', 'domain', 'dns1', 'dns2' ]
2665 bool_types
= [ 'known', 'available', 'roaming', 'encrypted', 'use_wpa', 'use_dhcp' ]
2666 int_types
= [ 'signal' ]
2667 profile
= get_new_profile()
2668 for option
in bool_types
:
2669 profile
[option
] = self
.get_opt_as_bool(section_name
+ "." + option
)
2670 for option
in int_types
:
2671 option_tmp
= self
.get_opt_as_int(section_name
+ "." + option
)
2673 profile
[option
] = option_tmp
2674 for option
in str_types
:
2675 option_tmp
= self
.get_opt(section_name
+ "." + option
)
2677 profile
[option
] = option_tmp
2681 # Get a config option and handle exceptions.
2685 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2686 # period and the option key. (E.g. "DEFAULT.interface")
2690 # string or None - option value as string or None on failure
2691 def get_opt( self
, option_path
):
2692 #print "ConfigFile.get_opt: ", option_path
2693 (section
, option
) = option_path
.split('.')
2695 return self
.get(section
, option
, self
.raw
)
2696 except (ConfigParser
.NoSectionError
, ConfigParser
.NoOptionError
):
2699 # Get a config option and return as a boolean type.
2703 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2704 # period and the option key. (E.g. "DEFAULT.interface")
2708 # boolean - option value as boolean
2709 def get_opt_as_bool( self
, option_path
):
2710 option
= self
.get_opt(option_path
)
2711 if isinstance(option
, BooleanType
) or isinstance(option
, NoneType
):
2713 if option
== 'True':
2715 if option
== 'False':
2717 raise ValueError, 'boolean option was not True or False'
2719 # Get a config option and return as an integer type.
2723 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2724 # period and the option key. (E.g. "DEFAULT.interface")
2728 # integer- option value as integer
2729 def get_opt_as_int( self
, option_path
):
2730 return int(float(self
.get_opt(option_path
)))
2732 # Convert boolean type to string and set config option.
2736 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2737 # period and the option key. (E.g. "DEFAULT.interface")
2739 # 'value' -- boolean - Value to set.
2744 def set_bool_opt( self
, option_path
, value
):
2745 if ( value
== True ) or ( value
> 0 ) or ( value
== 'True' ):
2747 elif ( value
== False ) or ( value
== 0 ) or ( value
== 'False' ):
2750 raise ValueError, 'cannot convert value to string'
2751 self
.set_opt(option_path
, repr(value
))
2753 # Convert integer type to string and set config option.
2757 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2758 # period and the option key. (E.g. "DEFAULT.interface")
2760 # 'value' -- integer - Value to set.
2765 def set_int_opt( self
, option_path
, value
):
2766 if not isinstance(value
, IntType
):
2767 raise ValueError, 'value is not an integer'
2768 self
.set_opt(option_path
, repr(value
))
2770 # Convert float type to string and set config option.
2774 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2775 # period and the option key. (E.g. "DEFAULT.interface")
2777 # 'value' -- float - Value to set.
2782 def set_float_opt( self
, option_path
, value
):
2783 if not isinstance(value
, FloatType
):
2784 raise ValueError, 'value is not a float'
2785 self
.set_opt(option_path
, repr(int(value
)))
2787 # Set a config option while handling exceptions.
2791 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
2792 # period and the option key. (E.g. "DEFAULT.interface")
2794 # 'value' -- string - Value to set.
2799 def set_opt( self
, option_path
, value
):
2800 (section
, option
) = option_path
.split('.')
2802 self
.set(section
, option
, value
)
2803 except ConfigParser
.NoSectionError
:
2804 self
.add_section(section
)
2805 self
.set_opt(option_path
, value
)
2807 # Return a list of the section names which denote AP profiles.
2815 # list - profile names
2816 def profiles( self
):
2818 for section
in self
.sections():
2820 profile_list
.append(section
)
2823 # Read configuration file from disk into instance variables.
2833 fp
= open( self
.filename
, "r" )
2835 # convert the auto_profile_order to a list for ordering
2836 self
.auto_profile_order
= eval(self
.get_opt('DEFAULT.auto_profile_order'))
2837 for ap
in self
.profiles():
2838 self
.set_bool_opt( ap
+ '.known', True)
2839 if ap
in self
.auto_profile_order
: continue
2840 self
.auto_profile_order
.append( ap
)
2842 # Remove any auto_profile_order AP without a matching section.
2843 auto_profile_order_copy
= self
.auto_profile_order
[:]
2844 for ap
in auto_profile_order_copy
:
2845 if ap
not in self
.profiles():
2846 self
.auto_profile_order
.remove(ap
)
2848 # Write configuration file to disk from instance variables. Copied from
2849 # ConfigParser and modified to write options in alphabetical order.
2859 self
.set_opt('DEFAULT.auto_profile_order', str(self
.auto_profile_order
))
2860 self
.set_opt('DEFAULT.version', WIFI_RADAR_VERSION
)
2861 (fd
, tempfilename
) = tempfile
.mkstemp(prefix
="wifi-radar.conf.")
2862 fp
= os
.fdopen(fd
, "w")
2863 # write DEFAULT section first
2865 fp
.write("[DEFAULT]\n")
2866 for key
in sorted(self
._defaults
.keys()):
2867 fp
.write("%s = %s\n" % (key
, str(self
._defaults
[key
]).replace('\n','\n\t')))
2869 # write other non-profile sections next
2870 for section
in self
._sections
:
2871 if section
not in self
.profiles():
2872 fp
.write("[%s]\n" % section
)
2873 for key
in sorted(self
._sections
[section
].keys()):
2874 if key
!= "__name__":
2875 fp
.write("%s = %s\n" %
2876 (key
, str(self
._sections
[section
][key
]).replace('\n', '\n\t')))
2878 # write profile sections
2879 for section
in self
._sections
:
2880 if section
in self
.profiles():
2881 fp
.write("[%s]\n" % section
)
2882 for key
in sorted(self
._sections
[section
].keys()):
2883 if key
!= "__name__":
2884 fp
.write("%s = %s\n" %
2885 (key
, str(self
._sections
[section
][key
]).replace('\n', '\n\t')))
2888 move(tempfilename
, self
.filename
)
2890 # Load our conf file and known profiles
2891 # Defaults, these may get overridden by values found in the conf file.
2892 config_defaults
= { # The network interface you use.
2893 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2894 'interface': "auto_detect",
2895 # How long should the scan for access points last?
2896 #'scan_timeout': '5',
2897 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2898 # Set the speak_up option to false if you do not have or want this.
2899 'speak_command': '/usr/bin/say',
2900 # Should I speak up when connecting to a network? (If you have a speech command)
2901 'speak_up': 'False',
2902 # You may set this to true for cards that require a "commit" command with iwconfig
2903 'commit_required': 'False',
2904 # You may set this to true for cards that require the interface to be brought up first
2905 'ifup_required': 'False',
2906 # set the location and verbosity of the log file
2907 'logfile': '/var/log/wifi-radar.log',
2909 # Set the location of several important programs
2910 'iwlist_command': '/sbin/iwlist',
2911 'iwconfig_command': '/sbin/iwconfig',
2912 'ifconfig_command': '/sbin/ifconfig',
2913 'route_command': '/sbin/route',
2914 'auto_profile_order': '[]',
2915 'version': WIFI_RADAR_VERSION
}
2917 config_dhcp
= { # DHCP client
2918 'command': '/sbin/dhcpcd',
2919 # How long to wait for an IP addr from DHCP server
2921 # Arguments to use with DHCP client on connect
2922 'args': '-D -o -i dhcp_client -t %(timeout)s',
2923 # Argument to use with DHCP client on disconnect
2925 # The file where DHCP client PID is written
2926 'pidfile': '/etc/dhcpc/dhcpcd-%(interface)s.pid' }
2928 config_wpa
= { # WPA Supplicant
2929 'command': '/usr/sbin/wpa_supplicant',
2930 # Arguments to use with WPA Supplicant on connect
2931 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2932 # Arguments to use with WPA Supplicant on disconnect
2934 # Where the WPA Supplicant config file can be found
2935 'configuration': '/etc/wpa_supplicant.conf',
2936 # Driver to use with WPA Supplicant
2938 # The file where WPA Supplicant PID is written
2939 'pidfile': '/var/run/wpa_supplicant.pid' }
2941 # initialize config, with defaults
2942 confFile
= ConfigFile(CONF_FILE
, config_defaults
)
2943 confFile
.set_section("DHCP", config_dhcp
)
2944 confFile
.set_section("WPA", config_wpa
)
2946 if not os
.path
.isfile( CONF_FILE
):
2947 confFile
.set_bool_opt('DEFAULT.new_file', True)
2949 if not os
.access(CONF_FILE
, os
.R_OK
):
2950 print "Can't open " + CONF_FILE
+ "."
2951 print "Are you root?"
2956 error_dlg
= ErrorDialog(None, "A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. Because %s may contain information that you might wish to use when configuring WiFi Radar %s, rename this file and run the program again.\n" % (CONF_FILE
, CONF_FILE
, WIFI_RADAR_VERSION
))
2958 print "ERROR: A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. Because %s may contain information that you might wish to use when configuring WiFi Radar %s, rename this file and run the program again.\n" % (CONF_FILE
, CONF_FILE
, WIFI_RADAR_VERSION
)
2961 error_dlg
= ErrorDialog(None, "A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. The old configuration file is probably empty and can be removed. Rename %s if you want to be very careful. After removing or renaming %s, run this program again.\n" % (CONF_FILE
, CONF_FILE
, CONF_FILE
))
2963 print "ERROR: A configuration file from a pre-2.0 version of WiFi Radar was found at %s.\n\nWiFi Radar v2.0.x does not read configuration files from previous versions. The old configuration file is probably empty and can be removed. Rename %s if you want to be very careful. After removing or renaming %s, run this program again.\n" % (CONF_FILE
, CONF_FILE
, CONF_FILE
)
2967 ####################################################################################################
2969 wifi_radar_icon
= [ ""
2976 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2977 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2978 "\377\0\7\0\0\0\10\0\0\0\25\0\0\0\35\0\0\0%\0\0\0-\0\0\0\"\0\0\0\11\327"
2979 "\377\377\377\0\6\0\0\0\"\0\0\0_\0\0\0\213\0\0\0\266\0\0\0\341\0\0\0\376"
2980 "\206\0\0\0\377\6\0\0\0\356\0\0\0\324\0\0\0\265\0\0\0~\0\0\0@\0\0\0\10"
2981 "\315\377\377\377\0\4\0\0\0\2\0\0\0;\0\0\0\210\0\0\0\325\221\0\0\0\377"
2982 "\4\0\0\0\371\0\0\0\303\0\0\0w\0\0\0\31\310\377\377\377\0\3\0\0\0\6\0"
2983 "\0\0m\0\0\0\342\227\0\0\0\377\4\0\0\0\374\0\0\0\264\0\0\0Q\0\0\0\5\303"
2984 "\377\377\377\0\3\0\0\0\4\0\0\0d\0\0\0\341\234\0\0\0\377\3\0\0\0\341\0"
2985 "\0\0`\0\0\0\2\277\377\377\377\0\3\0\0\0\2\0\0\0[\0\0\0\333\240\0\0\0"
2986 "\377\2\0\0\0\323\0\0\0K\274\377\377\377\0\3\0\0\0\1\0\0\0R\0\0\0\324"
2987 "\244\0\0\0\377\2\0\0\0\276\0\0\0#\271\377\377\377\0\2\0\0\0\31\0\0\0"
2988 "\277\247\0\0\0\377\2\0\0\0\363\0\0\0c\267\377\377\377\0\2\0\0\0/\0\0"
2989 "\0\343\252\0\0\0\377\2\0\0\0\257\0\0\0\24\264\377\377\377\0\2\0\0\0M"
2990 "\0\0\0\363\220\0\0\0\377\14\0\0\0\357\0\0\0\304\0\0\0\230\0\0\0v\0\0"
2991 "\0l\0\0\0c\0\0\0[\0\0\0j\0\0\0\205\0\0\0\240\0\0\0\311\0\0\0\373\220"
2992 "\0\0\0\377\2\0\0\0\346\0\0\0""4\262\377\377\377\0\2\0\0\0q\0\0\0\375"
2993 "\215\0\0\0\377\4\0\0\0\373\0\0\0\300\0\0\0t\0\0\0)\213\377\377\377\0"
2994 "\4\0\0\0\14\0\0\0E\0\0\0\205\0\0\0\334\216\0\0\0\377\2\0\0\0\363\0\0"
2995 "\0D\257\377\377\377\0\2\0\0\0\4\0\0\0\230\215\0\0\0\377\3\0\0\0\372\0"
2996 "\0\0\231\0\0\0\34\221\377\377\377\0\4\0\0\0\1\0\0\0C\0\0\0\251\0\0\0"
2997 "\372\214\0\0\0\377\2\0\0\0\371\0\0\0W\255\377\377\377\0\2\0\0\0\17\0"
2998 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2999 "\0\2\0\0\0\"\0\0\0\252\214\0\0\0\377\2\0\0\0\375\0\0\0k\253\377\377\377"
3000 "\0\2\0\0\0\25\0\0\0\324\213\0\0\0\377\3\0\0\0\376\0\0\0\252\0\0\0(\232"
3001 "\377\377\377\0\2\0\0\0""9\0\0\0\312\214\0\0\0\377\1\0\0\0\200\251\377"
3002 "\377\377\0\2\0\0\0\5\0\0\0\303\213\0\0\0\377\2\0\0\0\332\0\0\0""1\235"
3003 "\377\377\377\0\3\0\0\0\4\0\0\0\201\0\0\0\374\213\0\0\0\377\1\0\0\0p\250"
3004 "\377\377\377\0\1\0\0\0\222\213\0\0\0\377\2\0\0\0\301\0\0\0\22\240\377"
3005 "\377\377\0\2\0\0\0:\0\0\0\336\212\0\0\0\377\2\0\0\0\374\0\0\0I\246\377"
3006 "\377\377\0\1\0\0\0[\213\0\0\0\377\2\0\0\0\241\0\0\0\6\212\377\377\377"
3007 "\0\15\0\0\0\2\0\0\0&\0\0\0U\0\0\0\203\0\0\0\242\0\0\0\243\0\0\0\234\0"
3008 "\0\0\225\0\0\0\215\0\0\0\206\0\0\0}\0\0\0\\\0\0\0!\213\377\377\377\0"
3009 "\2\0\0\0\22\0\0\0\307\212\0\0\0\377\2\0\0\0\361\0\0\0+\244\377\377\377"
3010 "\0\2\0\0\0.\0\0\0\365\211\0\0\0\377\2\0\0\0\376\0\0\0|\211\377\377\377"
3011 "\0\4\0\0\0#\0\0\0d\0\0\0\223\0\0\0\277\214\0\0\0\310\4\0\0\0\253\0\0"
3012 "\0l\0\0\0-\0\0\0\2\210\377\377\377\0\2\0\0\0\12\0\0\0\267\212\0\0\0\377"
3013 "\2\0\0\0\336\0\0\0\24\242\377\377\377\0\2\0\0\0\20\0\0\0\334\211\0\0"
3014 "\0\377\2\0\0\0\367\0\0\0W\210\377\377\377\0\2\0\0\0#\0\0\0\211\223\0"
3015 "\0\0\310\3\0\0\0\266\0\0\0t\0\0\0\27\207\377\377\377\0\2\0\0\0\5\0\0"
3016 "\0\244\212\0\0\0\377\2\0\0\0\302\0\0\0\6\240\377\377\377\0\2\0\0\0\1"
3017 "\0\0\0\264\211\0\0\0\377\2\0\0\0\363\0\0\0""9\207\377\377\377\0\3\0\0"
3018 "\0\34\0\0\0\201\0\0\0\306\226\0\0\0\310\3\0\0\0\277\0\0\0Y\0\0\0\2\206"
3019 "\377\377\377\0\2\0\0\0\1\0\0\0\217\212\0\0\0\377\1\0\0\0\203\240\377"
3020 "\377\377\0\1\0\0\0\177\212\0\0\0\377\1\0\0\0T\206\377\377\377\0\3\0\0"
3021 "\0\25\0\0\0z\0\0\0\305\232\0\0\0\310\2\0\0\0\242\0\0\0*\207\377\377\377"
3022 "\0\1\0\0\0\243\211\0\0\0\377\2\0\0\0\372\0\0\0,\236\377\377\377\0\2\0"
3023 "\0\0D\0\0\0\375\211\0\0\0\377\1\0\0\0\213\206\377\377\377\0\2\0\0\0""8"
3024 "\0\0\0\274\235\0\0\0\310\3\0\0\0\306\0\0\0u\0\0\0\14\205\377\377\377"
3025 "\0\2\0\0\0\7\0\0\0\306\211\0\0\0\377\2\0\0\0\306\0\0\0\2\234\377\377"
3026 "\377\0\2\0\0\0\4\0\0\0\331\211\0\0\0\377\2\0\0\0\276\0\0\0\3\205\377"
3027 "\377\377\0\2\0\0\0T\0\0\0\306\214\0\0\0\310\10\0\0\0\260\0\0\0\202\0"
3028 "\0\0v\0\0\0~\0\0\0\207\0\0\0\217\0\0\0\227\0\0\0\264\214\0\0\0\310\2"
3029 "\0\0\0\264\0\0\0""2\205\377\377\377\0\2\0\0\0\27\0\0\0\341\211\0\0\0"
3030 "\377\1\0\0\0k\234\377\377\377\0\1\0\0\0c\211\0\0\0\377\2\0\0\0\343\0"
3031 "\0\0\26\204\377\377\377\0\2\0\0\0\2\0\0\0s\212\0\0\0\310\4\0\0\0\265"
3032 "\0\0\0s\0\0\0D\0\0\0\26\207\377\377\377\0\4\0\0\0\1\0\0\0+\0\0\0j\0\0"
3033 "\0\250\212\0\0\0\310\2\0\0\0\303\0\0\0A\205\377\377\377\0\2\0\0\0/\0"
3034 "\0\0\364\210\0\0\0\377\2\0\0\0\362\0\0\0\33\232\377\377\377\0\2\0\0\0"
3035 "\7\0\0\0\341\210\0\0\0\377\2\0\0\0\371\0\0\0""7\204\377\377\377\0\2\0"
3036 "\0\0\12\0\0\0\217\211\0\0\0\310\3\0\0\0\271\0\0\0]\0\0\0\10\216\377\377"
3037 "\377\0\3\0\0\0\36\0\0\0t\0\0\0\306\210\0\0\0\310\2\0\0\0\306\0\0\0P\205"
3038 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
3039 "\0\0\0n\211\0\0\0\377\1\0\0\0h\204\377\377\377\0\2\0\0\0\20\0\0\0\245"
3040 "\210\0\0\0\310\3\0\0\0\274\0\0\0c\0\0\0\12\222\377\377\377\0\2\0\0\0"
3041 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
3042 "\0\0\0\377\1\0\0\0:\230\377\377\377\0\2\0\0\0\13\0\0\0\350\210\0\0\0"
3043 "\377\1\0\0\0\250\204\377\377\377\0\2\0\0\0\3\0\0\0\230\210\0\0\0\310"
3044 "\2\0\0\0\213\0\0\0\15\225\377\377\377\0\3\0\0\0\2\0\0\0Z\0\0\0\277\210"
3045 "\0\0\0\310\1\0\0\0U\204\377\377\377\0\2\0\0\0%\0\0\0\370\210\0\0\0\377"
3046 "\1\0\0\0\265\230\377\377\377\0\1\0\0\0y\210\0\0\0\377\2\0\0\0\372\0\0"
3047 "\0\40\204\377\377\377\0\1\0\0\0o\210\0\0\0\310\2\0\0\0o\0\0\0\2\230\377"
3048 "\377\377\0\2\0\0\0\30\0\0\0\226\207\0\0\0\310\2\0\0\0\306\0\0\0""7\204"
3049 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
3050 "\0\0\0\20\0\0\0\356\210\0\0\0\377\1\0\0\0\226\204\377\377\377\0\1\0\0"
3051 "\0C\207\0\0\0\310\2\0\0\0\305\0\0\0R\233\377\377\377\0\2\0\0\0\5\0\0"
3052 "\0\210\207\0\0\0\310\2\0\0\0\273\0\0\0\37\203\377\377\377\0\2\0\0\0\6"
3053 "\0\0\0\325\210\0\0\0\377\1\0\0\0\251\226\377\377\377\0\1\0\0\0\204\210"
3054 "\0\0\0\377\2\0\0\0\366\0\0\0\32\203\377\377\377\0\2\0\0\0!\0\0\0\277"
3055 "\206\0\0\0\310\2\0\0\0\275\0\0\0""8\235\377\377\377\0\2\0\0\0\2\0\0\0"
3056 "|\207\0\0\0\310\2\0\0\0\254\0\0\0\15\203\377\377\377\0\1\0\0\0J\210\0"
3057 "\0\0\377\2\0\0\0\375\0\0\0&\224\377\377\377\0\2\0\0\0\26\0\0\0\364\210"
3058 "\0\0\0\377\1\0\0\0\214\203\377\377\377\0\2\0\0\0\12\0\0\0\251\206\0\0"
3059 "\0\310\2\0\0\0\305\0\0\0""0\240\377\377\377\0\1\0\0\0r\207\0\0\0\310"
3060 "\1\0\0\0[\204\377\377\377\0\1\0\0\0\317\210\0\0\0\377\1\0\0\0\236\224"
3061 "\377\377\377\0\1\0\0\0\204\210\0\0\0\377\2\0\0\0\362\0\0\0\24\203\377"
3062 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
3063 "\0\0\5\0\0\0$\0\0\0G\0\0\0X\0\0\0T\0\0\0O\0\0\0K\0\0\0B\0\0\0\35\214"
3064 "\377\377\377\0\2\0\0\0\2\0\0\0\214\206\0\0\0\310\2\0\0\0\307\0\0\0""1"
3065 "\203\377\377\377\0\1\0\0\0V\210\0\0\0\377\2\0\0\0\372\0\0\0\27\223\377"
3066 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
3067 "\0\0\0@\207\0\0\0\310\1\0\0\0\204\212\377\377\377\0\4\0\0\0\7\0\0\0E"
3068 "\0\0\0u\0\0\0\222\210\0\0\0\226\4\0\0\0\204\0\0\0T\0\0\0$\0\0\0\1\211"
3069 "\377\377\377\0\2\0\0\0\12\0\0\0\245\206\0\0\0\310\2\0\0\0\251\0\0\0\5"
3070 "\202\377\377\377\0\2\0\0\0\2\0\0\0\331\210\0\0\0\377\1\0\0\0C\223\377"
3071 "\377\377\0\1\0\0\0\342\207\0\0\0\377\2\0\0\0\356\0\0\0\17\202\377\377"
3072 "\377\0\2\0\0\0\2\0\0\0\246\206\0\0\0\310\2\0\0\0\246\0\0\0\11\210\377"
3073 "\377\377\0\3\0\0\0\5\0\0\0D\0\0\0\212\216\0\0\0\226\2\0\0\0z\0\0\0\40"
3074 "\211\377\377\377\0\2\0\0\0\32\0\0\0\274\206\0\0\0\310\1\0\0\0d\203\377"
3075 "\377\377\0\1\0\0\0a\210\0\0\0\377\1\0\0\0b\222\377\377\377\0\2\0\0\0"
3076 "\10\0\0\0\375\207\0\0\0\377\1\0\0\0x\203\377\377\377\0\1\0\0\0G\206\0"
3077 "\0\0\310\2\0\0\0\275\0\0\0\36\210\377\377\377\0\2\0\0\0""3\0\0\0\207"
3078 "\221\0\0\0\226\3\0\0\0\225\0\0\0X\0\0\0\11\210\377\377\377\0\1\0\0\0"
3079 "R\206\0\0\0\310\2\0\0\0\302\0\0\0\23\202\377\377\377\0\2\0\0\0\5\0\0"
3080 "\0\342\207\0\0\0\377\1\0\0\0\201\223\377\377\377\0\1\0\0\0m\206\0\0\0"
3081 "\377\2\0\0\0\321\0\0\0\12\202\377\377\377\0\2\0\0\0\3\0\0\0\254\206\0"
3082 "\0\0\310\1\0\0\0J\207\377\377\377\0\2\0\0\0\1\0\0\0O\210\0\0\0\226\1"
3083 "\0\0\0\206\202\0\0\0h\3\0\0\0m\0\0\0s\0\0\0\214\207\0\0\0\226\2\0\0\0"
3084 "\210\0\0\0)\207\377\377\377\0\2\0\0\0\1\0\0\0\233\206\0\0\0\310\1\0\0"
3085 "\0l\203\377\377\377\0\2\0\0\0P\0\0\0\374\205\0\0\0\377\2\0\0\0\337\0"
3086 "\0\0\"\224\377\377\377\0\1\0\0\0s\204\0\0\0\377\2\0\0\0\315\0\0\0\23"
3087 "\203\377\377\377\0\1\0\0\0N\206\0\0\0\310\2\0\0\0\245\0\0\0\2\206\377"
3088 "\377\377\0\2\0\0\0\6\0\0\0f\206\0\0\0\226\3\0\0\0w\0\0\0""7\0\0\0\23"
3089 "\205\377\377\377\0\4\0\0\0\3\0\0\0*\0\0\0[\0\0\0\212\205\0\0\0\226\2"
3090 "\0\0\0\222\0\0\0*\207\377\377\377\0\2\0\0\0#\0\0\0\304\205\0\0\0\310"
3091 "\2\0\0\0\277\0\0\0\16\203\377\377\377\0\2\0\0\0]\0\0\0\376\203\0\0\0"
3092 "\377\2\0\0\0\332\0\0\0\35\226\377\377\377\0\5\0\0\0;\0\0\0j\0\0\0\223"
3093 "\0\0\0\244\0\0\0\20\203\377\377\377\0\2\0\0\0\5\0\0\0\260\206\0\0\0\310"
3094 "\1\0\0\0>\206\377\377\377\0\2\0\0\0\14\0\0\0z\205\0\0\0\226\2\0\0\0|"
3095 "\0\0\0/\213\377\377\377\0\3\0\0\0\10\0\0\0U\0\0\0\224\204\0\0\0\226\2"
3096 "\0\0\0\221\0\0\0%\207\377\377\377\0\1\0\0\0s\206\0\0\0\310\1\0\0\0d\204"
3097 "\377\377\377\0\5\0\0\0a\0\0\0\240\0\0\0\177\0\0\0]\0\0\0\26\237\377\377"
3098 "\377\0\1\0\0\0U\206\0\0\0\310\1\0\0\0\235\206\377\377\377\0\2\0\0\0\2"
3099 "\0\0\0r\204\0\0\0\226\3\0\0\0\225\0\0\0J\0\0\0\1\216\377\377\377\0\2"
3100 "\0\0\0\35\0\0\0w\204\0\0\0\226\2\0\0\0\217\0\0\0\40\206\377\377\377\0"
3101 "\2\0\0\0\27\0\0\0\304\205\0\0\0\310\2\0\0\0\273\0\0\0\12\247\377\377"
3102 "\377\0\1\0\0\0\236\206\0\0\0\310\1\0\0\0""5\206\377\377\377\0\1\0\0\0"
3103 "T\204\0\0\0\226\2\0\0\0\221\0\0\0""3\221\377\377\377\0\2\0\0\0\4\0\0"
3104 "\0l\204\0\0\0\226\2\0\0\0\215\0\0\0\34\206\377\377\377\0\1\0\0\0}\206"
3105 "\0\0\0\310\1\0\0\0E\247\377\377\377\0\1\0\0\0\276\205\0\0\0\310\1\0\0"
3106 "\0\224\206\377\377\377\0\1\0\0\0""4\204\0\0\0\226\2\0\0\0\214\0\0\0\40"
3107 "\223\377\377\377\0\2\0\0\0\5\0\0\0q\204\0\0\0\226\2\0\0\0\211\0\0\0\14"
3108 "\205\377\377\377\0\2\0\0\0\37\0\0\0\306\205\0\0\0\310\1\0\0\0`\246\377"
3109 "\377\377\0\2\0\0\0\12\0\0\0\277\205\0\0\0\310\1\0\0\0+\205\377\377\377"
3110 "\0\2\0\0\0\30\0\0\0\220\203\0\0\0\226\2\0\0\0\225\0\0\0*\225\377\377"
3111 "\377\0\2\0\0\0\10\0\0\0v\204\0\0\0\226\1\0\0\0X\206\377\377\377\0\1\0"
3112 "\0\0\207\205\0\0\0\310\1\0\0\0m\247\377\377\377\0\2\0\0\0""3\0\0\0\301"
3113 "\203\0\0\0\310\1\0\0\0[\206\377\377\377\0\1\0\0\0n\204\0\0\0\226\1\0"
3114 "\0\0G\227\377\377\377\0\2\0\0\0\12\0\0\0z\203\0\0\0\226\2\0\0\0\224\0"
3115 "\0\0\27\205\377\377\377\0\2\0\0\0\20\0\0\0\246\203\0\0\0\310\2\0\0\0"
3116 "\224\0\0\0\11\250\377\377\377\0\4\0\0\0,\0\0\0h\0\0\0\210\0\0\0R\206"
3117 "\377\377\377\0\1\0\0\0&\204\0\0\0\226\2\0\0\0f\0\0\0\1\230\377\377\377"
3118 "\0\2\0\0\0\26\0\0\0\224\203\0\0\0\226\1\0\0\0g\206\377\377\377\0\5\0"
3119 "\0\0\22\0\0\0\206\0\0\0y\0\0\0]\0\0\0\6\263\377\377\377\0\1\0\0\0t\203"
3120 "\0\0\0\226\2\0\0\0\216\0\0\0\13\232\377\377\377\0\1\0\0\0X\204\0\0\0"
3121 "\226\1\0\0\0#\274\377\377\377\0\1\0\0\0-\204\0\0\0\226\1\0\0\0K\233\377"
3122 "\377\377\0\2\0\0\0\15\0\0\0\217\203\0\0\0\226\1\0\0\0v\274\377\377\377"
3123 "\0\1\0\0\0t\203\0\0\0\226\2\0\0\0\213\0\0\0\10\213\377\377\377\0\5\0"
3124 "\0\0\5\0\0\0\30\0\0\0\40\0\0\0\36\0\0\0\22\214\377\377\377\0\1\0\0\0"
3125 "J\204\0\0\0\226\1\0\0\0*\273\377\377\377\0\1\0\0\0`\203\0\0\0\226\1\0"
3126 "\0\0E\212\377\377\377\0\3\0\0\0\13\0\0\0@\0\0\0Y\204\0\0\0Z\3\0\0\0Q"
3127 "\0\0\0""1\0\0\0\5\211\377\377\377\0\2\0\0\0\6\0\0\0\207\203\0\0\0\226"
3128 "\1\0\0\0\26\273\377\377\377\0\5\0\0\0""1\0\0\0\226\0\0\0\224\0\0\0n\0"
3129 "\0\0\5\211\377\377\377\0\2\0\0\0$\0\0\0U\202\0\0\0Z\4\0\0\0P\0\0\0E\0"
3130 "\0\0I\0\0\0X\202\0\0\0Z\2\0\0\0P\0\0\0\33\211\377\377\377\0\4\0\0\0""3"
3131 "\0\0\0\206\0\0\0\226\0\0\0\201\274\377\377\377\0\3\0\0\0\6\0\0\0""8\0"
3132 "\0\0\13\211\377\377\377\0\2\0\0\0\7\0\0\0A\202\0\0\0Z\2\0\0\0I\0\0\0"
3133 "\20\203\377\377\377\0\6\0\0\0\4\0\0\0\37\0\0\0O\0\0\0Z\0\0\0Y\0\0\0\36"
3134 "\212\377\377\377\0\2\0\0\0\34\0\0\0)\310\377\377\377\0\5\0\0\0<\0\0\0"
3135 "Z\0\0\0Y\0\0\0.\0\0\0\2\206\377\377\377\0\5\0\0\0\3\0\0\0;\0\0\0Z\0\0"
3136 "\0X\0\0\0\32\322\377\377\377\0\1\0\0\0\34\202\0\0\0Z\1\0\0\0\30\211\377"
3137 "\377\377\0\5\0\0\0\1\0\0\0>\0\0\0Z\0\0\0W\0\0\0\13\320\377\377\377\0"
3138 "\4\0\0\0\5\0\0\0P\0\0\0Z\0\0\0""5\213\377\377\377\0\4\0\0\0\2\0\0\0H"
3139 "\0\0\0Z\0\0\0:\320\377\377\377\0\4\0\0\0""4\0\0\0Z\0\0\0P\0\0\0\5\214"
3140 "\377\377\377\0\1\0\0\0\26\202\0\0\0Z\1\0\0\0\22\317\377\377\377\0\3\0"
3141 "\0\0+\0\0\0X\0\0\0\33\216\377\377\377\0\3\0\0\0>\0\0\0I\0\0\0\23\320"
3142 "\377\377\377\0\1\0\0\0\12\217\377\377\377\0\2\0\0\0\6\0\0\0\1\377\377"
3143 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
3144 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
3145 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
3148 known_profile_icon
= [ ""
3155 "\210\0\0\0\0\4\0\0\0\3\0\0\0\16\0\0\0\23\0\0\0\11\216\0\0\0\0\11\0\0"
3156 "\0\16\0\0\0h\0\0\0\301\0\0\0\345\0\0\0\352\0\0\0\331\0\0\0\237\0\0\0"
3157 "9\0\0\0\3\212\0\0\0\0\13\0\0\0@\0\0\0\323\0\0\0\376\0\0\0\350\0\0\0\304"
3158 "\0\0\0\271\0\0\0\323\0\0\0\367\0\0\0\370\0\0\0\227\0\0\0\17\210\0\0\0"
3159 "\0\15\0\0\0K\0\0\0\354\0\0\0\365\0\0\0\206\0\0\0#\0\0\0\6\0\0\0\3\0\0"
3160 "\0\15\0\0\0C\0\0\0\304\0\0\0\376\0\0\0\260\0\0\0\22\206\0\0\0\0\17\0"
3161 "\0\0""2\0\0\0\346\0\0\0\351\0\0\0L\0\0\0#\0\0\0u\0\0\0\246\0\0\0\257"
3162 "\0\0\0\223\0\0\0M\0\0\0\27\0\0\0\235\0\0\0\375\0\0\0\242\0\0\0\7\204"
3163 "\0\0\0\0\20\0\0\0\13\0\0\0\300\0\0\0\372\0\0\0W\0\0\0O\0\0\0\271\0\0"
3164 "\0\233\0\0\0b\0\0\0V\0\0\0z\0\0\0\267\0\0\0\223\0\0\0$\0\0\0\267\0\0"
3165 "\0\374\0\0\0X\204\0\0\0\0\7\0\0\0S\0\0\0\374\0\0\0\240\0\0\0H\0\0\0\275"
3166 "\0\0\0a\0\0\0\12\202\0\0\0\0\10\0\0\0\1\0\0\0%\0\0\0\240\0\0\0\241\0"
3167 "\0\0""9\0\0\0\352\0\0\0\320\0\0\0\12\203\0\0\0\0\21\0\0\0\262\0\0\0\351"
3168 "\0\0\0A\0\0\0\272\0\0\0g\0\0\0\6\0\0\0""4\0\0\0e\0\0\0l\0\0\0T\0\0\0"
3169 "\25\0\0\0\27\0\0\0\251\0\0\0v\0\0\0\214\0\0\0\367\0\0\0<\203\0\0\0\0"
3170 "\21\0\0\0""6\0\0\0G\0\0\0r\0\0\0\244\0\0\0\17\0\0\0P\0\0\0b\0\0\0#\0"
3171 "\0\0\27\0\0\0;\0\0\0s\0\0\0\33\0\0\0E\0\0\0\270\0\0\0""6\0\0\0\\\0\0"
3172 "\0\15\205\0\0\0\0\15\0\0\0T\0\0\0""8\0\0\0""0\0\0\0f\0\0\0\6\0\0\0\0"
3173 "\0\0\0\1\0\0\0\0\0\0\0(\0\0\0l\0\0\0\13\0\0\0k\0\0\0\33\206\0\0\0\0\16"
3174 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
3175 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
3176 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
3177 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
3178 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
3179 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
3180 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
3181 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
3182 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
3183 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
3184 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
3185 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
3186 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
3187 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
3188 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
3189 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
3190 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
3191 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
3192 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
3193 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
3194 "\313\377\272\272\272\377\24\24\24\226\0\0\0\30\0\0\0\10\0\0\0\5\0\0\0"
3195 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
3196 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
3197 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
3198 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
3199 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
3200 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
3201 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
3202 "\377\231\231\231\376\16\16\16\240\0\0\0\35\0\0\0\6\0\0\0\2\0\0\0\12\0"
3203 "\0\0/\0\0\0n\0\0\0|\0\0\0\177\202\0\0\0\200\202\0\0\0\201\1\0\0\0\203"
3204 "\204\0\0\0\205\12\0\0\0\201\0\0\0y\0\0\0<\0\0\0\15\0\0\0\2\0\0\0\0\0"
3205 "\0\0\2\0\0\0\6\0\0\0\14\0\0\0\20\204\0\0\0\24\202\0\0\0\25\203\0\0\0"
3206 "\26\6\0\0\0\25\0\0\0\22\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\0"]
3208 unknown_profile_icon
= [ ""
3215 "\210\0\0\0\0\4\0\0\0\1\0\0\0\4\0\0\0\6\0\0\0\3\216\0\0\0\0\11\0\0\0\4"
3216 "\0\0\0\37\0\0\0""9\0\0\0D\0\0\0F\0\0\0@\0\0\0/\0\0\0\21\0\0\0\1\212\0"
3217 "\0\0\0\7\0\0\0\23\0\0\0\77\0\0\0K\0\0\0E\0\0\0:\0\0\0""7\0\0\0\77\202"
3218 "\0\0\0I\2\0\0\0-\0\0\0\4\210\0\0\0\0\15\0\0\0\26\0\0\0F\0\0\0I\0\0\0"
3219 "(\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\4\0\0\0\24\0\0\0:\0\0\0K\0\0\0""4\0"
3220 "\0\0\6\206\0\0\0\0\17\0\0\0\17\0\0\0D\0\0\0E\0\0\0\26\0\0\0\13\0\0\0"
3221 "#\0\0\0""1\0\0\0""4\0\0\0,\0\0\0\27\0\0\0\7\0\0\0/\0\0\0K\0\0\0""0\0"
3222 "\0\0\2\204\0\0\0\0\20\0\0\0\3\0\0\0""9\0\0\0J\0\0\0\32\0\0\0\30\0\0\0"
3223 "7\0\0\0.\0\0\0\35\0\0\0\32\0\0\0$\0\0\0""6\0\0\0,\0\0\0\13\0\0\0""6\0"
3224 "\0\0K\0\0\0\32\204\0\0\0\0\7\0\0\0\31\0\0\0K\0\0\0""0\0\0\0\25\0\0\0"
3225 "8\0\0\0\35\0\0\0\3\202\0\0\0\0\2\0\0\0\1\0\0\0\13\202\0\0\0""0\4\0\0"
3226 "\0\21\0\0\0F\0\0\0>\0\0\0\3\203\0\0\0\0\21\0\0\0""5\0\0\0E\0\0\0\23\0"
3227 "\0\0""7\0\0\0\37\0\0\0\2\0\0\0\20\0\0\0\36\0\0\0\40\0\0\0\31\0\0\0\6"
3228 "\0\0\0\7\0\0\0""2\0\0\0#\0\0\0)\0\0\0I\0\0\0\22\203\0\0\0\0\21\0\0\0"
3229 "\20\0\0\0\25\0\0\0\"\0\0\0""1\0\0\0\4\0\0\0\30\0\0\0\35\0\0\0\13\0\0"
3230 "\0\7\0\0\0\21\0\0\0\"\0\0\0\10\0\0\0\25\0\0\0""6\0\0\0\20\0\0\0\33\0"
3231 "\0\0\4\205\0\0\0\0\15\0\0\0\31\0\0\0\21\0\0\0\16\0\0\0\36\0\0\0\2\0\0"
3232 "\0\0\0\0\0\1\0\0\0\0\0\0\0\14\0\0\0\40\0\0\0\3\0\0\0\40\0\0\0\10\206"
3233 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
3234 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
3235 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
3236 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
3237 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
3238 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
3239 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
3240 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
3241 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
3242 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
3243 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
3244 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
3245 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
3246 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
3247 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
3248 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
3249 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
3250 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
3251 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
3252 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
3253 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
3254 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
3255 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
3256 "\16\16""0\0\0\0\10\0\0\0\2\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0!\0\0\0%\205"
3257 "\0\0\0&\205\0\0\0'\12\0\0\0&\0\0\0$\0\0\0\22\0\0\0\4\0\0\0\1\0\0\0\0"
3258 "\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\206\0\0\0\6\203\0\0\0\7\202\0\0\0\6"
3259 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
3261 signal_xpm_barely
= [
3288 "*%%%%=&&&&@@@@@@@@@+",
3289 "%&&&&&&&&&@@@@@@@@@+",
3290 "%&&&&&&&&&@@@@@@@@@+",
3291 "%&&&&&&&&&@@@@@@@@@+",
3292 "*%%%%%%%%%+++++++++."
3319 "$++++#@@@@@@@@@@@@@+",
3320 "+@@@@@@@@@@@@@@@@@@+",
3321 "+@@@@@@@@@@@@@@@@@@+",
3322 "+@@@@@@@@@@@@@@@@@@+",
3323 "$++++++++++++++++++."
3349 "$++++#@@@@@@@@@@@@@+",
3350 "+@@@@@@@@@@@@@@@@@@+",
3351 "+@@@@@@@@@@@@@@@@@@+",
3352 "+@@@@@@@@@@@@@@@@@@+",
3353 "$++++++++++++++++++."
3383 "=$$$$*&&&&&&&&&@@@@+",
3384 "$&&&&&&&&&&&&&&@@@@+",
3385 "$&&&&&&&&&&&&&&@@@@+",
3386 "$&&&&&&&&&&&&&&@@@@+",
3387 "=$$$$$$$$$$$$$$++++."
3416 "$%%%%#@@@@@@@@@@@@@+",
3417 "%&&&&@@@@@@@@@@@@@@+",
3418 "%&&&&@@@@@@@@@@@@@@+",
3419 "%&&&&@@@@@@@@@@@@@@+",
3420 "$%%%%++++++++++++++."
3424 ####################################################################################################
3425 # Make so we can be imported
3426 if __name__
== "__main__":
3427 if len( sys
.argv
) > 1 and ( sys
.argv
[1] == '--version' or sys
.argv
[1] == '-v' ):
3428 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3429 elif len( sys
.argv
) > 1 and ( sys
.argv
[1] == '--help' or sys
.argv
[1] == '-h' ):
3430 print "WiFi Radar version %s" % WIFI_RADAR_VERSION
3431 print "For help, check man pages for wifi-radar and wifi-radar.conf,"
3432 print "or visit http://wifi-radar.berlios.de"
3435 gtk
.gdk
.threads_init()
3436 apQueue
= Queue
.Queue(100)
3437 commQueue
= Queue
.Queue(2)
3439 logger
= logging
.getLogger("wrlog")
3440 logger
.setLevel(confFile
.get_opt_as_int('DEFAULT.loglevel'))
3442 fileLogHandler
= logging
.handlers
.RotatingFileHandler(confFile
.get_opt('DEFAULT.logfile'), maxBytes
=64*1024, backupCount
=5)
3443 except IOError, (error_number
, error_str
):
3444 error_dlg
= ErrorDialog(None, "Cannot open log file for writing: %s.\n\nWiFi Radar will work, but a log file will not be recorded." % (error_str
))
3447 fileLogHandler
.setFormatter(logging
.Formatter('%(asctime)s: %(levelname)s: %(funcName)s: %(message)s'))
3448 logger
.addHandler(fileLogHandler
)
3449 consoleLogHandler
= logging
.StreamHandler()
3450 consoleLogHandler
.setFormatter(logging
.Formatter('%(asctime)s: %(funcName)s: %(message)s'))
3451 logger
.addHandler(consoleLogHandler
)
3453 logger
.setLevel(logging
.INFO
)
3455 exit_event
= threading
.Event()
3457 threading
.Thread(None, scanning_thread
, None, (confFile
, apQueue
, commQueue
, logger
, exit_event
)).start()
3458 main_radar_window
= radar_window(confFile
, apQueue
, commQueue
, logger
, exit_event
)
3459 gobject
.timeout_add( 500, main_radar_window
.update_window
)
3460 main_radar_window
.main()