4 # vi:set filetype=python noet:
6 # A wireless profile manager for Linux
8 # Originally created for x1000 Linux:
9 # http://x1000.bitbuilder.com
12 # Ahmad Baitalmal <ahmad@baitalmal.com>
15 # Brian Elliott Finley <brian@thefinleys.com>
20 # http://www.bitbuilder.com/wifi_radar
21 # http://svn.systemimager.org
23 # See CREDITS file for more contributors.
24 # See ChangeLog file for, well, changes.
27 import ConfigParser
, os
, Queue
, re
, string
, sys
, threading
28 from signal
import SIGTERM
29 from subprocess
import call
, Popen
, PIPE
30 from time
import sleep
33 WIFI_RADAR_VERSION
= "0.0.0"
36 print '__debug__ is True'
37 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
41 # Where the conf file should live could be different for your distro. Please change
42 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
44 CONF_FILE
= "/etc/wifi-radar.conf"
46 os
.environ
['LC_MESSAGES'] = 'C'
49 #####################################
51 USE_DHCP_LABEL
= "Automatic network configuration (DHCP)"
52 USE_IP_LABEL
= "Manual network configuration"
53 WIFI_SET_LABEL
= "WiFi Options"
54 CON_PP_LABEL
= "Connection Commands"
55 DIS_PP_LABEL
= "Disconnection Commands"
56 USE_WPA_LABEL
= "Use WPA"
57 NO_WPA_LABEL
= "No WPA"
58 ####################################################################################################
60 ####################################################################################################
61 ####################################################################################################
63 # Sets the interface to the specified network device
67 # 'device' -- string - The network device to use
72 def set_network_device( device
):
73 #print "set_network_device: ", device
74 if device
!= "auto_detect":
75 confFile
.set_opt('DEFAULT.interface', device
)
76 else: # auto detect network device
77 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
78 # If no devices are found, default to eth1.
79 # call iwconfig command and read output
80 iwconfig_info
= Popen(confFile
.get_opt('DEFAULT.iwconfig_command'), shell
=True, stdout
=PIPE
).stdout
81 wireless_devices
= [ (x
[0:x
.find(" ")]) for x
in iwconfig_info
if("ESSID" in x
)]
82 if len(wireless_devices
) > 0:
83 confFile
.set_opt('DEFAULT.interface', wireless_devices
[0])
85 #print "No wifi-device found. Exiting."
88 # Return a blank profile
96 # dictionary -- An AP profile with defaults set.
97 def get_new_profile():
98 return { 'known': False,
107 'con_postscript': '',
109 'dis_postscript': '',
124 # Combine essid and bssid to make a config file section name
128 # 'essid' -- string - AP ESSID
130 # 'bssid' -- string - AP BSSID
134 # string -- the bssid concatenated to a colon, concatenated to the essid
135 def make_section_name( essid
, bssid
):
136 return essid
+ ':' + bssid
138 # Split a config file section name into an essid and a bssid
142 # 'section' -- string - Config file section name
146 # list -- the essid and bssid
147 def split_section_name( section
):
148 parts
= re
.split(':', section
)
149 return [ ':'.join(parts
[0:len(parts
)-6]), ':'.join(parts
[len(parts
)-6:len(parts
)]) ]
151 # Run commands through the shell
155 # 'command' -- tuple - The command and arguments to run
159 # boolean -- True on success, otherwise, False
160 def shellcmd( command
, environment
= None ):
163 env_tmp
.update(environment
)
164 command
= ' '.join(command
)
165 return_code
= call(command
, shell
=True, env
=env_tmp
)
169 print >>sys
.stderr
, "Child was terminated by signal", -return_code
170 except OSError, exception
:
171 print >>sys
.stderr
, "Execution failed:", exception
174 # Speak feedback message to user
178 # 'words' -- string - Message to speak to user
184 if not confFile
.get_opt_as_bool('DEFAULT.speak_up'): return
185 words
= words
.replace( "\"", "\\\"" )
186 shellcmd([confFile
.get_opt('DEFAULT.speak_command'), words
])
188 # Scan for a limited time and return AP names and bssid found.
189 # Access points we find will be put on the outgoing Queue, apQueue.
193 # 'confFile' -- ConfigFile - Config file object
195 # 'apQueue' -- Queue - Queue on which to put AP profiles
197 # 'commandQueue' -- Queue - Queue from which to read commands
202 def scanning_thread( confFile
, apQueue
, commandQueue
):
203 # Setup our essid pattern matcher
204 essid_pattern
= re
.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re
.I | re
.M | re
.S
)
205 protocol_pattern
= re
.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abg]+)", re
.I | re
.M | re
.S
)
206 mode_pattern
= re
.compile( "Mode\s*(:|=)\s*([^\n]+)", re
.I | re
.M | re
.S
)
207 channel_pattern
= re
.compile( "Channel\s*(:|=)*\s*(\d+)", re
.I | re
.M | re
.S
)
208 enckey_pattern
= re
.compile( "Encryption key\s*(:|=)\s*(on|off)", re
.I | re
.M | re
.S
)
209 signal_pattern
= re
.compile( "Signal level\s*(:|=)\s*(-?[0-9]+)", re
.I | re
.M | re
.S
)
215 command
= commandQueue
.get_nowait()
216 if __debug__
: print "scanning_thread received command:", command
220 if command
== "scan":
221 if __debug__
: print "Beginning scan pass"
222 # Some cards need to have the interface up to scan
223 if confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
224 # call ifconfig command and wait for return
225 shellcmd([confFile
.get_opt('DEFAULT.ifconfig_command'), confFile
.get_opt('DEFAULT.interface'), 'up'])
226 # update the signal strengths
227 scandata
= Popen([confFile
.get_opt('DEFAULT.iwlist_command'), confFile
.get_opt('DEFAULT.interface'), 'scan'], stdout
=PIPE
).stdout
.read()
229 #print "Current IP ", get_current_ip()
230 #print "Current ESSID ", get_current_essid()
231 #print "Current BSSID ", get_current_bssid()
232 # zero out the signal levels for all access points
233 for bssid
in access_points
:
234 access_points
[bssid
]['signal'] = 0
235 # split the scan data based on the address line
236 hits
= scandata
.split(' - ')
238 # set the defaults for profile template
239 profile
= get_new_profile()
240 m
= essid_pattern
.search( hit
)
243 profile
['essid'] = m
.groups()[1]
244 m
= bssid_pattern
.search( hit
) # get BSSID from scan
245 if m
: profile
['bssid'] = m
.groups()[1]
246 m
= protocol_pattern
.search( hit
) # get protocol from scan
247 if m
: profile
['protocol'] = m
.groups()[1]
248 m
= mode_pattern
.search( hit
) # get mode from scan
249 if m
: profile
['mode'] = m
.groups()[1]
250 m
= channel_pattern
.search( hit
) # get channel from scan
251 if m
: profile
['channel'] = m
.groups()[1]
252 m
= enckey_pattern
.search( hit
) # get encryption key from scan
253 if m
: profile
['encrypted'] = ( m
.groups()[1] == 'on' )
254 m
= signal_pattern
.search( hit
) # get signal strength from scan
255 if m
: profile
['signal'] = m
.groups()[1]
256 access_points
[ profile
['bssid'] ] = profile
257 for bssid
in access_points
:
258 access_points
[bssid
]['available'] = ( access_points
[bssid
]['signal'] > 0 )
259 # Put all, now or previously, sensed access_points into apQueue
261 apQueue
.put_nowait( access_points
[bssid
] )
264 elif command
== "exit":
265 if __debug__
: print "Exiting scanning_thread"
267 if command_read
: commandQueue
.task_done()
268 if confFile
.get_opt('DEFAULT.interface').find('ath') == 0:
274 # Manage a connection; including reporting connection state,
275 # connecting/disconnecting from an AP, and returning current IP, ESSID, and BSSID.
276 class ConnectionManager():
277 # Create a new connection manager which can read a config file and send to scanning thread
278 # command Queue. A new manager checks for a pre-existing connection and takes
279 # its AP profile from the ESSID and BSSID to which it is currently attached.
283 # 'confFile' -- ConfigFile - Config file object
285 # 'commandQueue' -- Queue - The Queue on which to put commands to the scanning thread
289 # ConnectionManager instance
290 def __init__( self
, confFile
, commandQueue
):
291 self
.confFile
= confFile
292 self
.commQueue
= commandQueue
293 # is connection running?
295 if self
.get_current_ip():
297 self
.profile
= self
.confFile
.get_profile( make_section_name(self
.get_current_essid(), self
.get_current_bssid()) )
299 # Change the interface state: up or down.
303 # 'state' -- string - The state to which to change the interface.
308 def if_change( self
, state
):
309 if ( (state
.lower() == 'up') or (state
.lower() == 'down') ):
310 # call ifconfig command and wait for return
311 shellcmd([self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), state
])
313 # Connect to the specified AP.
317 # 'profile' -- dictionary - The profile for the AP (i.e. network) with which to connect.
319 # 'status_window' -- status_window - Instance of window to use to update user.
324 def connect_to_network( self
, profile
, status_window
):
325 self
.profile
= profile
326 msg
= "Connecting to the %s (%s) network" % ( self
.profile
['essid'], self
.profile
['bssid'] )
328 if __debug__
: print " %s" % msg
330 # Let's run the connection prescript
331 if self
.profile
['con_prescript'].strip() != '':
332 # got something to execute
333 # run connection prescript through shell and wait for return
334 if __debug__
: print "executing connection prescript:", self
.profile
['con_prescript']
335 shellcmd([self
.profile
['con_prescript']], environment
={"WIFIRADAR_IF": self
.confFile
.get_opt('DEFAULT.interface')})
338 # Some cards need to have the interface up
339 if self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
341 # Start building iwconfig command line, command
342 iwconfig_command
= [ self
.confFile
.get_opt('DEFAULT.iwconfig_command') ]
343 iwconfig_command
.append( self
.confFile
.get_opt('DEFAULT.interface') )
345 iwconfig_command
.append( 'essid' )
346 iwconfig_command
.append( self
.profile
['essid'] )
348 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
350 iwconfig_command
.append( 'key' )
351 if self
.profile
['key'] == '':
352 iwconfig_command
.append( 'off' )
354 iwconfig_command
.append( self
.profile
['key'] )
355 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
356 # Setting mode to Master, this will cause association with the AP to fail
357 #iwconfig_command.append( 'mode' )
358 #iwconfig_command.append( 'Master' )
360 if self
.profile
['channel'] != '':
361 iwconfig_command
.append( 'channel' )
362 iwconfig_command
.append( self
.profile
['channel'] )
363 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
364 iwconfig_command
.append( 'ap' )
365 iwconfig_command
.append( self
.profile
['bssid'] )
366 # Some cards require a commit
367 if self
.confFile
.get_opt_as_bool('DEFAULT.commit_required'):
368 if __debug__
: print 'iwconfig_args %s ' % ( iwconfig_args
, )
369 iwconfig_command
.append( 'commit' )
370 # call iwconfig command and wait for return
371 if not shellcmd(iwconfig_command
): return
372 # Now normal network stuff
373 # Kill off any existing DHCP clients running
374 if os
.path
.isfile(self
.confFile
.get_opt('DHCP.pidfile')):
375 if __debug__
: print "Killing existing DHCP..."
377 if self
.confFile
.get_opt('DHCP.kill_args') != '':
378 # call DHCP client kill command and wait for return
379 if __debug__
: print self
.confFile
.get_opt('DHCP.command') + " " + self
.confFile
.get_opt('DHCP.kill_args')
380 shellcmd([self
.confFile
.get_opt('DHCP.command'), self
.confFile
.get_opt('DHCP.kill_args')])
382 os
.kill(int(open(self
.confFile
.get_opt('DHCP.pidfile'), mode
='r').readline()), SIGTERM
)
384 print "failed to kill DHCP client"
387 print "Stale pid file. Removing..."
388 os
.remove(self
.confFile
.get_opt('DHCP.pidfile'))
389 # Kill off any existing WPA supplicants running
390 if os
.access(self
.confFile
.get_opt('WPA.pidfile'), os
.R_OK
):
391 if __debug__
: print "Killing existing WPA supplicant..."
393 if not self
.confFile
.get_opt('WPA.kill_command') != '':
394 # call WPA supplicant kill command and wait for return
395 shellcmd([self
.confFile
.get_opt('WPA.kill_command'), self
.confFile
.get_opt('WPA.kill_command')])
397 os
.kill(int(open(self
.confFile
.get_opt('WPA.pidfile'), mode
='r').readline()), SIGTERM
)
399 print "failed to kill WPA supplicant"
402 print "Stale pid file. Removing..."
403 os
.remove(self
.confFile
.get_opt('WPA.pidfile'))
404 # Begin WPA supplicant
405 if self
.profile
['use_wpa'] :
406 if __debug__
: print "WPA args: %s" % ( wpa_options
, )
408 status_window
.update_message("WPA supplicant starting")
409 # call WPA supplicant command and do not wait for return
410 wpa_proc
= Popen([self
.confFile
.get_opt('WPA.command'), self
.confFile
.get_opt('WPA.args'), self
.confFile
.get_opt('DEFAULT.interface')])
411 if self
.profile
['use_dhcp'] :
412 if __debug__
: print "Disable iwlist while dhcp in progress..."
414 self
.commQueue
.put("pause")
418 status_window
.update_message("Acquiring IP Address (DHCP)")
419 # call DHCP client command and do not wait for return
420 dhcp_command
= [ self
.confFile
.get_opt('DHCP.command') ]
421 dhcp_command
.extend( self
.confFile
.get_opt('DHCP.args').split(' ') )
422 dhcp_command
.append( self
.confFile
.get_opt('DEFAULT.interface') )
423 dhcp_proc
= Popen(dhcp_command
, stdout
=None)
424 timer
= self
.confFile
.get_opt_as_int('DHCP.timeout') + 3
426 waiting
= dhcp_proc
.poll()
427 while waiting
== None:
428 waiting
= dhcp_proc
.poll()
430 os
.kill(dhcp_proc
.pid
, SIGTERM
)
432 if sys
.modules
.has_key("gtk"):
433 while gtk
.events_pending():
434 gtk
.main_iteration(False)
437 if not self
.get_current_ip():
439 status_window
.update_message("Could not get IP address!")
440 if sys
.modules
.has_key("gtk"):
441 while gtk
.events_pending():
442 gtk
.main_iteration(False)
445 print "Could not get IP address!"
446 self
.disconnect_interface()
449 status_window
.update_message("Got IP address. Done.")
451 if sys
.modules
.has_key("gtk"):
452 while gtk
.events_pending():
453 gtk
.main_iteration(False)
457 self
.commQueue
.put("scan")
461 ifconfig_command
= "%s %s down; %s %s %s netmask %s" % ( self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), self
.profile
['ip'], self
.profile
['netmask'] )
462 route_command
= "%s add default gw %s" % ( self
.confFile
.get_opt('DEFAULT.route_command'), self
.profile
['gateway'] )
464 if self
.profile
['domain'] != '': resolv_contents
+= "domain %s\n" % domain
465 if self
.profile
['dns1'] != '': resolv_contents
+= "nameserver %s\n" % dns1
466 if self
.profile
['dns2'] != '': resolv_contents
+= "nameserver %s\n" % dns2
467 if ( resolv_contents
!= '' ):
468 resolv_file
=open('/etc/resolv.conf', 'w')
471 if not shellcmd([ifconfig_command
]): return
472 if not shellcmd([route_command
]): return
474 # Let's run the connection postscript
475 con_postscript
= self
.profile
['con_postscript']
476 if self
.profile
['con_postscript'].strip() != '':
477 if __debug__
: print "executing connection postscript:", self
.profile
['con_postscript']
478 shellcmd([self
.profile
['con_postscript']], environment
={"WIFIRADAR_IP": self
.get_current_ip(), "WIFIRADAR_ESSID": self
.get_current_essid(), "WIFIRADAR_BSSID": self
.get_current_bssid(), "WIFIRADAR_IF": self
.confFile
.get_opt('DEFAULT.interface')})
480 status_window
.destroy()
482 # Disconnect from the AP with which a connection has been established/attempted.
491 def disconnect_interface( self
):
492 msg
= "Disconnecting"
494 if __debug__
: print msg
495 # Pause scanning while manipulating card
497 self
.commQueue
.put("pause")
501 self
.profile
= self
.confFile
.get_profile( make_section_name(self
.get_current_essid(), self
.get_current_bssid()) )
502 # Let's run the disconnection prescript
503 if self
.profile
['dis_prescript'].strip() != '':
504 if __debug__
: print "executing disconnection prescript:", self
.profile
['dis_prescript']
505 shellcmd([self
.profile
['dis_prescript']], environment
={"WIFIRADAR_IP": self
.get_current_ip(), "WIFIRADAR_ESSID": self
.get_current_essid(), "WIFIRADAR_BSSID": self
.get_current_bssid(), "WIFIRADAR_IF": self
.confFile
.get_opt('DEFAULT.interface')})
506 if __debug__
: print "Kill off any existing DHCP clients running..."
507 if os
.path
.isfile(self
.confFile
.get_opt('DHCP.pidfile')):
508 if __debug__
: print "Killing existing DHCP..."
510 if self
.confFile
.get_opt('DHCP.kill_args') != '':
511 dhcp_command
= [ self
.confFile
.get_opt('DHCP.command') ]
512 dhcp_command
.extend( self
.confFile
.get_opt('DHCP.kill_args').split(' ') )
513 dhcp_command
.append( self
.confFile
.get_opt('DEFAULT.interface') )
514 if __debug__
: print "DHCP command", dhcp_command
515 # call DHCP client command and wait for return
516 if not shellcmd(dhcp_command
): return
518 os
.kill(int(open(self
.confFile
.get_opt('DHCP.pidfile'), mode
='r').readline()), SIGTERM
)
520 print "failed to kill DHCP client"
521 if __debug__
: print "Kill off any existing WPA supplicants running..."
522 if os
.access(self
.confFile
.get_opt('WPA.pidfile'), os
.R_OK
):
523 if __debug__
: print "Killing existing WPA supplicant..."
525 if not self
.confFile
.get_opt('WPA.kill_command') != '':
526 wpa_command
= [ self
.confFile
.get_opt('WPA.kill_command').split(' ') ]
527 if not shellcmd(wpa_command
): return
529 os
.kill(int(open(self
.confFile
.get_opt('WPA.pidfile'), mode
='r').readline()), SIGTERM
)
531 print "failed to kill WPA supplicant"
532 shellcmd([self
.confFile
.get_opt('DEFAULT.iwconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), 'essid', 'any', 'key', 'off', 'mode', 'managed', 'channel', 'auto', 'ap', 'off'])
533 if __debug__
: print "Let's clear out the wireless stuff"
534 if __debug__
: print 'Now take the interface down'
535 self
.if_change('down')
536 if __debug__
: print 'Since it may be brought back up by the next scan, lets unset its IP'
537 shellcmd([self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), '0.0.0.0'])
538 # Let's run the disconnection postscript
539 if self
.profile
['dis_postscript'].strip() != '':
540 if __debug__
: print "executing disconnection postscript:", self
.profile
['dis_postscript']
541 shellcmd([self
.profile
['dis_postscript']], environment
={"WIFIRADAR_IF": self
.confFile
.get_opt('DEFAULT.interface')})
543 if __debug__
: print 'Disconnect complete.'
544 # Begin scanning again
546 self
.commQueue
.put("scan")
550 # Returns the current IP, if any, by calling ifconfig.
558 # string or None -- the IP address or None (if no there is no current connection)
559 def get_current_ip( self
):
560 ifconfig_command
= [ confFile
.get_opt('DEFAULT.ifconfig_command'), confFile
.get_opt('DEFAULT.interface') ]
561 ifconfig_info
= Popen(ifconfig_command
, stdout
=PIPE
).stdout
562 # Be careful to the language (inet adr: in French for example)
566 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
567 # There the string in ifconfig is inet Adresse for the IP which isn't
568 # found by the current get_current_ip function in wifi-radar. I changed
569 # the according line (#289; gentoo, v1.9.6-r1) to
570 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
571 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
573 # I'd be happy if you could incorporate this small change because as now
574 # I've got to change the file every time it is updated.
579 ip_re
= re
.compile(r
'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
580 line
= ifconfig_info
.read()
581 if ip_re
.search( line
):
582 return ip_re
.search( line
).group(1)
585 # Returns the current ESSID, if any, by calling iwconfig.
593 # string or None -- the ESSID or None (if no there is no current association)
594 def get_current_essid( self
):
595 """Returns the current ESSID if any by calling iwconfig"""
596 iwconfig_info
= Popen([self
.confFile
.get_opt('DEFAULT.iwconfig_command'), self
.confFile
.get_opt('DEFAULT.interface')], stdout
=PIPE
).stdout
597 # Be careful to the language (inet adr: in French for example)
598 essid_re
= re
.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re
.I | re
.M | re
.S
)
599 line
= iwconfig_info
.read()
600 if essid_re
.search( line
):
601 return essid_re
.search( line
).group(2)
604 # Returns the current BSSID, if any, by calling iwconfig.
612 # string or None -- the BSSID or None (if no there is no current association)
613 def get_current_bssid( self
):
614 """Returns the current BSSID if any by calling iwconfig"""
615 iwconfig_info
= Popen([self
.confFile
.get_opt('DEFAULT.iwconfig_command'), self
.confFile
.get_opt('DEFAULT.interface')], stdout
=PIPE
).stdout
616 bssid_re
= re
.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re
.I | re
.M | re
.S
)
617 line
= iwconfig_info
.read()
618 if bssid_re
.search( line
):
619 return bssid_re
.search( line
).group(2)
624 # The main user interface window for WiFi Radar. This class also is the control
625 # center for most of the rest of the operations.
627 # Create a new radar_window.
631 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
633 # 'apQueue' -- Queue - The Queue from which AP profiles are read.
635 # 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
639 # radar_window instance
640 def __init__( self
, confFile
, apQueue
, commQueue
):
641 global signal_xpm_none
642 global signal_xpm_low
643 global signal_xpm_barely
645 global signal_xpm_best
646 global known_profile_icon
647 global unknown_profile_icon
648 global wifi_radar_icon
650 self
.confFile
= confFile
651 self
.apQueue
= apQueue
652 self
.commandQueue
= commQueue
653 self
.access_points
= {}
654 self
.connection
= None
656 self
.known_profile_icon
= gtk
.gdk
.pixbuf_new_from_inline( len( known_profile_icon
[0] ), known_profile_icon
[0], False )
657 self
.unknown_profile_icon
= gtk
.gdk
.pixbuf_new_from_inline( len( unknown_profile_icon
[0] ), unknown_profile_icon
[0], False )
658 self
.signal_none_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_none
)
659 self
.signal_low_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_low
)
660 self
.signal_barely_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_barely
)
661 self
.signal_ok_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_ok
)
662 self
.signal_best_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_best
)
663 self
.window
= gtk
.Dialog('WiFi Radar', None, gtk
.DIALOG_MODAL
)
664 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
665 self
.window
.set_icon( icon
)
666 self
.window
.set_border_width( 10 )
667 self
.window
.set_size_request( 550, 300 )
668 self
.window
.set_title( "WiFi Radar" )
669 self
.window
.connect( 'delete_event', self
.delete_event
)
670 self
.window
.connect( 'destroy', self
.destroy
)
671 # let's create all our widgets
672 self
.current_network
= gtk
.Label()
673 self
.current_network
.set_property('justify', gtk
.JUSTIFY_CENTER
)
674 self
.current_network
.show()
675 self
.close_button
= gtk
.Button( "Close", gtk
.STOCK_CLOSE
)
676 self
.close_button
.show()
677 self
.close_button
.connect( 'clicked', self
.delete_event
, None )
678 self
.preferences_button
= gtk
.Button( "Preferences", gtk
.STOCK_PREFERENCES
)
679 self
.preferences_button
.show()
680 self
.preferences_button
.connect( 'clicked', self
.edit_preferences
, None )
681 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
682 self
.pstore
= gtk
.ListStore( str, gtk
.gdk
.Pixbuf
, bool, bool, str, gtk
.gdk
.Pixbuf
, str, str, str )
683 self
.plist
= gtk
.TreeView( self
.pstore
)
684 # The icons column, known and encryption
685 self
.pix_cell
= gtk
.CellRendererPixbuf()
686 self
.wep_cell
= gtk
.CellRendererPixbuf()
687 self
.icons_cell
= gtk
.CellRendererText()
688 self
.icons_col
= gtk
.TreeViewColumn()
689 self
.icons_col
.pack_start( self
.pix_cell
, False )
690 self
.icons_col
.pack_start( self
.wep_cell
, False )
691 self
.icons_col
.add_attribute( self
.pix_cell
, 'pixbuf', 1 )
692 self
.icons_col
.add_attribute( self
.wep_cell
, 'stock-id', 4 )
693 self
.plist
.append_column( self
.icons_col
)
695 self
.ap_cell
= gtk
.CellRendererText()
696 self
.ap_col
= gtk
.TreeViewColumn( "Access Point" )
697 self
.ap_col
.pack_start( self
.ap_cell
, True )
698 self
.ap_col
.add_attribute( self
.ap_cell
, 'text', 0 )
699 self
.plist
.append_column( self
.ap_col
)
701 self
.sig_cell
= gtk
.CellRendererPixbuf()
702 self
.signal_col
= gtk
.TreeViewColumn( "Signal" )
703 self
.signal_col
.pack_start( self
.sig_cell
, True )
704 self
.signal_col
.add_attribute( self
.sig_cell
, 'pixbuf', 5 )
705 self
.plist
.append_column( self
.signal_col
)
707 self
.mode_cell
= gtk
.CellRendererText()
708 self
.mode_col
= gtk
.TreeViewColumn( "Mode" )
709 self
.mode_col
.pack_start( self
.mode_cell
, True )
710 self
.mode_col
.add_attribute( self
.mode_cell
, 'text', 6 )
711 self
.plist
.append_column( self
.mode_col
)
712 # The protocol column
713 self
.prot_cell
= gtk
.CellRendererText()
714 self
.protocol_col
= gtk
.TreeViewColumn( "802.11" )
715 self
.protocol_col
.pack_start( self
.prot_cell
, True )
716 self
.protocol_col
.add_attribute( self
.prot_cell
, 'text', 7 )
717 self
.plist
.append_column( self
.protocol_col
)
719 self
.channel_cell
= gtk
.CellRendererText()
720 self
.channel_col
= gtk
.TreeViewColumn( "Channel" )
721 self
.channel_col
.pack_start( self
.channel_cell
, True )
722 self
.channel_col
.add_attribute( self
.channel_cell
, 'text', 8 )
723 self
.plist
.append_column( self
.channel_col
)
725 self
.plist
.set_reorderable( True )
726 self
.pstore
.connect( 'row-changed', self
.update_auto_profile_order
)
727 # enable/disable buttons based on the selected network
728 self
.selected_network
= self
.plist
.get_selection()
729 self
.selected_network
.connect( 'changed', self
.on_network_selection
, None )
730 # the list scroll bar
731 sb
= gtk
.VScrollbar( self
.plist
.get_vadjustment() )
735 self
.new_button
= gtk
.Button( "_New" )
736 self
.new_button
.connect( 'clicked', self
.create_new_profile
, get_new_profile(), None )
737 self
.new_button
.show()
738 # Add Configure button
739 self
.edit_button
= gtk
.Button( "C_onfigure" )
740 self
.edit_button
.connect( 'clicked', self
.edit_profile
, None )
741 self
.edit_button
.show()
742 self
.edit_button
.set_sensitive(False)
744 self
.delete_button
= gtk
.Button( "_Delete" )
745 self
.delete_button
.connect( 'clicked', self
.delete_profile
, None )
746 self
.delete_button
.show()
747 self
.delete_button
.set_sensitive(False)
749 self
.connect_button
= gtk
.Button( "Co_nnect" )
750 self
.connect_button
.connect( 'clicked', self
.connect_profile
, None )
751 # Add Disconnect button
752 self
.disconnect_button
= gtk
.Button( "D_isconnect" )
753 self
.disconnect_button
.connect( 'clicked', self
.disconnect_profile
, None )
754 # lets add our widgets
755 rows
= gtk
.VBox( False, 3 )
756 net_list
= gtk
.HBox( False, 0 )
757 listcols
= gtk
.HBox( False, 0 )
758 prows
= gtk
.VBox( False, 0 )
761 net_list
.pack_start( self
.plist
, True, True, 0 )
762 net_list
.pack_start( sb
, False, False, 0 )
764 rows
.pack_start( net_list
, True, True, 0 )
765 rows
.pack_start( self
.current_network
, False, True, 0 )
767 listcols
.pack_start( rows
, True, True, 0 )
768 listcols
.pack_start( prows
, False, False, 5 )
770 prows
.pack_start( self
.new_button
, False, False, 2 )
771 prows
.pack_start( self
.edit_button
, False, False, 2 )
772 prows
.pack_start( self
.delete_button
, False, False, 2 )
773 prows
.pack_end( self
.connect_button
, False, False, 2 )
774 prows
.pack_end( self
.disconnect_button
, False, False, 2 )
776 self
.window
.action_area
.pack_start( self
.preferences_button
)
777 self
.window
.action_area
.pack_start( self
.close_button
)
782 self
.window
.vbox
.add( listcols
)
783 self
.window
.vbox
.set_spacing( 3 )
784 self
.window
.show_all()
786 # Now, immediately hide these two. The proper one will be
787 # displayed later, based on interface state. -BEF-
788 self
.disconnect_button
.hide()
789 self
.connect_button
.hide()
790 self
.connect_button
.set_sensitive(False)
792 # set up connection manager for later use
793 self
.connection
= ConnectionManager( self
.confFile
, self
.commandQueue
)
795 # Add our known profiles in order
796 for ap
in self
.confFile
.auto_profile_order
:
798 self
.access_points
[ ap
] = self
.confFile
.get_profile( ap
)
800 if self
.access_points
[ ap
]['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
801 self
.pstore
.append( [ self
.access_points
[ ap
]['essid'] + "\n" + self
.access_points
[ ap
]['bssid'], self
.known_profile_icon
, self
.access_points
[ ap
]['known'], self
.access_points
[ ap
]['available'], wep
, self
.signal_none_pb
, self
.access_points
[ ap
]['mode'], self
.access_points
[ ap
]['protocol'], self
.access_points
[ ap
]['channel'] ] )
802 # This is the first run (or, at least, no config file was present), so pop up the preferences window
803 if ( self
.confFile
.get_opt_as_bool('DEFAULT.new_file') == True ):
804 self
.confFile
.remove_option('DEFAULT', 'new_file')
805 self
.edit_preferences(self
.preferences_button
)
807 # Begin running radar_window in Gtk event loop.
819 # Write the config file to disk and quit application.
823 # 'widget' -- gtk.Widget - The widget sending the event.
828 def destroy( self
, widget
= None):
829 self
.confFile
.write()
832 # Kill scanning thread, update profile order for config file, and ask to be destroyed.
836 # 'widget' -- gtk.Widget - The widget sending the event.
838 # 'data' -- tuple - list of arbitrary arguments (not used)
842 # boolean -- always return False (i.e. do not propigate the signal which called)
843 def delete_event( self
, widget
, data
= None ):
844 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
846 self
.commandQueue
.put("exit", True)
849 # Save the preferred networks order
850 self
.update_auto_profile_order()
854 # Updates the on-screen profiles list.
862 # boolean -- always return True
863 def update_plist_items( self
):
864 # Indicate to PyGtk that only one Gtk thread should run here
865 gtk
.gdk
.threads_enter()
866 # update the current ip and essid
867 # set the state of connect/disconnect buttons based on whether we have an IP address
869 if self
.connection
.state
:
870 self
.current_network
.set_text( "Connected to %s\nip %s" % ( make_section_name( self
.connection
.get_current_essid(), self
.connection
.get_current_bssid() ), self
.connection
.get_current_ip() ) )
871 self
.connect_button
.hide()
872 self
.disconnect_button
.show()
874 self
.current_network
.set_text( "Not Connected." )
875 self
.disconnect_button
.hide()
876 self
.connect_button
.show()
879 # Get profiles scanned by iwlist
881 profile
= self
.apQueue
.get_nowait()
885 prow_iter
= self
.get_row_by_ap( profile
['essid'], profile
['bssid'] )
887 if prow_iter
!= None:
888 # the AP is in the list of APs on the screen
889 apname
= make_section_name(profile
['essid'], profile
['bssid'])
890 if self
.access_points
.has_key(apname
):
891 # This AP has been configured and is/should be stored in the config file
892 profile
['known'] = self
.access_points
[apname
]['known']
893 self
.access_points
[apname
]['available'] = profile
['available']
894 self
.access_points
[apname
]['encrypted'] = profile
['encrypted']
895 self
.access_points
[apname
]['signal'] = profile
['signal']
896 self
.access_points
[apname
]['mode'] = profile
['mode']
897 self
.access_points
[apname
]['protocol'] = profile
['protocol']
898 self
.access_points
[apname
]['channel'] = profile
['channel']
899 # Set the 'known' values; False is default, overridden to True by self.access_points
900 self
.pstore
.set_value(prow_iter
, 1, self
.pixbuf_from_known( profile
[ 'known' ] ))
901 self
.pstore
.set_value(prow_iter
, 2, profile
[ 'known' ])
902 self
.pstore
.set_value(prow_iter
, 3, profile
['available'])
903 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
904 self
.pstore
.set_value(prow_iter
, 4, wep
)
905 self
.pstore
.set_value(prow_iter
, 5, self
.pixbuf_from_signal( profile
['signal'] ))
906 self
.pstore
.set_value(prow_iter
, 6, profile
['mode'])
907 self
.pstore
.set_value(prow_iter
, 7, profile
['protocol'])
908 self
.pstore
.set_value(prow_iter
, 8, profile
['channel'])
909 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
910 #for val in self.pstore[prow_iter]:
913 # the AP is not in the list of APs on the screen
914 self
.pstore
.append( [ profile
[ 'essid' ] + "\n" + profile
['bssid'], self
.pixbuf_from_known( profile
['known'] ), profile
['known'], profile
['available'], wep
, self
.pixbuf_from_signal( profile
['signal'] ), profile
['mode'], profile
['protocol'], profile
['channel'] ] )
915 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
916 # Allow other Gtk threads to run
917 gtk
.gdk
.threads_leave()
918 #print "update_plist_items: Empty apQueue"
921 # Return the proper icon for a value of known.
925 # 'known' -- boolean - Whether the AP is known (i.e. configured)
929 # gtk.gdk.Pixbuf -- icon for a known or unknown AP
930 def pixbuf_from_known( self
, known
):
931 """ return the proper icon for value of known """
933 return self
.known_profile_icon
935 return self
.unknown_profile_icon
937 # Return an icon indicating the signal level.
941 # 'signal' -- integer - signal level reported by iwlist (may be arbitrary scale in 0-100 or -X dBm)
945 # gtk.gdk.Pixbuf -- 1 of 5 icons indicating signal level
946 def pixbuf_from_signal( self
, signal
):
947 signal
= int( signal
)
948 #print "signal level:", signal
950 return self
.signal_none_pb
952 return self
.signal_low_pb
954 return self
.signal_barely_pb
956 return self
.signal_ok_pb
958 return self
.signal_best_pb
962 # Return row which holds specified ESSID and BSSID.
966 # 'essid' -- string - ESSID to match
968 # 'bssid' -- string - BSSID to match
972 # gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
973 def get_row_by_ap( self
, essid
, bssid
):
974 for row
in self
.pstore
:
975 if ( row
[0] == essid
+ "\n" + bssid
):
976 #print "matched:", row.iter, essid, bssid
980 # Enable/disable buttons based on the selected network.
984 # 'widget' -- gtk.Widget - The widget sending the event.
986 # 'data' -- tuple - list of arbitrary arguments (not used)
991 def on_network_selection( self
, widget
, data
= None ):
992 ( store
, selected_iter
) = self
.selected_network
.get_selected()
993 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
994 # if no networks are selected, disable all buttons except New
995 # (this occurs after a drag-and-drop)
996 if selected_iter
== None:
997 self
.edit_button
.set_sensitive(False)
998 self
.delete_button
.set_sensitive(False)
999 self
.connect_button
.set_sensitive(False)
1001 # enable/disable buttons
1002 self
.connect_button
.set_sensitive(True)
1003 if (store
.get_value(selected_iter
, 2) == True): # is selected network known?
1004 self
.edit_button
.set_sensitive(True)
1005 self
.delete_button
.set_sensitive(True)
1007 self
.edit_button
.set_sensitive(True)
1008 self
.delete_button
.set_sensitive(False)
1010 # Init and run the preferences dialog
1014 # 'widget' -- gtk.Widget - The widget sending the event.
1016 # 'data' -- tuple - list of arbitrary arguments (not used)
1021 def edit_preferences( self
, widget
, data
=None ):
1022 prefs
= preferences_dialog( self
, self
.confFile
)
1023 response
= prefs
.run()
1025 if response
== int(gtk
.RESPONSE_ACCEPT
):
1028 # Respond to a request to create a new AP profile
1032 # 'widget' -- gtk.Widget - The widget sending the event.
1034 # 'profile' -- dictionary - The AP profile to use as basis for new profile.
1036 # 'data' -- tuple - list of arbitrary arguments (not used)
1040 # boolean -- True if a profile was created and False if profile creation was canceled.
1041 def create_new_profile( self
, widget
, profile
, data
=None ):
1042 profile_editor
= profile_dialog( self
, profile
)
1043 profile
= profile_editor
.run()
1044 profile_editor
.destroy()
1046 apname
= make_section_name( profile
['essid'], profile
['bssid'] )
1047 # Check that the ap does not exist already
1048 if apname
in self
.confFile
.profiles():
1049 dlg
= gtk
.MessageDialog( self
.window
, gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
, "A profile for %s already exists" % (apname
) )
1054 self
.access_points
[ apname
] = profile
1055 self
.confFile
.set_section( apname
, profile
)
1056 # if it is not in the auto_profile_order add it
1057 if apname
not in self
.confFile
.auto_profile_order
:
1058 self
.confFile
.auto_profile_order
.append(apname
)
1061 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
1064 # Did not create new profile
1067 # Respond to a request to edit an AP profile. Edit selected AP profile if it
1068 # is known. Otherwise, create a new profile with the selected ESSID and BSSID.
1072 # 'widget' -- gtk.Widget - The widget sending the event.
1074 # 'data' -- tuple - list of arbitrary arguments (not used)
1079 def edit_profile( self
, widget
, data
=None ):
1080 ( store
, selected_iter
) = self
.plist
.get_selection().get_selected()
1081 if not selected_iter
: return
1082 row_start
= str(self
.pstore
.get_value( selected_iter
, 0 )).split("\n")
1083 apname
= make_section_name( row_start
[0], row_start
[1] )
1084 profile
= self
.confFile
.get_profile( apname
)
1086 profile_editor
= profile_dialog( self
, profile
)
1087 profile
= profile_editor
.run()
1088 profile_editor
.destroy()
1091 print "Got edited profile ", profile
1092 apname
= make_section_name( profile
['essid'], profile
['bssid'] )
1093 self
.access_points
[ apname
] = profile
1094 self
.confFile
.set_section( apname
, profile
)
1096 profile
= get_new_profile()
1097 ( profile
['essid'], profile
['bssid'] ) = store
.get_value( selected_iter
, 0 ).split("\n")
1098 self
.create_new_profile( widget
, profile
, data
)
1100 # Respond to a request to delete an AP profile (i.e. make profile unknown)
1104 # 'widget' -- gtk.Widget - The widget sending the event.
1106 # 'data' -- tuple - list of arbitrary arguments (not used)
1111 def delete_profile( self
, widget
, data
=None ):
1112 ( store
, selected_iter
) = self
.plist
.get_selection().get_selected()
1113 if not selected_iter
: return
1114 ( essid
, bssid
) = store
.get_value( selected_iter
, 0 ).split("\n")
1115 known
= store
.get_value( selected_iter
, 1 )
1116 if not known
: return
1117 dlg
= gtk
.MessageDialog(
1119 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
1120 gtk
.MESSAGE_QUESTION
,
1122 "Are you sure you want to delete the %s (%s) profile?" % (essid
, bssid
) )
1126 if res
== gtk
.RESPONSE_NO
: return
1128 apname
= make_section_name( essid
, bssid
)
1129 del self
.access_points
[ apname
]
1130 self
.confFile
.remove_section( apname
)
1131 print "delete_profile: ", apname
, ":", self
.confFile
.auto_profile_order
1132 if apname
in self
.confFile
.auto_profile_order
: self
.confFile
.auto_profile_order
.remove(apname
)
1133 self
.pstore
.remove( selected_iter
)
1134 # Let's save our current state
1135 self
.update_auto_profile_order()
1137 # Respond to a request to connect to an AP.
1141 # 'widget' -- gtk.Widget - The widget sending the event.
1143 # 'profile' -- dictionary - The AP profile to which to connect.
1145 # 'data' -- tuple - list of arbitrary arguments (not used)
1150 def connect_profile( self
, widget
, profile
, data
=None ):
1151 ( store
, selected_iter
) = self
.plist
.get_selection().get_selected()
1152 if not selected_iter
: return
1153 ( essid
, bssid
) = store
.get_value( selected_iter
, 0 ).split("\n")
1154 known
= store
.get_value( selected_iter
, 2 )
1156 if data
!= 'noconnect':
1157 dlg
= gtk
.MessageDialog(
1159 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
1160 gtk
.MESSAGE_QUESTION
,
1162 "This network does not have a profile configured.\n\nWould you like to create one now?" )
1166 if res
== gtk
.RESPONSE_NO
: return
1167 profile
= get_new_profile()
1168 profile
['essid'] = essid
1169 profile
['bssid'] = bssid
1170 if not self
.create_new_profile( widget
, profile
, data
):
1172 apname
= make_section_name( essid
, bssid
)
1173 self
.connection
.connect_to_network( self
.access_points
[apname
], status_window( self
) )
1175 # Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
1179 # 'widget' -- gtk.Widget - The widget sending the event.
1181 # 'data' -- tuple - list of arbitrary arguments (not used)
1186 def disconnect_profile( self
, widget
, data
=None ):
1187 self
.connection
.disconnect_interface()
1189 # Update the config file auto profile order from the on-screen order
1193 # 'widget' -- gtk.Widget - The widget sending the event.
1195 # 'data' -- tuple - list of arbitrary arguments (not used)
1197 # 'data2' -- tuple - list of arbitrary arguments (not used)
1202 def update_auto_profile_order( self
, widget
= None, data
= None, data2
= None ):
1203 # recreate the auto_profile_order
1204 auto_profile_order
= []
1205 piter
= self
.pstore
.get_iter_first()
1207 # only if it's known
1208 if self
.pstore
.get_value( piter
, 2 ) == True:
1209 row_start
= str(self
.pstore
.get_value( piter
, 0 )).split("\n")
1210 auto_profile_order
.append( make_section_name( row_start
[0], row_start
[1] ) )
1211 piter
= self
.pstore
.iter_next( piter
)
1212 self
.confFile
.auto_profile_order
= auto_profile_order
1214 # The preferences dialog. Edits some items in the DEFAULT section of the config file.
1215 class preferences_dialog
:
1216 # Create a new preferences_dialog.
1220 # 'parent' -- gtk.Object - Usually, the calling window.
1222 # 'confFile' -- ConfigFile - The config file in which to store/read settings.
1226 # preferences_dialog instance
1227 def __init__( self
, parent
, confFile
):
1228 global wifi_radar_icon
1229 self
.parent
= parent
1230 self
.confFile
= confFile
1231 self
.dialog
= gtk
.Dialog('WiFi Radar Preferences', self
.parent
.window
,
1232 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
1233 ( gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
, gtk
.STOCK_SAVE
, gtk
.RESPONSE_ACCEPT
) )
1234 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
1235 self
.dialog
.set_icon( icon
)
1236 self
.dialog
.set_resizable( True )
1237 self
.dialog
.set_transient_for( self
.parent
.window
)
1238 self
.tooltips
= gtk
.Tooltips()
1240 # set up preferences widgets
1243 # auto detect wireless device
1244 self
.w_auto_detect
= gtk
.CheckButton("Auto-detect wireless device")
1246 print "prefs: ", self
.confFile
.get_opt('DEFAULT.interface')
1248 self
.w_auto_detect
.set_active( self
.confFile
.get_opt('DEFAULT.interface') == "auto_detect" )
1249 self
.w_auto_detect
.connect("toggled", self
.toggle_auto_detect
)
1250 self
.tooltips
.set_tip(self
.w_auto_detect
, "Automatically select wireless device to configure")
1251 self
.dialog
.vbox
.pack_start(self
.w_auto_detect
, False, False, 5)
1252 # network interface selecter
1253 self
.w_interface
= gtk
.combo_box_entry_new_text()
1254 iwconfig_info
= Popen([self
.confFile
.get_opt('DEFAULT.iwconfig_command')], stdout
=PIPE
).stdout
1255 wireless_devices
= [ (x
[0:x
.find(" ")]) for x
in iwconfig_info
if ("ESSID" in x
)]
1256 for device
in wireless_devices
:
1257 if device
!= self
.confFile
.get_opt('DEFAULT.interface'):
1258 self
.w_interface
.append_text(device
)
1259 if self
.confFile
.get_opt('DEFAULT.interface') != "auto_detect":
1260 self
.w_interface
.prepend_text( self
.confFile
.get_opt('DEFAULT.interface') )
1261 self
.w_interface
.set_active(0)
1262 self
.w_interface_label
= gtk
.Label("Wireless device")
1263 self
.w_hbox1
= gtk
.HBox(False, 0)
1264 self
.w_hbox1
.pack_start(self
.w_interface_label
, False, False, 5)
1265 self
.w_hbox1
.pack_start(self
.w_interface
, True, True, 0)
1266 self
.w_interface
.set_sensitive( self
.confFile
.get_opt('DEFAULT.interface') != "auto_detect" )
1267 self
.dialog
.vbox
.pack_start(self
.w_hbox1
, False, False, 5)
1268 # scan timeout (spin button of integers from 1 to 100)
1269 self
.time_in_seconds
= gtk
.Adjustment( self
.confFile
.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
1270 self
.w_scan_timeout
= gtk
.SpinButton(self
.time_in_seconds
, 1, 0)
1271 self
.w_scan_timeout
.set_update_policy(gtk
.UPDATE_IF_VALID
)
1272 self
.w_scan_timeout
.set_numeric(True)
1273 self
.w_scan_timeout
.set_snap_to_ticks(True)
1274 self
.w_scan_timeout
.set_wrap(False)
1275 self
.tooltips
.set_tip(self
.w_scan_timeout
, "How long should WiFi Radar scan for access points?")
1276 self
.w_scan_timeout_label
= gtk
.Label("Scan timeout (seconds)")
1277 self
.w_hbox2
= gtk
.HBox(False, 0)
1278 self
.w_hbox2
.pack_start(self
.w_scan_timeout_label
, False, False, 5)
1279 self
.w_hbox2
.pack_start(self
.w_scan_timeout
, True, True, 0)
1280 self
.dialog
.vbox
.pack_start(self
.w_hbox2
, False, False, 5)
1282 self
.w_speak_up
= gtk
.CheckButton("Use speak-up")
1283 self
.w_speak_up
.set_active( self
.confFile
.get_opt_as_bool('DEFAULT.speak_up') )
1284 self
.w_speak_up
.connect("toggled", self
.toggle_speak
)
1285 self
.tooltips
.set_tip(self
.w_speak_up
, "Should I speak up when connecting to a network? (If you have a speech command)")
1286 self
.dialog
.vbox
.pack_start(self
.w_speak_up
, False, False, 5)
1289 self
.w_speak_cmd
= gtk
.Entry()
1290 self
.w_speak_cmd
.set_text(self
.confFile
.get_opt('DEFAULT.speak_command'))
1291 self
.w_speak_cmd_label
= gtk
.Label("Speak Command")
1292 self
.w_hbox3
= gtk
.HBox(False, 0)
1293 self
.w_hbox3
.pack_start(self
.w_speak_cmd_label
, False, False, 5)
1294 self
.w_hbox3
.pack_start(self
.w_speak_cmd
, True, True, 0)
1295 self
.w_speak_cmd
.set_sensitive(self
.w_speak_up
.get_active())
1296 self
.dialog
.vbox
.pack_start(self
.w_hbox3
, False, False, 5)
1299 self
.w_commit_required
= gtk
.CheckButton("Commit required")
1300 self
.w_commit_required
.set_active( self
.confFile
.get_opt_as_bool('DEFAULT.commit_required') )
1301 self
.tooltips
.set_tip(self
.w_commit_required
, "Check this box if your card requires a \"commit\" command with iwconfig")
1302 self
.dialog
.vbox
.pack_start(self
.w_commit_required
, False, False, 5)
1304 self
.w_ifup_required
= gtk
.CheckButton("Ifup required")
1305 self
.w_ifup_required
.set_active( self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required') )
1306 self
.tooltips
.set_tip(self
.w_ifup_required
, "Check this box if your system requires the interface to be brought up first")
1308 self
.w_ifconfig_cmd
= gtk
.Entry()
1309 self
.w_ifconfig_cmd
.set_text(self
.confFile
.get_opt('DEFAULT.ifconfig_command'))
1310 self
.w_ifconfig_cmd_label
= gtk
.Label("ifconfig Command")
1311 self
.w_hbox4
= gtk
.HBox(True, 0)
1312 self
.w_hbox4
.pack_start(self
.w_ifconfig_cmd_label
, False, False, 5)
1313 self
.w_hbox4
.pack_start(self
.w_ifconfig_cmd
, True, True, 0)
1314 self
.dialog
.vbox
.pack_start(self
.w_hbox4
, False, False, 5)
1315 self
.dialog
.vbox
.pack_start(self
.w_ifup_required
, False, False, 5)
1317 # Respond to Auto-detect checkbox toggle by activating/de-activating the interface combobox.
1321 # 'auto_detect_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1323 # 'data' -- tuple - list of arbitrary arguments (not used)
1328 def toggle_auto_detect(self
, auto_detect_toggle
, data
=None):
1329 self
.w_interface
.set_sensitive(not auto_detect_toggle
.get_active())
1331 # Respond to Speak-up checkbox toggle by activating/de-activating the Speak Cmd Entry.
1335 # 'speak_toggle' -- gtk.CheckButton - The checkbox sending the signal.
1337 # 'data' -- tuple - list of arbitrary arguments (not used)
1342 def toggle_speak(self
, speak_toggle
, data
=None):
1343 self
.w_speak_cmd
.set_sensitive(speak_toggle
.get_active())
1345 # Display preferences dialog and operate until canceled or okayed.
1353 # integer -- gtk response ID
1355 self
.dialog
.show_all()
1356 return self
.dialog
.run()
1358 # Write updated values to config file.
1368 print "saving prefs"
1369 if self
.w_auto_detect
.get_active():
1370 set_network_device("auto_detect")
1371 self
.confFile
.set_opt('DEFAULT.interface', "auto_detect")
1373 set_network_device(self
.w_interface
.child
.get_text())
1374 self
.confFile
.set_opt('DEFAULT.interface', self
.w_interface
.child
.get_text())
1375 self
.confFile
.set_float_opt('DEFAULT.scan_timeout', self
.w_scan_timeout
.get_value())
1376 self
.confFile
.set_bool_opt('DEFAULT.speak_up', self
.w_speak_up
.get_active())
1377 self
.confFile
.set_bool_opt('DEFAULT.commit_required', self
.w_commit_required
.get_active())
1378 self
.confFile
.set_bool_opt('DEFAULT.ifup_required', self
.w_ifup_required
.get_active())
1379 if self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
1380 shellcmd([self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), 'up'])
1382 # Remove preferences window.
1392 self
.dialog
.destroy()
1396 # Edit and return an AP profile.
1397 class profile_dialog
:
1398 # Create a new profile_dialog.
1402 # 'parent' -- gtk.Object - Usually, the calling window.
1404 # 'profile' -- dictionary - The profile to edit. May be mostly-empty default profile.
1408 # profile_dialog instance
1409 def __init__( self
, parent
, profile
):
1410 global wifi_radar_icon
1411 self
.parent
= parent
1412 self
.profile
= profile
1413 self
.WIFI_MODES
= [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1414 self
.WIFI_SECURITY
= [ '', 'open', 'restricted' ]
1415 self
.WIFI_CHANNELS
= [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1416 self
.dialog
= gtk
.Dialog('WiFi Profile', self
.parent
.window
,
1417 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
1418 ( gtk
.STOCK_CANCEL
, False, gtk
.STOCK_SAVE
, True ) )
1419 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
1420 self
.dialog
.set_icon( icon
)
1421 self
.dialog
.set_resizable( False )
1422 self
.dialog
.set_transient_for( self
.parent
.window
)
1423 #self.dialog.set_size_request( 400, 400 )
1425 essid_table
= gtk
.Table( 1, 2, False )
1426 essid_table
.set_row_spacings( 3 )
1427 essid_table
.set_col_spacings( 3 )
1430 essid_table
.attach( gtk
.Label( 'Network Name' ), 0, 1, 0, 1 )
1431 # The essid textboxes
1432 self
.essid_entry
= gtk
.Entry( 32 )
1433 self
.essid_entry
.set_text( self
.profile
['essid'] )
1434 essid_table
.attach( self
.essid_entry
, 1, 2, 0, 1 )
1435 # Add the essid table to the dialog
1436 self
.dialog
.vbox
.pack_start( essid_table
, True, True, 5 )
1438 bssid_table
= gtk
.Table( 1, 2, False )
1439 bssid_table
.set_row_spacings( 3 )
1440 bssid_table
.set_col_spacings( 3 )
1442 bssid_table
.attach( gtk
.Label( 'Network bssid' ), 0, 1, 0, 1 )
1443 # The bssid textboxes
1444 self
.bssid_entry
= gtk
.Entry( 32 )
1445 self
.bssid_entry
.set_text( self
.profile
['bssid'] )
1446 bssid_table
.attach( self
.bssid_entry
, 1, 2, 0, 1 )
1447 #self.key = gtk.Entry( 32 )
1448 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1449 # Add the bssid table to the dialog
1450 self
.dialog
.vbox
.pack_start( bssid_table
, True, True, 5 )
1452 # create the wifi expander
1453 self
.wifi_expander
= gtk
.Expander( WIFI_SET_LABEL
)
1454 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1455 wifi_table
= gtk
.Table( 4, 2, False )
1456 wifi_table
.set_row_spacings( 3 )
1457 wifi_table
.set_col_spacings( 3 )
1459 wifi_table
.attach( gtk
.Label( 'Mode' ), 0, 1, 0, 1 )
1460 wifi_table
.attach( gtk
.Label( 'Channel' ), 0, 1, 1, 2 )
1461 wifi_table
.attach( gtk
.Label( 'Key' ), 0, 1, 2, 3 )
1462 wifi_table
.attach( gtk
.Label( 'Security' ), 0, 1, 3, 4 )
1463 # The Wifi text boxes
1464 self
.mode_combo
= gtk
.combo_box_new_text()
1465 for mode
in self
.WIFI_MODES
:
1466 self
.mode_combo
.append_text( mode
)
1467 self
.mode_combo
.set_active( self
.get_array_index( self
.profile
['mode'], self
.WIFI_MODES
) )
1468 wifi_table
.attach( self
.mode_combo
, 1, 2, 0, 1 )
1469 self
.channel_combo
= gtk
.combo_box_new_text()
1470 for channel
in self
.WIFI_CHANNELS
:
1471 self
.channel_combo
.append_text( channel
)
1472 self
.channel_combo
.set_active( self
.get_array_index( self
.profile
['channel'], self
.WIFI_CHANNELS
) )
1473 wifi_table
.attach( self
.channel_combo
, 1, 2, 1, 2 )
1475 self
.key_entry
= gtk
.Entry( 64 )
1476 self
.key_entry
.set_text( self
.profile
['key'] )
1477 wifi_table
.attach( self
.key_entry
, 1, 2, 2, 3 )
1479 self
.security_combo
= gtk
.combo_box_new_text()
1480 for security
in self
.WIFI_SECURITY
:
1481 self
.security_combo
.append_text( security
)
1482 self
.security_combo
.set_active( self
.get_array_index( self
.profile
['security'], self
.WIFI_SECURITY
) )
1483 wifi_table
.attach( self
.security_combo
, 1, 2, 3, 4 )
1484 # Add the wifi table to the expander
1485 self
.wifi_expander
.add( wifi_table
)
1486 # Add the expander to the dialog
1487 self
.dialog
.vbox
.pack_start( self
.wifi_expander
, False, False, 5 )
1489 # create the wpa expander
1490 self
.wpa_expander
= gtk
.Expander( NO_WPA_LABEL
)
1491 self
.wpa_expander
.connect( 'notify::expanded', self
.toggle_use_wpa
)
1492 wpa_table
= gtk
.Table( 1, 2, False )
1493 wpa_table
.set_row_spacings( 3 )
1494 wpa_table
.set_col_spacings( 3 )
1496 wpa_table
.attach( gtk
.Label( 'Driver' ), 0, 1, 0, 1 )
1498 self
.wpa_driver_entry
= gtk
.Entry()
1499 self
.wpa_driver_entry
.set_text( self
.profile
['wpa_driver'] )
1500 wpa_table
.attach( self
.wpa_driver_entry
, 1, 2, 0, 1 )
1501 # Add the wpa table to the expander
1502 self
.wpa_expander
.add( wpa_table
)
1503 # Add the expander to the dialog
1504 self
.dialog
.vbox
.pack_start( self
.wpa_expander
, False, False, 5 )
1506 # create the dhcp expander
1507 self
.dhcp_expander
= gtk
.Expander( USE_DHCP_LABEL
)
1508 self
.dhcp_expander
.connect( 'notify::expanded', self
.toggle_use_dhcp
)
1509 ip_table
= gtk
.Table( 6, 2, False )
1510 ip_table
.set_row_spacings( 3 )
1511 ip_table
.set_col_spacings( 3 )
1513 ip_table
.attach( gtk
.Label( 'IP' ), 0, 1, 0, 1 )
1514 ip_table
.attach( gtk
.Label( 'Netmask' ), 0, 1, 1, 2 )
1515 ip_table
.attach( gtk
.Label( 'Gateway' ), 0, 1, 2, 3 )
1516 ip_table
.attach( gtk
.Label( 'Domain' ), 0, 1, 3, 4 )
1517 ip_table
.attach( gtk
.Label( 'DNS' ), 0, 1, 4, 5 )
1518 ip_table
.attach( gtk
.Label( 'DNS' ), 0, 1, 5, 6 )
1520 self
.ip_entry
= gtk
.Entry( 15 )
1521 self
.ip_entry
.set_text( self
.profile
['ip'] )
1522 ip_table
.attach( self
.ip_entry
, 1, 2, 0, 1 )
1523 self
.netmask_entry
= gtk
.Entry( 15 )
1524 self
.netmask_entry
.set_text( self
.profile
['netmask'] )
1525 ip_table
.attach( self
.netmask_entry
, 1, 2, 1, 2 )
1526 self
.gw_entry
= gtk
.Entry( 15 )
1527 self
.gw_entry
.set_text( self
.profile
['gateway'] )
1528 ip_table
.attach( self
.gw_entry
, 1, 2, 2, 3 )
1529 self
.domain_entry
= gtk
.Entry( 32 )
1530 self
.domain_entry
.set_text( self
.profile
['domain'] )
1531 ip_table
.attach( self
.domain_entry
, 1, 2, 3, 4 )
1532 self
.dns1_entry
= gtk
.Entry( 15 )
1533 self
.dns1_entry
.set_text( self
.profile
['dns1'] )
1534 ip_table
.attach( self
.dns1_entry
, 1, 2, 4, 5 )
1535 self
.dns2_entry
= gtk
.Entry( 15 )
1536 self
.dns2_entry
.set_text( self
.profile
['dns2'] )
1537 ip_table
.attach( self
.dns2_entry
, 1, 2, 5, 6 )
1538 # Add the ip table to the expander
1539 self
.dhcp_expander
.add( ip_table
)
1540 # Add the expander to the dialog
1541 self
.dialog
.vbox
.pack_start( self
.dhcp_expander
, False, False, 5 )
1543 # create the connection-building postpre expander
1544 self
.con_pp_expander
= gtk
.Expander( CON_PP_LABEL
)
1545 con_pp_table
= gtk
.Table( 2, 2, False )
1546 con_pp_table
.set_row_spacings( 3 )
1547 con_pp_table
.set_col_spacings( 3 )
1549 con_pp_table
.attach( gtk
.Label( 'Before' ), 0, 1, 0, 1 )
1550 con_pp_table
.attach( gtk
.Label( 'After' ), 0, 1, 1, 2 )
1552 self
.con_prescript_entry
= gtk
.Entry()
1553 self
.con_prescript_entry
.set_text( self
.profile
['con_prescript'] )
1554 con_pp_table
.attach( self
.con_prescript_entry
, 1, 2, 0, 1 )
1555 self
.con_postscript_entry
= gtk
.Entry()
1556 self
.con_postscript_entry
.set_text( self
.profile
['con_postscript'] )
1557 con_pp_table
.attach( self
.con_postscript_entry
, 1, 2, 1, 2 )
1558 # Add the pp table to the expander
1559 self
.con_pp_expander
.add( con_pp_table
)
1560 # Add the expander to the dialog
1561 self
.dialog
.vbox
.pack_start( self
.con_pp_expander
, False, False, 5 )
1563 # create the disconnection postpre expander
1564 self
.dis_pp_expander
= gtk
.Expander( DIS_PP_LABEL
)
1565 dis_pp_table
= gtk
.Table( 2, 2, False )
1566 dis_pp_table
.set_row_spacings( 3 )
1567 dis_pp_table
.set_col_spacings( 3 )
1569 dis_pp_table
.attach( gtk
.Label( 'Before' ), 0, 1, 0, 1 )
1570 dis_pp_table
.attach( gtk
.Label( 'After' ), 0, 1, 1, 2 )
1572 self
.dis_prescript_entry
= gtk
.Entry()
1573 self
.dis_prescript_entry
.set_text( self
.profile
['dis_prescript'] )
1574 dis_pp_table
.attach( self
.dis_prescript_entry
, 1, 2, 0, 1 )
1575 self
.dis_postscript_entry
= gtk
.Entry()
1576 self
.dis_postscript_entry
.set_text( self
.profile
['dis_postscript'] )
1577 dis_pp_table
.attach( self
.dis_postscript_entry
, 1, 2, 1, 2 )
1578 # Add the pp table to the expander
1579 self
.dis_pp_expander
.add( dis_pp_table
)
1580 # Add the expander to the dialog
1581 self
.dialog
.vbox
.pack_start( self
.dis_pp_expander
, False, False, 5 )
1583 # Display profile dialog, operate until canceled or okayed, and, possibly, return edited profile values.
1591 # dictionary or None -- a profile, or None on cancel
1593 self
.dialog
.show_all()
1594 if self
.dialog
.run():
1595 self
.profile
['known'] = True
1596 self
.profile
['essid'] = self
.essid_entry
.get_text().strip()
1597 self
.profile
['bssid'] = self
.bssid_entry
.get_text().strip()
1598 self
.profile
['key'] = self
.key_entry
.get_text().strip()
1599 self
.profile
['mode'] = self
.get_array_item( self
.mode_combo
.get_active(), self
.WIFI_MODES
)
1600 self
.profile
['security'] = self
.get_array_item( self
.security_combo
.get_active(), self
.WIFI_SECURITY
)
1601 self
.profile
['encrypted'] = ( self
.profile
['security'] != '' )
1602 self
.profile
['channel'] = self
.get_array_item( self
.channel_combo
.get_active(), self
.WIFI_CHANNELS
)
1603 self
.profile
['protocol'] = 'g'
1604 self
.profile
['available'] = ( self
.profile
['signal'] > 0 )
1605 self
.profile
['con_prescript'] = self
.con_prescript_entry
.get_text().strip()
1606 self
.profile
['con_postscript'] = self
.con_postscript_entry
.get_text().strip()
1607 self
.profile
['dis_prescript'] = self
.dis_prescript_entry
.get_text().strip()
1608 self
.profile
['dis_postscript'] = self
.dis_postscript_entry
.get_text().strip()
1610 self
.profile
['use_wpa'] = self
.wpa_expander
.get_expanded()
1611 self
.profile
['wpa_driver'] = self
.wpa_driver_entry
.get_text().strip()
1613 self
.profile
['use_dhcp'] = not self
.dhcp_expander
.get_expanded()
1614 self
.profile
['ip'] = self
.ip_entry
.get_text().strip()
1615 self
.profile
['netmask'] = self
.netmask_entry
.get_text().strip()
1616 self
.profile
['gateway'] = self
.gw_entry
.get_text().strip()
1617 self
.profile
['domain'] = self
.domain_entry
.get_text().strip()
1618 self
.profile
['dns1'] = self
.dns1_entry
.get_text().strip()
1619 self
.profile
['dns2'] = self
.dns2_entry
.get_text().strip()
1623 # Remove profile dialog.
1632 def destroy( self
):
1633 self
.dialog
.destroy()
1636 # Respond to expanding/hiding IP segment.
1640 # 'widget' -- gtk.Widget - The widget sending the event.
1642 # 'data' -- tuple - List of arbitrary arguments (not used)
1647 def toggle_use_dhcp( self
, widget
, data
= None ):
1648 expanded
= self
.dhcp_expander
.get_expanded()
1650 self
.dhcp_expander
.set_label( USE_IP_LABEL
)
1652 self
.dhcp_expander
.set_label( USE_DHCP_LABEL
)
1654 # Respond to expanding/hiding WPA segment.
1658 # 'widget' -- gtk.Widget - The widget sending the event.
1660 # 'data' -- tuple - List of arbitrary arguments (not used)
1665 def toggle_use_wpa( self
, widget
, data
= None ):
1666 expanded
= self
.wpa_expander
.get_expanded()
1668 self
.wpa_expander
.set_label( USE_WPA_LABEL
)
1670 self
.wpa_expander
.set_label( NO_WPA_LABEL
)
1672 # Return the index where item matches a cell in array.
1676 # 'item' -- string - Item to find in array
1678 # 'array' -- list - List in which to find match.
1682 # integer - 0 (no match) or higher (index of match)
1683 def get_array_index( self
, item
, array
):
1685 return array
.index( item
.strip() )
1690 # Return the value in array[ index ]
1694 # 'index' -- integer - The index to look up.
1696 # 'array' -- list - List in which to look up value.
1700 # string -- empty string (no match) or looked up value
1701 def get_array_item( self
, index
, array
):
1703 return array
[ index
]
1708 # A simple class for putting up a "Please wait" dialog so the user
1709 # doesn't think we've forgotten about them.
1710 class status_window
:
1711 # Create a new status_window.
1715 # 'parent' -- gtk.Object - Usually, the calling window.
1719 # status_window instance
1720 def __init__( self
, parent
):
1721 global wifi_radar_icon
1722 self
.parent
= parent
1723 self
.dialog
= gtk
.Dialog("Working", self
.parent
.window
, gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
)
1724 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
1725 self
.dialog
.set_icon( icon
)
1726 self
.lbl
= gtk
.Label("Please wait...")
1727 self
.bar
= gtk
.ProgressBar()
1728 self
.dialog
.vbox
.pack_start(self
.lbl
)
1729 self
.dialog
.vbox
.pack_start(self
.bar
)
1730 self
.dialog
.show_all()
1732 # Change the message displayed to the user.
1736 # 'message' -- string - The message to show to the user.
1741 def update_message( self
, message
):
1742 self
.lbl
.set_text(message
)
1744 # Update the status_window progress bar.
1752 # True -- always return True
1753 def update_window( self
):
1757 # Display and operate the status_window.
1767 self
.dialog
.show_all()
1768 self
.timer
= gobject
.timeout_add(250,self
.update_window
)
1771 # Remove the status_window.
1780 def destroy( self
):
1781 gobject
.source_remove(self
.timer
)
1782 self
.dialog
.destroy()
1785 # Manage the configuration for the application, including reading and writing the config from/to a file.
1786 class ConfigFile(ConfigParser
.SafeConfigParser
):
1787 # Create a new ConfigFile.
1791 # 'filename' -- string - The configuration file's name.
1793 # 'defaults' -- dictionary - Default values for the DEFAULT section.
1797 # ConfigFile instance
1798 def __init__( self
, filename
, defaults
):
1799 self
.filename
= filename
1800 self
.auto_profile_order
= []
1801 ConfigParser
.SafeConfigParser
.__init
__(self
, defaults
)
1803 # Set the contents of a section to values from a dictionary.
1807 # 'section_name' -- string - Configuration file section.
1809 # 'section_dict' -- dictionary - Values to add to section.
1814 def set_section( self
, section_name
, section_dict
):
1816 self
.add_section(section_name
)
1817 except ConfigParser
.DuplicateSectionError
:
1819 for key
in section_dict
.keys():
1820 if type(section_dict
[key
]) == BooleanType
:
1821 self
.set_bool_opt(section_name
+ "." + key
, section_dict
[key
])
1822 elif type(section_dict
[key
]) == IntType
:
1823 self
.set_int_opt(section_name
+ "." + key
, section_dict
[key
])
1824 elif type(section_dict
[key
]) == FloatType
:
1825 self
.set_float_opt(section_name
+ "." + key
, section_dict
[key
])
1827 self
.set_opt(section_name
+ "." + key
, section_dict
[key
])
1829 # Return the profile recorded in the specified section.
1833 # 'section_name' -- string - Configuration file section.
1837 # dictionary or None - The specified profile or None if not found
1838 def get_profile( self
, section_name
):
1839 if section_name
in self
.profiles():
1840 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' ]
1841 bool_types
= [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
1842 int_types
= [ 'signal' ]
1844 for option
in bool_types
:
1845 profile
[option
] = self
.get_opt_as_bool( section_name
+ "." + option
)
1846 for option
in int_types
:
1847 profile
[option
] = self
.get_opt_as_int( section_name
+ "." + option
)
1848 for option
in str_types
:
1849 profile
[option
] = self
.get_opt( section_name
+ "." + option
)
1853 # Get a config option and handle exceptions.
1857 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1858 # period and the option key. (E.g. "DEFAULT.interface")
1862 # string or None - option value as string or None on failure
1863 def get_opt( self
, option_path
):
1864 #print "ConfigFile.get_opt: ", option_path
1865 (section
, option
) = option_path
.split('.')
1867 return self
.get(section
, option
)
1868 except (ConfigParser
.NoSectionError
, ConfigParser
.NoOptionError
):
1871 # Get a config option and return as a boolean type.
1875 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1876 # period and the option key. (E.g. "DEFAULT.interface")
1880 # boolean - option value as boolean
1881 def get_opt_as_bool( self
, option_path
):
1882 option
= self
.get_opt(option_path
)
1883 if isinstance(option
, BooleanType
) or isinstance(option
, NoneType
):
1885 if option
== 'True':
1887 if option
== 'False':
1889 raise ValueError, 'boolean option was not True or False'
1891 # Get a config option and return as an integer type.
1895 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1896 # period and the option key. (E.g. "DEFAULT.interface")
1900 # integer- option value as integer
1901 def get_opt_as_int( self
, option_path
):
1902 return int(float(self
.get_opt(option_path
)))
1904 # Convert boolean type to string and set config option.
1908 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1909 # period and the option key. (E.g. "DEFAULT.interface")
1911 # 'value' -- boolean - Value to set.
1916 def set_bool_opt( self
, option_path
, value
):
1917 if ( value
== True ) or ( value
> 0 ) or ( value
== 'True' ):
1919 elif ( value
== False ) or ( value
== 0 ) or ( value
== 'False' ):
1922 raise ValueError, 'cannot convert value to string'
1923 self
.set_opt(option_path
, repr(value
))
1925 # Convert integer type to string and set config option.
1929 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1930 # period and the option key. (E.g. "DEFAULT.interface")
1932 # 'value' -- integer - Value to set.
1937 def set_int_opt( self
, option_path
, value
):
1938 if not isinstance(value
, IntType
):
1939 raise ValueError, 'value is not an integer'
1940 self
.set_opt(option_path
, repr(value
))
1942 # Convert float type to string and set config option.
1946 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1947 # period and the option key. (E.g. "DEFAULT.interface")
1949 # 'value' -- float - Value to set.
1954 def set_float_opt( self
, option_path
, value
):
1955 if not isinstance(value
, FloatType
):
1956 raise ValueError, 'value is not a float'
1957 self
.set_opt(option_path
, repr(int(value
)))
1959 # Set a config option while handling exceptions.
1963 # 'option_path' -- string - Section (a.k.a. profile) name concatenated with a
1964 # period and the option key. (E.g. "DEFAULT.interface")
1966 # 'value' -- string - Value to set.
1971 def set_opt( self
, option_path
, value
):
1972 (section
, option
) = option_path
.split('.')
1974 self
.set(section
, option
, value
)
1975 except ConfigParser
.NoSectionError
:
1976 self
.add_section(section
)
1977 self
.set_opt(option_path
, value
)
1979 # Return a list of the section names which denote AP profiles.
1987 # list - profile names
1988 def profiles( self
):
1990 for section
in self
.sections():
1992 profile_list
.append(section
)
1995 # Read configuration file from disk into instance variables.
2005 fp
= open( self
.filename
, "r" )
2007 # convert the auto_profile_order to a list for ordering
2008 self
.auto_profile_order
= eval(self
.get_opt('DEFAULT.auto_profile_order'))
2009 for ap
in self
.profiles():
2010 self
.set_bool_opt( ap
+ '.known', True)
2011 if ap
in self
.auto_profile_order
: continue
2012 self
.auto_profile_order
.append( ap
)
2014 # Write configuration file to disk from instance variables. Copied from
2015 # ConfigParser and modified to write options in alphabetical order.
2025 self
.set_opt('DEFAULT.auto_profile_order', str(self
.auto_profile_order
))
2026 fp
= open( self
.filename
, "w" )
2027 # write DEFAULT section first
2029 fp
.write("[DEFAULT]\n")
2030 for key
in sorted(self
._defaults
.keys()):
2031 fp
.write("%s = %s\n" % (key
, str(self
._defaults
[key
]).replace('\n','\n\t')))
2033 # write non-profile sections first
2034 for section
in self
._sections
:
2035 if section
not in self
.profiles():
2036 fp
.write("[%s]\n" % section
)
2037 for key
in sorted(self
._sections
[section
].keys()):
2038 if key
!= "__name__":
2039 fp
.write("%s = %s\n" %
2040 (key
, str(self
._sections
[section
][key
]).replace('\n', '\n\t')))
2042 # write profile sections
2043 for section
in self
._sections
:
2044 if section
in self
.profiles():
2045 fp
.write("[%s]\n" % section
)
2046 for key
in sorted(self
._sections
[section
].keys()):
2047 if key
!= "__name__":
2048 fp
.write("%s = %s\n" %
2049 (key
, str(self
._sections
[section
][key
]).replace('\n', '\n\t')))
2054 # Load our conf file and known profiles
2055 # Defaults, these may get overridden by values found in the conf file.
2056 config_defaults
= { # The network interface you use.
2057 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
2058 'interface': "auto_detect",
2059 # How long should the scan for access points last?
2060 'scan_timeout': '5',
2061 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
2062 # Set the speak_up option to false if you do not have or want this.
2063 'speak_command': '/usr/bin/say',
2064 # Should I speak up when connecting to a network? (If you have a speech command)
2065 'speak_up': 'False',
2066 # You may set this to true for cards that require a "commit" command with iwconfig
2067 'commit_required': 'False',
2068 # You may set this to true for cards that require the interface to be brought up first
2069 'ifup_required': 'False',
2070 # Set the location of several important programs
2071 'iwlist_command': '/sbin/iwlist',
2072 'iwconfig_command': '/sbin/iwconfig',
2073 'ifconfig_command': '/sbin/ifconfig',
2074 'route_command': '/sbin/route',
2075 'auto_profile_order': '[]',
2076 'version': WIFI_RADAR_VERSION
}
2078 config_dhcp
= { # DHCP client
2079 'command': 'dhcpcd',
2080 # How long to wait for an IP addr from DHCP server
2082 # Arguments to use with DHCP client on connect
2083 'args': '-D -o -i dhcp_client -t %(timeout)s',
2084 # Argument to use with DHCP client on disconnect
2086 # The file where DHCP client PID is written
2087 'pidfile': '/etc/dhcpcd-%(interface)s.pid' }
2089 config_wpa
= { # WPA Supplicant
2090 'command': '/usr/sbin/wpa_supplicant',
2091 # Arguments to use with WPA Supplicant on connect
2092 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
2093 # Arguments to use with WPA Supplicant on disconnect
2095 # Where the WPA Supplicant config file can be found
2096 'configuration': '/etc/wpa_supplicant.conf',
2097 # Driver to use with WPA Supplicant
2099 # The file where WPA Supplicant PID is written
2100 'pidfile': '/var/run/wpa_supplicant.pid' }
2102 # initialize config, with defaults
2103 confFile
= ConfigFile(CONF_FILE
, config_defaults
)
2104 confFile
.set_section("DHCP", config_dhcp
)
2105 confFile
.set_section("WPA", config_wpa
)
2107 if not os
.path
.isfile( CONF_FILE
):
2108 confFile
.set_bool_opt('DEFAULT.new_file', True)
2110 if not os
.access(CONF_FILE
, os
.R_OK
):
2111 print "Can't open " + CONF_FILE
+ "."
2112 print "Are you root?"
2117 ####################################################################################################
2119 wifi_radar_icon
= [ ""
2126 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
2127 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
2128 "\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"
2129 "\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"
2130 "\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"
2131 "\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"
2132 "\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"
2133 "\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"
2134 "\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"
2135 "\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"
2136 "\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"
2137 "\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"
2138 "\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"
2139 "\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"
2140 "\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"
2141 "\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"
2142 "\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"
2143 "\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"
2144 "\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"
2145 "\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"
2146 "\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"
2147 "\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"
2148 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
2149 "\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"
2150 "\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"
2151 "\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"
2152 "\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"
2153 "\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"
2154 "\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"
2155 "\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"
2156 "\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"
2157 "\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"
2158 "\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"
2159 "\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"
2160 "\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"
2161 "\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"
2162 "\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"
2163 "\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"
2164 "\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"
2165 "\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"
2166 "\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"
2167 "\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"
2168 "\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"
2169 "\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"
2170 "\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"
2171 "\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"
2172 "\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"
2173 "\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"
2174 "\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"
2175 "\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"
2176 "\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"
2177 "\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"
2178 "\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"
2179 "\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"
2180 "\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"
2181 "\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"
2182 "\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"
2183 "\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"
2184 "\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"
2185 "\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"
2186 "\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"
2187 "\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"
2188 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
2189 "\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"
2190 "\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"
2191 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
2192 "\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"
2193 "\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"
2194 "\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"
2195 "\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"
2196 "\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"
2197 "\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"
2198 "\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"
2199 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
2200 "\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"
2201 "\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"
2202 "\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"
2203 "\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"
2204 "\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"
2205 "\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"
2206 "|\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"
2207 "\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"
2208 "\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"
2209 "\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"
2210 "\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"
2211 "\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"
2212 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
2213 "\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"
2214 "\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"
2215 "\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"
2216 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
2217 "\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"
2218 "\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"
2219 "\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"
2220 "\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"
2221 "\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"
2222 "\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"
2223 "\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"
2224 "\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"
2225 "\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"
2226 "\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"
2227 "\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"
2228 "\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"
2229 "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"
2230 "\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"
2231 "\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"
2232 "\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"
2233 "\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"
2234 "\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"
2235 "\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"
2236 "\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"
2237 "\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"
2238 "\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"
2239 "\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"
2240 "\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"
2241 "\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"
2242 "\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"
2243 "\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"
2244 "\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|"
2245 "\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"
2246 "\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"
2247 "\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"
2248 "\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"
2249 "\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"
2250 "\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"
2251 "\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"
2252 "\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"
2253 "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"
2254 "\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"
2255 "\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"
2256 "\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"
2257 "\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"
2258 "\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"
2259 "\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"
2260 "\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"
2261 "\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"
2262 "\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"
2263 "\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"
2264 "\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"
2265 "\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"
2266 "\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"
2267 "\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"
2268 "\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"
2269 "\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"
2270 "\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"
2271 "\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"
2272 "\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"
2273 "\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"
2274 "\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"
2275 "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"
2276 "\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"
2277 "\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"
2278 "\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"
2279 "\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"
2280 "\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"
2281 "\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"
2282 "\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"
2283 "\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"
2284 "\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"
2285 "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"
2286 "\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"
2287 "\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"
2288 "\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"
2289 "\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"
2290 "\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"
2291 "\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"
2292 "\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"
2293 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
2294 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
2295 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
2298 known_profile_icon
= [ ""
2305 "\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"
2306 "\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"
2307 "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"
2308 "\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"
2309 "\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"
2310 "\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"
2311 "\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"
2312 "\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"
2313 "\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"
2314 "\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"
2315 "\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"
2316 "\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"
2317 "\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"
2318 "\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"
2319 "\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"
2320 "\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"
2321 "\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"
2322 "\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"
2323 "\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"
2324 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
2325 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
2326 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
2327 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
2328 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
2329 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
2330 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
2331 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
2332 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
2333 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
2334 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
2335 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
2336 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
2337 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
2338 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
2339 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
2340 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
2341 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
2342 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
2343 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
2344 "\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"
2345 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
2346 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
2347 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
2348 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
2349 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
2350 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
2351 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
2352 "\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"
2353 "\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"
2354 "\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"
2355 "\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"
2356 "\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"]
2358 unknown_profile_icon
= [ ""
2365 "\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"
2366 "\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"
2367 "\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"
2368 "\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"
2369 "(\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"
2370 "\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"
2371 "#\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"
2372 "\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"
2373 "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"
2374 "\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"
2375 "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"
2376 "\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"
2377 "\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"
2378 "\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"
2379 "\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"
2380 "\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"
2381 "\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"
2382 "\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"
2383 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
2384 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
2385 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
2386 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
2387 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
2388 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
2389 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
2390 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
2391 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
2392 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
2393 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
2394 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
2395 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
2396 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
2397 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
2398 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
2399 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
2400 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
2401 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
2402 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
2403 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
2404 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
2405 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
2406 "\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"
2407 "\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"
2408 "\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"
2409 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
2411 signal_xpm_barely
= [
2438 "*%%%%=&&&&@@@@@@@@@+",
2439 "%&&&&&&&&&@@@@@@@@@+",
2440 "%&&&&&&&&&@@@@@@@@@+",
2441 "%&&&&&&&&&@@@@@@@@@+",
2442 "*%%%%%%%%%+++++++++."
2469 "$++++#@@@@@@@@@@@@@+",
2470 "+@@@@@@@@@@@@@@@@@@+",
2471 "+@@@@@@@@@@@@@@@@@@+",
2472 "+@@@@@@@@@@@@@@@@@@+",
2473 "$++++++++++++++++++."
2499 "$++++#@@@@@@@@@@@@@+",
2500 "+@@@@@@@@@@@@@@@@@@+",
2501 "+@@@@@@@@@@@@@@@@@@+",
2502 "+@@@@@@@@@@@@@@@@@@+",
2503 "$++++++++++++++++++."
2533 "=$$$$*&&&&&&&&&@@@@+",
2534 "$&&&&&&&&&&&&&&@@@@+",
2535 "$&&&&&&&&&&&&&&@@@@+",
2536 "$&&&&&&&&&&&&&&@@@@+",
2537 "=$$$$$$$$$$$$$$++++."
2566 "$%%%%#@@@@@@@@@@@@@+",
2567 "%&&&&@@@@@@@@@@@@@@+",
2568 "%&&&&@@@@@@@@@@@@@@+",
2569 "%&&&&@@@@@@@@@@@@@@+",
2570 "$%%%%++++++++++++++."
2574 ####################################################################################################
2575 # Make so we can be imported
2576 if __name__
== "__main__":
2577 if len( sys
.argv
) > 1 and ( sys
.argv
[1] == '--version' or sys
.argv
[1] == '-v' ):
2578 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
2581 gtk
.gdk
.threads_init()
2582 apQueue
= Queue
.Queue(100)
2583 commQueue
= Queue
.Queue(2)
2584 threading
.Thread( None, scanning_thread
, None, ( confFile
, apQueue
, commQueue
) ).start()
2585 main_radar_window
= radar_window(confFile
, apQueue
, commQueue
)
2586 gobject
.timeout_add( 500, main_radar_window
.update_plist_items
)
2587 main_radar_window
.main()