Update get_current_essid() with Popen and read entire iwconfig output at once
[wifi-radar.git] / wifi-radar
blobfd7aef7a8fc5f94129a68a17065e698a3893bd92
1 #!/usr/bin/python
3 # $Id$
4 # vi:set filetype=python noet:
6 # A wireless profile manager for Linux
8 # Originally created for x1000 Linux:
9 # http://x1000.bitbuilder.com
11 # Created by:
12 # Ahmad Baitalmal <ahmad@baitalmal.com>
14 # Maintained by:
15 # Brian Elliott Finley <brian@thefinleys.com>
17 # License:
18 # GPL
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
31 from types import *
33 WIFI_RADAR_VERSION = "0.0.0"
35 if __debug__:
36 print '__debug__ is True'
37 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
38 # turn on debugging.
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 #####################################
50 # Labels
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])
76 else:
77 print "No wifi-device found. Exiting."
78 sys.exit()
80 # Helper function to reduce code duplication
81 def get_new_profile():
82 """ Return a blank profile """
83 return { 'known': False,
84 'available': False,
85 'encrypted': False,
86 'essid': '',
87 'bssid': '',
88 'protocol': 'g',
89 'signal': 0,
90 'channel': 'auto',
91 'con_prescript': '',
92 'con_postscript': '',
93 'dis_prescript': '',
94 'dis_postscript': '',
95 'key': '',
96 'mode': '',
97 'security': '',
98 'use_wpa': False,
99 'wpa_driver': '',
100 'use_dhcp': True,
101 'ip': '',
102 'netmask': '',
103 'gateway': '',
104 'domain': '',
105 'dns1': '',
106 'dns2': ''
109 def get_profile_from_conf_file( essid, bssid, confFile ):
110 # We got the essid, get the key
111 profile = {}
112 section_name = make_section_name( essid, bssid )
113 if not confFile.has_section( section_name ):
114 return None
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)
119 return profile
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 )
150 access_points = {}
151 command = "scan"
152 while True:
153 try:
154 command = commandQueue.get_nowait()
155 if __debug__: print "scanning_thread received command", command
156 command_read = True
157 except Queue.Empty:
158 command_read = False
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()
167 #if __debug__:
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(' - ')
176 for hit in hits:
177 # set the defaults for profile template
178 profile = get_new_profile()
179 m = essid_pattern.search( hit )
180 if m:
181 # we found an essid
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
199 try:
200 apQueue.put_nowait( access_points[bssid] )
201 except Queue.Full:
202 pass
203 elif command == "exit":
204 if __debug__: print "Exiting scanning_thread"
205 return
206 if command_read: commandQueue.task_done()
207 if confFile.get_opt('DEFAULT.interface').find('ath') == 0:
208 sleep( 30 )
209 else:
210 sleep( 1 )
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
220 self.state = False
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'] )
229 say( msg )
230 if __debug__: print " %s" % msg
231 # ready to dance
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']
238 if status_window:
239 status_window.run()
240 # Some cards need to have the interface up
241 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
242 self.if_change('up')
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') )
246 # Setting essid
247 iwconfig_command.append( 'essid' )
248 iwconfig_command.append( self.profile['essid'] )
249 # Setting nick
250 #iwconfig_commands.append( 'nick "%s"' % self.profile['essid'] )
251 # Setting key
252 iwconfig_command.append( 'key' )
253 if self.profile['key'] == '':
254 iwconfig_command.append( 'off' )
255 else:
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' )
261 # Setting channel
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..."
278 try:
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')
283 else:
284 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
285 except OSError:
286 print "failed to kill DHCP client"
287 sys.exit()
288 finally:
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..."
294 try:
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')])
298 else:
299 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
300 except OSError:
301 print "failed to kill WPA supplicant"
302 sys.exit()
303 finally:
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, )
309 if status_window:
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..."
315 try:
316 self.commQueue.put("pause")
317 except Queue.Full:
318 pass
319 if status_window:
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
325 tick = 0.25
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)
330 if timer < 0:
331 os.kill(dhcp_pid, SIGTERM)
332 break
333 if sys.modules.has_key("gtk"):
334 while gtk.events_pending():
335 gtk.main_iteration(False)
336 timer -= tick
337 if timer%1 == 0:
338 print "timer: ", timer, " *** tick: ", tick
339 print "wait:", waiting
340 sleep(tick)
341 # Re-enable iwlist
342 try:
343 self.commQueue.put("scan")
344 except Queue.Full:
345 pass
346 if not self.get_current_ip():
347 if status_window:
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)
352 sleep(3)
353 else:
354 print "Could not get IP address!"
355 return
356 else:
357 if status_window:
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)
362 sleep(2)
363 else:
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'] )
366 resolv_contents = ''
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')
372 resolv_file.write(s)
373 resolv_file.close
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']
381 if status_window:
382 status_window.destroy()
384 def disconnect_interface( self, status_window ):
385 msg = "Disconnecting"
386 say( msg )
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']
392 if status_window:
393 status_window.run()
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..."
397 try:
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(' ') )
401 Popen(dhcp_command)
402 if __debug__: print self.confFile.get_opt('DHCP.command') + " " + self.confFile.get_opt('DHCP.kill_args')
403 else:
404 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
405 except OSError:
406 print "failed to kill DHCP client"
407 sys.exit()
408 finally:
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..."
414 try:
415 if not self.confFile.get_opt('WPA.kill_command') != '':
416 wpa_command = [ self.confFile.get_opt('WPA.kill_command').split(' ') ]
417 Popen(wpa_command)
418 else:
419 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
420 except OSError:
421 print "failed to kill WPA supplicant"
422 sys.exit()
423 finally:
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']
437 if status_window:
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)
447 # Hi Brian
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.
459 # Best wishes
461 # Simon
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)
466 return None
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)
476 return None
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)
485 return None
489 ##################
490 # The main window
491 class radar_window:
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
497 global signal_xpm_ok
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 )
536 # The essid column
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 )
552 # The signal column
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 )
558 # The mode column
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 )
570 # The channel column
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 )
576 # DnD Ordering
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() )
584 sb.show()
585 self.plist.show()
586 # Add New button
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)
595 # Add Delete button
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)
600 # Add Connect button
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 )
611 # lets start packing
612 # the network list
613 net_list.pack_start( self.plist, True, True, 0 )
614 net_list.pack_start( sb, False, False, 0 )
615 # the rows level
616 rows.pack_start( net_list , True, True, 0 )
617 rows.pack_start( self.current_network, False, True, 0 )
618 # the list columns
619 listcols.pack_start( rows, True, True, 0 )
620 listcols.pack_start( prows, False, False, 5 )
621 # the list buttons
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 )
631 rows.show()
632 prows.show()
633 listcols.show()
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:
645 ap = ap.strip()
646 self.access_points[ ap ] = self.confFile.get_profile( ap )
647 wep = None
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():
652 ap = ap.strip()
653 if ap in self.confFile.auto_profile_order: continue
654 self.access_points[ ap ] = self.confFile.get_profile( ap )
655 wep = None
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)
663 def main( self ):
664 gtk.main()
666 def destroy( self, widget = None):
667 self.confFile.write()
668 gtk.main_quit()
670 def delete_event( self, widget, event, data=None ):
671 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
672 try:
673 self.commandQueue.put("exit", True)
674 except Queue.Full:
675 pass
676 # Save the preferred networks order
677 self.update_auto_profile_order()
678 self.destroy()
679 return False
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
687 if self.connection:
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()
692 else:
693 self.disconnect_button.hide()
694 self.connect_button.show()
696 while True:
697 # Get profiles scanned by iwlist
698 try:
699 profile = self.apQueue.get_nowait()
700 wep = None
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]:
726 #print val,
727 else:
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']
731 except Queue.Empty:
732 # Allow other Gtk threads to run
733 gtk.gdk.threads_leave()
734 #print "update_plist_items: Empty apQueue"
735 return True
737 def pixbuf_from_known( self, known ):
738 """ return the proper icon for value of known """
739 if known:
740 return self.known_profile_icon
741 else:
742 return self.unknown_profile_icon
744 def pixbuf_from_signal( self, signal ):
745 signal = int( signal )
746 #print signal
747 if signal >= 80 or signal == 0:
748 return self.signal_none_pb
749 elif signal >= 78:
750 return self.signal_low_pb
751 elif signal >= 75:
752 return self.signal_barely_pb
753 elif signal >= 60:
754 return self.signal_ok_pb
755 elif signal < 60:
756 return self.signal_best_pb
757 else:
758 return None
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
764 return row.iter
765 return None
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)
776 return
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)
781 else:
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 )
788 ok = p.run()
789 p.destroy()
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()
795 if profile:
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) )
800 dlg.run()
801 dlg.destroy()
802 del dlg
803 # try again
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)
809 # add to the store
810 wep = None
811 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
812 return True
813 else:
814 # Did not create new profile
815 return False
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 )
822 if profile:
823 profile_editor = profile_dialog( self, profile )
824 profile = profile_editor.run()
825 profile_editor.destroy()
826 if profile:
827 if __debug__:
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 )
832 else:
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 )
845 if not known: return
846 dlg = gtk.MessageDialog(
847 self.window,
848 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
849 gtk.MESSAGE_QUESTION,
850 gtk.BUTTONS_YES_NO,
851 "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid) )
852 res = dlg.run()
853 dlg.destroy()
854 del dlg
855 if res == gtk.RESPONSE_NO: return
856 # Remove it
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 )
872 if not known:
873 if data != 'noconnect':
874 dlg = gtk.MessageDialog(
875 self.window,
876 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
877 gtk.MESSAGE_QUESTION,
878 gtk.BUTTONS_YES_NO,
879 "This network does not have a profile configured.\n\nWould you like to create one now?" )
880 res = dlg.run()
881 dlg.destroy()
882 del dlg
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 ):
888 return
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()
900 while piter:
901 # only if it's known
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
913 self.parent = parent
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)
965 # speak up
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)
970 # commit required
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)
975 # ifup required
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())
984 def run(self):
985 self.dialog.show_all()
986 return self.dialog.run()
988 def destroy(self):
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")
993 else:
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()
1005 del self.dialog
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 )
1025 #################
1026 essid_table = gtk.Table( 1, 2, False )
1027 essid_table.set_row_spacings( 3 )
1028 essid_table.set_col_spacings( 3 )
1030 # The essid labels
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 )
1042 # The bssid labels
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 )
1059 # The Wifi labels
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 )
1096 # The labels
1097 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1098 # The text boxes
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 )
1113 # The IP labels
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 )
1120 # The IP text boxes
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 )
1149 # The labels
1150 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1151 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1152 # The text boxes
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 )
1169 # The labels
1170 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1171 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1172 # The text boxes
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 )
1184 def run( self ):
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()
1201 # wpa
1202 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
1203 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
1204 # dhcp
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()
1212 return self.profile
1214 def destroy( self ):
1215 self.dialog.destroy()
1216 del self.dialog
1218 def toggle_use_dhcp( self, widget, data = None ):
1219 expanded = self.dhcp_expander.get_expanded()
1220 if expanded:
1221 self.dhcp_expander.set_label( USE_IP_LABEL )
1222 else:
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()
1227 if expanded:
1228 self.wpa_expander.set_label( USE_WPA_LABEL )
1229 else:
1230 self.wpa_expander.set_label( NO_WPA_LABEL )
1232 def get_array_index( self, item, array ):
1233 try:
1234 return array.index( item.strip() )
1235 except:
1236 pass
1237 return 0
1239 def get_array_item( self, index, array ):
1240 try:
1241 return array[ index ]
1242 except:
1243 pass
1244 return ''
1246 ### status_window
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 ):
1267 return True
1269 def update_window( self ):
1270 # Do stuff
1271 self.bar.pulse()
1272 return True
1274 def run( self ):
1275 self.dialog.show_all()
1276 self.timer = gobject.timeout_add(250,self.update_window)
1277 return
1279 def destroy( self ):
1280 gobject.source_remove(self.timer)
1281 self.dialog.destroy()
1282 del self.dialog
1284 ### ConfigFile
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 """
1295 try:
1296 self.add_section(section_name)
1297 except ConfigParser.DuplicateSectionError:
1298 pass
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])
1306 else:
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' ]
1315 profile = {}
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 )
1322 return profile
1323 return None
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('.')
1329 try:
1330 return self.get(section, option)
1331 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
1332 return None
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):
1338 return option
1339 if option == 'True':
1340 return True
1341 if option == 'False':
1342 return 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' ):
1352 value == 'True'
1353 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
1354 value == 'False'
1355 else:
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('.')
1374 try:
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 """
1382 profile_list = []
1383 for section in self.sections():
1384 if ':' in section:
1385 profile_list.append(section)
1386 return profile_list
1388 def read ( self ):
1389 fp = open( self.filename, "r" )
1390 self.readfp(fp)
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
1395 def write( self ):
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
1401 if self._defaults:
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')))
1405 fp.write("\n")
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')))
1414 fp.write("\n")
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')))
1423 fp.write("\n")
1426 ####################################################################################################
1427 # Speaking up
1428 def say( words ):
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',
1460 'timeout': '30',
1461 'args': '-D -H -o -i dhcp_client -t %(timeout)s',
1462 'kill_args': '-k',
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',
1467 'kill_command': '',
1468 'configuration': '/etc/wpa_supplicant.conf',
1469 'driver': 'wext',
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)
1479 else:
1480 if not os.access(CONF_FILE, os.R_OK):
1481 print "Can't open " + CONF_FILE + "."
1482 print "Are you root?"
1483 sys.exit()
1484 confFile.read()
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 ####################################################################################################
1501 # Embedded Images
1502 wifi_radar_icon = [ ""
1503 "GdkP"
1504 "\0\0\22""7"
1505 "\2\1\0\2"
1506 "\0\0\1\214"
1507 "\0\0\0c"
1508 "\0\0\0O"
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"
1679 "\0"]
1681 known_profile_icon = [ ""
1682 "GdkP"
1683 "\0\0\5""0"
1684 "\2\1\0\2"
1685 "\0\0\0P"
1686 "\0\0\0\24"
1687 "\0\0\0\24"
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 = [ ""
1742 "GdkP"
1743 "\0\0\5\22"
1744 "\2\1\0\2"
1745 "\0\0\0P"
1746 "\0\0\0\24"
1747 "\0\0\0\24"
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 = [
1795 "20 20 10 1",
1796 " c None",
1797 ". c #C6C6C6",
1798 "+ c #CCCCCC",
1799 "@ c #DBDBDB",
1800 "# c #D3D3D3",
1801 "$ c #A9B099",
1802 "% c #95A173",
1803 "& c #6B8428",
1804 "* c #B4B7AC",
1805 "= c #80924D",
1806 " .+++.",
1807 " +@@@+",
1808 " +@@@+",
1809 " +@@@+",
1810 " +@@@+",
1811 " .++++#@@@+",
1812 " +@@@@@@@@+",
1813 " +@@@@@@@@+",
1814 " +@@@@@@@@+",
1815 " +@@@@@@@@+",
1816 " $%%%%#@@@@@@@@+",
1817 " %&&&&@@@@@@@@@+",
1818 " %&&&&@@@@@@@@@+",
1819 " %&&&&@@@@@@@@@+",
1820 " %&&&&@@@@@@@@@+",
1821 "*%%%%=&&&&@@@@@@@@@+",
1822 "%&&&&&&&&&@@@@@@@@@+",
1823 "%&&&&&&&&&@@@@@@@@@+",
1824 "%&&&&&&&&&@@@@@@@@@+",
1825 "*%%%%%%%%%+++++++++."
1829 signal_xpm_best = [
1830 "20 20 6 1",
1831 " c None",
1832 ". c #9DAABF",
1833 "+ c #7B96BF",
1834 "@ c #386EBF",
1835 "# c #5982BF",
1836 "$ c #AEB4BF",
1837 " .+++.",
1838 " +@@@+",
1839 " +@@@+",
1840 " +@@@+",
1841 " +@@@+",
1842 " .++++#@@@+",
1843 " +@@@@@@@@+",
1844 " +@@@@@@@@+",
1845 " +@@@@@@@@+",
1846 " +@@@@@@@@+",
1847 " .++++#@@@@@@@@+",
1848 " +@@@@@@@@@@@@@+",
1849 " +@@@@@@@@@@@@@+",
1850 " +@@@@@@@@@@@@@+",
1851 " +@@@@@@@@@@@@@+",
1852 "$++++#@@@@@@@@@@@@@+",
1853 "+@@@@@@@@@@@@@@@@@@+",
1854 "+@@@@@@@@@@@@@@@@@@+",
1855 "+@@@@@@@@@@@@@@@@@@+",
1856 "$++++++++++++++++++."
1859 signal_xpm_none = [
1860 "20 20 6 1",
1861 " c None",
1862 ". c #C6C6C6",
1863 "+ c #CCCCCC",
1864 "@ c #DBDBDB",
1865 "# c #D3D3D3",
1866 "$ c #C2C2C2",
1867 " .+++.",
1868 " +@@@+",
1869 " +@@@+",
1870 " +@@@+",
1871 " +@@@+",
1872 " .++++#@@@+",
1873 " +@@@@@@@@+",
1874 " +@@@@@@@@+",
1875 " +@@@@@@@@+",
1876 " +@@@@@@@@+",
1877 " .++++#@@@@@@@@+",
1878 " +@@@@@@@@@@@@@+",
1879 " +@@@@@@@@@@@@@+",
1880 " +@@@@@@@@@@@@@+",
1881 " +@@@@@@@@@@@@@+",
1882 "$++++#@@@@@@@@@@@@@+",
1883 "+@@@@@@@@@@@@@@@@@@+",
1884 "+@@@@@@@@@@@@@@@@@@+",
1885 "+@@@@@@@@@@@@@@@@@@+",
1886 "$++++++++++++++++++."
1889 signal_xpm_ok = [
1890 "20 20 10 1",
1891 " c None",
1892 ". c #C6C6C6",
1893 "+ c #CCCCCC",
1894 "@ c #DBDBDB",
1895 "# c #A1A5B2",
1896 "$ c #848DA5",
1897 "% c #D3D3D3",
1898 "& c #4A5B8C",
1899 "* c #677498",
1900 "= c #B0B2B8",
1901 " .+++.",
1902 " +@@@+",
1903 " +@@@+",
1904 " +@@@+",
1905 " +@@@+",
1906 " #$$$$%@@@+",
1907 " $&&&&@@@@+",
1908 " $&&&&@@@@+",
1909 " $&&&&@@@@+",
1910 " $&&&&@@@@+",
1911 " #$$$$*&&&&@@@@+",
1912 " $&&&&&&&&&@@@@+",
1913 " $&&&&&&&&&@@@@+",
1914 " $&&&&&&&&&@@@@+",
1915 " $&&&&&&&&&@@@@+",
1916 "=$$$$*&&&&&&&&&@@@@+",
1917 "$&&&&&&&&&&&&&&@@@@+",
1918 "$&&&&&&&&&&&&&&@@@@+",
1919 "$&&&&&&&&&&&&&&@@@@+",
1920 "=$$$$$$$$$$$$$$++++."
1924 signal_xpm_low = [
1925 "20 20 8 1",
1926 " c None",
1927 ". c #C6C6C6",
1928 "+ c #CCCCCC",
1929 "@ c #DBDBDB",
1930 "# c #D3D3D3",
1931 "$ c #BFB0B5",
1932 "% c #C18799",
1933 "& c #C54F74",
1934 " .+++.",
1935 " +@@@+",
1936 " +@@@+",
1937 " +@@@+",
1938 " +@@@+",
1939 " .++++#@@@+",
1940 " +@@@@@@@@+",
1941 " +@@@@@@@@+",
1942 " +@@@@@@@@+",
1943 " +@@@@@@@@+",
1944 " .++++#@@@@@@@@+",
1945 " +@@@@@@@@@@@@@+",
1946 " +@@@@@@@@@@@@@+",
1947 " +@@@@@@@@@@@@@+",
1948 " +@@@@@@@@@@@@@+",
1949 "$%%%%#@@@@@@@@@@@@@+",
1950 "%&&&&@@@@@@@@@@@@@@+",
1951 "%&&&&@@@@@@@@@@@@@@+",
1952 "%&&&&@@@@@@@@@@@@@@+",
1953 "$%%%%++++++++++++++."
1955 signal_none_pb = None
1956 signal_low_pb = None
1957 signal_barely_pb= None
1958 signal_ok_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
1966 else:
1967 import gtk, gobject
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()