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 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
64 def set_network_device(device
):
65 #print "set_network_device: ", device
66 if device
!= "auto_detect":
67 confFile
.set_opt('DEFAULT.interface', device
)
68 else: # auto detect network device
69 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
70 # If no devices are found, default to eth1.
71 # call iwconfig command and read output
72 iwconfig_info
= Popen(confFile
.get_opt('DEFAULT.iwconfig_command'), shell
=True, stdout
=PIPE
).stdout
73 wireless_devices
= [ (x
[0:x
.find(" ")]) for x
in iwconfig_info
if("ESSID" in x
)]
74 if len(wireless_devices
) > 0:
75 confFile
.set_opt('DEFAULT.interface', wireless_devices
[0])
77 print "No wifi-device found. Exiting."
80 # Helper function to reduce code duplication
81 def get_new_profile():
82 """ Return a blank profile """
83 return { 'known': False,
109 def get_profile_from_conf_file( essid
, bssid
, confFile
):
110 # We got the essid, get the key
112 section_name
= make_section_name( essid
, bssid
)
113 if not confFile
.has_section( section_name
):
115 # read the option values
116 for option
in confFile
.options(section_name
):
117 (profile
['essid'], profile
['bssid']) = split_section_name( section_name
)
118 profile
[option
] = confFile
.get_opt(section_name
+ "." + option
)
121 def make_section_name( essid
, bssid
):
122 return essid
+ ':' + bssid
124 def split_section_name( section
):
125 parts
= re
.split(':', section
)
126 return [ ':'.join(parts
[0:len(parts
)-6]), ':'.join(parts
[len(parts
)-6:len(parts
)]) ]
128 def convert_confFile():
129 for apname
in confFile
.sections():
130 if confFile
.has_option( apname
, 'prescript' ):
131 confFile
.set( apname
, 'con_prescript', confFile
.get( apname
, 'prescript' ) )
132 confFile
.remove_option( apname
, 'prescript' )
133 if confFile
.has_option( apname
, 'postscript' ):
134 confFile
.set( apname
, 'con_postscript', confFile
.get( apname
, 'postscript' ) )
135 confFile
.remove_option( apname
, 'postscript' )
136 confFile
.set( apname
, 'dis_prescript', '' )
137 confFile
.set( apname
, 'dis_postscript', '' )
139 # Scan for a limited time, return ap names and bssid found
140 # Access points we find will be put on the outgoing Queue, apQueue
141 def scanning_thread( confFile
, apQueue
, commandQueue
):
142 # Setup our essid pattern matcher
143 essid_pattern
= re
.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re
.I | re
.M | re
.S
)
144 protocol_pattern
= re
.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abg]+)", re
.I | re
.M | re
.S
)
145 mode_pattern
= re
.compile( "Mode\s*(:|=)\s*([^\n]+)", re
.I | re
.M | re
.S
)
146 channel_pattern
= re
.compile( "Channel\s*(:|=)*\s*(\d+)", re
.I | re
.M | re
.S
)
147 enckey_pattern
= re
.compile( "Encryption key\s*(:|=)\s*(on|off)", re
.I | re
.M | re
.S
)
148 signal_pattern
= re
.compile( "Signal level\s*(:|=)\s*-?([0-9]+)", re
.I | re
.M | re
.S
)
154 command
= commandQueue
.get_nowait()
155 if __debug__
: print "scanning_thread received command", command
159 if command
== "scan":
160 if __debug__
: print "Beginning scan pass"
161 # Some cards need to have the interface up to scan
162 if confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
163 # call ifconfig command and wait for return
164 Popen([confFile
.get_opt('DEFAULT.ifconfig_command'), confFile
.get_opt('DEFAULT.interface'), 'up'])
165 # update the signal strengths
166 scandata
= Popen([confFile
.get_opt('DEFAULT.iwlist_command'), confFile
.get_opt('DEFAULT.interface'), 'scan'], stdout
=PIPE
).stdout
.read()
168 #print "Current IP ", get_current_ip()
169 #print "Current ESSID ", get_current_essid()
170 #print "Current BSSID ", get_current_bssid()
171 # zero out the signal levels for all access points
172 for bssid
in access_points
:
173 access_points
[bssid
]['signal'] = 0
174 # split the scan data based on the address line
175 hits
= scandata
.split(' - ')
177 # set the defaults for profile template
178 profile
= get_new_profile()
179 m
= essid_pattern
.search( hit
)
182 profile
['essid'] = m
.groups()[1]
183 m
= bssid_pattern
.search( hit
) # get BSSID from scan
184 if m
: profile
['bssid'] = m
.groups()[1]
185 m
= protocol_pattern
.search( hit
) # get protocol from scan
186 if m
: profile
['protocol'] = m
.groups()[1]
187 m
= mode_pattern
.search( hit
) # get mode from scan
188 if m
: profile
['mode'] = m
.groups()[1]
189 m
= channel_pattern
.search( hit
) # get channel from scan
190 if m
: profile
['channel'] = m
.groups()[1]
191 m
= enckey_pattern
.search( hit
) # get encryption key from scan
192 if m
: profile
['encrypted'] = ( m
.groups()[1] == 'on' )
193 m
= signal_pattern
.search( hit
) # get signal strength from scan
194 if m
: profile
['signal'] = m
.groups()[1]
195 access_points
[ profile
['bssid'] ] = profile
196 for bssid
in access_points
:
197 access_points
[bssid
]['available'] = ( access_points
[bssid
]['signal'] > 0 )
198 # Put all, now or previously, sensed access_points into apQueue
200 apQueue
.put_nowait( access_points
[bssid
] )
203 elif command
== "exit":
204 if __debug__
: print "Exiting scanning_thread"
206 if command_read
: commandQueue
.task_done()
207 if confFile
.get_opt('DEFAULT.interface').find('ath') == 0:
213 ### ConnectionManager
214 ### class to manage connections
215 class ConnectionManager():
216 def __init__( self
, profile
, confFile
, commandQueue
):
217 self
.profile
= profile
218 self
.confFile
= confFile
219 self
.commQueue
= commandQueue
222 def if_change( self
, state
):
223 if ( (state
.lower() == 'up') or (state
.lower() == 'down') ):
224 # call ifconfig command and wait for return
225 Popen([self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), state
])
227 def connect_to_network( self
, status_window
):
228 msg
= "Connecting to the %s (%s) network" % ( self
.profile
['essid'], self
.profile
['bssid'] )
230 if __debug__
: print " %s" % msg
232 # Let's run the connection prescript
233 if self
.profile
['con_prescript'].strip() != '':
234 # got something to execute
235 # run connection prescript through shell and wait for return
236 Popen([self
.profile
['con_prescript']], shell
=True)
237 if __debug__
: print "executing connection prescript:", self
.profile
['con_prescript']
240 # Some cards need to have the interface up
241 if self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
243 # Start building iwconfig command line, command
244 iwconfig_command
= [ self
.confFile
.get_opt('DEFAULT.iwconfig_command') ]
245 iwconfig_command
.append( confFile
.get_opt('DEFAULT.interface') )
247 iwconfig_command
.append( 'essid' )
248 iwconfig_command
.append( self
.profile
['essid'] )
250 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
252 iwconfig_command
.append( 'key' )
253 if self
.profile
['key'] == '':
254 iwconfig_command
.append( 'off' )
256 iwconfig_command
.append( self
.profile
['key'] )
257 #iwconfig_commands.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
258 # Setting mode to Master, this will cause association with the AP to fail
259 #iwconfig_command.append( 'mode' )
260 #iwconfig_command.append( 'Master' )
262 if self
.profile
['channel'] != '':
263 iwconfig_command
.append( 'channel' )
264 iwconfig_command
.append( self
.profile
['channel'] )
265 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
266 iwconfig_command
.append( 'ap' )
267 iwconfig_command
.append( self
.profile
['bssid'] )
268 # Some cards require a commit
269 if self
.confFile
.get_opt_as_bool('DEFAULT.commit_required'):
270 if __debug__
: print 'iwconfig_args %s ' % ( iwconfig_args
, )
271 iwconfig_command
.append( 'commit' )
272 # call iwconfig command and wait for return
273 Popen(iwconfig_command
)
274 # Now normal network stuff
275 # Kill off any existing DHCP clients running
276 if os
.path
.isfile(self
.confFile
.get_opt('DHCP.pidfile')):
277 if __debug__
: print "Killing existing DHCP..."
279 if self
.confFile
.get_opt('DHCP.kill_args') != '':
280 # call DHCP client kill command and wait for return
281 Popen([self
.confFile
.get_opt('DHCP.command'), self
.confFile
.get_opt('DHCP.kill_args')])
282 if __debug__
: print self
.confFile
.get_opt('DHCP.command') + " " + self
.confFile
.get_opt('DHCP.kill_args')
284 os
.kill(int(open(self
.confFile
.get_opt('DHCP.pidfile'), mode
='r').readline()), SIGTERM
)
286 print "failed to kill DHCP client"
289 print "Stale pid file. Removing..."
290 os
.remove(self
.confFile
.get_opt('DHCP.pidfile'))
291 # Kill off any existing WPA supplicants running
292 if os
.access(self
.confFile
.get_opt('WPA.pidfile'), os
.R_OK
):
293 if __debug__
: print "Killing existing WPA supplicant..."
295 if not self
.confFile
.get_opt('WPA.kill_command') != '':
296 # call WPA supplicant kill command and wait for return
297 Popen([self
.confFile
.get_opt('WPA.kill_command'), self
.confFile
.get_opt('WPA.kill_command')])
299 os
.kill(int(open(self
.confFile
.get_opt('WPA.pidfile'), mode
='r').readline()), SIGTERM
)
301 print "failed to kill WPA supplicant"
304 print "Stale pid file. Removing..."
305 os
.remove(self
.confFile
.get_opt('WPA.pidfile'))
306 # Begin WPA supplicant
307 if self
.profile
['use_wpa'] :
308 if __debug__
: print "WPA args: %s" % ( wpa_options
, )
310 status_window
.update_message("WPA supplicant starting")
311 # call WPA supplicant command and do not wait for return
312 wpa_proc
= Popen([self
.confFile
.get_opt('WPA.command'), self
.confFile
.get_opt('WPA.args'), self
.confFile
.get_opt('DEFAULT.interface')])
313 if self
.profile
['use_dhcp'] :
314 if __debug__
: print "Disable iwlist while dhcp in progress..."
316 self
.commQueue
.put("pause")
320 status_window
.update_message("Acquiring IP Address (DHCP)")
322 dhcp_pid
= os
.spawnlp(os
.P_NOWAIT
, self
.confFile
.get_opt('DHCP.command'), self
.confFile
.get_opt('DHCP.args') + " " + self
.confFile
.get_opt('DEFAULT.interface'))
324 timer
= self
.confFile
.get_opt_as_int('DHCP.timeout') + 10
326 print "DHCP process:", dhcp_pid
327 waiting
= os
.waitpid(dhcp_pid
, os
.WNOHANG
)
328 while waiting
== (0,0):
329 waiting
= os
.waitpid(dhcp_pid
, os
.WNOHANG
)
331 os
.kill(dhcp_pid
, SIGTERM
)
333 if sys
.modules
.has_key("gtk"):
334 while gtk
.events_pending():
335 gtk
.main_iteration(False)
338 print "timer: ", timer
, " *** tick: ", tick
339 print "wait:", waiting
343 self
.commQueue
.put("scan")
346 if not self
.get_current_ip():
348 status_window
.update_message("Could not get IP address!")
349 if sys
.modules
.has_key("gtk"):
350 while gtk
.events_pending():
351 gtk
.main_iteration(False)
354 print "Could not get IP address!"
358 status_window
.update_message("Got IP address. Done.")
359 if sys
.modules
.has_key("gtk"):
360 while gtk
.events_pending():
361 gtk
.main_iteration(False)
364 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'] )
365 route_command
= "%s add default gw %s" % ( self
.confFile
.get_opt('DEFAULT.route_command'), self
.profile
['gateway'] )
367 if self
.profile
['domain'] != '': resolv_contents
+= "domain %s\n" % domain
368 if self
.profile
['dns1'] != '': resolv_contents
+= "nameserver %s\n" % dns1
369 if self
.profile
['dns2'] != '': resolv_contents
+= "nameserver %s\n" % dns2
370 if ( resolv_contents
!= '' ):
371 resolv_file
=open('/etc/resolv.conf', 'w')
374 Popen([ifconfig_command
])
375 Popen([route_command
])
376 # Let's run the connection postscript
377 con_postscript
= self
.profile
['con_postscript']
378 if self
.profile
['con_postscript'].strip() != '':
379 Popen([self
.profile
['con_postscript']], shell
=True)
380 if __debug__
: print "executing connection postscript:", self
.profile
['con_postscript']
382 status_window
.destroy()
384 def disconnect_interface( self
, status_window
):
385 msg
= "Disconnecting"
387 if __debug__
: print msg
388 # Let's run the disconnection prescript
389 if self
.profile
['dis_prescript'].strip() != '':
390 Popen([self
.profile
['dis_prescript']], shell
=True)
391 if __debug__
: print "executing disconnection prescript:", self
.profile
['dis_prescript']
394 # Kill off any existing DHCP clients running
395 if os
.path
.isfile(self
.confFile
.get_opt('DHCP.pidfile')):
396 if __debug__
: print "Killing existing DHCP..."
398 if self
.confFile
.get_opt('DHCP.kill_args') != '':
399 dhcp_command
= [ self
.confFile
.get_opt('DHCP.command') ]
400 dhcp_command
.extend( self
.confFile
.get_opt('DHCP.kill_args').split(' ') )
402 if __debug__
: print self
.confFile
.get_opt('DHCP.command') + " " + self
.confFile
.get_opt('DHCP.kill_args')
404 os
.kill(int(open(self
.confFile
.get_opt('DHCP.pidfile'), mode
='r').readline()), SIGTERM
)
406 print "failed to kill DHCP client"
409 print "Stale pid file. Removing..."
410 os
.remove(self
.confFile
.get_opt('DHCP.pidfile'))
411 # Kill off any existing WPA supplicants running
412 if os
.access(self
.confFile
.get_opt('WPA.pidfile'), os
.R_OK
):
413 if __debug__
: print "Killing existing WPA supplicant..."
415 if not self
.confFile
.get_opt('WPA.kill_command') != '':
416 wpa_command
= [ self
.confFile
.get_opt('WPA.kill_command').split(' ') ]
419 os
.kill(int(open(self
.confFile
.get_opt('WPA.pidfile'), mode
='r').readline()), SIGTERM
)
421 print "failed to kill WPA supplicant"
424 print "Stale pid file. Removing..."
425 os
.remove(self
.confFile
.get_opt('WPA.pidfile'))
426 Popen([self
.confFile
.get_opt('DEFAULT.iwconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), 'essid', 'off', 'key', 'off', 'mode', 'auto', 'channel', 'auto', 'ap', 'off'])
427 # Lets clear out the wireless stuff
428 if __debug__
: print "Let's clear out the wireless stuff"
429 if __debug__
: print 'Now take the interface down'
430 self
.if_change('down')
431 Popen([self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), '0.0.0.0'])
432 if __debug__
: print 'Since it may be brought back up by the next scan, lets unset its IP'
433 # Let's run the disconnection postscript
434 if self
.profile
['dis_postscript'].strip() != '':
435 Popen([self
.profile
['dis_postscript']], shell
=True)
436 if __debug__
: print "executing disconnection postscript:", self
.profile
['dis_postscript']
438 status_window
.destroy()
439 if __debug__
: print 'Disconnect complete.'
441 def get_current_ip( self
):
442 """Returns the current IP if any by calling ifconfig"""
443 ifconfig_command
= [ confFile
.get_opt('DEFAULT.ifconfig_command'), confFile
.get_opt('DEFAULT.interface') ]
444 ifconfig_info
= Popen(ifconfig_command
, stdout
=PIPE
).stdout
445 # Be careful to the language (inet adr: in French for example)
449 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
450 # There the string in ifconfig is inet Adresse for the IP which isn't
451 # found by the current get_current_ip function in wifi-radar. I changed
452 # the according line (#289; gentoo, v1.9.6-r1) to
453 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
454 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
456 # I'd be happy if you could incorporate this small change because as now
457 # I've got to change the file every time it is updated.
462 ip_re
= re
.compile(r
'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
463 line
= ifconfig_info
.read()
464 if ip_re
.search( line
):
465 return ip_re
.search( line
).group(1)
468 def get_current_essid( self
):
469 """Returns the current ESSID if any by calling iwconfig"""
470 iwconfig_info
= Popen([confFile
.get_opt('DEFAULT.iwconfig_command'), confFile
.get_opt('DEFAULT.interface')], stdout
=PIPE
).stdout
471 # Be careful to the language (inet adr: in French for example)
472 essid_re
= re
.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re
.I | re
.M | re
.S
)
473 line
= iwconfig_info
.read()
474 if essid_re
.search( line
):
475 return essid_re
.search( line
).group(2)
478 def get_current_bssid( self
):
479 """Returns the current BSSID if any by calling iwconfig"""
480 iwconfig_info
= os
.popen(confFile
.get_opt('DEFAULT.iwconfig_command') + " " + confFile
.get_opt('DEFAULT.interface'), 'r')
481 bssid_re
= re
.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re
.I | re
.M | re
.S
)
482 for line
in iwconfig_info
:
483 if bssid_re
.search( line
):
484 return bssid_re
.search( line
).group(2)
492 def __init__( self
, confFile
, apQueue
, commQueue
):
493 # create the main window and connect the normal events
494 global signal_xpm_none
495 global signal_xpm_low
496 global signal_xpm_barely
498 global signal_xpm_best
499 global known_profile_icon
500 global unknown_profile_icon
501 global wifi_radar_icon
503 self
.confFile
= confFile
504 self
.apQueue
= apQueue
505 self
.commandQueue
= commQueue
506 self
.access_points
= {}
507 self
.connection
= None
509 self
.signal_none_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_none
)
510 self
.known_profile_icon
= gtk
.gdk
.pixbuf_new_from_inline( len( known_profile_icon
[0] ), known_profile_icon
[0], False )
511 self
.unknown_profile_icon
= gtk
.gdk
.pixbuf_new_from_inline( len( unknown_profile_icon
[0] ), unknown_profile_icon
[0], False )
512 self
.signal_low_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_low
)
513 self
.signal_barely_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_barely
)
514 self
.signal_ok_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_ok
)
515 self
.signal_best_pb
= gtk
.gdk
.pixbuf_new_from_xpm_data( signal_xpm_best
)
516 self
.window
= gtk
.Dialog('WiFi Radar', None, gtk
.DIALOG_MODAL
)
517 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
518 self
.window
.set_icon( icon
)
519 self
.window
.set_border_width( 10 )
520 self
.window
.set_size_request( 720, 300 )
521 self
.window
.set_title( "WiFi Radar" )
522 self
.window
.connect( 'delete_event', self
.delete_event
)
523 self
.window
.connect( 'destroy', self
.destroy
, )
524 # let's create all our widgets
525 self
.current_network
= gtk
.Label()
526 self
.current_network
.show()
527 self
.close_button
= gtk
.Button( "Close", gtk
.STOCK_CLOSE
)
528 self
.close_button
.show()
529 self
.close_button
.connect( 'clicked', self
.delete_event
, None )
530 self
.preferences_button
= gtk
.Button( "Preferences", gtk
.STOCK_PREFERENCES
)
531 self
.preferences_button
.show()
532 self
.preferences_button
.connect( 'clicked', self
.edit_preferences
, None )
533 # essid bssid known_icon known available wep_icon signal_level mode protocol channel
534 self
.pstore
= gtk
.ListStore( str, str, gtk
.gdk
.Pixbuf
, bool, bool, str, gtk
.gdk
.Pixbuf
, str, str, str )
535 self
.plist
= gtk
.TreeView( self
.pstore
)
537 self
.pix_cell
= gtk
.CellRendererPixbuf()
538 self
.wep_cell
= gtk
.CellRendererPixbuf()
539 self
.essid_cell
= gtk
.CellRendererText()
540 self
.bssid_cell
= gtk
.CellRendererText()
541 self
.bssid_cell
.set_property("width-chars", 20)
542 self
.pcol
= gtk
.TreeViewColumn( "SSID" )
543 self
.pcol
.pack_start( self
.pix_cell
, False )
544 self
.pcol
.pack_start( self
.wep_cell
, False )
545 self
.pcol
.pack_start( self
.essid_cell
, True )
546 self
.pcol
.pack_start( self
.bssid_cell
, True )
547 self
.pcol
.add_attribute( self
.wep_cell
, 'stock-id', 5 )
548 self
.pcol
.add_attribute( self
.pix_cell
, 'pixbuf', 2 )
549 self
.pcol
.add_attribute( self
.essid_cell
, 'text', 0 )
550 self
.pcol
.add_attribute( self
.bssid_cell
, 'text', 1 )
551 self
.plist
.append_column( self
.pcol
)
553 self
.sig_cell
= gtk
.CellRendererPixbuf()
554 self
.scol
= gtk
.TreeViewColumn( "Signal" )
555 self
.scol
.pack_start( self
.sig_cell
, True )
556 self
.scol
.add_attribute( self
.sig_cell
, 'pixbuf', 6 )
557 self
.plist
.append_column( self
.scol
)
559 self
.mode_cell
= gtk
.CellRendererText()
560 self
.mcol
= gtk
.TreeViewColumn( "Mode" )
561 self
.mcol
.pack_start( self
.mode_cell
, True )
562 self
.mcol
.add_attribute( self
.mode_cell
, 'text', 7 )
563 self
.plist
.append_column( self
.mcol
)
564 # The protocol column
565 self
.prot_cell
= gtk
.CellRendererText()
566 self
.protcol
= gtk
.TreeViewColumn( "802.11" )
567 self
.protcol
.pack_start( self
.prot_cell
, True )
568 self
.protcol
.add_attribute( self
.prot_cell
, 'text', 8 )
569 self
.plist
.append_column( self
.protcol
)
571 self
.channel_cell
= gtk
.CellRendererText()
572 self
.channel
= gtk
.TreeViewColumn( "Channel" )
573 self
.channel
.pack_start( self
.channel_cell
, True )
574 self
.channel
.add_attribute( self
.channel_cell
, 'text', 9 )
575 self
.plist
.append_column( self
.channel
)
577 self
.plist
.set_reorderable( True )
578 self
.pstore
.connect( 'row-changed', self
.update_auto_profile_order
)
579 # enable/disable buttons based on the selected network
580 self
.selected_network
= self
.plist
.get_selection()
581 self
.selected_network
.connect( 'changed', self
.on_network_selection
, None )
582 # the list scroll bar
583 sb
= gtk
.VScrollbar( self
.plist
.get_vadjustment() )
587 self
.new_button
= gtk
.Button( "_New" )
588 self
.new_button
.connect( 'clicked', self
.create_new_profile
, get_new_profile(), None )
589 self
.new_button
.show()
590 # Add Configure button
591 self
.edit_button
= gtk
.Button( "C_onfigure" )
592 self
.edit_button
.connect( 'clicked', self
.edit_profile
, None )
593 self
.edit_button
.show()
594 self
.edit_button
.set_sensitive(False)
596 self
.delete_button
= gtk
.Button( "_Delete" )
597 self
.delete_button
.connect( 'clicked', self
.delete_profile
, None )
598 self
.delete_button
.show()
599 self
.delete_button
.set_sensitive(False)
601 self
.connect_button
= gtk
.Button( "Co_nnect" )
602 self
.connect_button
.connect( 'clicked', self
.connect_profile
, None )
603 # Add Disconnect button
604 self
.disconnect_button
= gtk
.Button( "D_isconnect" )
605 self
.disconnect_button
.connect( 'clicked', self
.disconnect_profile
, None )
606 # lets add our widgets
607 rows
= gtk
.VBox( False, 3 )
608 net_list
= gtk
.HBox( False, 0 )
609 listcols
= gtk
.HBox( False, 0 )
610 prows
= gtk
.VBox( False, 0 )
613 net_list
.pack_start( self
.plist
, True, True, 0 )
614 net_list
.pack_start( sb
, False, False, 0 )
616 rows
.pack_start( net_list
, True, True, 0 )
617 rows
.pack_start( self
.current_network
, False, True, 0 )
619 listcols
.pack_start( rows
, True, True, 0 )
620 listcols
.pack_start( prows
, False, False, 5 )
622 prows
.pack_start( self
.new_button
, False, False, 2 )
623 prows
.pack_start( self
.edit_button
, False, False, 2 )
624 prows
.pack_start( self
.delete_button
, False, False, 2 )
625 prows
.pack_end( self
.connect_button
, False, False, 2 )
626 prows
.pack_end( self
.disconnect_button
, False, False, 2 )
628 self
.window
.action_area
.pack_start( self
.preferences_button
)
629 self
.window
.action_area
.pack_start( self
.close_button
)
634 self
.window
.vbox
.add( listcols
)
635 self
.window
.vbox
.set_spacing( 3 )
636 self
.window
.show_all()
638 # Now, immediately hide these two. The proper one will be
639 # displayed later, based on interface state. -BEF-
640 self
.disconnect_button
.hide()
641 self
.connect_button
.hide()
643 # Add our known profiles in order
644 for ap
in self
.confFile
.auto_profile_order
:
646 self
.access_points
[ ap
] = self
.confFile
.get_profile( ap
)
648 if self
.access_points
[ ap
]['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
649 self
.pstore
.append( [ self
.access_points
[ ap
]['essid'], 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'] ] )
650 # Now add any known profiles not in the profile order
651 for ap
in self
.confFile
.profiles():
653 if ap
in self
.confFile
.auto_profile_order
: continue
654 self
.access_points
[ ap
] = self
.confFile
.get_profile( ap
)
656 if self
.access_points
[ ap
]['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
657 self
.pstore
.append( [ self
.access_points
[ ap
]['essid'], self
.access_points
[ ap
]['bssid'], self
.unknown_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'] ] )
658 # This is the first run (or, at least, no config file was present), so pop up the preferences window
659 if ( self
.confFile
.get_opt_as_bool('DEFAULT.new_file') == True ):
660 self
.confFile
.remove_option('DEFAULT', 'new_file')
661 self
.edit_preferences(self
.preferences_button
)
666 def destroy( self
, widget
= None):
667 self
.confFile
.write()
670 def delete_event( self
, widget
, event
, data
=None ):
671 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
673 self
.commandQueue
.put("exit", True)
676 # Save the preferred networks order
677 self
.update_auto_profile_order()
681 def update_plist_items( self
):
682 """ Updates the profiles list """
683 # Indicate to PyGtk that only one Gtk thread should run here
684 gtk
.gdk
.threads_enter()
685 # update the current ip and essid
686 # set the state of connect/disconnect buttons based on whether we have an IP address
688 self
.current_network
.set_text( "Connected to %s ip(%s)" % ( self
.connection
.get_current_essid(), self
.connection
.get_current_ip() ) )
689 if self
.connection
.get_current_ip():
690 self
.connect_button
.hide()
691 self
.disconnect_button
.show()
693 self
.disconnect_button
.hide()
694 self
.connect_button
.show()
697 # Get profiles scanned by iwlist
699 profile
= self
.apQueue
.get_nowait()
701 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
702 prow_iter
= self
.get_row_by_ap( profile
['essid'], profile
['bssid'] )
703 if prow_iter
!= None:
704 # the AP is in the list of APs on the screen
705 apname
= make_section_name(profile
['essid'], profile
['bssid'])
706 if self
.access_points
.has_key(apname
):
707 # This AP has been configured and is/should be stored in the config file
708 profile
['known'] = self
.access_points
[apname
]['known']
709 self
.access_points
[apname
]['available'] = profile
['available']
710 self
.pstore
.set_value(prow_iter
, 4, profile
['available'])
711 self
.access_points
[apname
]['encrypted'] = profile
['encrypted']
712 self
.pstore
.set_value(prow_iter
, 5, wep
)
713 self
.access_points
[apname
]['signal'] = profile
['signal']
714 self
.pstore
.set_value(prow_iter
, 6, self
.pixbuf_from_signal( profile
['signal'] ))
715 self
.access_points
[apname
]['mode'] = profile
['mode']
716 self
.pstore
.set_value(prow_iter
, 7, profile
['mode'])
717 self
.access_points
[apname
]['protocol'] = profile
['protocol']
718 self
.pstore
.set_value(prow_iter
, 8, profile
['protocol'])
719 self
.access_points
[apname
]['channel'] = profile
['channel']
720 self
.pstore
.set_value(prow_iter
, 9, profile
['channel'])
721 # Set the 'known' values; False is default, overridden to True by self.access_points
722 self
.pstore
.set_value(prow_iter
, 2, self
.pixbuf_from_known( profile
[ 'known' ] ))
723 self
.pstore
.set_value(prow_iter
, 3, profile
[ 'known' ])
724 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
725 #for val in self.pstore[prow_iter]:
728 # the AP is not in the list of APs on the screen
729 self
.pstore
.append( [ profile
[ 'essid' ], 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'] ] )
730 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
732 # Allow other Gtk threads to run
733 gtk
.gdk
.threads_leave()
734 #print "update_plist_items: Empty apQueue"
737 def pixbuf_from_known( self
, known
):
738 """ return the proper icon for value of known """
740 return self
.known_profile_icon
742 return self
.unknown_profile_icon
744 def pixbuf_from_signal( self
, signal
):
745 signal
= int( signal
)
747 if signal
>= 80 or signal
== 0:
748 return self
.signal_none_pb
750 return self
.signal_low_pb
752 return self
.signal_barely_pb
754 return self
.signal_ok_pb
756 return self
.signal_best_pb
760 def get_row_by_ap( self
, essid
, bssid
):
761 for row
in self
.pstore
:
762 if ( (row
[0] == essid
) and (row
[1] == bssid
) ):
763 #print "matched:", row.iter, essid, bssid
767 # enable/disable buttons based on the selected network
768 def on_network_selection( self
, widget
, data
=None ):
769 ( store
, selected_iter
) = self
.selected_network
.get_selected()
770 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
771 # if no networks are selected, disable all buttons except New
772 # (this occurs after a drag-and-drop)
773 if selected_iter
== None:
774 self
.edit_button
.set_sensitive(False)
775 self
.delete_button
.set_sensitive(False)
777 # enable/disable buttons
778 if (store
.get_value(selected_iter
, 3) == True): # is selected network known?
779 self
.edit_button
.set_sensitive(True)
780 self
.delete_button
.set_sensitive(True)
782 self
.edit_button
.set_sensitive(True)
783 self
.delete_button
.set_sensitive(False)
785 # init and run the preferences dialog
786 def edit_preferences( self
, widget
, data
=None ):
787 p
= preferences_dialog( self
, self
.confFile
)
791 def create_new_profile( self
, widget
, profile
, data
=None ):
792 profile_editor
= profile_dialog( self
, profile
)
793 profile
= profile_editor
.run()
794 profile_editor
.destroy()
796 apname
= make_section_name( profile
['essid'], profile
['bssid'] )
797 # Check that the ap does not exist already
798 if apname
in self
.confFile
.profiles():
799 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
) )
804 self
.access_points
[ apname
] = profile
805 self
.confFile
.set_section( apname
, profile
)
806 # if it is not in the auto_profile_order add it
807 if not apname
in self
.confFile
.auto_profile_order
:
808 self
.confFile
.auto_profile_order
.append(apname
)
811 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
814 # Did not create new profile
817 def edit_profile( self
, widget
, data
=None ):
818 ( store
, selected_iter
) = self
.plist
.get_selection().get_selected()
819 if not selected_iter
: return
820 apname
= make_section_name( store
.get_value( selected_iter
, 0 ), store
.get_value( selected_iter
, 1 ) )
821 profile
= self
.confFile
.get_profile( apname
)
823 profile_editor
= profile_dialog( self
, profile
)
824 profile
= profile_editor
.run()
825 profile_editor
.destroy()
828 print "Got edited profile ", profile
829 apname
= make_section_name( profile
['essid'], profile
['bssid'] )
830 self
.access_points
[ apname
] = profile
831 self
.confFile
.set_section( apname
, profile
)
833 profile
= get_new_profile()
834 profile
['essid'] = store
.get_value( selected_iter
, 0 )
835 profile
['bssid'] = store
.get_value( selected_iter
, 1 )
836 self
.create_new_profile( widget
, profile
, data
)
837 print "edited: ", profile
['essid'], profile
['bssid']
839 def delete_profile( self
, widget
, data
=None ):
840 ( store
, selected_iter
) = self
.plist
.get_selection().get_selected()
841 if not selected_iter
: return
842 essid
= store
.get_value( selected_iter
, 0 )
843 bssid
= store
.get_value( selected_iter
, 1 )
844 known
= store
.get_value( selected_iter
, 3 )
846 dlg
= gtk
.MessageDialog(
848 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
849 gtk
.MESSAGE_QUESTION
,
851 "Are you sure you want to delete the %s (%s) profile?" % (essid
, bssid
) )
855 if res
== gtk
.RESPONSE_NO
: return
857 apname
= make_section_name( essid
, bssid
)
858 del self
.access_points
[ apname
]
859 self
.confFile
.remove_section( apname
)
860 print "delete_profile: ", apname
, ":", self
.confFile
.auto_profile_order
861 if apname
in self
.confFile
.auto_profile_order
: self
.confFile
.auto_profile_order
.remove(apname
)
862 self
.pstore
.remove( selected_iter
)
863 # Let's save our current state
864 self
.update_auto_profile_order()
866 def connect_profile( self
, widget
, profile
, data
=None ):
867 ( store
, selected_iter
) = self
.plist
.get_selection().get_selected()
868 if not selected_iter
: return
869 essid
= store
.get_value( selected_iter
, 0 )
870 bssid
= store
.get_value( selected_iter
, 1 )
871 known
= store
.get_value( selected_iter
, 3 )
873 if data
!= 'noconnect':
874 dlg
= gtk
.MessageDialog(
876 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
877 gtk
.MESSAGE_QUESTION
,
879 "This network does not have a profile configured.\n\nWould you like to create one now?" )
883 if res
== gtk
.RESPONSE_NO
: return
884 profile
= get_new_profile()
885 profile
['essid'] = store
.get_value( selected_iter
, 0 )
886 profile
['bssid'] = store
.get_value( selected_iter
, 1 )
887 if not self
.create_new_profile( widget
, profile
, data
):
889 apname
= make_section_name( store
.get_value( selected_iter
, 0 ), store
.get_value( selected_iter
, 1 ) )
890 self
.connection
= ConnectionManager( self
.access_points
[apname
], self
.confFile
, self
.commandQueue
)
891 self
.connection
.connect_to_network( status_window( self
) )
893 def disconnect_profile( self
, widget
, data
=None ):
894 self
.connection
.disconnect_interface( status_window( self
) )
896 def update_auto_profile_order( self
, widget
= None, data
= None, data2
= None ):
897 # recreate the auto_profile_order
898 auto_profile_order
= []
899 piter
= self
.pstore
.get_iter_first()
902 if self
.pstore
.get_value( piter
, 3 ) == True:
903 auto_profile_order
.append( make_section_name( self
.pstore
.get_value( piter
, 0 ), self
.pstore
.get_value( piter
, 1 ) ) )
904 piter
= self
.pstore
.iter_next( piter
)
905 self
.confFile
.auto_profile_order
= auto_profile_order
907 ###################################
908 # the preferences dialog
909 class preferences_dialog
:
910 def __init__( self
, parent
, confFile
):
911 # set up the preferences window
912 global wifi_radar_icon
914 self
.confFile
= confFile
915 self
.dialog
= gtk
.Dialog('WiFi Radar Preferences', self
.parent
.window
,
916 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
917 ( gtk
.STOCK_CLOSE
, True ) )
918 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
919 self
.dialog
.set_icon( icon
)
920 self
.dialog
.set_resizable( True )
921 self
.dialog
.set_transient_for( self
.parent
.window
)
922 self
.tooltips
= gtk
.Tooltips()
924 # set up preferences widgets
927 # auto detect wireless device
928 self
.w_auto_detect
= gtk
.CheckButton("Auto-detect wireless device")
930 print "prefs: ", self
.confFile
.get_opt('DEFAULT.interface')
932 self
.w_auto_detect
.set_active( self
.confFile
.get_opt('DEFAULT.interface') == "auto_detect" )
933 self
.w_auto_detect
.connect("toggled", self
.toggle_auto_detect
)
934 self
.tooltips
.set_tip(self
.w_auto_detect
, "Automatically select wireless device to configure")
935 self
.dialog
.vbox
.pack_start(self
.w_auto_detect
, False, False, 5)
936 # network interface selecter
937 self
.w_interface
= gtk
.combo_box_entry_new_text()
938 iwconfig_info
= Popen([self
.confFile
.get_opt('DEFAULT.iwconfig_command')], stdout
=PIPE
).stdout
939 wireless_devices
= [ (x
[0:x
.find(" ")]) for x
in iwconfig_info
if ("ESSID" in x
)]
940 for device
in wireless_devices
:
941 if device
!= self
.confFile
.get_opt('DEFAULT.interface'):
942 self
.w_interface
.append_text(device
)
943 if self
.confFile
.get_opt('DEFAULT.interface') != "auto_detect":
944 self
.w_interface
.prepend_text( self
.confFile
.get_opt('DEFAULT.interface') )
945 self
.w_interface
.set_active(0)
946 self
.w_interface_label
= gtk
.Label("Wireless device")
947 self
.w_hbox1
= gtk
.HBox(False, 0)
948 self
.w_hbox1
.pack_start(self
.w_interface_label
, False, False, 5)
949 self
.w_hbox1
.pack_start(self
.w_interface
, True, True, 0)
950 self
.w_interface
.set_sensitive( self
.confFile
.get_opt('DEFAULT.interface') != "auto_detect" )
951 self
.dialog
.vbox
.pack_start(self
.w_hbox1
, False, False, 5)
952 # scan timeout (spin button of integers from 1 to 100)
953 self
.time_in_seconds
= gtk
.Adjustment( self
.confFile
.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
954 self
.w_scan_timeout
= gtk
.SpinButton(self
.time_in_seconds
, 1, 0)
955 self
.w_scan_timeout
.set_update_policy(gtk
.UPDATE_IF_VALID
)
956 self
.w_scan_timeout
.set_numeric(True)
957 self
.w_scan_timeout
.set_snap_to_ticks(True)
958 self
.w_scan_timeout
.set_wrap(False)
959 self
.tooltips
.set_tip(self
.w_scan_timeout
, "How long should WiFi Radar scan for access points?")
960 self
.w_scan_timeout_label
= gtk
.Label("Scan timeout (seconds)")
961 self
.w_hbox2
= gtk
.HBox(False, 0)
962 self
.w_hbox2
.pack_start(self
.w_scan_timeout_label
, False, False, 5)
963 self
.w_hbox2
.pack_start(self
.w_scan_timeout
, True, True, 0)
964 self
.dialog
.vbox
.pack_start(self
.w_hbox2
, False, False, 5)
966 self
.w_speak_up
= gtk
.CheckButton("Use speak-up")
967 self
.w_speak_up
.set_active( self
.confFile
.get_opt_as_bool('DEFAULT.speak_up') )
968 self
.tooltips
.set_tip(self
.w_speak_up
, "Should I speak up when connecting to a network? (If you have a speech command)")
969 self
.dialog
.vbox
.pack_start(self
.w_speak_up
, False, False, 5)
971 self
.w_commit_required
= gtk
.CheckButton("Commit required")
972 self
.w_commit_required
.set_active( self
.confFile
.get_opt_as_bool('DEFAULT.commit_required') )
973 self
.tooltips
.set_tip(self
.w_commit_required
, "Check this box if your card requires a \"commit\" command with iwconfig")
974 self
.dialog
.vbox
.pack_start(self
.w_commit_required
, False, False, 5)
976 self
.w_ifup_required
= gtk
.CheckButton("Ifup required")
977 self
.w_ifup_required
.set_active( self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required') )
978 self
.tooltips
.set_tip(self
.w_ifup_required
, "Check this box if your system requires the interface to be brought up first")
979 self
.dialog
.vbox
.pack_start(self
.w_ifup_required
, False, False, 5)
981 def toggle_auto_detect(self
, auto_detect_toggle
, data
=None):
982 self
.w_interface
.set_sensitive(not auto_detect_toggle
.get_active())
985 self
.dialog
.show_all()
986 return self
.dialog
.run()
989 # save preferences to config file
990 if self
.w_auto_detect
.get_active():
991 set_network_device("auto_detect")
992 self
.confFile
.set_opt('DEFAULT.interface', "auto_detect")
994 set_network_device(self
.w_interface
.child
.get_text())
995 self
.confFile
.set_opt('DEFAULT.interface', self
.w_interface
.child
.get_text())
996 # reconfigure the dhcp commands in case the network device was changed
997 self
.confFile
.set_float_opt('DEFAULT.scan_timeout', self
.w_scan_timeout
.get_value())
998 self
.confFile
.set_bool_opt('DEFAULT.speak_up', self
.w_speak_up
.get_active())
999 self
.confFile
.set_bool_opt('DEFAULT.commit_required', self
.w_commit_required
.get_active())
1000 self
.confFile
.set_bool_opt('DEFAULT.ifup_required', self
.w_ifup_required
.get_active())
1001 if self
.confFile
.get_opt_as_bool('DEFAULT.ifup_required'):
1002 Popen([self
.confFile
.get_opt('DEFAULT.ifconfig_command'), self
.confFile
.get_opt('DEFAULT.interface'), 'up'])
1003 self
.confFile
.write()
1004 self
.dialog
.destroy()
1008 ###################################
1009 class profile_dialog
:
1010 def __init__( self
, parent
, profile
):
1011 global wifi_radar_icon
1012 self
.parent
= parent
1013 self
.profile
= profile
1014 self
.WIFI_MODES
= [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
1015 self
.WIFI_SECURITY
= [ '', 'open', 'restricted' ]
1016 self
.WIFI_CHANNELS
= [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
1017 self
.dialog
= gtk
.Dialog('WiFi Profile', self
.parent
.window
,
1018 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
1019 ( gtk
.STOCK_CANCEL
, False, gtk
.STOCK_SAVE
, True ) )
1020 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
1021 self
.dialog
.set_icon( icon
)
1022 self
.dialog
.set_resizable( False )
1023 self
.dialog
.set_transient_for( self
.parent
.window
)
1024 #self.dialog.set_size_request( 400, 400 )
1026 essid_table
= gtk
.Table( 1, 2, False )
1027 essid_table
.set_row_spacings( 3 )
1028 essid_table
.set_col_spacings( 3 )
1031 essid_table
.attach( gtk
.Label( 'Network Name' ), 0, 1, 0, 1 )
1032 # The essid textboxes
1033 self
.essid_entry
= gtk
.Entry( 32 )
1034 self
.essid_entry
.set_text( self
.profile
['essid'] )
1035 essid_table
.attach( self
.essid_entry
, 1, 2, 0, 1 )
1036 # Add the essid table to the dialog
1037 self
.dialog
.vbox
.pack_start( essid_table
, True, True, 5 )
1039 bssid_table
= gtk
.Table( 1, 2, False )
1040 bssid_table
.set_row_spacings( 3 )
1041 bssid_table
.set_col_spacings( 3 )
1043 bssid_table
.attach( gtk
.Label( 'Network bssid' ), 0, 1, 0, 1 )
1044 # The bssid textboxes
1045 self
.bssid_entry
= gtk
.Entry( 32 )
1046 self
.bssid_entry
.set_text( self
.profile
['bssid'] )
1047 bssid_table
.attach( self
.bssid_entry
, 1, 2, 0, 1 )
1048 #self.key = gtk.Entry( 32 )
1049 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1050 # Add the bssid table to the dialog
1051 self
.dialog
.vbox
.pack_start( bssid_table
, True, True, 5 )
1053 # create the wifi expander
1054 self
.wifi_expander
= gtk
.Expander( WIFI_SET_LABEL
)
1055 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1056 wifi_table
= gtk
.Table( 4, 2, False )
1057 wifi_table
.set_row_spacings( 3 )
1058 wifi_table
.set_col_spacings( 3 )
1060 wifi_table
.attach( gtk
.Label( 'Mode' ), 0, 1, 0, 1 )
1061 wifi_table
.attach( gtk
.Label( 'Channel' ), 0, 1, 1, 2 )
1062 wifi_table
.attach( gtk
.Label( 'Key' ), 0, 1, 2, 3 )
1063 wifi_table
.attach( gtk
.Label( 'Security' ), 0, 1, 3, 4 )
1064 # The Wifi text boxes
1065 self
.mode_combo
= gtk
.combo_box_new_text()
1066 for mode
in self
.WIFI_MODES
:
1067 self
.mode_combo
.append_text( mode
)
1068 self
.mode_combo
.set_active( self
.get_array_index( self
.profile
['mode'], self
.WIFI_MODES
) )
1069 wifi_table
.attach( self
.mode_combo
, 1, 2, 0, 1 )
1070 self
.channel_combo
= gtk
.combo_box_new_text()
1071 for channel
in self
.WIFI_CHANNELS
:
1072 self
.channel_combo
.append_text( channel
)
1073 self
.channel_combo
.set_active( self
.get_array_index( self
.profile
['channel'], self
.WIFI_CHANNELS
) )
1074 wifi_table
.attach( self
.channel_combo
, 1, 2, 1, 2 )
1076 self
.key_entry
= gtk
.Entry( 32 )
1077 self
.key_entry
.set_text( self
.profile
['key'] )
1078 wifi_table
.attach( self
.key_entry
, 1, 2, 2, 3 )
1080 self
.security_combo
= gtk
.combo_box_new_text()
1081 for security
in self
.WIFI_SECURITY
:
1082 self
.security_combo
.append_text( security
)
1083 self
.security_combo
.set_active( self
.get_array_index( self
.profile
['security'], self
.WIFI_SECURITY
) )
1084 wifi_table
.attach( self
.security_combo
, 1, 2, 3, 4 )
1085 # Add the wifi table to the expander
1086 self
.wifi_expander
.add( wifi_table
)
1087 # Add the expander to the dialog
1088 self
.dialog
.vbox
.pack_start( self
.wifi_expander
, False, False, 5 )
1090 # create the wpa expander
1091 self
.wpa_expander
= gtk
.Expander( NO_WPA_LABEL
)
1092 self
.wpa_expander
.connect( 'notify::expanded', self
.toggle_use_wpa
)
1093 wpa_table
= gtk
.Table( 1, 2, False )
1094 wpa_table
.set_row_spacings( 3 )
1095 wpa_table
.set_col_spacings( 3 )
1097 wpa_table
.attach( gtk
.Label( 'Driver' ), 0, 1, 0, 1 )
1099 self
.wpa_driver_entry
= gtk
.Entry()
1100 self
.wpa_driver_entry
.set_text( self
.profile
['wpa_driver'] )
1101 wpa_table
.attach( self
.wpa_driver_entry
, 1, 2, 0, 1 )
1102 # Add the wpa table to the expander
1103 self
.wpa_expander
.add( wpa_table
)
1104 # Add the expander to the dialog
1105 self
.dialog
.vbox
.pack_start( self
.wpa_expander
, False, False, 5 )
1107 # create the dhcp expander
1108 self
.dhcp_expander
= gtk
.Expander( USE_DHCP_LABEL
)
1109 self
.dhcp_expander
.connect( 'notify::expanded', self
.toggle_use_dhcp
)
1110 ip_table
= gtk
.Table( 6, 2, False )
1111 ip_table
.set_row_spacings( 3 )
1112 ip_table
.set_col_spacings( 3 )
1114 ip_table
.attach( gtk
.Label( 'IP' ), 0, 1, 0, 1 )
1115 ip_table
.attach( gtk
.Label( 'Netmask' ), 0, 1, 1, 2 )
1116 ip_table
.attach( gtk
.Label( 'Gateway' ), 0, 1, 2, 3 )
1117 ip_table
.attach( gtk
.Label( 'Domain' ), 0, 1, 3, 4 )
1118 ip_table
.attach( gtk
.Label( 'DNS' ), 0, 1, 4, 5 )
1119 ip_table
.attach( gtk
.Label( 'DNS' ), 0, 1, 5, 6 )
1121 self
.ip_entry
= gtk
.Entry( 15 )
1122 self
.ip_entry
.set_text( self
.profile
['ip'] )
1123 ip_table
.attach( self
.ip_entry
, 1, 2, 0, 1 )
1124 self
.netmask_entry
= gtk
.Entry( 15 )
1125 self
.netmask_entry
.set_text( self
.profile
['netmask'] )
1126 ip_table
.attach( self
.netmask_entry
, 1, 2, 1, 2 )
1127 self
.gw_entry
= gtk
.Entry( 15 )
1128 self
.gw_entry
.set_text( self
.profile
['gateway'] )
1129 ip_table
.attach( self
.gw_entry
, 1, 2, 2, 3 )
1130 self
.domain_entry
= gtk
.Entry( 32 )
1131 self
.domain_entry
.set_text( self
.profile
['domain'] )
1132 ip_table
.attach( self
.domain_entry
, 1, 2, 3, 4 )
1133 self
.dns1_entry
= gtk
.Entry( 15 )
1134 self
.dns1_entry
.set_text( self
.profile
['dns1'] )
1135 ip_table
.attach( self
.dns1_entry
, 1, 2, 4, 5 )
1136 self
.dns2_entry
= gtk
.Entry( 15 )
1137 self
.dns2_entry
.set_text( self
.profile
['dns2'] )
1138 ip_table
.attach( self
.dns2_entry
, 1, 2, 5, 6 )
1139 # Add the ip table to the expander
1140 self
.dhcp_expander
.add( ip_table
)
1141 # Add the expander to the dialog
1142 self
.dialog
.vbox
.pack_start( self
.dhcp_expander
, False, False, 5 )
1144 # create the connection-building postpre expander
1145 self
.con_pp_expander
= gtk
.Expander( CON_PP_LABEL
)
1146 con_pp_table
= gtk
.Table( 2, 2, False )
1147 con_pp_table
.set_row_spacings( 3 )
1148 con_pp_table
.set_col_spacings( 3 )
1150 con_pp_table
.attach( gtk
.Label( 'Before' ), 0, 1, 0, 1 )
1151 con_pp_table
.attach( gtk
.Label( 'After' ), 0, 1, 1, 2 )
1153 self
.con_prescript_entry
= gtk
.Entry()
1154 self
.con_prescript_entry
.set_text( self
.profile
['con_prescript'] )
1155 con_pp_table
.attach( self
.con_prescript_entry
, 1, 2, 0, 1 )
1156 self
.con_postscript_entry
= gtk
.Entry()
1157 self
.con_postscript_entry
.set_text( self
.profile
['con_postscript'] )
1158 con_pp_table
.attach( self
.con_postscript_entry
, 1, 2, 1, 2 )
1159 # Add the pp table to the expander
1160 self
.con_pp_expander
.add( con_pp_table
)
1161 # Add the expander to the dialog
1162 self
.dialog
.vbox
.pack_start( self
.con_pp_expander
, False, False, 5 )
1164 # create the disconnection postpre expander
1165 self
.dis_pp_expander
= gtk
.Expander( DIS_PP_LABEL
)
1166 dis_pp_table
= gtk
.Table( 2, 2, False )
1167 dis_pp_table
.set_row_spacings( 3 )
1168 dis_pp_table
.set_col_spacings( 3 )
1170 dis_pp_table
.attach( gtk
.Label( 'Before' ), 0, 1, 0, 1 )
1171 dis_pp_table
.attach( gtk
.Label( 'After' ), 0, 1, 1, 2 )
1173 self
.dis_prescript_entry
= gtk
.Entry()
1174 self
.dis_prescript_entry
.set_text( self
.profile
['dis_prescript'] )
1175 dis_pp_table
.attach( self
.dis_prescript_entry
, 1, 2, 0, 1 )
1176 self
.dis_postscript_entry
= gtk
.Entry()
1177 self
.dis_postscript_entry
.set_text( self
.profile
['dis_postscript'] )
1178 dis_pp_table
.attach( self
.dis_postscript_entry
, 1, 2, 1, 2 )
1179 # Add the pp table to the expander
1180 self
.dis_pp_expander
.add( dis_pp_table
)
1181 # Add the expander to the dialog
1182 self
.dialog
.vbox
.pack_start( self
.dis_pp_expander
, False, False, 5 )
1185 self
.dialog
.show_all()
1186 if self
.dialog
.run():
1187 self
.profile
['known'] = True
1188 self
.profile
['essid'] = self
.essid_entry
.get_text().strip()
1189 self
.profile
['bssid'] = self
.bssid_entry
.get_text().strip()
1190 self
.profile
['key'] = self
.key_entry
.get_text().strip()
1191 self
.profile
['mode'] = self
.get_array_item( self
.mode_combo
.get_active(), self
.WIFI_MODES
)
1192 self
.profile
['security'] = self
.get_array_item( self
.security_combo
.get_active(), self
.WIFI_SECURITY
)
1193 self
.profile
['encrypted'] = ( self
.profile
['security'] != '' )
1194 self
.profile
['channel'] = self
.get_array_item( self
.channel_combo
.get_active(), self
.WIFI_CHANNELS
)
1195 self
.profile
['protocol'] = 'g'
1196 self
.profile
['available'] = ( self
.profile
['signal'] > 0 )
1197 self
.profile
['con_prescript'] = self
.con_prescript_entry
.get_text().strip()
1198 self
.profile
['con_postscript'] = self
.con_postscript_entry
.get_text().strip()
1199 self
.profile
['dis_prescript'] = self
.dis_prescript_entry
.get_text().strip()
1200 self
.profile
['dis_postscript'] = self
.dis_postscript_entry
.get_text().strip()
1202 self
.profile
['use_wpa'] = self
.wpa_expander
.get_expanded()
1203 self
.profile
['wpa_driver'] = self
.wpa_driver_entry
.get_text().strip()
1205 self
.profile
['use_dhcp'] = not self
.dhcp_expander
.get_expanded()
1206 self
.profile
['ip'] = self
.ip_entry
.get_text().strip()
1207 self
.profile
['netmask'] = self
.netmask_entry
.get_text().strip()
1208 self
.profile
['gateway'] = self
.gw_entry
.get_text().strip()
1209 self
.profile
['domain'] = self
.domain_entry
.get_text().strip()
1210 self
.profile
['dns1'] = self
.dns1_entry
.get_text().strip()
1211 self
.profile
['dns2'] = self
.dns2_entry
.get_text().strip()
1214 def destroy( self
):
1215 self
.dialog
.destroy()
1218 def toggle_use_dhcp( self
, widget
, data
= None ):
1219 expanded
= self
.dhcp_expander
.get_expanded()
1221 self
.dhcp_expander
.set_label( USE_IP_LABEL
)
1223 self
.dhcp_expander
.set_label( USE_DHCP_LABEL
)
1225 def toggle_use_wpa( self
, widget
, data
= None ):
1226 expanded
= self
.wpa_expander
.get_expanded()
1228 self
.wpa_expander
.set_label( USE_WPA_LABEL
)
1230 self
.wpa_expander
.set_label( NO_WPA_LABEL
)
1232 def get_array_index( self
, item
, array
):
1234 return array
.index( item
.strip() )
1239 def get_array_item( self
, index
, array
):
1241 return array
[ index
]
1248 ### A simple class for putting up a "Please wait" dialog so the user
1249 ### doesn't think we've forgotten about them.
1250 class status_window
:
1251 def __init__( self
, parent
):
1252 global wifi_radar_icon
1253 self
.parent
= parent
1254 self
.dialog
= gtk
.Dialog("Working", self
.parent
.window
, gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
)
1255 icon
= gtk
.gdk
.pixbuf_new_from_inline( len( wifi_radar_icon
[0] ), wifi_radar_icon
[0], False )
1256 self
.dialog
.set_icon( icon
)
1257 self
.lbl
= gtk
.Label("Please wait...")
1258 self
.bar
= gtk
.ProgressBar()
1259 self
.dialog
.vbox
.pack_start(self
.lbl
)
1260 self
.dialog
.vbox
.pack_start(self
.bar
)
1261 self
.dialog
.show_all()
1263 def update_message( self
, message
):
1264 self
.lbl
.set_text(message
)
1266 def inhibit_close( self
, widget
, signal
):
1269 def update_window( self
):
1275 self
.dialog
.show_all()
1276 self
.timer
= gobject
.timeout_add(250,self
.update_window
)
1279 def destroy( self
):
1280 gobject
.source_remove(self
.timer
)
1281 self
.dialog
.destroy()
1285 ### class to manage the configuration file
1286 class ConfigFile(ConfigParser
.SafeConfigParser
):
1287 """ class to manage the configuration file """
1288 def __init__( self
, filename
, defaults
):
1289 self
.filename
= filename
1290 self
.auto_profile_order
= []
1291 ConfigParser
.SafeConfigParser
.__init
__(self
, defaults
)
1293 def set_section( self
, section_name
, section_dict
):
1294 """ set the contents of a section to values from a dictionary """
1296 self
.add_section(section_name
)
1297 except ConfigParser
.DuplicateSectionError
:
1299 for key
in section_dict
.keys():
1300 if type(section_dict
[key
]) == BooleanType
:
1301 self
.set_bool_opt(section_name
+ "." + key
, section_dict
[key
])
1302 elif type(section_dict
[key
]) == IntType
:
1303 self
.set_int_opt(section_name
+ "." + key
, section_dict
[key
])
1304 elif type(section_dict
[key
]) == FloatType
:
1305 self
.set_float_opt(section_name
+ "." + key
, section_dict
[key
])
1307 self
.set_opt(section_name
+ "." + key
, section_dict
[key
])
1309 def get_profile( self
, section_name
):
1310 """ return the profile recorded in the specified section """
1311 if section_name
in self
.profiles():
1312 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' ]
1313 bool_types
= [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
1314 int_types
= [ 'signal' ]
1316 for option
in bool_types
:
1317 profile
[option
] = self
.get_opt_as_bool( section_name
+ "." + option
)
1318 for option
in int_types
:
1319 profile
[option
] = self
.get_opt_as_int( section_name
+ "." + option
)
1320 for option
in str_types
:
1321 profile
[option
] = self
.get_opt( section_name
+ "." + option
)
1325 def get_opt( self
, option_path
):
1326 """ get a config option and handle exceptions """
1327 #print "ConfigFile.get_opt: ", option_path
1328 (section
, option
) = option_path
.split('.')
1330 return self
.get(section
, option
)
1331 except (ConfigParser
.NoSectionError
, ConfigParser
.NoOptionError
):
1334 def get_opt_as_bool( self
, option_path
):
1335 """ get a config option and return as a boolean type """
1336 option
= self
.get_opt(option_path
)
1337 if isinstance(option
, BooleanType
) or isinstance(option
, NoneType
):
1339 if option
== 'True':
1341 if option
== 'False':
1343 raise ValueError, 'boolean option was not True or False'
1345 def get_opt_as_int( self
, option_path
):
1346 """ get a config option and return as an integer type """
1347 return int(float(self
.get_opt(option_path
)))
1349 def set_bool_opt( self
, option_path
, value
):
1350 """ convert boolean type to string and set config option """
1351 if ( value
== True ) or ( value
> 0 ) or ( value
== 'True' ):
1353 elif ( value
== False ) or ( value
== 0 ) or ( value
== 'False' ):
1356 raise ValueError, 'cannot convert value to string'
1357 self
.set_opt(option_path
, repr(value
))
1359 def set_int_opt( self
, option_path
, value
):
1360 """ convert integer type to string and set config option """
1361 if not isinstance(value
, IntType
):
1362 raise ValueError, 'value is not an integer'
1363 self
.set_opt(option_path
, repr(value
))
1365 def set_float_opt( self
, option_path
, value
):
1366 """ convert float type to string and set config option """
1367 if not isinstance(value
, FloatType
):
1368 raise ValueError, 'value is not a float'
1369 self
.set_opt(option_path
, repr(int(value
)))
1371 def set_opt( self
, option_path
, value
):
1372 """ set a config option and handle exceptions """
1373 (section
, option
) = option_path
.split('.')
1375 self
.set(section
, option
, value
)
1376 except ConfigParser
.NoSectionError
:
1377 self
.add_section(section
)
1378 self
.set_opt(option_path
, value
)
1380 def profiles( self
):
1381 """ return a list of the section names which denote AP profiles """
1383 for section
in self
.sections():
1385 profile_list
.append(section
)
1389 fp
= open( self
.filename
, "r" )
1391 # convert the auto_profile_order to a list for ordering
1392 self
.auto_profile_order
= eval(self
.get_opt('DEFAULT.auto_profile_order'))
1393 print "ConfigFile.read(): ", self
.auto_profile_order
1396 """ Copied from ConfigParser and modified to write options in alphabetical order """
1397 print "ConfigFile.write(): ", self
.auto_profile_order
1398 self
.set_opt('DEFAULT.auto_profile_order', str(self
.auto_profile_order
))
1399 fp
= open( self
.filename
, "w" )
1400 # write DEFAULT section first
1402 fp
.write("[DEFAULT]\n")
1403 for key
in sorted(self
._defaults
.keys()):
1404 fp
.write("%s = %s\n" % (key
, str(self
._defaults
[key
]).replace('\n','\n\t')))
1406 # write non-profile sections first
1407 for section
in self
._sections
:
1408 if not section
in self
.profiles():
1409 fp
.write("[%s]\n" % section
)
1410 for key
in sorted(self
._sections
[section
].keys()):
1411 if key
!= "__name__":
1412 fp
.write("%s = %s\n" %
1413 (key
, str(self
._sections
[section
][key
]).replace('\n', '\n\t')))
1415 # write profile sections
1416 for section
in self
._sections
:
1417 if section
in self
.profiles():
1418 fp
.write("[%s]\n" % section
)
1419 for key
in sorted(self
._sections
[section
].keys()):
1420 if key
!= "__name__":
1421 fp
.write("%s = %s\n" %
1422 (key
, str(self
._sections
[section
][key
]).replace('\n', '\n\t')))
1426 ####################################################################################################
1429 if not confFile
.get_opt_as_bool('DEFAULT.speak_up'): return
1430 words
= words
.replace( "\"", "\\\"" )
1431 Popen([confFile
.get_opt('DEFAULT.speak_command'), words
])
1434 # load our conf file and known profiles
1435 ####################################################################################################
1436 # Defaults, these may get overridden by values found in the conf file.
1437 # The network interface you use.
1438 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
1439 config_defaults
= { 'interface': "auto_detect",
1440 # How long should the scan for access points last?
1441 'scan_timeout': '5',
1442 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
1443 # Set the speak_up option to false if you do not have or want this.
1444 'speak_command': '/usr/bin/say',
1445 # Should I speak up when connecting to a network? (If you have a speech command)
1446 'speak_up': 'False',
1447 # You may set this to true for cards that require a "commit" command with iwconfig
1448 'commit_required': 'False',
1449 # You may set this to true for cards that require the interface to be brought up first
1450 'ifup_required': 'False',
1451 # Set the location of several important programs
1452 'iwlist_command': '/sbin/iwlist',
1453 'iwconfig_command': '/sbin/iwconfig',
1454 'ifconfig_command': '/sbin/ifconfig',
1455 'route_command': '/sbin/route',
1456 'auto_profile_order': '[]',
1457 'version': WIFI_RADAR_VERSION
}
1459 config_dhcp
= { 'command': 'dhcpcd',
1461 'args': '-D -H -o -i dhcp_client -t %(timeout)s',
1463 'pidfile': '/var/run/dhcpcd-%(interface)s.pid' }
1465 config_wpa
= { 'command': '/usr/sbin/wpa_supplicant',
1466 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
1468 'configuration': '/etc/wpa_supplicant.conf',
1470 'pidfile': '/var/run/wpa_supplicant.pid' }
1472 # initialize config, with defaults
1473 confFile
= ConfigFile(CONF_FILE
, config_defaults
)
1474 confFile
.set_section("DHCP", config_dhcp
)
1475 confFile
.set_section("WPA", config_wpa
)
1477 if not os
.path
.isfile( CONF_FILE
):
1478 confFile
.set_bool_opt('DEFAULT.new_file', True)
1480 if not os
.access(CONF_FILE
, os
.R_OK
):
1481 print "Can't open " + CONF_FILE
+ "."
1482 print "Are you root?"
1487 # First, we add our known profiles
1488 for apname
in confFile
.profiles():
1489 ap
= get_new_profile()
1490 (ap
['essid'], ap
['bssid']) = split_section_name( apname
)
1491 # read the important values
1492 for key
in ap
.keys():
1493 ap
[key
] = confFile
.get_opt(apname
+ "." + key
)
1494 # if it is not in the auto_profile_order add it
1495 if not apname
in confFile
.auto_profile_order
:
1496 confFile
.auto_profile_order
.append( apname
)
1498 #set_network_device(confFile.get_opt('DEFAULT.interface'))
1500 ####################################################################################################
1502 wifi_radar_icon
= [ ""
1509 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
1510 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
1511 "\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"
1512 "\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"
1513 "\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"
1514 "\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"
1515 "\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"
1516 "\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"
1517 "\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"
1518 "\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"
1519 "\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"
1520 "\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"
1521 "\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"
1522 "\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"
1523 "\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"
1524 "\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"
1525 "\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"
1526 "\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"
1527 "\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"
1528 "\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"
1529 "\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"
1530 "\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"
1531 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
1532 "\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"
1533 "\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"
1534 "\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"
1535 "\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"
1536 "\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"
1537 "\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"
1538 "\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"
1539 "\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"
1540 "\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"
1541 "\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"
1542 "\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"
1543 "\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"
1544 "\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"
1545 "\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"
1546 "\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"
1547 "\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"
1548 "\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"
1549 "\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"
1550 "\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"
1551 "\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"
1552 "\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"
1553 "\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"
1554 "\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"
1555 "\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"
1556 "\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"
1557 "\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"
1558 "\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"
1559 "\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"
1560 "\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"
1561 "\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"
1562 "\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"
1563 "\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"
1564 "\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"
1565 "\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"
1566 "\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"
1567 "\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"
1568 "\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"
1569 "\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"
1570 "\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"
1571 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
1572 "\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"
1573 "\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"
1574 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
1575 "\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"
1576 "\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"
1577 "\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"
1578 "\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"
1579 "\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"
1580 "\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"
1581 "\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"
1582 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
1583 "\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"
1584 "\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"
1585 "\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"
1586 "\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"
1587 "\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"
1588 "\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"
1589 "|\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"
1590 "\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"
1591 "\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"
1592 "\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"
1593 "\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"
1594 "\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"
1595 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
1596 "\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"
1597 "\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"
1598 "\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"
1599 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
1600 "\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"
1601 "\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"
1602 "\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"
1603 "\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"
1604 "\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"
1605 "\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"
1606 "\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"
1607 "\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"
1608 "\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"
1609 "\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"
1610 "\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"
1611 "\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"
1612 "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"
1613 "\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"
1614 "\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"
1615 "\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"
1616 "\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"
1617 "\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"
1618 "\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"
1619 "\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"
1620 "\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"
1621 "\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"
1622 "\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"
1623 "\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"
1624 "\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"
1625 "\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"
1626 "\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"
1627 "\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|"
1628 "\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"
1629 "\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"
1630 "\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"
1631 "\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"
1632 "\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"
1633 "\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"
1634 "\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"
1635 "\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"
1636 "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"
1637 "\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"
1638 "\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"
1639 "\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"
1640 "\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"
1641 "\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"
1642 "\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"
1643 "\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"
1644 "\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"
1645 "\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"
1646 "\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"
1647 "\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"
1648 "\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"
1649 "\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"
1650 "\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"
1651 "\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"
1652 "\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"
1653 "\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"
1654 "\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"
1655 "\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"
1656 "\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"
1657 "\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"
1658 "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"
1659 "\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"
1660 "\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"
1661 "\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"
1662 "\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"
1663 "\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"
1664 "\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"
1665 "\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"
1666 "\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"
1667 "\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"
1668 "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"
1669 "\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"
1670 "\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"
1671 "\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"
1672 "\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"
1673 "\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"
1674 "\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"
1675 "\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"
1676 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
1677 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
1678 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
1681 known_profile_icon
= [ ""
1688 "\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"
1689 "\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"
1690 "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"
1691 "\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"
1692 "\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"
1693 "\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"
1694 "\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"
1695 "\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"
1696 "\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"
1697 "\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"
1698 "\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"
1699 "\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"
1700 "\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"
1701 "\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"
1702 "\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"
1703 "\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"
1704 "\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"
1705 "\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"
1706 "\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"
1707 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
1708 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
1709 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
1710 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
1711 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
1712 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
1713 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
1714 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
1715 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
1716 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
1717 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
1718 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
1719 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
1720 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
1721 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
1722 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
1723 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
1724 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
1725 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
1726 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
1727 "\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"
1728 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
1729 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
1730 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
1731 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
1732 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
1733 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
1734 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
1735 "\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"
1736 "\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"
1737 "\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"
1738 "\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"
1739 "\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"]
1741 unknown_profile_icon
= [ ""
1748 "\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"
1749 "\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"
1750 "\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"
1751 "\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"
1752 "(\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"
1753 "\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"
1754 "#\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"
1755 "\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"
1756 "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"
1757 "\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"
1758 "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"
1759 "\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"
1760 "\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"
1761 "\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"
1762 "\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"
1763 "\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"
1764 "\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"
1765 "\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"
1766 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
1767 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
1768 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
1769 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
1770 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
1771 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
1772 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
1773 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
1774 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
1775 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
1776 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
1777 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
1778 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
1779 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
1780 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
1781 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
1782 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
1783 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
1784 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
1785 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
1786 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
1787 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
1788 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
1789 "\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"
1790 "\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"
1791 "\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"
1792 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
1794 signal_xpm_barely
= [
1821 "*%%%%=&&&&@@@@@@@@@+",
1822 "%&&&&&&&&&@@@@@@@@@+",
1823 "%&&&&&&&&&@@@@@@@@@+",
1824 "%&&&&&&&&&@@@@@@@@@+",
1825 "*%%%%%%%%%+++++++++."
1852 "$++++#@@@@@@@@@@@@@+",
1853 "+@@@@@@@@@@@@@@@@@@+",
1854 "+@@@@@@@@@@@@@@@@@@+",
1855 "+@@@@@@@@@@@@@@@@@@+",
1856 "$++++++++++++++++++."
1882 "$++++#@@@@@@@@@@@@@+",
1883 "+@@@@@@@@@@@@@@@@@@+",
1884 "+@@@@@@@@@@@@@@@@@@+",
1885 "+@@@@@@@@@@@@@@@@@@+",
1886 "$++++++++++++++++++."
1916 "=$$$$*&&&&&&&&&@@@@+",
1917 "$&&&&&&&&&&&&&&@@@@+",
1918 "$&&&&&&&&&&&&&&@@@@+",
1919 "$&&&&&&&&&&&&&&@@@@+",
1920 "=$$$$$$$$$$$$$$++++."
1949 "$%%%%#@@@@@@@@@@@@@+",
1950 "%&&&&@@@@@@@@@@@@@@+",
1951 "%&&&&@@@@@@@@@@@@@@+",
1952 "%&&&&@@@@@@@@@@@@@@+",
1953 "$%%%%++++++++++++++."
1955 signal_none_pb
= None
1956 signal_low_pb
= None
1957 signal_barely_pb
= None
1959 signal_best_pb
= None
1961 ####################################################################################################
1962 # Make so we can be imported
1963 if __name__
== "__main__":
1964 if len( sys
.argv
) > 1 and ( sys
.argv
[1] == '--version' or sys
.argv
[1] == '-v' ):
1965 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
1968 gtk
.gdk
.threads_init()
1969 apQueue
= Queue
.Queue(100)
1970 commQueue
= Queue
.Queue(2)
1971 threading
.Thread( None, scanning_thread
, None, ( confFile
, apQueue
, commQueue
) ).start()
1972 main_radar_window
= radar_window(confFile
, apQueue
, commQueue
)
1973 gobject
.timeout_add( 500, main_radar_window
.update_plist_items
)
1974 main_radar_window
.main()