~ubuntu-branches/ubuntu/wily/wicd/wily

« back to all changes in this revision

Viewing changes to .pc/26-support_etc-network_scripts.patch/wicd/networking.py

  • Committer: Bazaar Package Importer
  • Author(s): David Paleino
  • Date: 2011-02-12 00:16:58 UTC
  • mfrom: (8.2.12 sid)
  • Revision ID: james.westby@ubuntu.com-20110212001658-a7yd2p5vepbr7xyp
Tags: 1.7.0+ds1-6
* debian/patches/:
  - 26-support_etc-network_scripts.patch refreshed, /etc/network/
    scripts should now be properly supported (Closes: #579497)
  - 31-dont_crash_on_notification_exceptions.patch added
    (Closes: #569755, #587303)
  - 32-prefer_gksu.patch added (Closes: #575403)
  - 33-deepcopy_python27_fixes.patch backported from Ubuntu,
    thanks to Matthieu Baerts (LP: #602825)
  - 34-dont_save_useless_config.patch added: don't save link quality,
    signal strength and bitrates in the configuration files.
    (Closes: #612918)
  - 35-restrict_netmode_characters.patch added, don't crash
    if the network mode is not what we expect. Thanks to Julien
    Blache for the patch (Closes: #550957)
* debian/control:
  - removed depedency on python-iniparse from wicd-daemon
  - removed Build-Depends on quilt
  - fixed typo in long description, thanks to Martin Eberhard Schauer
    (Closes: #611567)
  - bump Standards-Version to 3.9.1, no changes needed
  - use Breaks+Replaces instead of Conflicts+Replaces
* debian/rules:
  - don't use "--with quilt" anymore
* debian/po/pt_BR.po added: debconf translation for Brazilian
  Portuguese, thanks to Adriano Rafael Gomes (Closes: #594266)
* debian/wicd-daemon.config: don't ask if all users are already
  in the netdev group (Closes: #588078)
* debian/wicd-cli.8: explain -w/--save and -m/--name (Closes: #583586)
* debian/wicd-daemon.wicd.init, export $PATH, makes the daemon work
  in a clean environment. Thanks to Peter Palfrader (Closes: #604810)
* debian/wicd-curses.postrm: redirect stderr (Closes: #605338)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: utf-8 -*-
 
3
 
 
4
""" networking - Provides wrappers for common network operations
 
5
 
 
6
This module provides wrappers of the common network tasks as well as
 
7
threads to perform the actual connecting to networks.
 
8
 
 
9
class Controller() -- Parent class to Wireless and Wired
 
10
class ConnectThread() -- Parent class to WirelessConnectThread and
 
11
    WiredConnectThread
 
12
class Wireless() -- Wrapper for various wireless functions
 
13
class Wired() -- Wrapper for various wired functions
 
14
class WirelessConnectThread() -- Connection thread for wireless
 
15
    interface
 
16
class WiredConnectThread() -- Connection thread for wired
 
17
    interface
 
18
 
 
19
"""
 
20
 
 
21
#
 
22
#   Copyright (C) 2007 - 2009 Adam Blackburn
 
23
#   Copyright (C) 2007 - 2009 Dan O'Reilly
 
24
#   Copyright (C) 2007 - 2009 Byron Hillis
 
25
#   Copyright (C)        2009 Andrew Psaltis
 
26
#
 
27
#   This program is free software; you can redistribute it and/or modify
 
28
#   it under the terms of the GNU General Public License Version 2 as
 
29
#   published by the Free Software Foundation.
 
30
#
 
31
#   This program is distributed in the hope that it will be useful,
 
32
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
33
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
34
#   GNU General Public License for more details.
 
35
#
 
36
#   You should have received a copy of the GNU General Public License
 
37
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
38
#
 
39
 
 
40
#
 
41
# Much thanks to wieman01 for help and support with various types of encyption.
 
42
# Also thanks to foxy123, yopnono, and the many others who reported bugs helped
 
43
# and helped keep this project moving.
 
44
#
 
45
 
 
46
import re
 
47
import time
 
48
import threading
 
49
import os
 
50
from signal import SIGTERM
 
51
 
 
52
# wicd imports 
 
53
import misc
 
54
import wpath
 
55
from backend import BackendManager
 
56
 
 
57
if __name__ == '__main__':
 
58
    wpath.chdir(__file__)
 
59
 
 
60
BACKEND = None
 
61
BACKEND_MGR = BackendManager()
 
62
 
 
63
def abortable(func):
 
64
    """ Mark a method in a ConnectionThread as abortable. 
 
65
    
 
66
    This decorator runs a check that will abort the connection thread
 
67
    if necessary before running a given method.
 
68
    
 
69
    """
 
70
    def wrapper(self, *__args, **__kargs):
 
71
        self.abort_if_needed()
 
72
        return func(self, *__args, **__kargs)
 
73
    
 
74
    wrapper.__name__ = func.__name__
 
75
    wrapper.__dict__ = func.__dict__
 
76
    wrapper.__doc__ = func.__doc__
 
77
    wrapper.__module = func.__module__
 
78
    return wrapper
 
79
 
 
80
def get_backend_list():
 
81
    """ Returns a list of available backends. """
 
82
    if BACKEND_MGR:
 
83
        return BACKEND_MGR.get_available_backends()
 
84
    else:
 
85
        return [""]
 
86
    
 
87
def get_backend_update_interval():
 
88
    """ Returns the suggested connection status update interval. """
 
89
    if BACKEND_MGR:
 
90
        return BACKEND_MGR.get_update_interval()
 
91
    else:
 
92
        return 5  # Seconds, this should never happen though.
 
93
    
 
94
def get_current_backend():
 
95
    """ Returns the current backend instance. """
 
96
    if BACKEND_MGR:
 
97
        return BACKEND_MGR.get_current_backend()
 
98
    else:
 
99
        return None
 
100
    
 
101
def get_backend_description(backend_name):
 
102
    """ Returns the description of the currently loaded backend. """
 
103
    return BACKEND_MGR.get_backend_description(backend_name)
 
104
 
 
105
def get_backend_description_dict():
 
106
    """ Returns a dict of all available backend descriptions. """
 
107
    d = {}
 
108
    for be in get_backend_list():
 
109
        if be:
 
110
            d[be] = get_backend_description(be)
 
111
    return d
 
112
 
 
113
def expand_script_macros(script, msg, bssid, essid):
 
114
    """Expands any supported macros in a script.
 
115
 
 
116
    Keyword arguments:
 
117
    script -- the script to execute.
 
118
    msg -- the name of the script, %{script} will be expanded to this.
 
119
    bssid -- the bssid of the network we connect to, defaults to 'wired'.
 
120
    essid -- the essid of the network we connect to, defaults to 'wired'.
 
121
    
 
122
    """
 
123
    def repl(match):
 
124
        macro = match.group(1).lower()
 
125
        if macro_dict.has_key(macro):
 
126
            return macro_dict[macro]
 
127
        print 'Warning: found illegal macro %s in %s script' % (macro, msg)
 
128
        return match.group()
 
129
    
 
130
    macro_dict = { 'script' : msg,
 
131
             'bssid' : bssid,
 
132
             'essid' : essid }
 
133
    regex = re.compile(r'%\{([a-zA-Z0-9]+)\}')
 
134
    expanded = regex.sub(repl, script)
 
135
    print "Expanded '%s' to '%s'" % (script, expanded)
 
136
    return expanded
 
137
 
 
138
 
 
139
class Controller(object):
 
140
    """ Parent class for the different interface types. """
 
141
    def __init__(self, debug=False):
 
142
        """ Initialise the class. """
 
143
        self.global_dns_1 = None
 
144
        self.global_dns_2 = None
 
145
        self.global_dns_3 = None
 
146
        self.global_dns_dom = None
 
147
        self.global_search_dom = None
 
148
        self._dhcp_client = None
 
149
        self._flush_tool = None
 
150
        self._debug = debug
 
151
        self._backend = None
 
152
        self.connecting_thread = None
 
153
        self.before_script = None
 
154
        self.after_script = None
 
155
        self.pre_disconnect_script = None
 
156
        self.post_disconnect_script = None
 
157
        self.driver = None
 
158
        self.iface = None
 
159
    
 
160
    def get_debug(self): return self._debug
 
161
    def set_debug(self, value):
 
162
        self._debug = value
 
163
        if self.iface:
 
164
            self.iface.SetDebugMode(value)
 
165
    debug = property(get_debug, set_debug)
 
166
    
 
167
    def set_dhcp_client(self, value):
 
168
        self._dhcp_client = value
 
169
        if self.iface:
 
170
            self.iface.DHCP_CLIENT = value
 
171
    def get_dhcp_client(self): return self._dhcp_client
 
172
    dhcp_client = property(get_dhcp_client, set_dhcp_client)
 
173
    
 
174
    def set_flush_tool(self, value):
 
175
        self._flush_tool = value
 
176
        if self.iface:
 
177
            self.iface.flush_tool = value
 
178
    def get_flush_tool(self): return self._flush_tool
 
179
    flush_tool = property(get_flush_tool, set_flush_tool)
 
180
    
 
181
    def LoadBackend(self, backend_name):
 
182
        """ Load the given networking backend. """
 
183
        global BACKEND
 
184
        if backend_name == self._backend:
 
185
            return
 
186
        self._backend = BACKEND_MGR.load_backend(backend_name)
 
187
        BACKEND = self._backend
 
188
        
 
189
    def NeedsExternalCalls(self):
 
190
        """ Returns true if the loaded backend needs external calls. """
 
191
        if self._backend:
 
192
            return self._backend.NeedsExternalCalls()
 
193
        else:
 
194
            return True
 
195
        
 
196
    def GetIP(self, ifconfig=""):
 
197
        """ Get the IP of the interface.
 
198
 
 
199
        Returns:
 
200
        The IP address of the interface in dotted notation.
 
201
 
 
202
        """
 
203
        return self.iface.GetIP(ifconfig)
 
204
 
 
205
    def Disconnect(self, nettype, name, mac):
 
206
        """ Disconnect from the network. """
 
207
        iface = self.iface
 
208
        # mac and name need to be strings
 
209
        if mac in (None, ''):
 
210
            mac = 'X'
 
211
        if name in (None, ''):
 
212
            name = 'X'
 
213
        misc.ExecuteScripts(wpath.predisconnectscripts, self.debug,
 
214
                           extra_parameters=(nettype, name, mac))
 
215
        if self.pre_disconnect_script:
 
216
            print 'Running pre-disconnect script'
 
217
            misc.ExecuteScript(expand_script_macros(self.pre_disconnect_script,
 
218
                                                    'pre-disconnection',
 
219
                                                    mac, name),
 
220
                               self.debug)
 
221
        iface.ReleaseDHCP()
 
222
        iface.SetAddress('0.0.0.0')
 
223
        iface.FlushRoutes()
 
224
        iface.Down()
 
225
        iface.Up()
 
226
        misc.ExecuteScripts(wpath.postdisconnectscripts, self.debug,
 
227
                            extra_parameters=(nettype, name, mac))
 
228
        if self.post_disconnect_script:
 
229
            print 'Running post-disconnect script'
 
230
            misc.ExecuteScript(expand_script_macros(self.post_disconnect_script,
 
231
                                                    'post-disconnection',
 
232
                                                    mac, name),
 
233
                               self.debug)
 
234
        
 
235
    def ReleaseDHCP(self):
 
236
        """ Release the DHCP lease for this interface. """
 
237
        return self.iface.ReleaseDHCP()
 
238
    
 
239
    def KillDHCP(self):
 
240
        """ Kill the managed DHCP client if its in a connecting state. """
 
241
        print 'running kill dhcp.'
 
242
        if (self.connecting_thread.is_connecting and 
 
243
            self.iface.dhcp_object):
 
244
            if self.iface.dhcp_object.poll() is None:
 
245
                os.kill(self.iface.dhcp_object.pid, SIGTERM)
 
246
                self.iface.dhcp_object = None
 
247
    
 
248
    def IsUp(self):
 
249
        """ Calls the IsUp method for the wired interface.
 
250
        
 
251
        Returns:
 
252
        True if the interface is up, False otherwise.
 
253
        
 
254
        """
 
255
        return self.iface.IsUp()
 
256
    
 
257
    def EnableInterface(self):
 
258
        """ Puts the interface up.
 
259
        
 
260
        Returns:
 
261
        True if the interface was put up succesfully, False otherwise.
 
262
        
 
263
        """
 
264
        return self.iface.Up()
 
265
    
 
266
    def DisableInterface(self):
 
267
        """ Puts the interface down.
 
268
        
 
269
        Returns:
 
270
        True if the interface was put down succesfully, False otherwise.
 
271
        
 
272
        """
 
273
        return self.iface.Down()
 
274
    
 
275
    def AppAvailable(self, app):
 
276
        """ Determine if the given application is installed. """
 
277
        return self.iface.AppAvailable(app)
 
278
    
 
279
 
 
280
class ConnectThread(threading.Thread):
 
281
    """ A class to perform network connections in a multi-threaded way.
 
282
 
 
283
    Useless on it's own, this class provides the generic functions
 
284
    necessary for connecting using a separate thread.
 
285
    
 
286
    """
 
287
 
 
288
    is_connecting = None
 
289
    should_die = False
 
290
    lock = threading.Lock()
 
291
 
 
292
    def __init__(self, network, interface_name, before_script, after_script, 
 
293
                 pre_disconnect_script, post_disconnect_script, gdns1,
 
294
                 gdns2, gdns3, gdns_dom, gsearch_dom, iface,
 
295
                 debug):
 
296
        """ Initialise the required object variables and the thread.
 
297
 
 
298
        Keyword arguments:
 
299
        network -- the network to connect to
 
300
        wireless -- name of the wireless interface
 
301
        wired -- name of the wired interface
 
302
        before_script -- script to run before bringing up the interface
 
303
        after_script -- script to run after bringing up the interface
 
304
        pre_disconnect_script -- script to run before disconnection
 
305
        post_disconnect_script -- script to run after disconnection
 
306
        gdns1 -- global DNS server 1
 
307
        gdns2 -- global DNS server 2
 
308
        gdns3 -- global DNS server 3
 
309
        debug -- debug mode status
 
310
 
 
311
        """
 
312
        threading.Thread.__init__(self)
 
313
        self.network = network
 
314
        self.is_connecting = False
 
315
        self.is_aborted = False
 
316
        self.connect_result = None
 
317
        self.before_script = before_script
 
318
        self.after_script = after_script
 
319
        self.pre_disconnect_script = pre_disconnect_script
 
320
        self.post_disconnect_script = post_disconnect_script
 
321
        self._should_die = False
 
322
        self.abort_reason = ""
 
323
        self.connect_result = ""
 
324
 
 
325
        self.global_dns_1 = gdns1
 
326
        self.global_dns_2 = gdns2
 
327
        self.global_dns_3 = gdns3
 
328
        self.global_dns_dom = gdns_dom
 
329
        self.global_search_dom = gsearch_dom
 
330
 
 
331
        self.iface = iface
 
332
 
 
333
        self.connecting_message = None
 
334
        self.debug = debug
 
335
        
 
336
        self.SetStatus('interface_down')
 
337
        
 
338
    def run(self):
 
339
        self.connect_result = "Failed"
 
340
        try:
 
341
            self._connect()
 
342
        finally:
 
343
            self.is_connecting = False
 
344
        
 
345
    def set_should_die(self, val):
 
346
        self.lock.acquire()
 
347
        try:
 
348
            self._should_die = val
 
349
        finally:
 
350
            self.lock.release()
 
351
    def get_should_die(self): return self._should_die
 
352
    should_die = property(get_should_die, set_should_die)
 
353
 
 
354
    def SetStatus(self, status):
 
355
        """ Set the threads current status message in a thread-safe way.
 
356
 
 
357
        Keyword arguments:
 
358
        status -- the current connection status
 
359
 
 
360
        """
 
361
        self.lock.acquire()
 
362
        try:
 
363
            self.connecting_message = status
 
364
        finally:
 
365
            self.lock.release()
 
366
 
 
367
    def GetStatus(self):
 
368
        """ Get the threads current status message in a thread-safe way.
 
369
 
 
370
        Returns:
 
371
        The current connection status.
 
372
 
 
373
        """
 
374
        self.lock.acquire()
 
375
        try:
 
376
            message = self.connecting_message
 
377
        finally:
 
378
            self.lock.release()
 
379
        return message
 
380
    
 
381
    @abortable
 
382
    def reset_ip_addresses(self, iface):
 
383
        """ Resets the IP addresses for both wired/wireless interfaces.
 
384
        
 
385
        Sets a false ip so that when we set the real one, the correct
 
386
        routing entry is created.
 
387
        
 
388
        """
 
389
        print 'Setting false IP...'
 
390
        self.SetStatus('resetting_ip_address')
 
391
        iface.SetAddress('0.0.0.0')
 
392
    
 
393
    @abortable
 
394
    def put_iface_down(self, iface):
 
395
        """ Puts the given interface down. """
 
396
        print 'Putting interface down'
 
397
        self.SetStatus('interface_down')
 
398
        iface.Down()
 
399
        
 
400
    @abortable
 
401
    def run_global_scripts_if_needed(self, script_dir, extra_parameters=()):
 
402
        misc.ExecuteScripts(script_dir, verbose=self.debug,
 
403
                            extra_parameters=extra_parameters)
 
404
 
 
405
    @abortable
 
406
    def run_script_if_needed(self, script, msg, bssid='wired', essid='wired'):
 
407
        """ Execute a given script if needed.
 
408
        
 
409
        Keyword arguments:
 
410
        script -- the script to execute, or None/'' if there isn't one.
 
411
        msg -- the name of the script to display in the log.
 
412
        
 
413
        """
 
414
        if script:
 
415
            print 'Executing %s script' % (msg)
 
416
            misc.ExecuteScript(expand_script_macros(script, msg, bssid, essid),
 
417
                               self.debug)
 
418
        
 
419
    @abortable
 
420
    def flush_routes(self, iface):
 
421
        """ Flush the routes for both wired/wireless interfaces. """
 
422
        self.SetStatus('flushing_routing_table')
 
423
        print 'Flushing the routing table...'
 
424
        iface.FlushRoutes()
 
425
        
 
426
    @abortable
 
427
    def set_broadcast_address(self, iface):
 
428
        """ Set the broadcast address for the given interface. """
 
429
        if not self.network.get('broadcast') == None:
 
430
            self.SetStatus('setting_broadcast_address')
 
431
            print 'Setting the broadcast address...' + self.network['broadcast']
 
432
            iface.SetAddress(broadcast=self.network['broadcast'])
 
433
        
 
434
    @abortable
 
435
    def set_ip_address(self, iface):
 
436
        """ Set the IP address for the given interface. 
 
437
        
 
438
        Assigns a static IP if one is requested, otherwise calls DHCP.
 
439
        
 
440
        """
 
441
        if self.network.get('ip'):
 
442
            self.SetStatus('setting_static_ip')
 
443
            print 'Setting static IP : ' + self.network['ip']
 
444
            iface.SetAddress(self.network['ip'], self.network['netmask'])
 
445
            if self.network.get('gateway'):
 
446
                print 'Setting default gateway : ' + self.network['gateway']
 
447
                iface.SetDefaultRoute(self.network['gateway'])
 
448
        else:
 
449
            # Run dhcp...
 
450
            self.SetStatus('running_dhcp')
 
451
            if self.network.get('usedhcphostname') == None:
 
452
                self.network['usedhcphostname'] = False
 
453
            if self.network.get('dhcphostname') == None:
 
454
                self.network['dhcphostname'] = os.uname()[1]
 
455
            if not self.network['usedhcphostname']:
 
456
                hname = os.uname()[1]
 
457
            else:
 
458
                hname = self.network['dhcphostname']
 
459
            print "Running DHCP with hostname",hname
 
460
            dhcp_status = iface.StartDHCP(hname)
 
461
            if dhcp_status in ['no_dhcp_offers', 'dhcp_failed']:
 
462
                if self.connect_result != "aborted":
 
463
                    self.abort_connection(dhcp_status)
 
464
                return
 
465
 
 
466
    @abortable
 
467
    def set_dns_addresses(self, iface):
 
468
        """ Set the DNS address(es).
 
469
 
 
470
        If static DNS servers or global DNS servers are specified, set them.
 
471
        Otherwise do nothing.
 
472
        
 
473
        """
 
474
        if self.network.get('use_global_dns'):
 
475
            iface.SetDNS(misc.Noneify(self.global_dns_1),
 
476
                         misc.Noneify(self.global_dns_2), 
 
477
                         misc.Noneify(self.global_dns_3),
 
478
                         misc.Noneify(self.global_dns_dom),
 
479
                         misc.Noneify(self.global_search_dom))
 
480
        elif self.network.get('use_static_dns') and (self.network.get('dns1') or
 
481
                    self.network.get('dns2') or self.network.get('dns3')):
 
482
            self.SetStatus('setting_static_dns')
 
483
            iface.SetDNS(self.network.get('dns1'),
 
484
                         self.network.get('dns2'),
 
485
                         self.network.get('dns3'),
 
486
                         self.network.get('dns_domain'),
 
487
                         self.network.get('search_domain'))
 
488
 
 
489
    @abortable
 
490
    def release_dhcp_clients(self, iface):
 
491
        """ Release all running dhcp clients. """
 
492
        print "Releasing DHCP leases..."
 
493
        iface.ReleaseDHCP()
 
494
        
 
495
    def connect_aborted(self, reason):
 
496
        """ Sets the thread status to aborted. """
 
497
        if self.abort_reason:
 
498
            reason = self.abort_reason
 
499
        self.connecting_message = reason
 
500
        self.is_aborted = True
 
501
        self.connect_result = reason
 
502
        self.is_connecting = False
 
503
        print 'exiting connection thread'
 
504
        
 
505
    def abort_connection(self, reason=""):
 
506
        """ Schedule a connection abortion for the given reason. """
 
507
        self.abort_reason = reason
 
508
        self.should_die = True
 
509
        
 
510
    def abort_if_needed(self):
 
511
        """ Abort the thread is it has been requested. """
 
512
        self.lock.acquire()
 
513
        try:
 
514
            if self._should_die:
 
515
                self.connect_aborted('aborted')
 
516
                raise SystemExit
 
517
        finally:
 
518
            self.lock.release()
 
519
        
 
520
    @abortable
 
521
    def put_iface_up(self, iface):
 
522
        """ Bring up given interface. """
 
523
        print 'Putting interface up...'
 
524
        self.SetStatus('interface_up')
 
525
        iface.Up()
 
526
        for x in range(0, 5):
 
527
            time.sleep(2)
 
528
            if iface.IsUp():
 
529
                return
 
530
            self.abort_if_needed()
 
531
         
 
532
        # If we get here, the interface never came up
 
533
        print "WARNING: Timed out waiting for interface to come up"
 
534
 
 
535
 
 
536
class Wireless(Controller):
 
537
    """ A wrapper for common wireless interface functions. """
 
538
 
 
539
    def __init__(self, debug=False):
 
540
        """ Initialize the class. """
 
541
        Controller.__init__(self, debug=debug)
 
542
        self._wpa_driver = None
 
543
        self._wireless_interface = None
 
544
        self.wiface = None 
 
545
        self.should_verify_ap = True
 
546
        
 
547
    def set_wireless_iface(self, value):
 
548
        self._wireless_interface = value
 
549
        if self.wiface:
 
550
            self.wiface.SetInterface(value)
 
551
    def get_wireless_iface(self): return self._wireless_interface
 
552
    wireless_interface = property(get_wireless_iface, set_wireless_iface) 
 
553
        
 
554
    def set_wpa_driver(self, value):
 
555
        self._wpa_driver = value
 
556
        if self.wiface:
 
557
            self.SetWPADriver(value)
 
558
    def get_wpa_driver(self): return self._wpa_driver
 
559
    wpa_driver = property(get_wpa_driver, set_wpa_driver)
 
560
    
 
561
    def set_iface(self, value):
 
562
        self.wiface = value
 
563
    def get_iface(self):
 
564
        return self.wiface
 
565
    iface = property(get_iface, set_iface)
 
566
        
 
567
    def LoadBackend(self, backend):
 
568
        """ Load a given backend. 
 
569
 
 
570
        Load up a backend into the backend manager and associate with
 
571
        the networking interface.
 
572
        
 
573
        """
 
574
        Controller.LoadBackend(self, backend)
 
575
        if self._backend:
 
576
            self.wiface = self._backend.WirelessInterface(self.wireless_interface,
 
577
                                                    self.debug, self.wpa_driver)
 
578
 
 
579
    def Scan(self, essid=None):
 
580
        """ Scan for available wireless networks.
 
581
 
 
582
        Keyword arguments:
 
583
        essid -- The essid of a hidden network
 
584
 
 
585
        Returns:
 
586
        A list of available networks sorted by strength.
 
587
 
 
588
        """
 
589
        def comp(x, y):
 
590
            if 'quality' in x:
 
591
                key = 'quality'
 
592
            else:
 
593
                key = 'strength'
 
594
            return cmp(x[key], y[key])
 
595
                
 
596
        if not self.wiface: return []
 
597
        wiface = self.wiface
 
598
 
 
599
        # Prepare the interface for scanning
 
600
        wiface.Up()
 
601
 
 
602
        # If there is a hidden essid then set it now, so that when it is
 
603
        # scanned it will be recognized.
 
604
        essid = misc.Noneify(essid)
 
605
        if essid is not None:
 
606
            print 'Setting hidden essid' + essid
 
607
            wiface.SetEssid(essid)
 
608
            # sleep for a bit; scanning to fast will result in nothing
 
609
            time.sleep(1)
 
610
 
 
611
        aps = wiface.GetNetworks()
 
612
        aps.sort(cmp=comp, reverse=True)
 
613
        
 
614
        return aps
 
615
 
 
616
    def Connect(self, network, debug=False):
 
617
        """ Spawn a connection thread to connect to the network.
 
618
 
 
619
        Keyword arguments:
 
620
        network -- network to connect to
 
621
 
 
622
        """
 
623
        if not self.wiface: return False
 
624
        
 
625
        self.connecting_thread = WirelessConnectThread(network,
 
626
            self.wireless_interface, self.wpa_driver, self.before_script,
 
627
            self.after_script, self.pre_disconnect_script,
 
628
            self.post_disconnect_script, self.global_dns_1,
 
629
            self.global_dns_2, self.global_dns_3, self.global_dns_dom,
 
630
            self.global_search_dom, self.wiface, self.should_verify_ap, debug)
 
631
        self.connecting_thread.setDaemon(True)
 
632
        self.connecting_thread.start()
 
633
        return True
 
634
 
 
635
    def GetSignalStrength(self, iwconfig=""):
 
636
        """ Get signal strength of the current network.
 
637
 
 
638
        Returns:
 
639
        The current signal strength.
 
640
 
 
641
        """
 
642
        return self.wiface.GetSignalStrength(iwconfig)
 
643
 
 
644
    def GetDBMStrength(self, iwconfig=""):
 
645
        """ Get the dBm signal strength of the current network.
 
646
 
 
647
        Returns:
 
648
        The current dBm signal strength.
 
649
 
 
650
        """
 
651
        return self.wiface.GetDBMStrength(iwconfig)
 
652
 
 
653
    def GetCurrentNetwork(self, iwconfig=""):
 
654
        """ Get current network name.
 
655
 
 
656
        Returns:
 
657
        The name of the currently connected network.
 
658
 
 
659
        """
 
660
        return self.wiface.GetCurrentNetwork(iwconfig)
 
661
    
 
662
    def GetBSSID(self):
 
663
        """ Get the BSSID of the current access point. 
 
664
        
 
665
        Returns:
 
666
        The MAC Adress of the active access point as a string, or
 
667
        None the BSSID can't be found.
 
668
        
 
669
        """
 
670
        return self.wiface.GetBSSID()
 
671
 
 
672
    def GetCurrentBitrate(self, iwconfig):
 
673
        """ Get the current bitrate of the interface. 
 
674
        
 
675
        Returns:
 
676
        The bitrate of the active access point as a string, or
 
677
        None the bitrate can't be found.
 
678
        
 
679
        """
 
680
        return self.wiface.GetCurrentBitrate(iwconfig)
 
681
 
 
682
    def GetOperationalMode(self, iwconfig):
 
683
        """ Get the current operational mode of the interface. 
 
684
        
 
685
        Returns:
 
686
        The operational mode of the interface as a string, or
 
687
        None if the operational mode can't be found.
 
688
        
 
689
        """
 
690
        return self.wiface.GetOperationalMode(iwconfig)
 
691
 
 
692
    def GetAvailableAuthMethods(self, iwlistauth):
 
693
        """ Get the available authentication methods for the interface. 
 
694
        
 
695
        Returns:
 
696
        The available authentication methods of the interface as a string, or
 
697
        None if the auth methods can't be found.
 
698
        
 
699
        """
 
700
        return self.wiface.GetAvailableAuthMethods(iwlistauth)
 
701
 
 
702
    def GetIwconfig(self):
 
703
        """ Get the out of iwconfig. """
 
704
        return self.wiface.GetIwconfig()
 
705
    
 
706
    def GetWpaSupplicantDrivers(self):
 
707
        """ Returns all valid wpa_supplicant drivers on the system. """
 
708
        return BACKEND.GetWpaSupplicantDrivers()
 
709
    
 
710
    def StopWPA(self):
 
711
        return self.wiface.StopWPA()
 
712
 
 
713
    def CreateAdHocNetwork(self, essid, channel, ip, enctype, key,
 
714
            enc_used):
 
715
        """ Create an ad-hoc wireless network.
 
716
 
 
717
        Keyword arguments:
 
718
        essid -- essid of the ad-hoc network
 
719
        channel -- channel of the ad-hoc network
 
720
        ip -- ip of the ad-hoc network
 
721
        enctype -- unused
 
722
        key -- key of the ad-hoc network
 
723
        enc_used -- encrytion enabled on ad-hoc network
 
724
 
 
725
        """
 
726
        wiface = self.wiface
 
727
        print 'Creating ad-hoc network'
 
728
        print 'Stopping dhcp client and wpa_supplicant'
 
729
        wiface.ReleaseDHCP()
 
730
        wiface.StopWPA()
 
731
        print 'Putting wireless interface down'
 
732
        wiface.Down()
 
733
        print 'Setting mode, channel, and essid'
 
734
        wiface.SetMode('ad-hoc')
 
735
        wiface.SetChannel(channel)
 
736
        wiface.SetEssid(essid)
 
737
        # Right now it just assumes you're using WEP
 
738
        if enc_used:
 
739
            print 'Setting encryption key'
 
740
            wiface.SetKey(key)
 
741
        print 'Putting interface up'
 
742
        wiface.Up()
 
743
        print 'Setting IP address'
 
744
        wiface.SetAddress(ip, '255.255.255.0')
 
745
 
 
746
    def DetectWirelessInterface(self):
 
747
        """ Detect available wireless interfaces.
 
748
 
 
749
        Returns:
 
750
        The first available wireless interface.
 
751
 
 
752
        """
 
753
        ifaces = BACKEND.GetWirelessInterfaces()
 
754
        if ifaces:
 
755
            return ifaces[0]
 
756
        else:
 
757
            return None
 
758
 
 
759
    def GetKillSwitchStatus(self):
 
760
        """ Get the current status of the Killswitch. 
 
761
        
 
762
        Returns:
 
763
        True if the killswitch is on, False otherwise.
 
764
        
 
765
        """
 
766
        return self.wiface.GetKillSwitchStatus()
 
767
 
 
768
    def Disconnect(self):
 
769
        """ Disconnect the given iface.
 
770
        
 
771
        Executes the disconnect script associated with a given interface,
 
772
        Resets it's IP address, and puts the interface down then up.
 
773
        
 
774
        """
 
775
        if BACKEND.NeedsExternalCalls():
 
776
            iwconfig = self.GetIwconfig()
 
777
        else:
 
778
            iwconfig = None
 
779
        bssid = self.wiface.GetBSSID(iwconfig)
 
780
        essid = self.wiface.GetCurrentNetwork(iwconfig) 
 
781
 
 
782
        Controller.Disconnect(self, 'wireless', essid, bssid)
 
783
        self.StopWPA()
 
784
    
 
785
    def SetWPADriver(self, driver):
 
786
        """ Sets the wpa_supplicant driver associated with the interface. """
 
787
        self.wiface.SetWpaDriver(driver)
 
788
 
 
789
 
 
790
class WirelessConnectThread(ConnectThread):
 
791
    """ A thread class to perform the connection to a wireless network.
 
792
 
 
793
    This thread, when run, will perform the necessary steps to connect
 
794
    to the specified network.
 
795
 
 
796
    """
 
797
 
 
798
    def __init__(self, network, wireless, wpa_driver, before_script,
 
799
                 after_script, pre_disconnect_script, post_disconnect_script,
 
800
                 gdns1, gdns2, gdns3, gdns_dom, gsearch_dom, wiface, 
 
801
                 should_verify_ap, debug=False):
 
802
        """ Initialise the thread with network information.
 
803
 
 
804
        Keyword arguments:
 
805
        network -- the network to connect to
 
806
        wireless -- name of the wireless interface
 
807
        wpa_driver -- type of wireless interface
 
808
        before_script -- script to run before bringing up the interface
 
809
        after_script -- script to run after bringing up the interface
 
810
        pre_disconnect_script -- script to run before disconnection
 
811
        post_disconnect_script -- script to run after disconnection
 
812
        gdns1 -- global DNS server 1
 
813
        gdns2 -- global DNS server 2
 
814
        gdns3 -- global DNS server 3
 
815
 
 
816
        """
 
817
        ConnectThread.__init__(self, network, wireless, before_script, 
 
818
                               after_script, pre_disconnect_script,
 
819
                               post_disconnect_script, gdns1, gdns2,
 
820
                               gdns3, gdns_dom, gsearch_dom, wiface, debug)
 
821
        self.wpa_driver = wpa_driver
 
822
        self.should_verify_ap = should_verify_ap
 
823
 
 
824
 
 
825
    def _connect(self):
 
826
        """ The main function of the connection thread.
 
827
 
 
828
        This function performs the necessary calls to connect to the
 
829
        specified network, using the information provided. The following
 
830
        indicates the steps taken.
 
831
        1. Run pre-connection script.
 
832
        2. Take down the interface and clean up any previous
 
833
           connections.
 
834
        3. Generate a PSK if required and authenticate.
 
835
        4. Associate with the WAP.
 
836
        5. Get/set IP address and DNS servers.
 
837
 
 
838
        """
 
839
        wiface = self.iface
 
840
        self.is_connecting = True
 
841
        
 
842
        # Run pre-connection script.
 
843
        self.run_global_scripts_if_needed(wpath.preconnectscripts,
 
844
                                          extra_parameters=('wireless',
 
845
                                                    self.network['essid'],
 
846
                                                    self.network['bssid'])
 
847
                                         )
 
848
        self.run_script_if_needed(self.before_script, 'pre-connection', 
 
849
                                  self.network['bssid'], self.network['essid'])
 
850
 
 
851
        
 
852
        # Take down interface and clean up previous connections.
 
853
        self.put_iface_down(wiface)
 
854
        self.release_dhcp_clients(wiface)
 
855
        self.reset_ip_addresses(wiface)
 
856
        self.stop_wpa(wiface)
 
857
        self.flush_routes(wiface)
 
858
        wiface.SetMode(self.network['mode'])
 
859
 
 
860
        # Put interface up.
 
861
        self.SetStatus('configuring_interface')
 
862
        self.put_iface_up(wiface)
 
863
        
 
864
        # Generate PSK and authenticate if needed.
 
865
        if self.wpa_driver != 'ralink legacy':
 
866
            self.generate_psk_and_authenticate(wiface)
 
867
            
 
868
        # Associate.
 
869
        wiface.Associate(self.network['essid'], self.network['channel'],
 
870
                         self.network['bssid'])
 
871
 
 
872
        # Authenticate after association for Ralink legacy cards.
 
873
        if self.wpa_driver == 'ralink legacy':
 
874
            if self.network.get('key'):
 
875
                wiface.Authenticate(self.network)
 
876
                
 
877
        # Validate Authentication.
 
878
        if self.network.get('enctype'):
 
879
            self.SetStatus('validating_authentication')
 
880
            if not wiface.ValidateAuthentication(time.time()):
 
881
                print "connect result is %s" % self.connect_result
 
882
                if not self.connect_result or self.connect_result == 'Failed':
 
883
                    self.abort_connection('bad_pass')
 
884
 
 
885
        # Set up gateway, IP address, and DNS servers.
 
886
        self.set_broadcast_address(wiface)
 
887
        self.set_ip_address(wiface)
 
888
        self.set_dns_addresses(wiface)
 
889
        self.verify_association(wiface)
 
890
        
 
891
        # Run post-connection script.
 
892
        self.run_global_scripts_if_needed(wpath.postconnectscripts,
 
893
                                          extra_parameters=('wireless',
 
894
                                                    self.network['essid'],
 
895
                                                    self.network['bssid'])
 
896
                                         )
 
897
        self.run_script_if_needed(self.after_script, 'post-connection', 
 
898
                                  self.network['bssid'], self.network['essid'])
 
899
 
 
900
        self.SetStatus('done')
 
901
        print 'Connecting thread exiting.'
 
902
        if self.debug:
 
903
            print "IP Address is: " + str(wiface.GetIP())
 
904
        self.connect_result = "Success"
 
905
        self.is_connecting = False
 
906
        
 
907
    @abortable
 
908
    def verify_association(self, iface):
 
909
        """ Verify that our association the AP is valid.
 
910
        
 
911
        Try to ping the gateway we have set to see if we're
 
912
        really associated with it.  This is only done if
 
913
        we're using a static IP.
 
914
        
 
915
        """
 
916
        if self.network.get('gateway') and self.should_verify_ap:
 
917
            self.SetStatus('verifying_association')
 
918
            print "Verifying AP association..."
 
919
            for tries in range(1, 11):
 
920
                print "Attempt %d of 10..." % tries
 
921
                retcode = self.iface.VerifyAPAssociation(self.network['gateway'])
 
922
                if retcode == 0: 
 
923
                    print "Successfully associated."
 
924
                    break
 
925
                time.sleep(1)
 
926
            #TODO this should be in wnettools.py
 
927
            if retcode:
 
928
                print "Connection Failed: Failed to ping the access point!"
 
929
                # Clean up before aborting.
 
930
                iface.SetAddress('0.0.0.0')
 
931
                iface.FlushRoutes()
 
932
                if hasattr(iface, "StopWPA"):
 
933
                    iface.StopWPA()
 
934
                self.abort_connection("association_failed")
 
935
        else:
 
936
            print 'not verifying'
 
937
 
 
938
        
 
939
    @abortable
 
940
    def stop_wpa(self, wiface):
 
941
        """ Stops wpa_supplicant. """
 
942
        print 'Stopping wpa_supplicant'
 
943
        wiface.StopWPA()
 
944
        
 
945
    @abortable
 
946
    def generate_psk_and_authenticate(self, wiface):
 
947
        """ Generates a PSK and authenticates if necessary. 
 
948
        
 
949
        Generates a PSK, and starts the authentication process
 
950
        if encryption is on.
 
951
        
 
952
        """
 
953
        # Check to see if we need to generate a PSK (only for non-ralink
 
954
        # cards).
 
955
        if self.debug:
 
956
            print "enctype is %s" % self.network.get('enctype')
 
957
        if self.network.get('key') and 'wpa' in str(self.network.get('enctype')):
 
958
            self.SetStatus('generating_psk')
 
959
            print 'Generating psk...'
 
960
            self.network['psk'] = wiface.GeneratePSK(self.network)
 
961
            
 
962
            if not self.network.get('psk'):
 
963
                self.network['psk'] = self.network['key']
 
964
                print 'WARNING: PSK generation failed!  Falling back to ' + \
 
965
                      'wireless key.\nPlease report this error to the wicd ' + \
 
966
                      'developers!'
 
967
        # Generate the wpa_supplicant file...
 
968
        if self.network.get('enctype'):
 
969
            self.SetStatus('generating_wpa_config')
 
970
            print 'Attempting to authenticate...'
 
971
            wiface.Authenticate(self.network)
 
972
 
 
973
 
 
974
class Wired(Controller):
 
975
    """ A wrapper for common wired interface functions. """
 
976
 
 
977
    def __init__(self, debug=False):
 
978
        """ Initialise the class. """
 
979
        Controller.__init__(self, debug=debug)
 
980
        self.wpa_driver = None
 
981
        self._link_detect = None
 
982
        self._wired_interface = None
 
983
        self.liface = None
 
984
        
 
985
    def set_link_detect(self, value):
 
986
        self._link_detect = value
 
987
        if self.liface:
 
988
            self.liface.link_detect = value
 
989
    def get_link_detect(self): return self._link_detect
 
990
    link_detect = property(get_link_detect, set_link_detect)
 
991
    
 
992
    
 
993
    def set_wired_iface(self, value):
 
994
        self._wired_interface = value
 
995
        if self.liface:
 
996
            self.liface.SetInterface(value)            
 
997
    def get_wired_iface(self): return self._wired_interface
 
998
    wired_interface = property(get_wired_iface, set_wired_iface)
 
999
    
 
1000
    def set_iface(self, value):
 
1001
        self.liface = value
 
1002
    def get_iface(self):
 
1003
        return self.liface
 
1004
    iface = property(get_iface, set_iface)
 
1005
    
 
1006
    def LoadBackend(self, backend):
 
1007
        """ Load the backend up. """
 
1008
        Controller.LoadBackend(self, backend)
 
1009
        if self._backend:
 
1010
            self.liface = self._backend.WiredInterface(self.wired_interface,
 
1011
                                                       self.debug)
 
1012
 
 
1013
    def CheckPluggedIn(self):
 
1014
        """ Check whether the wired connection is plugged in.
 
1015
 
 
1016
        Returns:
 
1017
        The status of the physical connection link.
 
1018
 
 
1019
        """
 
1020
        return self.liface.GetPluggedIn()
 
1021
 
 
1022
    def Connect(self, network, debug=False):
 
1023
        """ Spawn a connection thread to connect to the network.
 
1024
 
 
1025
        Keyword arguments:
 
1026
        network -- network to connect to
 
1027
 
 
1028
        """
 
1029
        if not self.liface: return False
 
1030
        self.connecting_thread = WiredConnectThread(network,
 
1031
            self.wired_interface, self.before_script, self.after_script,
 
1032
            self.pre_disconnect_script, self.post_disconnect_script,
 
1033
            self.global_dns_1, self.global_dns_2, self.global_dns_3,
 
1034
            self.global_dns_dom, self.global_search_dom, self.liface,
 
1035
            debug)
 
1036
        self.connecting_thread.setDaemon(True)
 
1037
        self.connecting_thread.start()
 
1038
        return self.connecting_thread
 
1039
    
 
1040
    def Disconnect(self):
 
1041
        Controller.Disconnect(self, 'wired', 'wired', 'wired')
 
1042
    
 
1043
    def DetectWiredInterface(self):
 
1044
        """ Attempts to automatically detect a wired interface. """
 
1045
        try:
 
1046
            return BACKEND.GetWiredInterfaces()[0]
 
1047
        except IndexError:
 
1048
            return None
 
1049
 
 
1050
 
 
1051
class WiredConnectThread(ConnectThread):
 
1052
    """ A thread class to perform the connection to a wired network.
 
1053
 
 
1054
    This thread, when run, will perform the necessary steps to connect
 
1055
    to the specified network.
 
1056
 
 
1057
    """
 
1058
    def __init__(self, network, wired, before_script, after_script, 
 
1059
                 pre_disconnect_script, post_disconnect_script, gdns1,
 
1060
                 gdns2, gdns3, gdns_dom, gsearch_dom, liface, debug=False):
 
1061
        """ Initialise the thread with network information.
 
1062
 
 
1063
        Keyword arguments:
 
1064
        network -- the network to connect to
 
1065
        wireless -- name of the wireless interface
 
1066
        wired -- name of the wired interface
 
1067
        before_script -- script to run before bringing up the interface
 
1068
        after_script -- script to run after bringing up the interface
 
1069
        pre_disconnect_script -- script to run before disconnection
 
1070
        post_disconnect_script -- script to run after disconnection
 
1071
        gdns1 -- global DNS server 1
 
1072
        gdns2 -- global DNS server 2
 
1073
        gdns3 -- global DNS server 3
 
1074
 
 
1075
        """
 
1076
        ConnectThread.__init__(self, network, wired, before_script, 
 
1077
                               after_script, pre_disconnect_script,
 
1078
                               post_disconnect_script, gdns1, gdns2,
 
1079
                               gdns3, gdns_dom, gsearch_dom, liface,
 
1080
                               debug)
 
1081
 
 
1082
    def _connect(self):
 
1083
        """ The main function of the connection thread.
 
1084
 
 
1085
        This function performs the necessary calls to connect to the
 
1086
        specified network, using the information provided. The following
 
1087
        indicates the steps taken.
 
1088
        1. Run pre-connection script.
 
1089
        2. Take down the interface and clean up any previous
 
1090
           connections.
 
1091
        3. Bring up the interface.
 
1092
        4. Get/set IP address and DNS servers.
 
1093
        5. Run post-connection script.
 
1094
 
 
1095
        """
 
1096
        liface = self.iface
 
1097
 
 
1098
        self.is_connecting = True
 
1099
 
 
1100
        # Run pre-connection script.
 
1101
        self.run_global_scripts_if_needed(wpath.preconnectscripts,
 
1102
                                          extra_parameters=('wired', 'wired',
 
1103
                                                            self.network['profilename'])
 
1104
                                          )
 
1105
        self.run_script_if_needed(self.before_script, 'pre-connection', 'wired', 
 
1106
                                  'wired')
 
1107
 
 
1108
        # Take down interface and clean up previous connections.
 
1109
        self.put_iface_down(liface)
 
1110
        self.release_dhcp_clients(liface)
 
1111
        self.reset_ip_addresses(liface)
 
1112
        self.flush_routes(liface)
 
1113
        
 
1114
        # Bring up interface.
 
1115
        self.put_iface_up(liface)
 
1116
        
 
1117
        # Set gateway, IP adresses, and DNS servers.
 
1118
        self.set_broadcast_address(liface)
 
1119
        self.set_ip_address(liface)
 
1120
        self.set_dns_addresses(liface)
 
1121
        
 
1122
        # Run post-connection script.
 
1123
        self.run_global_scripts_if_needed(wpath.postconnectscripts,
 
1124
                                          extra_parameters=('wired', 'wired',
 
1125
                                                self.network['profilename'])
 
1126
                                         )
 
1127
        self.run_script_if_needed(self.after_script, 'post-connection', 'wired', 
 
1128
                                  'wired')
 
1129
 
 
1130
        self.SetStatus('done')
 
1131
        print 'Connecting thread exiting.'
 
1132
        if self.debug:
 
1133
            print "IP Address is: " + str(liface.GetIP())
 
1134
        
 
1135
        self.connect_result = "Success"
 
1136
        self.is_connecting = False