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