Rework scanning_thread() to keep all AP scanned info in access_points dictionary
[wifi-radar.git] / wifi-radar
blob5e4e9eeec40fa5681b2a05ff99d862e9f9a12644
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 time import sleep
30 from types import *
32 WIFI_RADAR_VERSION = "0.0.0"
34 if __debug__:
35 print '__debug__ is True'
36 # NOTE: Remove the '-OO' from '#!/usr/bin/python -OO' in the first line to
37 # turn on debugging.
40 # Where the conf file should live could be different for your distro. Please change
41 # at install time with "make install sysconfdir=/etc/wifi-radar" or similar. -BEF-
43 CONF_FILE = "/etc/wifi-radar.conf"
45 os.environ['LC_MESSAGES'] = 'C'
48 #####################################
49 # Labels
50 USE_DHCP_LABEL = "Automatic network configuration (DHCP)"
51 USE_IP_LABEL = "Manual network configuration"
52 WIFI_SET_LABEL = "WiFi Options"
53 CON_PP_LABEL = "Connection Commands"
54 DIS_PP_LABEL = "Disconnection Commands"
55 USE_WPA_LABEL = "Use WPA"
56 NO_WPA_LABEL = "No WPA"
57 ####################################################################################################
59 ####################################################################################################
60 ####################################################################################################
62 # Sets the interface to the specified network device
63 def set_network_device(device):
64 #print "set_network_device: ", device
65 if device != "auto_detect":
66 confFile.set_opt('DEFAULT.interface', device)
67 else: # auto detect network device
68 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
69 # If no devices are found, default to eth1.
70 iwconfig_info = os.popen(confFile.get_opt('DEFAULT.iwconfig_command') + " 2>&1", 'r')
71 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
72 if len(wireless_devices) > 0:
73 confFile.set_opt('DEFAULT.interface', wireless_devices[0])
74 else:
75 print "No wifi-device found. Exiting."
76 sys.exit()
78 # Helper function to reduce code duplication
79 def get_new_profile():
80 """ Return a blank profile """
81 return { 'known': False,
82 'available': False,
83 'encrypted': False,
84 'essid': '',
85 'bssid': '',
86 'protocol': 'g',
87 'signal': 0,
88 'channel': 'auto',
89 'con_prescript': '',
90 'con_postscript': '',
91 'dis_prescript': '',
92 'dis_postscript': '',
93 'key': '',
94 'mode': '',
95 'security': '',
96 'use_wpa': False,
97 'wpa_driver': '',
98 'use_dhcp': True,
99 'ip': '',
100 'netmask': '',
101 'gateway': '',
102 'domain': '',
103 'dns1': '',
104 'dns2': ''
107 def get_profile_from_conf_file( essid, bssid, confFile ):
108 # We got the essid, get the key
109 profile = {}
110 section_name = make_section_name( essid, bssid )
111 if not confFile.has_section( section_name ):
112 return None
113 # read the option values
114 for option in confFile.options(section_name):
115 (profile['essid'], profile['bssid']) = split_section_name( section_name )
116 profile[option] = confFile.get_opt(section_name + "." + option)
117 return profile
119 def make_section_name( essid, bssid ):
120 return essid + ':' + bssid
122 def split_section_name( section ):
123 parts = re.split(':', section)
124 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
126 def convert_confFile():
127 for apname in confFile.sections():
128 if confFile.has_option( apname, 'prescript' ):
129 confFile.set( apname, 'con_prescript', confFile.get( apname, 'prescript' ) )
130 confFile.remove_option( apname, 'prescript' )
131 if confFile.has_option( apname, 'postscript' ):
132 confFile.set( apname, 'con_postscript', confFile.get( apname, 'postscript' ) )
133 confFile.remove_option( apname, 'postscript' )
134 confFile.set( apname, 'dis_prescript', '' )
135 confFile.set( apname, 'dis_postscript', '' )
137 # Scan for a limited time, return ap names and bssid found
138 # Access points we find will be put on the outgoing Queue, apQueue
139 def scanning_thread( confFile, apQueue, commandQueue ):
140 # Setup our essid pattern matcher
141 essid_pattern = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
142 protocol_pattern = re.compile( "Protocol\s*(:|=)\s*IEEE 802.11\s*([abg]+)", re.I | re.M | re.S )
143 mode_pattern = re.compile( "Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S )
144 channel_pattern = re.compile( "Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S )
145 enckey_pattern = re.compile( "Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S )
146 signal_pattern = re.compile( "Signal level\s*(:|=)\s*-?([0-9]+)", re.I | re.M | re.S )
148 access_points = {}
149 command = "scan"
150 while True:
151 try:
152 command = commandQueue.get_nowait()
153 if __debug__: print "scanning_thread received command", command
154 command_read = True
155 except Queue.Empty:
156 command_read = False
157 if command == "scan":
158 if __debug__: print "Beginning scan pass"
159 # Some cards need to have the interface up to scan
160 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
161 os.spawnlp(os.P_WAIT, 'ifconfig', confFile.get_opt('DEFAULT.ifconfig_command'), confFile.get_opt('DEFAULT.interface'), "up")
162 # update the signal strengths
163 scan_command = "%s %s scan" % ( confFile.get_opt('DEFAULT.iwlist_command'), confFile.get_opt('DEFAULT.interface') )
164 f = os.popen(scan_command, "r")
165 scandata = f.read()
166 f.close()
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 os.spawnlp(os.P_WAIT, 'ifconfig',
225 self.confFile.get_opt('DEFAULT.ifconfig_command'),
226 self.confFile.get_opt('DEFAULT.interface'),
227 state)
229 def connect_to_network( self, status_window ):
230 msg = "Connecting to the %s (%s) network" % ( self.profile['essid'], self.profile['bssid'] )
231 say( msg )
232 if __debug__: print " %s" % msg
233 # ready to dance
234 # Let's run the connection prescript
235 if self.profile['con_prescript'].strip() != '':
236 # got something to execute
237 if __debug__: print "executing connection prescript:", self.profile['con_prescript']
238 os.system(self.profile['con_prescript'])
239 if status_window:
240 status_window.run()
241 # Some cards need to have the interface up
242 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
243 self.if_change('up')
244 # Start building iwconfig command line, beginning with interface
245 iwconfig_args = [ confFile.get_opt('DEFAULT.interface') ]
246 # Setting essid
247 iwconfig_args.append( 'essid "%s"' % self.profile['essid'] )
248 # Setting nick
249 #iwconfig_args.append( 'nick "%s"' % self.profile['essid'] )
250 # Setting key
251 if self.profile['key'] == '':
252 iwconfig_args.append( 'key off' )
253 else:
254 iwconfig_args.append( 'key "%s"' % ( self.profile['key'] ) )
255 #iwconfig_args.append( "key %s %s" % ( self.profile['security'], self.profile['key'] ) )
256 # Setting mode
257 if self.profile['mode'] != '':
258 iwconfig_args.append( 'mode %s' % self.profile['mode'] )
259 # Setting channel
260 if self.profile['channel'] != '':
261 iwconfig_args.append( 'channel %s' % self.profile['channel'] )
262 # Now we do the ap by address (do this last since iwconfig seems to want it only there)
263 iwconfig_args.append( 'ap "%s"' % self.profile['bssid'] )
264 # Some cards require a commit
265 if self.confFile.get_opt_as_bool('DEFAULT.commit_required'):
266 iwconfig_args.append( 'commit' )
267 if __debug__: print 'iwconfig_args %s ' % ( iwconfig_args, )
268 os.spawnvp(os.P_WAIT, self.confFile.get_opt('DEFAULT.iwconfig_command'), iwconfig_args )
269 # Now normal network stuff
270 # Kill off any existing DHCP clients running
271 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
272 if __debug__: print "Killing existing DHCP..."
273 try:
274 if self.confFile.get_opt('DHCP.kill_args') != '':
275 if __debug__: print self.confFile.get_opt('DHCP.command') + " " + self.confFile.get_opt('DHCP.kill_args')
276 os.spawnlp(os.P_WAIT, self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args'))
277 else:
278 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
279 except OSError:
280 print "failed to kill DHCP client"
281 sys.exit()
282 finally:
283 print "Stale pid file. Removing..."
284 os.remove(self.confFile.get_opt('DHCP.pidfile'))
285 # Kill off any existing WPA supplicants running
286 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
287 if __debug__: print "Killing existing WPA supplicant..."
288 try:
289 if not self.confFile.get_opt('WPA.kill_command') != '':
290 os.spawnlp(os.P_WAIT, self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command'))
291 else:
292 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
293 except OSError:
294 print "failed to kill WPA supplicant"
295 sys.exit()
296 finally:
297 print "Stale pid file. Removing..."
298 os.remove(self.confFile.get_opt('WPA.pidfile'))
299 if self.profile['use_wpa'] :
300 if __debug__: print "WPA args: %s" % ( wpa_options, )
301 if status_window:
302 status_window.update_message("WPA supplicant starting")
303 wpa_pid = os.spawnvp(os.P_NOWAIT, self.confFile.get_opt('WPA.command'), [ self.confFile.get_opt('WPA.args'), self.confFile.get_opt('DEFAULT.interface') ])
304 if self.profile['use_dhcp'] :
305 if __debug__: print "Disable iwlist while dhcp in progress..."
306 try:
307 self.commQueue.put("pause")
308 except Queue.Full:
309 pass
310 if status_window:
311 status_window.update_message("Acquiring IP Address (DHCP)")
313 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'))
315 timer = self.confFile.get_opt_as_int('DHCP.timeout') + 10
316 tick = 0.25
317 print "DHCP process:", dhcp_pid
318 waiting = os.waitpid(dhcp_pid, os.WNOHANG)
319 while waiting == (0,0):
320 waiting = os.waitpid(dhcp_pid, os.WNOHANG)
321 if timer < 0:
322 os.kill(dhcp_pid, SIGTERM)
323 break
324 if sys.modules.has_key("gtk"):
325 while gtk.events_pending():
326 gtk.main_iteration(False)
327 timer -= tick
328 if timer%1 == 0:
329 print "timer: ", timer, " *** tick: ", tick
330 print "wait:", waiting
331 sleep(tick)
332 # Re-enable iwlist
333 try:
334 self.commQueue.put("scan")
335 except Queue.Full:
336 pass
337 if not self.get_current_ip():
338 if status_window:
339 status_window.update_message("Could not get IP address!")
340 if sys.modules.has_key("gtk"):
341 while gtk.events_pending():
342 gtk.main_iteration(False)
343 sleep(3)
344 else:
345 print "Could not get IP address!"
346 return
347 else:
348 if status_window:
349 status_window.update_message("Got IP address. Done.")
350 if sys.modules.has_key("gtk"):
351 while gtk.events_pending():
352 gtk.main_iteration(False)
353 sleep(2)
354 else:
355 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'] )
356 route_command = "%s add default gw %s" % ( self.confFile.get_opt('DEFAULT.route_command'), self.profile['gateway'] )
357 resolv_contents = ''
358 if self.profile['domain'] != '': resolv_contents += "domain %s\n" % domain
359 if self.profile['dns1'] != '': resolv_contents += "nameserver %s\n" % dns1
360 if self.profile['dns2'] != '': resolv_contents += "nameserver %s\n" % dns2
361 if ( resolv_contents != '' ):
362 resolv_file=open('/etc/resolv.conf', 'w')
363 resolv_file.write(s)
364 resolv_file.close
365 os.system(ifconfig_command)
366 os.system(route_command)
367 # Let's run the connection postscript
368 con_postscript = self.profile['con_postscript']
369 if self.profile['con_postscript'].strip() != '':
370 if __debug__: print "executing connection postscript:", self.profile['con_postscript']
371 os.system(self.profile['con_postscript'])
372 if status_window:
373 status_window.destroy()
375 def disconnect_interface( self, status_window ):
376 msg = "Disconnecting"
377 say( msg )
378 if __debug__: print msg
379 # Let's run the disconnection prescript
380 if self.profile['dis_prescript'].strip() != '':
381 if __debug__: print "executing disconnection prescript:", self.profile['dis_prescript']
382 os.system(self.profile['dis_prescript'])
383 if status_window:
384 status_window.run()
385 # Kill off any existing DHCP clients running
386 if os.path.isfile(self.confFile.get_opt('DHCP.pidfile')):
387 if __debug__: print "Killing existing DHCP..."
388 try:
389 if self.confFile.get_opt('DHCP.kill_args') != '':
390 if __debug__: print self.confFile.get_opt('DHCP.command') + " " + self.confFile.get_opt('DHCP.kill_args')
391 os.spawnlp(os.P_WAIT, self.confFile.get_opt('DHCP.command'), self.confFile.get_opt('DHCP.kill_args'))
392 else:
393 os.kill(int(open(self.confFile.get_opt('DHCP.pidfile'), mode='r').readline()), SIGTERM)
394 except OSError:
395 print "failed to kill DHCP client"
396 sys.exit()
397 finally:
398 print "Stale pid file. Removing..."
399 os.remove(self.confFile.get_opt('DHCP.pidfile'))
400 # Kill off any existing WPA supplicants running
401 if os.access(self.confFile.get_opt('WPA.pidfile'), os.R_OK):
402 if __debug__: print "Killing existing WPA supplicant..."
403 try:
404 if not self.confFile.get_opt('WPA.kill_command') != '':
405 os.spawnlp(os.P_WAIT, self.confFile.get_opt('WPA.kill_command'), self.confFile.get_opt('WPA.kill_command'))
406 else:
407 os.kill(int(open(self.confFile.get_opt('WPA.pidfile'), mode='r').readline()), SIGTERM)
408 except OSError:
409 print "failed to kill WPA supplicant"
410 sys.exit()
411 finally:
412 print "Stale pid file. Removing..."
413 os.remove(self.confFile.get_opt('WPA.pidfile'))
414 # Lets clear out the wireless stuff
415 if __debug__: print "Let's clear out the wireless stuff"
416 os.spawnlp(os.P_WAIT, 'iwconfig', self.confFile.get_opt('DEFAULT.iwconfig_command'), self.confFile.get_opt('DEFAULT.interface'), 'essid', 'off', 'key', 'off', 'mode', 'auto', 'channel', 'auto', 'ap', 'off')
417 if __debug__: print 'Now take the interface down'
418 self.if_change('down')
419 if __debug__: print 'Since it may be brought back up by the next scan, lets unset its IP'
420 os.spawnlp(os.P_WAIT, 'ifconfig', self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), '0.0.0.0')
421 # Let's run the disconnection postscript
422 if self.profile['dis_postscript'].strip() != '':
423 if __debug__: print "executing disconnection postscript:", self.profile['dis_postscript']
424 os.system(self.profile['dis_postscript'])
425 if status_window:
426 status_window.destroy()
427 if __debug__: print 'Disconnect complete.'
429 def get_current_ip( self ):
430 """Returns the current IP if any by calling ifconfig"""
431 ifconfig_info = os.popen(confFile.get_opt('DEFAULT.ifconfig_command') + " " + confFile.get_opt('DEFAULT.interface'), 'r')
432 # Be careful to the language (inet adr: in French for example)
434 # Hi Brian
436 # I'm using wifi-radar on a system with German translations (de_CH-UTF-8).
437 # There the string in ifconfig is inet Adresse for the IP which isn't
438 # found by the current get_current_ip function in wifi-radar. I changed
439 # the according line (#289; gentoo, v1.9.6-r1) to
440 # >ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
441 # which works on my system (LC_ALL=de_CH.UTF-8) and still works with LC_ALL=C.
443 # I'd be happy if you could incorporate this small change because as now
444 # I've got to change the file every time it is updated.
446 # Best wishes
448 # Simon
449 ip_re = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)')
450 for line in ifconfig_info:
451 if ip_re.search( line ):
452 return ip_re.search( line ).group(1)
453 return None
455 def get_current_essid( self ):
456 """Returns the current ESSID if any by calling iwconfig"""
457 iwconfig_info = os.popen(confFile.get_opt('DEFAULT.iwconfig_command') + " " + confFile.get_opt('DEFAULT.interface'), 'r')
458 # Be careful to the language (inet adr: in French for example)
459 essid_re = re.compile( "ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S )
460 for line in iwconfig_info:
461 if essid_re.search( line ):
462 return essid_re.search( line ).group(2)
463 return None
465 def get_current_bssid( self ):
466 """Returns the current BSSID if any by calling iwconfig"""
467 iwconfig_info = os.popen(confFile.get_opt('DEFAULT.iwconfig_command') + " " + confFile.get_opt('DEFAULT.interface'), 'r')
468 bssid_re = re.compile( "Access Point\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S )
469 for line in iwconfig_info:
470 if bssid_re.search( line ):
471 return bssid_re.search( line ).group(2)
472 return None
476 ##################
477 # The main window
478 class radar_window:
479 def __init__( self, confFile, apQueue, commQueue ):
480 # create the main window and connect the normal events
481 global signal_xpm_none
482 global signal_xpm_low
483 global signal_xpm_barely
484 global signal_xpm_ok
485 global signal_xpm_best
486 global known_profile_icon
487 global unknown_profile_icon
488 global wifi_radar_icon
490 self.confFile = confFile
491 self.apQueue = apQueue
492 self.commandQueue = commQueue
493 self.access_points = {}
494 self.connection = None
496 self.signal_none_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_none )
497 self.known_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( known_profile_icon[0] ), known_profile_icon[0], False )
498 self.unknown_profile_icon = gtk.gdk.pixbuf_new_from_inline( len( unknown_profile_icon[0] ), unknown_profile_icon[0], False )
499 self.signal_low_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_low )
500 self.signal_barely_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_barely )
501 self.signal_ok_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_ok )
502 self.signal_best_pb = gtk.gdk.pixbuf_new_from_xpm_data( signal_xpm_best )
503 self.window = gtk.Dialog('WiFi Radar', None, gtk.DIALOG_MODAL )
504 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
505 self.window.set_icon( icon )
506 self.window.set_border_width( 10 )
507 self.window.set_size_request( 720, 300 )
508 self.window.set_title( "WiFi Radar" )
509 self.window.connect( 'delete_event', self.delete_event )
510 self.window.connect( 'destroy', self.destroy, )
511 # let's create all our widgets
512 self.current_network = gtk.Label()
513 self.current_network.show()
514 self.close_button = gtk.Button( "Close", gtk.STOCK_CLOSE )
515 self.close_button.show()
516 self.close_button.connect( 'clicked', self.delete_event, None )
517 self.preferences_button = gtk.Button( "Preferences", gtk.STOCK_PREFERENCES )
518 self.preferences_button.show()
519 self.preferences_button.connect( 'clicked', self.edit_preferences, None )
520 # essid bssid known_icon known available wep_icon signal_level mode protocol channel
521 self.pstore = gtk.ListStore( str, str, gtk.gdk.Pixbuf, bool, bool, str, gtk.gdk.Pixbuf, str, str, str )
522 self.plist = gtk.TreeView( self.pstore )
523 # The essid column
524 self.pix_cell = gtk.CellRendererPixbuf()
525 self.wep_cell = gtk.CellRendererPixbuf()
526 self.essid_cell = gtk.CellRendererText()
527 self.bssid_cell = gtk.CellRendererText()
528 self.bssid_cell.set_property("width-chars", 20)
529 self.pcol = gtk.TreeViewColumn( "SSID" )
530 self.pcol.pack_start( self.pix_cell, False )
531 self.pcol.pack_start( self.wep_cell, False )
532 self.pcol.pack_start( self.essid_cell, True )
533 self.pcol.pack_start( self.bssid_cell, True )
534 self.pcol.add_attribute( self.wep_cell, 'stock-id', 5 )
535 self.pcol.add_attribute( self.pix_cell, 'pixbuf', 2 )
536 self.pcol.add_attribute( self.essid_cell, 'text', 0 )
537 self.pcol.add_attribute( self.bssid_cell, 'text', 1 )
538 self.plist.append_column( self.pcol )
539 # The signal column
540 self.sig_cell = gtk.CellRendererPixbuf()
541 self.scol = gtk.TreeViewColumn( "Signal" )
542 self.scol.pack_start( self.sig_cell, True )
543 self.scol.add_attribute( self.sig_cell, 'pixbuf', 6 )
544 self.plist.append_column( self.scol )
545 # The mode column
546 self.mode_cell = gtk.CellRendererText()
547 self.mcol = gtk.TreeViewColumn( "Mode" )
548 self.mcol.pack_start( self.mode_cell, True )
549 self.mcol.add_attribute( self.mode_cell, 'text', 7 )
550 self.plist.append_column( self.mcol )
551 # The protocol column
552 self.prot_cell = gtk.CellRendererText()
553 self.protcol = gtk.TreeViewColumn( "802.11" )
554 self.protcol.pack_start( self.prot_cell, True )
555 self.protcol.add_attribute( self.prot_cell, 'text', 8 )
556 self.plist.append_column( self.protcol )
557 # The channel column
558 self.channel_cell = gtk.CellRendererText()
559 self.channel = gtk.TreeViewColumn( "Channel" )
560 self.channel.pack_start( self.channel_cell, True )
561 self.channel.add_attribute( self.channel_cell, 'text', 9 )
562 self.plist.append_column( self.channel )
563 # DnD Ordering
564 self.plist.set_reorderable( True )
565 self.pstore.connect( 'row-changed', self.update_auto_profile_order )
566 # enable/disable buttons based on the selected network
567 self.selected_network = self.plist.get_selection()
568 self.selected_network.connect( 'changed', self.on_network_selection, None )
569 # the list scroll bar
570 sb = gtk.VScrollbar( self.plist.get_vadjustment() )
571 sb.show()
572 self.plist.show()
573 # Add New button
574 self.new_button = gtk.Button( "_New" )
575 self.new_button.connect( 'clicked', self.create_new_profile, get_new_profile(), None )
576 self.new_button.show()
577 # Add Configure button
578 self.edit_button = gtk.Button( "C_onfigure" )
579 self.edit_button.connect( 'clicked', self.edit_profile, None )
580 self.edit_button.show()
581 self.edit_button.set_sensitive(False)
582 # Add Delete button
583 self.delete_button = gtk.Button( "_Delete" )
584 self.delete_button.connect( 'clicked', self.delete_profile, None )
585 self.delete_button.show()
586 self.delete_button.set_sensitive(False)
587 # Add Connect button
588 self.connect_button = gtk.Button( "Co_nnect" )
589 self.connect_button.connect( 'clicked', self.connect_profile, None )
590 # Add Disconnect button
591 self.disconnect_button = gtk.Button( "D_isconnect" )
592 self.disconnect_button.connect( 'clicked', self.disconnect_profile, None )
593 # lets add our widgets
594 rows = gtk.VBox( False, 3 )
595 net_list = gtk.HBox( False, 0 )
596 listcols = gtk.HBox( False, 0 )
597 prows = gtk.VBox( False, 0 )
598 # lets start packing
599 # the network list
600 net_list.pack_start( self.plist, True, True, 0 )
601 net_list.pack_start( sb, False, False, 0 )
602 # the rows level
603 rows.pack_start( net_list , True, True, 0 )
604 rows.pack_start( self.current_network, False, True, 0 )
605 # the list columns
606 listcols.pack_start( rows, True, True, 0 )
607 listcols.pack_start( prows, False, False, 5 )
608 # the list buttons
609 prows.pack_start( self.new_button, False, False, 2 )
610 prows.pack_start( self.edit_button, False, False, 2 )
611 prows.pack_start( self.delete_button, False, False, 2 )
612 prows.pack_end( self.connect_button, False, False, 2 )
613 prows.pack_end( self.disconnect_button, False, False, 2 )
615 self.window.action_area.pack_start( self.preferences_button )
616 self.window.action_area.pack_start( self.close_button )
618 rows.show()
619 prows.show()
620 listcols.show()
621 self.window.vbox.add( listcols )
622 self.window.vbox.set_spacing( 3 )
623 self.window.show_all()
625 # Now, immediately hide these two. The proper one will be
626 # displayed later, based on interface state. -BEF-
627 self.disconnect_button.hide()
628 self.connect_button.hide()
630 # Add our known profiles in order
631 for ap in self.confFile.auto_profile_order:
632 ap = ap.strip()
633 self.access_points[ ap ] = self.confFile.get_profile( ap )
634 wep = None
635 if self.access_points[ ap ]['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
636 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'] ] )
637 # Now add any known profiles not in the profile order
638 for ap in self.confFile.profiles():
639 if ap in self.confFile.auto_profile_order: continue
640 wep = None
641 if self.access_points[ ap ]['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
642 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'] ] )
643 # This is the first run (or, at least, no config file was present), so pop up the preferences window
644 if ( self.confFile.get_opt_as_bool('DEFAULT.new_file') == True ):
645 self.confFile.remove_option('DEFAULT', 'new_file')
646 self.edit_preferences(self.preferences_button)
648 def main( self ):
649 gtk.main()
651 def destroy( self, widget = None):
652 self.confFile.write()
653 gtk.main_quit()
655 def delete_event( self, widget, event, data=None ):
656 # Put "exit" on the command Queue and wait indefinitely for it to be accepted
657 try:
658 self.commandQueue.put("exit", True)
659 except Queue.Full:
660 pass
661 # Save the preferred networks order
662 self.update_auto_profile_order()
663 self.destroy()
664 return False
666 def update_plist_items( self ):
667 """ Updates the profiles list """
668 # Indicate to PyGtk that only one Gtk thread should run here
669 gtk.gdk.threads_enter()
670 # update the current ip and essid
671 if self.connection:
672 self.current_network.set_text( "Connected to %s ip(%s)" % ( self.connection.get_current_essid(), self.connection.get_current_ip() ) )
673 if self.connection.get_current_ip():
674 self.connect_button.hide()
675 self.disconnect_button.show()
676 else:
677 self.disconnect_button.hide()
678 self.connect_button.show()
680 while True:
681 # Get profiles scanned by iwlist
682 try:
683 profile = self.apQueue.get_nowait()
684 wep = None
685 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
686 prow_iter = self.get_row_by_ap( profile['essid'], profile['bssid'] )
687 if prow_iter != None:
688 apname = make_section_name(profile['essid'], profile['bssid'])
689 if self.access_points.has_key(apname):
690 profile['known'] = self.access_points[apname]['known']
691 self.access_points[apname]['available'] = profile['available']
692 self.pstore.set_value(prow_iter, 4, profile['available'])
693 self.access_points[apname]['signal'] = profile['signal']
694 self.access_points[apname]['encrypted'] = profile['encrypted']
695 self.pstore.set_value(prow_iter, 5, wep)
696 self.pstore.set_value(prow_iter, 6, self.pixbuf_from_signal( profile['signal'] ))
697 self.access_points[apname]['mode'] = profile['mode']
698 self.pstore.set_value(prow_iter, 7, profile['mode'])
699 self.access_points[apname]['protocol'] = profile['protocol']
700 self.pstore.set_value(prow_iter, 8, profile['protocol'])
701 self.access_points[apname]['channel'] = profile['channel']
702 self.pstore.set_value(prow_iter, 9, profile['channel'])
703 self.pstore.set_value(prow_iter, 2, self.pixbuf_from_known( profile[ 'known' ] ))
704 self.pstore.set_value(prow_iter, 3, profile[ 'known' ])
705 #print "update_plist_items: profile update", profile[ 'essid' ], profile['bssid']
706 #for val in self.pstore[prow_iter]:
707 #print val,
708 else:
709 #if __debug__:
710 # print "New profile", ap, access_points[ ap ]
711 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'] ] )
712 #print "update_plist_items: new profile", profile[ 'essid' ], profile['bssid']
713 except Queue.Empty:
714 # Allow other Gtk threads to run
715 gtk.gdk.threads_leave()
716 #print "update_plist_items: Empty apQueue"
717 return True
719 def pixbuf_from_known( self, known ):
720 """ return the proper icon for value of known """
721 if known:
722 return self.known_profile_icon
723 else:
724 return self.unknown_profile_icon
726 def pixbuf_from_signal( self, signal ):
727 signal = int( signal )
728 #print signal
729 if signal >= 80 or signal == 0:
730 return self.signal_none_pb
731 elif signal >= 78:
732 return self.signal_low_pb
733 elif signal >= 75:
734 return self.signal_barely_pb
735 elif signal >= 60:
736 return self.signal_ok_pb
737 elif signal < 60:
738 return self.signal_best_pb
739 else:
740 return None
742 def get_row_by_ap( self, essid, bssid ):
743 for row in self.pstore:
744 if ( (row[0] == essid) and (row[1] == bssid) ):
745 #print "matched:", row.iter, essid, bssid
746 return row.iter
747 return None
749 # enable/disable buttons based on the selected network
750 def on_network_selection( self, widget, data=None ):
751 ( store, selected_iter ) = self.selected_network.get_selected()
752 #print self.access_points[( make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) ) )]
753 # if no networks are selected, disable all buttons except New
754 # (this occurs after a drag-and-drop)
755 if selected_iter == None:
756 self.edit_button.set_sensitive(False)
757 self.delete_button.set_sensitive(False)
758 return
759 # enable/disable buttons
760 if (store.get_value(selected_iter, 3) == True): # is selected network known?
761 self.edit_button.set_sensitive(True)
762 self.delete_button.set_sensitive(True)
763 else:
764 self.edit_button.set_sensitive(True)
765 self.delete_button.set_sensitive(False)
767 # init and run the preferences dialog
768 def edit_preferences( self, widget, data=None ):
769 p = preferences_dialog( self, self.confFile )
770 ok = p.run()
771 p.destroy()
773 def create_new_profile( self, widget, profile, data=None ):
774 profile_editor = profile_dialog( self, profile )
775 profile = profile_editor.run()
776 profile_editor.destroy()
777 if profile:
778 apname = make_section_name( profile['essid'], profile['bssid'] )
779 # Check that the ap does not exist already
780 if apname in self.confFile.profiles():
781 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) )
782 dlg.run()
783 dlg.destroy()
784 del dlg
785 # try again
786 self.access_points[ apname ] = profile
787 self.confFile.set_section( apname, profile )
788 # if it is not in the auto_profile_order add it
789 if not apname in self.confFile.auto_profile_order:
790 self.confFile.auto_profile_order.append(apname)
791 # add to the store
792 wep = None
793 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
794 return True
795 else:
796 # Did not create new profile
797 return False
799 def edit_profile( self, widget, data=None ):
800 ( store, selected_iter ) = self.plist.get_selection().get_selected()
801 if not selected_iter: return
802 apname = make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) )
803 profile = self.confFile.get_profile( apname )
804 if profile:
805 profile_editor = profile_dialog( self, profile )
806 profile = profile_editor.run()
807 profile_editor.destroy()
808 if profile:
809 if __debug__:
810 print "Got edited profile ", profile
811 apname = make_section_name( profile['essid'], profile['bssid'] )
812 self.access_points[ apname ] = profile
813 self.confFile.set_section( apname, profile )
814 else:
815 profile = get_new_profile()
816 profile['essid'] = store.get_value( selected_iter, 0 )
817 profile['bssid'] = store.get_value( selected_iter, 1 )
818 self.create_new_profile( widget, profile, data )
819 print "edited: ", profile['essid'], profile['bssid']
821 def delete_profile( self, widget, data=None ):
822 ( store, selected_iter ) = self.plist.get_selection().get_selected()
823 if not selected_iter: return
824 essid = store.get_value( selected_iter, 0 )
825 bssid = store.get_value( selected_iter, 1 )
826 known = store.get_value( selected_iter, 3 )
827 if not known: return
828 dlg = gtk.MessageDialog(
829 self.window,
830 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
831 gtk.MESSAGE_QUESTION,
832 gtk.BUTTONS_YES_NO,
833 "Are you sure you want to delete the %s (%s) profile?" % (essid, bssid) )
834 res = dlg.run()
835 dlg.destroy()
836 del dlg
837 if res == gtk.RESPONSE_NO: return
838 # Remove it
839 apname = make_section_name( essid, bssid )
840 del self.access_points[ apname ]
841 self.confFile.remove_section( apname )
842 print "delete_profile: ", apname, ":", self.confFile.auto_profile_order
843 if apname in self.confFile.auto_profile_order: self.confFile.auto_profile_order.remove(apname)
844 self.pstore.remove( selected_iter )
845 # Let's save our current state
846 self.update_auto_profile_order()
848 def connect_profile( self, widget, profile, data=None ):
849 ( store, selected_iter ) = self.plist.get_selection().get_selected()
850 if not selected_iter: return
851 essid = store.get_value( selected_iter, 0 )
852 bssid = store.get_value( selected_iter, 1 )
853 known = store.get_value( selected_iter, 3 )
854 if not known:
855 if data != 'noconnect':
856 dlg = gtk.MessageDialog(
857 self.window,
858 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
859 gtk.MESSAGE_QUESTION,
860 gtk.BUTTONS_YES_NO,
861 "This network does not have a profile configured.\n\nWould you like to create one now?" )
862 res = dlg.run()
863 dlg.destroy()
864 del dlg
865 if res == gtk.RESPONSE_NO: return
866 profile = get_new_profile()
867 profile['essid'] = store.get_value( selected_iter, 0 )
868 profile['bssid'] = store.get_value( selected_iter, 1 )
869 if not self.create_new_profile( widget, profile, data ):
870 return
871 apname = make_section_name( store.get_value( selected_iter, 0 ), store.get_value( selected_iter, 1 ) )
872 self.connection = ConnectionManager( self.access_points[apname], self.confFile, self.commandQueue )
873 self.connection.connect_to_network( status_window( self ) )
875 def disconnect_profile( self, widget, data=None ):
876 self.connection.disconnect_interface( status_window( self ) )
878 def update_auto_profile_order( self, widget = None, data = None, data2 = None ):
879 # recreate the auto_profile_order
880 auto_profile_order = []
881 piter = self.pstore.get_iter_first()
882 while piter:
883 # only if it's known
884 if self.pstore.get_value( piter, 3 ) == True:
885 auto_profile_order.append( make_section_name( self.pstore.get_value( piter, 0 ), self.pstore.get_value( piter, 1 ) ) )
886 piter = self.pstore.iter_next( piter )
887 self.confFile.auto_profile_order = auto_profile_order
889 ###################################
890 # the preferences dialog
891 class preferences_dialog:
892 def __init__( self, parent, confFile ):
893 # set up the preferences window
894 global wifi_radar_icon
895 self.parent = parent
896 self.confFile = confFile
897 self.dialog = gtk.Dialog('WiFi Radar Preferences', self.parent.window,
898 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
899 ( gtk.STOCK_CLOSE, True ) )
900 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
901 self.dialog.set_icon( icon )
902 self.dialog.set_resizable( True )
903 self.dialog.set_transient_for( self.parent.window )
904 self.tooltips = gtk.Tooltips()
906 # set up preferences widgets
909 # auto detect wireless device
910 self.w_auto_detect = gtk.CheckButton("Auto-detect wireless device")
912 print "prefs: ", self.confFile.get_opt('DEFAULT.interface')
914 self.w_auto_detect.set_active( self.confFile.get_opt('DEFAULT.interface') == "auto_detect" )
915 self.w_auto_detect.connect("toggled", self.toggle_auto_detect)
916 self.tooltips.set_tip(self.w_auto_detect, "Automatically select wireless device to configure")
917 self.dialog.vbox.pack_start(self.w_auto_detect, False, False, 5)
918 # network interface selecter
919 self.w_interface = gtk.combo_box_entry_new_text()
920 iwconfig_info = os.popen(self.confFile.get_opt('DEFAULT.iwconfig_command') + " 2>&1", 'r')
921 wireless_devices = [ (x[0:x.find(" ")]) for x in iwconfig_info if ("ESSID" in x)]
922 for device in wireless_devices:
923 if device != self.confFile.get_opt('DEFAULT.interface'):
924 self.w_interface.append_text(device)
925 if self.confFile.get_opt('DEFAULT.interface') != "auto_detect":
926 self.w_interface.prepend_text( self.confFile.get_opt('DEFAULT.interface') )
927 self.w_interface.set_active(0)
928 self.w_interface_label = gtk.Label("Wireless device")
929 self.w_hbox1 = gtk.HBox(False, 0)
930 self.w_hbox1.pack_start(self.w_interface_label, False, False, 5)
931 self.w_hbox1.pack_start(self.w_interface, True, True, 0)
932 self.w_interface.set_sensitive( self.confFile.get_opt('DEFAULT.interface') != "auto_detect" )
933 self.dialog.vbox.pack_start(self.w_hbox1, False, False, 5)
934 # scan timeout (spin button of integers from 1 to 100)
935 self.time_in_seconds = gtk.Adjustment( self.confFile.get_opt_as_int('DEFAULT.scan_timeout'),1,100,1,1,1 )
936 self.w_scan_timeout = gtk.SpinButton(self.time_in_seconds, 1, 0)
937 self.w_scan_timeout.set_update_policy(gtk.UPDATE_IF_VALID)
938 self.w_scan_timeout.set_numeric(True)
939 self.w_scan_timeout.set_snap_to_ticks(True)
940 self.w_scan_timeout.set_wrap(False)
941 self.tooltips.set_tip(self.w_scan_timeout, "How long should WiFi Radar scan for access points?")
942 self.w_scan_timeout_label = gtk.Label("Scan timeout (seconds)")
943 self.w_hbox2 = gtk.HBox(False, 0)
944 self.w_hbox2.pack_start(self.w_scan_timeout_label, False, False, 5)
945 self.w_hbox2.pack_start(self.w_scan_timeout, True, True, 0)
946 self.dialog.vbox.pack_start(self.w_hbox2, False, False, 5)
947 # speak up
948 self.w_speak_up = gtk.CheckButton("Use speak-up")
949 self.w_speak_up.set_active( self.confFile.get_opt_as_bool('DEFAULT.speak_up') )
950 self.tooltips.set_tip(self.w_speak_up, "Should I speak up when connecting to a network? (If you have a speech command)")
951 self.dialog.vbox.pack_start(self.w_speak_up, False, False, 5)
952 # commit required
953 self.w_commit_required = gtk.CheckButton("Commit required")
954 self.w_commit_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.commit_required') )
955 self.tooltips.set_tip(self.w_commit_required, "Check this box if your card requires a \"commit\" command with iwconfig")
956 self.dialog.vbox.pack_start(self.w_commit_required, False, False, 5)
957 # ifup required
958 self.w_ifup_required = gtk.CheckButton("Ifup required")
959 self.w_ifup_required.set_active( self.confFile.get_opt_as_bool('DEFAULT.ifup_required') )
960 self.tooltips.set_tip(self.w_ifup_required, "Check this box if your system requires the interface to be brought up first")
961 self.dialog.vbox.pack_start(self.w_ifup_required, False, False, 5)
963 def toggle_auto_detect(self, auto_detect_toggle, data=None):
964 self.w_interface.set_sensitive(not auto_detect_toggle.get_active())
966 def run(self):
967 self.dialog.show_all()
968 return self.dialog.run()
970 def destroy(self):
971 # save preferences to config file
972 if self.w_auto_detect.get_active():
973 set_network_device("auto_detect")
974 self.confFile.set_opt('DEFAULT.interface', "auto_detect")
975 else:
976 set_network_device(self.w_interface.child.get_text())
977 self.confFile.set_opt('DEFAULT.interface', self.w_interface.child.get_text())
978 # reconfigure the dhcp commands in case the network device was changed
979 self.confFile.set_float_opt('DEFAULT.scan_timeout', self.w_scan_timeout.get_value())
980 self.confFile.set_bool_opt('DEFAULT.speak_up', self.w_speak_up.get_active())
981 self.confFile.set_bool_opt('DEFAULT.commit_required', self.w_commit_required.get_active())
982 self.confFile.set_bool_opt('DEFAULT.ifup_required', self.w_ifup_required.get_active())
983 if self.confFile.get_opt_as_bool('DEFAULT.ifup_required'):
984 os.spawnlp(os.P_WAIT, 'ifconfig', self.confFile.get_opt('DEFAULT.ifconfig_command'), self.confFile.get_opt('DEFAULT.interface'), "up")
985 self.confFile.write()
986 self.dialog.destroy()
987 del self.dialog
990 ###################################
991 class profile_dialog:
992 def __init__( self, parent, profile ):
993 global wifi_radar_icon
994 self.parent = parent
995 self.profile = profile
996 self.WIFI_MODES = [ '', 'auto', 'Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor' ]
997 self.WIFI_SECURITY = [ '', 'open', 'restricted' ]
998 self.WIFI_CHANNELS = [ '', 'auto', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14' ]
999 self.dialog = gtk.Dialog('WiFi Profile', self.parent.window,
1000 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
1001 ( gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True ) )
1002 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1003 self.dialog.set_icon( icon )
1004 self.dialog.set_resizable( False )
1005 self.dialog.set_transient_for( self.parent.window )
1006 #self.dialog.set_size_request( 400, 400 )
1007 #################
1008 essid_table = gtk.Table( 1, 2, False )
1009 essid_table.set_row_spacings( 3 )
1010 essid_table.set_col_spacings( 3 )
1012 # The essid labels
1013 essid_table.attach( gtk.Label( 'Network Name' ), 0, 1, 0, 1 )
1014 # The essid textboxes
1015 self.essid_entry = gtk.Entry( 32 )
1016 self.essid_entry.set_text( self.profile['essid'] )
1017 essid_table.attach( self.essid_entry, 1, 2, 0, 1 )
1018 # Add the essid table to the dialog
1019 self.dialog.vbox.pack_start( essid_table, True, True, 5 )
1021 bssid_table = gtk.Table( 1, 2, False )
1022 bssid_table.set_row_spacings( 3 )
1023 bssid_table.set_col_spacings( 3 )
1024 # The bssid labels
1025 bssid_table.attach( gtk.Label( 'Network bssid' ), 0, 1, 0, 1 )
1026 # The bssid textboxes
1027 self.bssid_entry = gtk.Entry( 32 )
1028 self.bssid_entry.set_text( self.profile['bssid'] )
1029 bssid_table.attach( self.bssid_entry, 1, 2, 0, 1 )
1030 #self.key = gtk.Entry( 32 )
1031 #bssid_table.attach( self.key, 1, 2, 1, 2 )
1032 # Add the bssid table to the dialog
1033 self.dialog.vbox.pack_start( bssid_table, True, True, 5 )
1035 # create the wifi expander
1036 self.wifi_expander = gtk.Expander( WIFI_SET_LABEL )
1037 #self.wifi_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1038 wifi_table = gtk.Table( 4, 2, False )
1039 wifi_table.set_row_spacings( 3 )
1040 wifi_table.set_col_spacings( 3 )
1041 # The Wifi labels
1042 wifi_table.attach( gtk.Label( 'Mode' ), 0, 1, 0, 1 )
1043 wifi_table.attach( gtk.Label( 'Channel' ), 0, 1, 1, 2 )
1044 wifi_table.attach( gtk.Label( 'Key' ), 0, 1, 2, 3 )
1045 wifi_table.attach( gtk.Label( 'Security' ), 0, 1, 3, 4 )
1046 # The Wifi text boxes
1047 self.mode_combo = gtk.combo_box_new_text()
1048 for mode in self.WIFI_MODES:
1049 self.mode_combo.append_text( mode )
1050 self.mode_combo.set_active( self.get_array_index( self.profile['mode'], self.WIFI_MODES ) )
1051 wifi_table.attach( self.mode_combo, 1, 2, 0, 1 )
1052 self.channel_combo = gtk.combo_box_new_text()
1053 for channel in self.WIFI_CHANNELS:
1054 self.channel_combo.append_text( channel )
1055 self.channel_combo.set_active( self.get_array_index( self.profile['channel'], self.WIFI_CHANNELS ) )
1056 wifi_table.attach( self.channel_combo, 1, 2, 1, 2 )
1058 self.key_entry = gtk.Entry( 32 )
1059 self.key_entry.set_text( self.profile['key'] )
1060 wifi_table.attach( self.key_entry, 1, 2, 2, 3 )
1062 self.security_combo = gtk.combo_box_new_text()
1063 for security in self.WIFI_SECURITY:
1064 self.security_combo.append_text( security )
1065 self.security_combo.set_active( self.get_array_index( self.profile['security'], self.WIFI_SECURITY ) )
1066 wifi_table.attach( self.security_combo, 1, 2, 3, 4 )
1067 # Add the wifi table to the expander
1068 self.wifi_expander.add( wifi_table )
1069 # Add the expander to the dialog
1070 self.dialog.vbox.pack_start( self.wifi_expander, False, False, 5 )
1072 # create the wpa expander
1073 self.wpa_expander = gtk.Expander( NO_WPA_LABEL )
1074 self.wpa_expander.connect( 'notify::expanded', self.toggle_use_wpa )
1075 wpa_table = gtk.Table( 1, 2, False )
1076 wpa_table.set_row_spacings( 3 )
1077 wpa_table.set_col_spacings( 3 )
1078 # The labels
1079 wpa_table.attach( gtk.Label( 'Driver' ), 0, 1, 0, 1 )
1080 # The text boxes
1081 self.wpa_driver_entry = gtk.Entry()
1082 self.wpa_driver_entry.set_text( self.profile['wpa_driver'] )
1083 wpa_table.attach( self.wpa_driver_entry, 1, 2, 0, 1 )
1084 # Add the wpa table to the expander
1085 self.wpa_expander.add( wpa_table )
1086 # Add the expander to the dialog
1087 self.dialog.vbox.pack_start( self.wpa_expander, False, False, 5 )
1089 # create the dhcp expander
1090 self.dhcp_expander = gtk.Expander( USE_DHCP_LABEL )
1091 self.dhcp_expander.connect( 'notify::expanded', self.toggle_use_dhcp )
1092 ip_table = gtk.Table( 6, 2, False )
1093 ip_table.set_row_spacings( 3 )
1094 ip_table.set_col_spacings( 3 )
1095 # The IP labels
1096 ip_table.attach( gtk.Label( 'IP' ), 0, 1, 0, 1 )
1097 ip_table.attach( gtk.Label( 'Netmask' ), 0, 1, 1, 2 )
1098 ip_table.attach( gtk.Label( 'Gateway' ), 0, 1, 2, 3 )
1099 ip_table.attach( gtk.Label( 'Domain' ), 0, 1, 3, 4 )
1100 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 4, 5 )
1101 ip_table.attach( gtk.Label( 'DNS' ), 0, 1, 5, 6 )
1102 # The IP text boxes
1103 self.ip_entry = gtk.Entry( 15 )
1104 self.ip_entry.set_text( self.profile['ip'] )
1105 ip_table.attach( self.ip_entry, 1, 2, 0, 1 )
1106 self.netmask_entry = gtk.Entry( 15 )
1107 self.netmask_entry.set_text( self.profile['netmask'] )
1108 ip_table.attach( self.netmask_entry, 1, 2, 1, 2 )
1109 self.gw_entry = gtk.Entry( 15 )
1110 self.gw_entry.set_text( self.profile['gateway'] )
1111 ip_table.attach( self.gw_entry, 1, 2, 2, 3 )
1112 self.domain_entry = gtk.Entry( 32 )
1113 self.domain_entry.set_text( self.profile['domain'] )
1114 ip_table.attach( self.domain_entry, 1, 2, 3, 4 )
1115 self.dns1_entry = gtk.Entry( 15 )
1116 self.dns1_entry.set_text( self.profile['dns1'] )
1117 ip_table.attach( self.dns1_entry, 1, 2, 4, 5 )
1118 self.dns2_entry = gtk.Entry( 15 )
1119 self.dns2_entry.set_text( self.profile['dns2'] )
1120 ip_table.attach( self.dns2_entry, 1, 2, 5, 6 )
1121 # Add the ip table to the expander
1122 self.dhcp_expander.add( ip_table )
1123 # Add the expander to the dialog
1124 self.dialog.vbox.pack_start( self.dhcp_expander, False, False, 5 )
1126 # create the connection-building postpre expander
1127 self.con_pp_expander = gtk.Expander( CON_PP_LABEL )
1128 con_pp_table = gtk.Table( 2, 2, False )
1129 con_pp_table.set_row_spacings( 3 )
1130 con_pp_table.set_col_spacings( 3 )
1131 # The labels
1132 con_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1133 con_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1134 # The text boxes
1135 self.con_prescript_entry = gtk.Entry()
1136 self.con_prescript_entry.set_text( self.profile['con_prescript'] )
1137 con_pp_table.attach( self.con_prescript_entry, 1, 2, 0, 1 )
1138 self.con_postscript_entry = gtk.Entry()
1139 self.con_postscript_entry.set_text( self.profile['con_postscript'] )
1140 con_pp_table.attach( self.con_postscript_entry, 1, 2, 1, 2 )
1141 # Add the pp table to the expander
1142 self.con_pp_expander.add( con_pp_table )
1143 # Add the expander to the dialog
1144 self.dialog.vbox.pack_start( self.con_pp_expander, False, False, 5 )
1146 # create the disconnection postpre expander
1147 self.dis_pp_expander = gtk.Expander( DIS_PP_LABEL )
1148 dis_pp_table = gtk.Table( 2, 2, False )
1149 dis_pp_table.set_row_spacings( 3 )
1150 dis_pp_table.set_col_spacings( 3 )
1151 # The labels
1152 dis_pp_table.attach( gtk.Label( 'Before' ), 0, 1, 0, 1 )
1153 dis_pp_table.attach( gtk.Label( 'After' ), 0, 1, 1, 2 )
1154 # The text boxes
1155 self.dis_prescript_entry = gtk.Entry()
1156 self.dis_prescript_entry.set_text( self.profile['dis_prescript'] )
1157 dis_pp_table.attach( self.dis_prescript_entry, 1, 2, 0, 1 )
1158 self.dis_postscript_entry = gtk.Entry()
1159 self.dis_postscript_entry.set_text( self.profile['dis_postscript'] )
1160 dis_pp_table.attach( self.dis_postscript_entry, 1, 2, 1, 2 )
1161 # Add the pp table to the expander
1162 self.dis_pp_expander.add( dis_pp_table )
1163 # Add the expander to the dialog
1164 self.dialog.vbox.pack_start( self.dis_pp_expander, False, False, 5 )
1166 def run( self ):
1167 self.dialog.show_all()
1168 if self.dialog.run():
1169 self.profile['known'] = True
1170 self.profile['essid'] = self.essid_entry.get_text().strip()
1171 self.profile['bssid'] = self.bssid_entry.get_text().strip()
1172 self.profile['key'] = self.key_entry.get_text().strip()
1173 self.profile['mode'] = self.get_array_item( self.mode_combo.get_active(), self.WIFI_MODES )
1174 self.profile['security'] = self.get_array_item( self.security_combo.get_active(), self.WIFI_SECURITY )
1175 self.profile['encrypted'] = ( self.profile['security'] != '' )
1176 self.profile['channel'] = self.get_array_item( self.channel_combo.get_active(), self.WIFI_CHANNELS )
1177 self.profile['protocol'] = 'g'
1178 self.profile['available'] = ( self.profile['signal'] > 0 )
1179 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
1180 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
1181 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
1182 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
1183 # wpa
1184 self.profile['use_wpa'] = self.wpa_expander.get_expanded()
1185 self.profile['wpa_driver'] = self.wpa_driver_entry.get_text().strip()
1186 # dhcp
1187 self.profile['use_dhcp'] = not self.dhcp_expander.get_expanded()
1188 self.profile['ip'] = self.ip_entry.get_text().strip()
1189 self.profile['netmask'] = self.netmask_entry.get_text().strip()
1190 self.profile['gateway'] = self.gw_entry.get_text().strip()
1191 self.profile['domain'] = self.domain_entry.get_text().strip()
1192 self.profile['dns1'] = self.dns1_entry.get_text().strip()
1193 self.profile['dns2'] = self.dns2_entry.get_text().strip()
1194 return self.profile
1196 def destroy( self ):
1197 self.dialog.destroy()
1198 del self.dialog
1200 def toggle_use_dhcp( self, widget, data = None ):
1201 expanded = self.dhcp_expander.get_expanded()
1202 if expanded:
1203 self.dhcp_expander.set_label( USE_IP_LABEL )
1204 else:
1205 self.dhcp_expander.set_label( USE_DHCP_LABEL )
1207 def toggle_use_wpa( self, widget, data = None ):
1208 expanded = self.wpa_expander.get_expanded()
1209 if expanded:
1210 self.wpa_expander.set_label( USE_WPA_LABEL )
1211 else:
1212 self.wpa_expander.set_label( NO_WPA_LABEL )
1214 def get_array_index( self, item, array ):
1215 try:
1216 return array.index( item.strip() )
1217 except:
1218 pass
1219 return 0
1221 def get_array_item( self, index, array ):
1222 try:
1223 return array[ index ]
1224 except:
1225 pass
1226 return ''
1228 ### status_window
1230 ### A simple class for putting up a "Please wait" dialog so the user
1231 ### doesn't think we've forgotten about them.
1232 class status_window:
1233 def __init__( self, parent ):
1234 global wifi_radar_icon
1235 self.parent = parent
1236 self.dialog = gtk.Dialog("Working", self.parent.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
1237 icon = gtk.gdk.pixbuf_new_from_inline( len( wifi_radar_icon[0] ), wifi_radar_icon[0], False )
1238 self.dialog.set_icon( icon )
1239 self.lbl = gtk.Label("Please wait...")
1240 self.bar = gtk.ProgressBar()
1241 self.dialog.vbox.pack_start(self.lbl)
1242 self.dialog.vbox.pack_start(self.bar)
1243 self.dialog.show_all()
1245 def update_message( self, message ):
1246 self.lbl.set_text(message)
1248 def inhibit_close( self, widget, signal ):
1249 return True
1251 def update_window( self ):
1252 # Do stuff
1253 self.bar.pulse()
1254 return True
1256 def run( self ):
1257 self.dialog.show_all()
1258 self.timer = gobject.timeout_add(250,self.update_window)
1259 return
1261 def destroy( self ):
1262 gobject.source_remove(self.timer)
1263 self.dialog.destroy()
1264 del self.dialog
1266 ### ConfigFile
1267 ### class to manage the configuration file
1268 class ConfigFile(ConfigParser.SafeConfigParser):
1269 """ class to manage the configuration file """
1270 def __init__( self, filename, defaults ):
1271 self.filename = filename
1272 self.auto_profile_order = []
1273 ConfigParser.SafeConfigParser.__init__(self, defaults)
1275 def set_section( self, section_name, section_dict ):
1276 """ set the contents of a section to values from a dictionary """
1277 try:
1278 self.add_section(section_name)
1279 except ConfigParser.DuplicateSectionError:
1280 pass
1281 for key in section_dict.keys():
1282 if type(section_dict[key]) == BooleanType:
1283 self.set_bool_opt(section_name + "." + key, section_dict[key])
1284 elif type(section_dict[key]) == IntType:
1285 self.set_int_opt(section_name + "." + key, section_dict[key])
1286 elif type(section_dict[key]) == FloatType:
1287 self.set_float_opt(section_name + "." + key, section_dict[key])
1288 else:
1289 self.set_opt(section_name + "." + key, section_dict[key])
1291 def get_profile( self, section_name ):
1292 """ return the profile recorded in the specified section """
1293 if section_name in self.profiles():
1294 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' ]
1295 bool_types = [ 'known', 'available', 'encrypted', 'use_wpa', 'use_dhcp' ]
1296 int_types = [ 'signal' ]
1297 profile = {}
1298 for option in bool_types:
1299 profile[option] = self.get_opt_as_bool( section_name + "." + option )
1300 for option in int_types:
1301 profile[option] = self.get_opt_as_int( section_name + "." + option )
1302 for option in str_types:
1303 profile[option] = self.get_opt( section_name + "." + option )
1304 return profile
1305 return None
1307 def get_opt( self, option_path ):
1308 """ get a config option and handle exceptions """
1309 #print "ConfigFile.get_opt: ", option_path
1310 (section, option) = option_path.split('.')
1311 try:
1312 return self.get(section, option)
1313 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
1314 return None
1316 def get_opt_as_bool( self, option_path ):
1317 """ get a config option and return as a boolean type """
1318 option = self.get_opt(option_path)
1319 if isinstance(option, BooleanType) or isinstance(option, NoneType):
1320 return option
1321 if option == 'True':
1322 return True
1323 if option == 'False':
1324 return False
1325 raise ValueError, 'boolean option was not True or False'
1327 def get_opt_as_int( self, option_path ):
1328 """ get a config option and return as an integer type """
1329 return int(float(self.get_opt(option_path)))
1331 def set_bool_opt( self, option_path, value ):
1332 """ convert boolean type to string and set config option """
1333 if ( value == True ) or ( value > 0 ) or ( value == 'True' ):
1334 value == 'True'
1335 elif ( value == False ) or ( value == 0 ) or ( value == 'False' ):
1336 value == 'False'
1337 else:
1338 raise ValueError, 'cannot convert value to string'
1339 self.set_opt(option_path, repr(value))
1341 def set_int_opt( self, option_path, value ):
1342 """ convert integer type to string and set config option """
1343 if not isinstance(value, IntType):
1344 raise ValueError, 'value is not an integer'
1345 self.set_opt(option_path, repr(value))
1347 def set_float_opt( self, option_path, value ):
1348 """ convert float type to string and set config option """
1349 if not isinstance(value, FloatType):
1350 raise ValueError, 'value is not a float'
1351 self.set_opt(option_path, repr(int(value)))
1353 def set_opt( self, option_path, value ):
1354 """ set a config option and handle exceptions """
1355 (section, option) = option_path.split('.')
1356 try:
1357 self.set(section, option, value)
1358 except ConfigParser.NoSectionError:
1359 self.add_section(section)
1360 self.set_opt(option_path, value)
1362 def profiles( self ):
1363 """ return a list of the section names which denote AP profiles """
1364 profile_list = []
1365 for section in self.sections():
1366 if ':' in section:
1367 profile_list.append(section)
1368 return profile_list
1370 def read ( self ):
1371 fp = open( self.filename, "r" )
1372 self.readfp(fp)
1373 # convert the auto_profile_order to a list for ordering
1374 self.auto_profile_order = eval(self.get_opt('DEFAULT.auto_profile_order'))
1375 print "ConfigFile.read(): ", self.auto_profile_order
1377 def write( self ):
1378 """ Copied from ConfigParser and modified to write options in alphabetical order """
1379 print "ConfigFile.write(): ", self.auto_profile_order
1380 self.set_opt('DEFAULT.auto_profile_order', str(self.auto_profile_order))
1381 fp = open( self.filename, "w" )
1382 # write DEFAULT section first
1383 if self._defaults:
1384 fp.write("[DEFAULT]\n")
1385 for key in sorted(self._defaults.keys()):
1386 fp.write("%s = %s\n" % (key, str(self._defaults[key]).replace('\n','\n\t')))
1387 fp.write("\n")
1388 # write non-profile sections first
1389 for section in self._sections:
1390 if not section in self.profiles():
1391 fp.write("[%s]\n" % section)
1392 for key in sorted(self._sections[section].keys()):
1393 if key != "__name__":
1394 fp.write("%s = %s\n" %
1395 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
1396 fp.write("\n")
1397 # write profile sections
1398 for section in self._sections:
1399 if section in self.profiles():
1400 fp.write("[%s]\n" % section)
1401 for key in sorted(self._sections[section].keys()):
1402 if key != "__name__":
1403 fp.write("%s = %s\n" %
1404 (key, str(self._sections[section][key]).replace('\n', '\n\t')))
1405 fp.write("\n")
1408 ####################################################################################################
1409 # Speaking up
1410 def say( words ):
1411 if not confFile.get_opt_as_bool('DEFAULT.speak_up'): return
1412 words = words.replace( "\"", "\\\"" )
1413 os.system("%s \"%s\"" % ( confFile.get_opt('DEFAULT.speak_command'), words ))
1414 #os.spawnlp(os.P_WAIT, confFile.get_opt('DEFAULT.speak_command'), confFile.get_opt('DEFAULT.speak_command'), words)
1417 # load our conf file and known profiles
1418 ####################################################################################################
1419 # Defaults, these may get overridden by values found in the conf file.
1420 # The network interface you use.
1421 # Specify "auto_detect" as the interface to make wifi-radar automatically detect your wireless card.
1422 config_defaults = { 'interface': "auto_detect",
1423 # How long should the scan for access points last?
1424 'scan_timeout': '5',
1425 # X1000 Linux has a say command (text to speech) to announce connecting to networks.
1426 # Set the speak_up option to false if you do not have or want this.
1427 'speak_command': '/usr/bin/say',
1428 # Should I speak up when connecting to a network? (If you have a speech command)
1429 'speak_up': 'False',
1430 # You may set this to true for cards that require a "commit" command with iwconfig
1431 'commit_required': 'False',
1432 # You may set this to true for cards that require the interface to be brought up first
1433 'ifup_required': 'False',
1434 # Set the location of several important programs
1435 'iwlist_command': '/sbin/iwlist',
1436 'iwconfig_command': '/sbin/iwconfig',
1437 'ifconfig_command': '/sbin/ifconfig',
1438 'route_command': '/sbin/route',
1439 'auto_profile_order': '[]',
1440 'version': WIFI_RADAR_VERSION }
1442 config_dhcp = { 'command': 'dhcpcd',
1443 'timeout': '30',
1444 'args': '-D -H -o -i dhcp_client -t %(timeout)s',
1445 'kill_args': '-k',
1446 'pidfile': '/var/run/dhcpcd-%(interface)s.pid' }
1448 config_wpa = { 'command': '/usr/sbin/wpa_supplicant',
1449 'args': '-B -i %(interface)s -c %(configuration)s -D %(driver)s -P %(pidfile)s',
1450 'kill_command': '',
1451 'configuration': '/etc/wpa_supplicant.conf',
1452 'driver': 'wext',
1453 'pidfile': '/var/run/wpa_supplicant.pid' }
1455 # initialize config, with defaults
1456 confFile = ConfigFile(CONF_FILE, config_defaults)
1457 confFile.set_section("DHCP", config_dhcp)
1458 confFile.set_section("WPA", config_wpa)
1460 if not os.path.isfile( CONF_FILE ):
1461 confFile.set_bool_opt('DEFAULT.new_file', True)
1462 else:
1463 if not os.access(CONF_FILE, os.R_OK):
1464 print "Can't open " + CONF_FILE + "."
1465 print "Are you root?"
1466 sys.exit()
1467 confFile.read()
1470 # First, we add our known profiles
1471 for apname in confFile.profiles():
1472 ap = get_new_profile()
1473 (ap['essid'], ap['bssid']) = split_section_name( apname )
1474 # read the important values
1475 for key in ap.keys():
1476 ap[key] = confFile.get_opt(apname + "." + key)
1477 # if it is not in the auto_profile_order add it
1478 if not apname in confFile.auto_profile_order:
1479 confFile.auto_profile_order.append( apname )
1481 #set_network_device(confFile.get_opt('DEFAULT.interface'))
1483 ####################################################################################################
1484 # Embedded Images
1485 wifi_radar_icon = [ ""
1486 "GdkP"
1487 "\0\0\22""7"
1488 "\2\1\0\2"
1489 "\0\0\1\214"
1490 "\0\0\0c"
1491 "\0\0\0O"
1492 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377"
1493 "\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\261\377\377"
1494 "\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"
1495 "\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"
1496 "\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"
1497 "\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"
1498 "\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"
1499 "\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"
1500 "\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"
1501 "\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"
1502 "\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"
1503 "\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"
1504 "\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"
1505 "\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"
1506 "\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"
1507 "\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"
1508 "\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"
1509 "\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"
1510 "\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"
1511 "\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"
1512 "\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"
1513 "\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"
1514 "\0\0\272\214\0\0\0\377\3\0\0\0\375\0\0\0\241\0\0\0\"\226\377\377\377"
1515 "\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"
1516 "\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"
1517 "\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"
1518 "\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"
1519 "\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"
1520 "\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"
1521 "\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"
1522 "\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"
1523 "\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"
1524 "\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"
1525 "\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"
1526 "\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"
1527 "\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"
1528 "\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"
1529 "\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"
1530 "\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"
1531 "\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"
1532 "\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"
1533 "\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"
1534 "\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"
1535 "\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"
1536 "\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"
1537 "\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"
1538 "\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"
1539 "\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"
1540 "\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"
1541 "\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"
1542 "\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"
1543 "\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"
1544 "\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"
1545 "\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"
1546 "\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"
1547 "\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"
1548 "\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"
1549 "\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"
1550 "\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"
1551 "\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"
1552 "\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"
1553 "\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"
1554 "\377\377\377\0\1\0\0\0a\211\0\0\0\377\1\0\0\0\257\232\377\377\377\0\1"
1555 "\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"
1556 "\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"
1557 "*\0\0\0\242\211\0\0\0\310\1\0\0\0`\205\377\377\377\0\1\0\0\0\276\211"
1558 "\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"
1559 "\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"
1560 "\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"
1561 "\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"
1562 "\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"
1563 "\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"
1564 "\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"
1565 "\377\377\377\0\1\0\0\0|\211\0\0\0\377\1\0\0\0""0\226\377\377\377\0\2"
1566 "\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"
1567 "\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"
1568 "\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"
1569 "\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"
1570 "\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"
1571 "\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"
1572 "|\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"
1573 "\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"
1574 "\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"
1575 "\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"
1576 "\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"
1577 "\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"
1578 "\377\377\0\1\0\0\0\206\207\0\0\0\310\1\0\0\0X\214\377\377\377\0\11\0"
1579 "\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"
1580 "\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"
1581 "\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"
1582 "\377\377\0\1\0\0\0\271\210\0\0\0\377\1\0\0\0\202\203\377\377\377\0\1"
1583 "\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"
1584 "\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"
1585 "\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"
1586 "\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"
1587 "\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"
1588 "\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"
1589 "\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"
1590 "\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"
1591 "\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"
1592 "\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"
1593 "\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"
1594 "\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"
1595 "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"
1596 "\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"
1597 "\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"
1598 "\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"
1599 "\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"
1600 "\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"
1601 "\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"
1602 "\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"
1603 "\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"
1604 "\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"
1605 "\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"
1606 "\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"
1607 "\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"
1608 "\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"
1609 "\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"
1610 "\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|"
1611 "\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"
1612 "\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"
1613 "\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"
1614 "\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"
1615 "\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"
1616 "\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"
1617 "\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"
1618 "\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"
1619 "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"
1620 "\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"
1621 "\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"
1622 "\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"
1623 "\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"
1624 "\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"
1625 "\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"
1626 "\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"
1627 "\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"
1628 "\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"
1629 "\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"
1630 "\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"
1631 "\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"
1632 "\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"
1633 "\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"
1634 "\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"
1635 "\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"
1636 "\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"
1637 "\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"
1638 "\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"
1639 "\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"
1640 "\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"
1641 "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"
1642 "\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"
1643 "\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"
1644 "\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"
1645 "\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"
1646 "\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"
1647 "\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"
1648 "\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"
1649 "\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"
1650 "\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"
1651 "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"
1652 "\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"
1653 "\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"
1654 "\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"
1655 "\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"
1656 "\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"
1657 "\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"
1658 "\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"
1659 "\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377"
1660 "\377\377\377\0\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0"
1661 "\377\377\377\377\0\377\377\377\377\0\377\377\377\377\0\234\377\377\377"
1662 "\0"]
1664 known_profile_icon = [ ""
1665 "GdkP"
1666 "\0\0\5""0"
1667 "\2\1\0\2"
1668 "\0\0\0P"
1669 "\0\0\0\24"
1670 "\0\0\0\24"
1671 "\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"
1672 "\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"
1673 "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"
1674 "\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"
1675 "\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"
1676 "\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"
1677 "\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"
1678 "\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"
1679 "\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"
1680 "\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"
1681 "\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"
1682 "\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"
1683 "\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"
1684 "\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"
1685 "\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"
1686 "\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"
1687 "\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"
1688 "\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"
1689 "\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"
1690 "***;\210\210\210\356\223\223\223\377iii\377\204\204\204\377\216\216\216"
1691 "\377~~~\377zzz\377\203\203\203\377\215\215\215\377ddd\377\202\202\202"
1692 "\377xxx\356\40\40\40;\205\0\0\0\0\2&&&#\251\251\251\353\202\374\374\374"
1693 "\377\202\372\372\372\377\5\335\335\335\377\353\353\353\377\366\366\366"
1694 "\377\327\327\327\377\357\357\357\377\202\365\365\365\377\3\362\362\362"
1695 "\377\226\226\226\353\27\27\27)\204\0\0\0\0\21,,,p\354\354\354\377\355"
1696 "\355\355\377\351\351\351\377\346\346\346\377\342\342\342\377\335\335"
1697 "\335\377\334\334\334\377\330\330\330\377\324\324\324\377\320\320\320"
1698 "\377\316\316\316\377\313\313\313\377\307\307\307\377\314\314\314\377"
1699 "\35\35\35y\0\0\0\1\202\0\0\0\0\14\0\0\0\2(((\203\357\357\357\377\345"
1700 "\345\345\377\341\341\341\377\337\337\337\377\333\333\333\377\326\326"
1701 "\326\377\322\322\322\377\316\316\316\377\312\312\312\377\306\306\306"
1702 "\377\202\302\302\302\377\30\314\314\314\377\311\311\311\377\33\33\33"
1703 "\204\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\10&&&\210\356\356\356\377\342\342"
1704 "\342\377\347\347\347\377\346\346\346\377\324GG\377\337\337\337\377\324"
1705 "GG\377\333\322\322\377\324GG\377<\341@\377\324GG\377<\341@\377\321\321"
1706 "\321\377\276\276\276\377\27\27\27\214\0\0\0\15\202\0\0\0\4+\0\0\0\21"
1707 "$$$\221\355\355\355\377\345\345\345\377\344\344\344\377\340\340\340\377"
1708 "\334\334\334\377\331\331\331\377\325\325\325\377\321\321\321\377\316"
1709 "\316\316\377\312\312\312\377\306\306\306\377\307\307\307\377\313\313"
1710 "\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"
1711 "\27\"\"\"\231\354\354\354\377\346\346\346\377\342\342\342\377\337\337"
1712 "\337\377\333\333\333\377\327\327\327\377\324\324\324\377\320\320\320"
1713 "\377\314\314\314\377\310\310\310\377\305\305\305\377\301\301\301\377"
1714 "\276\276\276\377\271\271\271\377\23\23\23\235\0\0\0\35\0\0\0\10\0\0\0"
1715 "\4\0\0\0\32\40\40\40\223\310\310\310\376\202\274\274\274\377\4\272\272"
1716 "\272\377\271\271\271\377\270\270\270\377\267\267\267\377\202\271\271"
1717 "\271\377\16\270\270\270\377\266\266\266\377\265\265\265\377\264\264\264"
1718 "\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"
1719 "\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"
1720 "\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"
1721 "\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"
1722 "\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"]
1724 unknown_profile_icon = [ ""
1725 "GdkP"
1726 "\0\0\5\22"
1727 "\2\1\0\2"
1728 "\0\0\0P"
1729 "\0\0\0\24"
1730 "\0\0\0\24"
1731 "\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"
1732 "\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"
1733 "\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"
1734 "\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"
1735 "(\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"
1736 "\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"
1737 "#\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"
1738 "\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"
1739 "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"
1740 "\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"
1741 "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"
1742 "\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"
1743 "\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"
1744 "\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"
1745 "\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"
1746 "\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"
1747 "\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"
1748 "\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"
1749 "\0\0\0\0\16***\21\210\210\210G\223\223\223LiiiL\204\204\204L\34=n\300"
1750 "\14""1i\361\12""0i\374\20""4j\342CXx}dddL\202\202\202LxxxG\40\40\40\21"
1751 "\205\0\0\0\0\2&&&\13\251\251\251F\202\374\374\374L\202\372\372\372L\5"
1752 "\"Cv\310Lf\217\226@]\211\245\12""0i\377\22""7n\353\202\365\365\365L\3"
1753 "\362\362\362L\226\226\226F\27\27\27\14\204\0\0\0\0\21,,,!\354\354\354"
1754 "L\355\355\355L\351\351\351L\346\346\346L\342\342\342L\335\335\335L\334"
1755 "\334\334L\210\227\255h\12""0i\377\21""6l\352\316\316\316L\313\313\313"
1756 "L\307\307\307L\314\314\314L\35\35\35$\0\0\0\1\202\0\0\0\0\14\0\0\0\1"
1757 "((('\357\357\357L\345\345\345L\341\341\341L\337\337\337L\333\333\333"
1758 "L\326\326\326L|\215\245l\20""5l\355\12""0i\374Sj\215\205\202\302\302"
1759 "\302L\4\314\314\314L\311\311\311L\33\33\33'\0\0\0\2\202\0\0\0\1\22\0"
1760 "\0\0\2&&&(\356\356\356L\342\342\342L\347\347\347L\346\346\346L\324GG"
1761 "L\337\337\337L\22""0g\351\12""0i\377^9Z\201<\341@L\324GGL<\341@L\321"
1762 "\321\321L\276\276\276L\27\27\27)\0\0\0\4\202\0\0\0\1\22\0\0\0\5$$$+\355"
1763 "\355\355L\345\345\345L\344\344\344L\340\340\340L\334\334\334L\331\331"
1764 "\331Law\227\177`u\226\177\316\316\316L\312\312\312L\306\306\306L\307"
1765 "\307\307L\313\313\313L\272\272\272L\24\24\24,\0\0\0\7\202\0\0\0\2\27"
1766 "\0\0\0\7\"\"\"-\354\354\354L\346\346\346L\342\342\342L\337\337\337L\333"
1767 "\333\333L\327\327\327LSk\217\212Qi\216\212\314\314\314L\310\310\310L"
1768 "\305\305\305L\301\301\301L\276\276\276L\271\271\271L\23\23\23/\0\0\0"
1769 "\10\0\0\0\2\0\0\0\1\0\0\0\10\40\40\40,\310\310\310K\202\274\274\274L"
1770 "\3\272\272\272L\271\271\271L\270\270\270L\202\12""0i\377\16\271\271\271"
1771 "L\270\270\270L\266\266\266L\265\265\265L\264\264\264L\231\231\231K\16"
1772 "\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"
1773 "\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"
1774 "\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"
1775 "\4\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0"]
1777 signal_xpm_barely = [
1778 "20 20 10 1",
1779 " c None",
1780 ". c #C6C6C6",
1781 "+ c #CCCCCC",
1782 "@ c #DBDBDB",
1783 "# c #D3D3D3",
1784 "$ c #A9B099",
1785 "% c #95A173",
1786 "& c #6B8428",
1787 "* c #B4B7AC",
1788 "= c #80924D",
1789 " .+++.",
1790 " +@@@+",
1791 " +@@@+",
1792 " +@@@+",
1793 " +@@@+",
1794 " .++++#@@@+",
1795 " +@@@@@@@@+",
1796 " +@@@@@@@@+",
1797 " +@@@@@@@@+",
1798 " +@@@@@@@@+",
1799 " $%%%%#@@@@@@@@+",
1800 " %&&&&@@@@@@@@@+",
1801 " %&&&&@@@@@@@@@+",
1802 " %&&&&@@@@@@@@@+",
1803 " %&&&&@@@@@@@@@+",
1804 "*%%%%=&&&&@@@@@@@@@+",
1805 "%&&&&&&&&&@@@@@@@@@+",
1806 "%&&&&&&&&&@@@@@@@@@+",
1807 "%&&&&&&&&&@@@@@@@@@+",
1808 "*%%%%%%%%%+++++++++."
1812 signal_xpm_best = [
1813 "20 20 6 1",
1814 " c None",
1815 ". c #9DAABF",
1816 "+ c #7B96BF",
1817 "@ c #386EBF",
1818 "# c #5982BF",
1819 "$ c #AEB4BF",
1820 " .+++.",
1821 " +@@@+",
1822 " +@@@+",
1823 " +@@@+",
1824 " +@@@+",
1825 " .++++#@@@+",
1826 " +@@@@@@@@+",
1827 " +@@@@@@@@+",
1828 " +@@@@@@@@+",
1829 " +@@@@@@@@+",
1830 " .++++#@@@@@@@@+",
1831 " +@@@@@@@@@@@@@+",
1832 " +@@@@@@@@@@@@@+",
1833 " +@@@@@@@@@@@@@+",
1834 " +@@@@@@@@@@@@@+",
1835 "$++++#@@@@@@@@@@@@@+",
1836 "+@@@@@@@@@@@@@@@@@@+",
1837 "+@@@@@@@@@@@@@@@@@@+",
1838 "+@@@@@@@@@@@@@@@@@@+",
1839 "$++++++++++++++++++."
1842 signal_xpm_none = [
1843 "20 20 6 1",
1844 " c None",
1845 ". c #C6C6C6",
1846 "+ c #CCCCCC",
1847 "@ c #DBDBDB",
1848 "# c #D3D3D3",
1849 "$ c #C2C2C2",
1850 " .+++.",
1851 " +@@@+",
1852 " +@@@+",
1853 " +@@@+",
1854 " +@@@+",
1855 " .++++#@@@+",
1856 " +@@@@@@@@+",
1857 " +@@@@@@@@+",
1858 " +@@@@@@@@+",
1859 " +@@@@@@@@+",
1860 " .++++#@@@@@@@@+",
1861 " +@@@@@@@@@@@@@+",
1862 " +@@@@@@@@@@@@@+",
1863 " +@@@@@@@@@@@@@+",
1864 " +@@@@@@@@@@@@@+",
1865 "$++++#@@@@@@@@@@@@@+",
1866 "+@@@@@@@@@@@@@@@@@@+",
1867 "+@@@@@@@@@@@@@@@@@@+",
1868 "+@@@@@@@@@@@@@@@@@@+",
1869 "$++++++++++++++++++."
1872 signal_xpm_ok = [
1873 "20 20 10 1",
1874 " c None",
1875 ". c #C6C6C6",
1876 "+ c #CCCCCC",
1877 "@ c #DBDBDB",
1878 "# c #A1A5B2",
1879 "$ c #848DA5",
1880 "% c #D3D3D3",
1881 "& c #4A5B8C",
1882 "* c #677498",
1883 "= c #B0B2B8",
1884 " .+++.",
1885 " +@@@+",
1886 " +@@@+",
1887 " +@@@+",
1888 " +@@@+",
1889 " #$$$$%@@@+",
1890 " $&&&&@@@@+",
1891 " $&&&&@@@@+",
1892 " $&&&&@@@@+",
1893 " $&&&&@@@@+",
1894 " #$$$$*&&&&@@@@+",
1895 " $&&&&&&&&&@@@@+",
1896 " $&&&&&&&&&@@@@+",
1897 " $&&&&&&&&&@@@@+",
1898 " $&&&&&&&&&@@@@+",
1899 "=$$$$*&&&&&&&&&@@@@+",
1900 "$&&&&&&&&&&&&&&@@@@+",
1901 "$&&&&&&&&&&&&&&@@@@+",
1902 "$&&&&&&&&&&&&&&@@@@+",
1903 "=$$$$$$$$$$$$$$++++."
1907 signal_xpm_low = [
1908 "20 20 8 1",
1909 " c None",
1910 ". c #C6C6C6",
1911 "+ c #CCCCCC",
1912 "@ c #DBDBDB",
1913 "# c #D3D3D3",
1914 "$ c #BFB0B5",
1915 "% c #C18799",
1916 "& c #C54F74",
1917 " .+++.",
1918 " +@@@+",
1919 " +@@@+",
1920 " +@@@+",
1921 " +@@@+",
1922 " .++++#@@@+",
1923 " +@@@@@@@@+",
1924 " +@@@@@@@@+",
1925 " +@@@@@@@@+",
1926 " +@@@@@@@@+",
1927 " .++++#@@@@@@@@+",
1928 " +@@@@@@@@@@@@@+",
1929 " +@@@@@@@@@@@@@+",
1930 " +@@@@@@@@@@@@@+",
1931 " +@@@@@@@@@@@@@+",
1932 "$%%%%#@@@@@@@@@@@@@+",
1933 "%&&&&@@@@@@@@@@@@@@+",
1934 "%&&&&@@@@@@@@@@@@@@+",
1935 "%&&&&@@@@@@@@@@@@@@+",
1936 "$%%%%++++++++++++++."
1938 signal_none_pb = None
1939 signal_low_pb = None
1940 signal_barely_pb= None
1941 signal_ok_pb = None
1942 signal_best_pb = None
1944 ####################################################################################################
1945 # Make so we can be imported
1946 if __name__ == "__main__":
1947 if len( sys.argv ) > 1 and ( sys.argv[1] == '--version' or sys.argv[1] == '-v' ):
1948 print "WiFi-Radar version %s" % WIFI_RADAR_VERSION
1949 else:
1950 import gtk, gobject
1951 gtk.gdk.threads_init()
1952 apQueue = Queue.Queue(100)
1953 commQueue = Queue.Queue(2)
1954 threading.Thread( None, scanning_thread, None, ( confFile, apQueue, commQueue ) ).start()
1955 main_radar_window = radar_window(confFile, apQueue, commQueue)
1956 gobject.timeout_add( 500, main_radar_window.update_plist_items )
1957 main_radar_window.main()