* Cleanups
[enigma2-plugins.git] / wirelesslan / src / iwlibs.py
1 # -*- coding: latin1 -*-
2 # python-wifi -- a wireless library to access wireless cards via python
3 # Copyright (C) 2004, 2005, 2006 Róman Joost
4
5 # Contributions from:
6 #   Mike Auty <m.auty@softhome.net> (Iwscanresult, Iwscan)
7 #
8 #    This library is free software; you can redistribute it and/or
9 #    modify it under the terms of the GNU Lesser General Public License
10 #    as published by the Free Software Foundation; either version 2.1 of
11 #    the License, or (at your option) any later version.
12 #
13 #    This library is distributed in the hope that it will be useful, but
14 #    WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 #    Lesser General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Lesser General Public
19 #    License along with this library; if not, write to the Free Software
20 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 #    USA 
22
23 import struct
24 import array
25 import math
26 import fcntl
27 import socket
28 import time
29 import re
30
31 from types import StringType
32 from flags import *    
33
34 def getNICnames():
35     """ extract wireless device names of /proc/net/wireless 
36         
37         returns empty list if no devices are present
38
39         >>> getNICnames()
40         ['eth1', 'wifi0']
41     """
42     device = re.compile('[a-z]+[0-9]+')
43     ifnames = []
44     
45     f = open('/proc/net/wireless', 'r')
46     data = f.readlines()
47     for line in data:
48         try:
49             ifnames.append(device.search(line).group())
50         except AttributeError:
51             pass 
52     # if we couldn't lookup the devices, try to ask the kernel
53     if ifnames == []:
54         ifnames = getConfiguredNICnames()
55     
56     return ifnames
57
58 def getConfiguredNICnames():
59     """get the *configured* ifnames by a systemcall
60        
61        >>> getConfiguredNICnames()
62        []
63     """
64     iwstruct = Iwstruct()
65     ifnames = []
66     buff = array.array('c', '\0'*1024)
67     caddr_t, length = buff.buffer_info()
68     s = iwstruct.pack('iP', length, caddr_t)
69     try:
70         result = iwstruct._fcntl(SIOCGIFCONF, s)
71     except IOError, (i, e):
72         return i, e
73    
74     # get the interface names out of the buffer
75     for i in range(0, 1024, 32):
76         ifname = buff.tostring()[i:i+32]
77         ifname = struct.unpack('32s', ifname)[0]
78         ifname = ifname.split('\0', 1)[0]
79         if ifname:
80             # verify if ifnames are really wifi devices
81             wifi = Wireless(ifname)
82             result = wifi.getAPaddr()
83             if result[0] == 0:
84                 ifnames.append(ifname)
85
86     return ifnames  
87
88 def makedict(**kwargs):
89     return kwargs
90
91
92 class Wireless(object):
93     """Access to wireless interfaces"""
94     
95     def __init__(self, ifname):
96         self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
97         self.ifname = ifname
98         self.iwstruct = Iwstruct()
99     
100     def getAPaddr(self):
101         """ returns accesspoint mac address 
102         
103             >>> from iwlibs import Wireless, getNICnames
104             >>> ifnames = getNICnames()
105             >>> ifnames
106             ['eth1', 'wifi0']
107             >>> wifi = Wireless(ifnames[0])
108             >>> wifi.getAPaddr()
109             '00:0D:88:8E:4E:93'
110
111             Test with non-wifi card:
112             >>> wifi = Wireless('eth0')
113             >>> wifi.getAPaddr()
114             (95, 'Operation not supported')
115
116             Test with non-existant card:
117             >>> wifi = Wireless('eth2')
118             >>> wifi.getAPaddr()
119             (19, 'No such device')
120         """
121         buff, s = self.iwstruct.pack_wrq(32)
122         i, result = self.iwstruct.iw_get_ext(self.ifname, 
123                                              SIOCGIWAP,
124                                              data=s)
125         if i > 0:
126             return result
127
128         return self.iwstruct.getMAC(result)
129    
130     def getBitrate(self):
131         """returns device currently set bit rate 
132         
133             >>> from iwlibs import Wireless
134             >>> wifi = Wireless('eth1')
135             >>> wifi.getBitrate()
136             '11 Mb/s'
137         """
138         i, result = self.iwstruct.iw_get_ext(self.ifname, 
139                                             SIOCGIWRATE)
140         if i > 0:
141             return result
142         iwfreq = Iwfreq(result)
143         return iwfreq.getBitrate()
144     
145     def getBitrates(self):
146         """returns the number of available bitrates for the device
147            
148             >>> from iwlibs import Wireless
149             >>> wifi = Wireless('eth1')
150             >>> num, rates = wifi.getBitrates()
151             >>> num == len(rates)
152             True
153         """
154         range = Iwrange(self.ifname)
155         if range.errorflag:
156             return (range.errorflag, range.error)
157         return (range.num_bitrates, range.bitrates)
158
159     def getChannelInfo(self):
160         """returns the number of channels and available frequency for
161            the device
162
163             >>> from iwlibs import Wireless
164             >>> wifi = Wireless('eth1')
165             >>> num, rates = wifi.getChannelInfo()
166             >>> num == len(rates)
167             True
168             """
169         range = Iwrange(self.ifname)
170         if range.errorflag:
171             return (range.errorflag, range.error)
172         return (range.num_channels, range.frequencies)
173
174     def getEssid(self):
175         """get essid information
176             
177             >>> from iwlibs import Wireless
178             >>> wifi = Wireless('eth1')
179             >>> wifi.getEssid()
180             'romanofski'
181         """
182         essid = ""
183         buff, s = self.iwstruct.pack_wrq(32)
184         i, result = self.iwstruct.iw_get_ext(self.ifname, 
185                                              SIOCGIWESSID, 
186                                              data=s)
187         if i > 0:
188             return result
189         str = buff.tostring()
190         return str.strip('\x00')
191
192     def setEssid(self, essid):
193         """set essid """
194         raise NotImplementedError
195         if len(essid) > IW_ESSID_MAX_SIZE:
196             return "essid to big"
197         buff, s = self.iwstruct.pack_test(essid, 32)
198         i, result = self.iwstruct.iw_get_ext(self.ifname, 
199                                              SIOCSIWESSID, 
200                                              data=s)
201         if i > 0:
202             return result
203
204     def getEncryption(self):
205         """get encryption information which is probably a string of '*',
206         'open', 'private'
207             
208             as a normal user, you will get a 'Operation not permitted'
209             error:
210         
211             >>> from iwlibs import Wireless
212             >>> wifi = Wireless('eth1')
213             >>> wifi.getEncryption()
214             (1, 'Operation not permitted')
215         """
216         iwpoint = Iwpoint(self.ifname)
217         if iwpoint.errorflag:
218             return (iwpoint.errorflag, iwpoint.error)
219         return iwpoint.getEncryptionKey()
220
221     def getFragmentation(self):
222         """returns fragmentation threshold 
223            
224            It depends on what the driver says. If you have fragmentation
225            threshold turned on, you'll get an int. If it's turned of
226            you'll get a string: 'off'.
227             >>> from iwlibs import Wireless
228             >>> wifi = Wireless('eth1')
229             >>> wifi.getFragmentation()
230             'off'
231         """
232         iwparam = Iwparam(self.ifname, SIOCGIWFRAG)
233         if iwparam.errorflag:
234             return (iwparam.errorflag, iwparam.error)
235         return iwparam.getValue()
236         
237     def getFrequency(self):
238         """returns currently set frequency of the card 
239             
240             >>> from iwlibs import Wireless
241             >>> wifi = Wireless('eth1')
242             >>> wifi.getFrequency()
243             '2.417GHz' 
244         """
245         i, r = self.iwstruct.iw_get_ext(self.ifname, 
246                                         SIOCGIWFREQ)
247         if i > 0:
248             return (i, r)
249         iwfreq = Iwfreq(r)
250         return iwfreq.getFrequency()
251     
252         
253     def getMode(self):
254         """returns currently set operation mode 
255             
256             >>> from iwlibs import Wireless
257             >>> wifi = Wireless('eth1')
258             >>> wifi.getMode()
259             'Managed' 
260         """
261         i, result = self.iwstruct.iw_get_ext(self.ifname, 
262                                              SIOCGIWMODE)
263         if i > 0:
264             return result
265         mode = self.iwstruct.unpack('i', result[:4])[0]
266         return modes[mode]
267
268     def setMode(self, mode):
269         """sets the operation mode """
270         try:
271             this_modes = [x.lower() for x in modes]
272             mode = mode.lower()
273             wifimode = this_modes.index(mode)
274         except ValueError:
275             return "Invalid operation mode!"
276         
277         s = self.iwstruct.pack('I', wifimode)
278         i, result = self.iwstruct.iw_get_ext(self.ifname, 
279                                              SIOCSIWMODE, 
280                                              data=s)
281         if i > 0:
282             return result
283     
284     def getWirelessName(self):
285         """ returns wireless name 
286             
287             >>> from iwlibs import Wireless
288             >>> wifi = Wireless('eth1')
289             >>> wifi.getWirelessName()
290             'IEEE 802.11-DS'
291         """
292         i, result = self.iwstruct.iw_get_ext(self.ifname, 
293                                              SIOCGIWNAME)
294         if i > 0:
295             return result
296         return result.split('\0')[0]
297     
298     def getPowermanagement(self):
299         """returns power management settings 
300             
301             >>> from iwlibs import Wireless
302             >>> wifi = Wireless('eth1')
303             >>> wifi.getPowermanagement()
304             'off'
305         """
306         iwparam = Iwparam(self.ifname, SIOCGIWPOWER)
307         if iwparam.errorflag:
308             return (iwparam.errorflag, iwparam.error)
309         return iwparam.getValue()
310
311     
312     def getRetrylimit(self):
313         """returns limit retry/lifetime
314
315             man iwconfig:
316             Most cards have MAC retransmissions, and some  allow  to set
317             the behaviour of the retry mechanism.
318                      
319             >>> from iwlibs import Wireless
320             >>> wifi = Wireless('eth1')
321             >>> wifi.getRetrylimit()
322             16
323         """
324         iwparam = Iwparam(self.ifname, SIOCGIWRETRY)
325         if iwparam.errorflag:
326             return (iwparam.errorflag, iwparam.error)
327         return iwparam.getValue()
328     
329     def getRTS(self):
330         """returns rts threshold 
331             
332             returns int, 'auto', 'fixed', 'off'
333         
334             man iwconfig:
335             RTS/CTS adds a handshake before each packet transmission to
336             make sure that the channel is clear. This adds overhead, but
337             increases performance in case of hidden  nodes or  a large
338             number of active nodes. This parameter sets the size of the
339             smallest packet for which the node sends RTS;  a value equal
340             to the maximum packet size disable the mechanism. 
341             
342             >>> from iwlibs import Wireless
343             >>> wifi = Wireless('eth1')
344             >>> wifi.getRTS()
345             'off'
346         """
347         iwparam = Iwparam(self.ifname, SIOCGIWRTS)
348         if iwparam.errorflag:
349             return (iwparam.errorflag, iwparam.error)
350         return iwparam.getValue()
351     
352     def getSensitivity(self):
353         """returns sensitivity information 
354         
355             man iwconfig:
356             This is the lowest signal level for which the hardware
357             attempt  packet  reception, signals  weaker  than  this are
358             ignored. This is used to avoid receiving background noise,
359             so you should  set  it according  to  the  average noise
360             level. Positive values are assumed to be the raw value used
361             by the hardware  or a percentage, negative values are
362             assumed to be dBm.
363         
364             >>> from iwlibs import Wireless
365             >>> wifi = Wireless('eth1')
366             >>> wifi.getSensitivity()
367             'off'
368             
369         """
370         iwparam = Iwparam(self.ifname, SIOCGIWSENS)
371         if iwparam.errorflag:
372             return (iwparam.errorflag, iwparam.error)
373         return iwparam.getValue()
374         
375     def getTXPower(self):
376         """returns transmit power in dBm 
377         
378             >>> from iwlibs import Wireless
379             >>> wifi = Wireless('eth1')
380             >>> wifi.getTXPower()
381             '17 dBm'
382         """
383         i, r = self.iwstruct.iw_get_ext(self.ifname, 
384                                         SIOCGIWTXPOW)
385         if i > 0:
386             return (i, r)
387         iwfreq = Iwfreq(r)
388         return iwfreq.getTransmitPower()
389          
390     def getStatistics(self):
391         """returns statistics information which can also be found in
392            /proc/net/wireless 
393         """
394         iwstats = Iwstats(self.ifname)
395         if iwstats.errorflag > 0:
396             return (iwstats.errorflag, iwstats.error)
397         return [iwstats.status, iwstats.qual, iwstats.discard,
398             iwstats.missed_beacon]
399
400     def scan(self):
401         """returns Iwscanresult objects, after a successful scan"""
402         iwscan = Iwscan(self.ifname)
403         return iwscan.scan()
404
405
406 class Iwstruct(object):
407     """basic class to handle iwstruct data """
408     
409     def __init__(self):
410         self.idx = 0
411         self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
412
413     def parse_data(self, fmt, data):
414         """ unpacks raw C data """
415         size = struct.calcsize(fmt)
416         idx = self.idx
417
418         str = data[idx:idx + size]
419         self.idx = idx+size
420         value = struct.unpack(fmt, str)
421
422         # take care of a tuple like (int, )
423         if len(value) == 1:
424             return value[0]
425         else:
426             return value
427     
428     def pack(self, fmt, *args):
429         """ calls struct.pack and returns the result """
430         return struct.pack(fmt, *args)
431
432     def pack_wrq(self, buffsize):
433         """ packs wireless request data for sending it to the kernel """
434         # Prepare a buffer
435         # We need the address of our buffer and the size for it. The
436         # ioctl itself looks for the pointer to the address in our
437         # memory and the size of it.
438         # Dont change the order how the structure is packed!!!
439         buff = array.array('c', '\0'*buffsize)
440         caddr_t, length = buff.buffer_info()
441         s = struct.pack('Pi', caddr_t, length)
442         return buff, s
443     
444     def pack_test(self, string, buffsize):
445         """ packs wireless request data for sending it to the kernel """
446         buffsize = buffsize - len(string)
447         buff = array.array('c', string+'\0'*buffsize)
448         caddr_t, length = buff.buffer_info()
449         s = struct.pack('Pii', caddr_t, length, 1)
450         return buff, s
451
452     def unpack(self, fmt, packed_data):
453         """ unpacks data with given format """
454         return struct.unpack(fmt, packed_data)
455
456     def _fcntl(self, request, args):
457         return fcntl.ioctl(self.sockfd.fileno(), request, args)
458     
459     def iw_get_ext(self, ifname, request, data=None):
460         """ read information from ifname """
461         # put some additional data behind the interface name
462         if data is not None:
463             buff = IFNAMSIZE-len(ifname)
464             ifreq = ifname + '\0'*buff
465             ifreq = ifreq + data
466         else:
467             ifreq = (ifname + '\0'*32)
468             
469         try:
470             result = self._fcntl(request, ifreq)
471         except IOError, (i, e):
472             return i, e
473         
474         return (0, result[16:])
475
476     def getMAC(self, packed_data):
477         """ extracts mac addr from packed data and returns it as str """
478         mac_addr = struct.unpack('xxBBBBBB', packed_data[:8])
479         return "%02X:%02X:%02X:%02X:%02X:%02X" % mac_addr
480
481 class Iwparam(object):
482     """class to hold iwparam data """
483     
484     def __init__(self, ifname, ioctl):
485         # (i) value, (b) fixed, (b) disabled, (b) flags
486         self.fmt = "ibbH"
487         self.value = 0
488         self.fixed = 0
489         self.disabled = 0
490         self.flags = 0
491         self.errorflag = 0
492         self.error = ""
493         self.ioctl = ioctl 
494         self.ifname = ifname
495         self.update()
496     
497     def getValue(self):
498         """returns the value if not disabled """
499
500         if self.disabled:
501             return 'off'
502         if self.flags & IW_RETRY_TYPE == 0:
503             return self.getRLAttributes()
504         else:
505             return self.getPMAttributes()
506
507     def getRLAttributes(self):
508         """returns a string with attributes determined by self.flags
509         """
510         return self.value
511
512     def getPMAttributes(self):
513         """returns a string with attributes determined by self.flags
514            and IW_POWER*
515         """
516         result = ""
517         
518         # Modifiers
519         if self.flags & IW_POWER_MIN == 0:
520             result = " min"
521         if self.flags & IW_POWER_MAX == 0:
522             result = " max"
523             
524         # Type
525         if self.flags & IW_POWER_TIMEOUT == 0:
526             result = " period:" 
527         else:
528             result = " timeout:"
529         # Value with or without units
530         # IW_POWER_RELATIVE - value is *not* in s/ms/us
531         if self.flags & IW_POWER_RELATIVE:
532             result += "%f" %(float(self.value)/MEGA)
533         else:
534             if self.value >= MEGA:
535                 result += "%fs" %(float(self.value)/MEGA)
536             elif self.value >= KILO:
537                 result += "%fms" %(float(self.value)/KILO)
538             else:
539                 result += "%dus" % self.value
540
541         return result
542         
543     def update(self):
544         iwstruct = Iwstruct()
545         i, r = iwstruct.iw_get_ext(self.ifname, 
546                                    self.ioctl)
547         if i > 0:
548             self.errorflag = i
549             self.error = r
550         self._parse(r)
551     
552     def _parse(self, data):
553         """ unpacks iwparam data """
554         iwstruct = Iwstruct()
555         self.value, self.fixed, self.disabled, self.flags =\
556             iwstruct.parse_data(self.fmt, data)
557         
558 class Iwfreq(object):
559     """ class to hold iwfreq data
560         delegates to Iwstruct class
561     """
562     
563     def __init__(self, data=None):
564         self.fmt = "ihbb"
565         if data is not None:
566             self.frequency = self.parse(data)
567         else:
568             self.frequency = 0
569         self.iwstruct = Iwstruct()
570         
571     def __getattr__(self, attr):
572         return getattr(self.iwstruct, attr)
573
574     def parse(self, data):
575         """ unpacks iwparam"""
576         
577         size = struct.calcsize(self.fmt)
578         m, e, i, pad = struct.unpack(self.fmt, data[:size])
579         # XXX well, its not *the* frequency - we need a better name
580         if e == 0:
581             return m
582         else:
583             return float(m)*10**e
584     
585     def getFrequency(self):
586         """returns Frequency (str) 
587             
588            data - binary data returned by systemcall (iw_get_ext())
589         """
590         freq = self.frequency
591         
592         if freq >= GIGA:
593             return "%0.3fGHz" %(freq/GIGA)
594
595         if freq >= MEGA:
596             return "%0.3fMHZ" %(freq/MEGA)
597
598         if freq >= KILO:
599             return "%0.3fKHz" %(freq/KILO)
600     
601     def getBitrate(self):
602         """ returns Bitrate in Mbit 
603         
604            data - binary data returned by systemcall (iw_get_ext())
605         """
606         bitrate = self.frequency
607
608         if bitrate >= GIGA:
609             return "%i Gb/s" %(bitrate/GIGA)
610
611         if bitrate >= MEGA:
612             return "%i Mb/s" %(bitrate/MEGA)
613         
614         if bitrate >= KILO:
615             return "%i Kb/s" %(bitrate/KILO)
616
617     def getTransmitPower(self):
618         """ returns transmit power in dbm """
619         # XXX something flaky is going on with m and e
620         # eg. m = 50 and e should than be 0, because the number is stored in
621         # m and don't needs to be recalculated
622         return "%i dBm" %self.mw2dbm(self.frequency/10)
623     
624     def getChannel(self, freq):
625         """returns channel information given by frequency
626            
627            returns None if frequency can't be converted
628            freq = frequency to convert (int)
629            iwrange = Iwrange object
630         """
631         
632         try:
633             freq = float(freq)
634         except:
635             return None
636         
637         lut = {}
638         #13 Channels beginning at 2.412GHz and inreasing by 0,005 GHz steps
639         for i in range(0,12):
640             cur = float( 2.412 + ( i * 0.005 ) )
641             lut[str(cur)] = i+1
642         # Channel 14 need special actions ;)
643         lut['2.484'] = 14
644         
645         
646         if str(freq) in lut.keys():
647                 return lut[str(freq)]
648         
649         return None
650     
651           
652     def mw2dbm(self, mwatt):
653         """ converts mw to dbm(float) """
654         return math.ceil(10.0 * math.log10(mwatt))
655         
656     def _setFrequency(self, list):
657         """sets self.frequency by given list 
658            
659            currently only used by Iwrange
660         """
661         assert len(list) == 4
662         m, e, i, pad = list
663         if e == 0:
664             self.frequency = m
665         else:
666             self.frequency = m #float(m)*10**e
667
668 class Iwstats(object):
669     """ class to hold iwstat data """
670
671     def __init__(self, ifname):
672         # (2B) status, 4B iw_quality, 6i iw_discarded
673         self.fmt = "2B4B6i"
674         self.status = 0
675         self.qual = Iwquality()
676         self.discard = {}
677         self.missed_beacon = 0
678         self.ifname = ifname
679         self.errorflag = 0
680         self.error = ""
681         self.update()
682
683     def update(self):
684         iwstruct = Iwstruct()
685         buff, s = iwstruct.pack_wrq(32)
686         i, result = iwstruct.iw_get_ext(self.ifname, 
687                                         SIOCGIWSTATS, 
688                                         data=s)
689         if i > 0:
690             self.error = result
691             self.errorflag = i
692         self._parse(buff.tostring())
693     
694     def _parse(self, data):
695         """ unpacks iwstruct data """
696         struct = Iwstruct()
697         iwqual = Iwquality()
698         iwstats_data = struct.parse_data(self.fmt, data)
699         
700         self.status = iwstats_data[0:2]
701         self.qual.quality, self.qual.sl, self.qual.nl,\
702             self.qual.flags = iwstats_data[2:6]
703         nwid, code, frag, retries, flags = iwstats_data[6:11]
704         self.missed_beacon = iwstats_data[11:12][0]
705         self.discard = makedict(nwid=nwid, code=code,
706             fragment=frag, retries=retries, misc=flags)
707
708 class Iwquality(object):
709     """ class to hold iwquality data """
710
711     def __init__(self):
712         self.quality = 0
713         self.sl = 0
714         self.nl = 0
715         self.updated = 0
716         self.fmt = "4B"
717
718     def parse(self, data):
719         """ unpacks iwquality data """
720         struct = Iwstruct()
721         qual, sl, nl, flags = struct.parse_data(self.fmt, data)
722
723         # compute signal and noise level
724         self.signal_level = sl
725         self.noise_level = nl
726
727         # asign the other values
728         self.quality = qual
729         self.updated = flags
730
731     def setValues(self, list):
732         """ assigns values given by a list to our attributes """
733         attributes = ["quality", "signallevel", "noise_level",
734             "updated"]
735         assert len(list) == 4
736         
737         for i in range(len(list)):
738             setattr(self, attributes[i], list[i])
739     
740     def getSignallevel(self):
741         """ returns signal level """
742         return self.sl-0x100
743
744     def setSignallevel(self, sl):
745         """ sets signal level """
746         self.sl = sl
747     signallevel = property(getSignallevel, setSignallevel)
748     
749     def getNoiselevel(self):
750         """ returns noise level """
751         return self.nl - 0x100
752
753     def setNoiselevel(self):
754         raise NotImplementedError
755         self.nl = nl
756     noiselevel = property(getNoiselevel, setNoiselevel)
757
758 class Iwpoint(object):
759     """ class to hold iwpoint data """
760
761     def __init__(self, ifname):
762         self.key = [0,0,0,0]
763         self.fields = 0
764         self.flags = 0
765         # (4B) pointer to data, H length, H flags
766         self.fmt = "4BHH"
767         self.errorflag = 0
768         self.error = ""
769         self.ifname = ifname
770         self.update()
771
772     def __getattr__(self, attr):
773         return getattr(self.iwstruct, attr)
774     
775     def update(self):
776         iwstruct = Iwstruct()
777         buff, s = iwstruct.pack_wrq(32)
778         i, result = iwstruct.iw_get_ext(self.ifname, 
779                                         SIOCGIWENCODE, 
780                                         data=s)
781         if i > 0:
782             self.errorflag = i
783             self.error = result
784         self._parse(result)
785         
786     def getEncryptionKey(self):
787         """ returns encryption key as '**' or 'off' as str """
788         if self.flags & IW_ENCODE_DISABLED != 0:
789             return 'off'
790         elif self.flags & IW_ENCODE_NOKEY != 0:
791             # a key is set, so print it
792             return '**' * self.fields
793     
794     def _parse(self, data):
795         """ unpacks iwpoint data
796         """
797         iwstruct = Iwstruct()
798         ptr, ptr, ptr, ptr, self.fields, self.flags =\
799             iwstruct.parse_data(self.fmt, data)
800         self.key = [ptr, ptr, ptr, ptr]
801
802 class Iwrange(object):
803     """holds iwrange struct """
804     IW_MAX_FREQUENCIES = 32
805
806     def __init__(self, ifname):
807         self.fmt = "iiihb6ii4B4Bi32i2i2i2i2i3h8h2b2bhi8i2b3h2i2ihB17x"\
808             + self.IW_MAX_FREQUENCIES*"ihbb"
809         
810         self.ifname = ifname
811         self.errorflag = 0
812         self.error = ""
813         
814         # informative stuff
815         self.throughput = 0
816         
817         # nwid (or domain id)
818         self.min_nwid = self.max_nwid = 0
819         
820         # frequency for backward compatibility
821         self.old_num_channels = self.old_num_frequency = self.old_freq = 0
822         
823         # signal level threshold
824         self.sensitivity = 0
825         
826         # link quality
827         self.max_qual = Iwquality()
828         self.avg_qual = Iwquality()
829
830         # rates
831         self.num_bitrates = 0
832         self.bitrates = []
833
834         # rts threshold
835         self.min_rts = self.max_rts = 0
836
837         # fragmention threshold
838         self.min_frag = self.max_frag = 0
839
840         # power managment
841         self.min_pmp = self.max_pmp = 0
842         self.min_pmt = self.max_pmt = 0
843         self.pmp_flags = self.pmt_flags = self.pm_capa = 0
844
845         # encoder stuff
846         self.encoding_size = 0
847         self.num_encoding_sizes = self.max_encoding_tokens = 0
848         self.encoding_login_index = 0
849
850         # transmit power
851         self.txpower_capa = self.num_txpower = self.txpower = 0
852
853         # wireless extension version info
854         self.we_vers_compiled = self.we_vers_src = 0
855
856         # retry limits and lifetime
857         self.retry_capa = self.retry_flags = self.r_time_flags = 0
858         self.min_retry = self.max_retry = 0
859         self.min_r_time = self.max_r_time = 0
860
861         # frequency
862         self.num_channels = self.num_frequency = 0
863         self.frequencies = []
864         self.update()
865     
866     def update(self):
867         """updates Iwrange object by a system call to the kernel 
868            and updates internal attributes
869         """
870         iwstruct = Iwstruct()
871         buff, s = iwstruct.pack_wrq(640)
872         i, result = iwstruct.iw_get_ext(self.ifname, 
873                                         SIOCGIWRANGE, 
874                                         data=s)
875         if i > 0:
876             self.errorflag = i
877             self.error = result
878         data = buff.tostring()
879         self._parse(data)
880         
881     def _parse(self, data):
882         struct = Iwstruct()
883         result = struct.parse_data(self.fmt, data)
884         
885         # XXX there is maybe a much more elegant way to do this
886         self.throughput, self.min_nwid, self.max_nwid = result[0:3]
887         self.old_num_channels, self.old_num_frequency = result[3:5]
888         self.old_freq = result[5:11]
889         self.sensitivity = result[11]
890         self.max_qual.setValues(result[12:16])
891         self.avg_qual.setValues(result[16:20])
892         self.num_bitrates = result[20] # <- XXX
893         raw_bitrates = result[21:53]
894         for rate in raw_bitrates:
895             iwfreq = Iwfreq()
896             iwfreq.frequency = rate
897             br = iwfreq.getBitrate()
898             if br is not None:
899                 self.bitrates.append(br)
900             
901         self.min_rts, self.max_rts = result[53:55]
902         self.min_frag, self.max_frag = result[55:57]
903         self.min_pmp, self.max_pmp = result[57:59]
904         self.min_pmt, self.max_pmt = result[59:61]
905         self.pmp_flags, self.pmt_flags, self.pm_capa = result[61:64]
906         self.encoding_size = result[64:72]
907         self.num_encoding_sizes, self.max_encoding_tokens = result[72:74]
908         self.encoding_login_index = result[74:76]
909         self.txpower_capa, self.num_txpower = result[76:78]
910         self.txpower = result[78:86]
911         self.we_vers_compiled, self.we_vers_src = result[86:88]
912         self.retry_capa, self.retry_flags, self.r_time_flags = result[88:91]
913         self.min_retry, self.max_retry = result[91:93]
914         self.min_r_time, self.max_r_time = result[93:95]
915         self.num_channels = result[95]
916         self.num_frequency = result[96]
917         freq = result[97:]
918         
919         i = self.num_frequency
920         for x in range(0, len(freq), 4):
921             iwfreq = Iwfreq()
922             iwfreq._setFrequency(freq[x:x+4])
923             fq = iwfreq.getFrequency()
924             if fq is not None:
925                 self.frequencies.append(fq)
926             i -= 1
927             if i <= 0:
928                 break
929         
930 class Iwscan(object):
931     """class to handle AP scanning"""
932     
933     def __init__(self, ifname):
934         self.ifname = ifname
935         self.range = Iwrange(ifname)
936         self.errorflag = 0
937         self.error = ""
938         self.stream = None
939         self.aplist = None
940                 
941     def scan(self, fullscan=True):
942         """Completes a scan for available access points,
943            and returns them in Iwscanresult format
944            
945            fullscan: If False, data is read from a cache of the last scan
946                      If True, a scan is conducted, and then the data is read
947         """
948         # By default everything is fine, do not wait
949         result = 1
950         if fullscan:
951             self.setScan()
952             if self.errorflag > EPERM:
953                 raise RuntimeError, 'setScan failure ' + str(self.errorflag) + " " + str(self.error)
954                 return None
955             elif self.errorflag < EPERM:
956                 # Permission was NOT denied, therefore we must WAIT to get results
957                 result = 250
958         
959         while (result > 0):
960             time.sleep(result/1000)
961             result = self.getScan()
962         
963         if result < 0 or self.errorflag != 0:
964             raise RuntimeError, 'getScan failure ' + str(self.errorflag) + " " + str(self.error)
965         
966         return self.aplist
967         
968         
969     def setScan(self):
970         """Triggers the scan, if we have permission
971         """
972         iwstruct = Iwstruct()
973         s = iwstruct.pack('Pii', 0, 0, 0)
974         i, result = iwstruct.iw_get_ext(self.ifname, 
975                                         SIOCSIWSCAN,s)
976         if i > 0:
977             self.errorflag = i
978             self.error = result
979         return result
980         
981     def getScan(self):
982         """Retreives results, stored from the most recent scan
983            Returns 0 if successful, a delay if the data isn't ready yet
984            or -1 if something really nasty happened
985         """
986         iwstruct = Iwstruct()
987         i = E2BIG
988         bufflen = IW_SCAN_MAX_DATA
989         
990         # Keep resizing the buffer until it's large enough to hold the scan
991         while (i == E2BIG):
992             buff, s = iwstruct.pack_wrq(bufflen)
993             i, result = iwstruct.iw_get_ext(self.ifname, 
994                                             SIOCGIWSCAN,
995                                             data=s)
996             if i == E2BIG:
997                 pbuff, newlen = iwstruct.unpack('Pi', s)
998                 if bufflen < newlen:
999                     bufflen = newlen
1000                 else:
1001                     bufflen = bufflen * 2
1002         
1003         if i == EAGAIN:
1004             return 100
1005         if i > 0:
1006             self.errorflag = i
1007             self.error = result
1008             return -1
1009         
1010         pbuff, reslen = iwstruct.unpack('Pi', s)
1011         if reslen > 0:
1012             # Initialize the stream, and turn it into an enumerator
1013             self.aplist = self._parse(buff.tostring())
1014             return 0
1015         
1016     def _parse(self, data):
1017         """Parse the event stream, and return a list of Iwscanresult objects
1018         """
1019         iwstruct = Iwstruct()
1020         scanresult = None
1021         aplist = []
1022
1023         # Run through the stream, until broken
1024         while 1:
1025             # If we're the stream doesn't have enough space left for a header, break
1026             if len(data) < IW_EV_LCP_LEN:
1027                 break;
1028         
1029             # Unpack the header
1030             length, cmd = iwstruct.unpack('HH', data[:4])
1031             # If the header says the following data is shorter than the header, then break
1032             if length < IW_EV_LCP_LEN:
1033                 break;
1034
1035             # Put the events into their respective result data
1036             if cmd == SIOCGIWAP:
1037                 if scanresult is not None:
1038                     aplist.append(scanresult)
1039                 scanresult = Iwscanresult(data[IW_EV_LCP_LEN:length], self.range)
1040             elif scanresult is None:
1041                 raise RuntimeError, 'Attempting to add an event without AP data'
1042             else:
1043                 scanresult.addEvent(cmd, data[IW_EV_LCP_LEN:length])
1044             
1045             # We're finished with the preveious event
1046             data = data[length:]
1047         
1048         # Don't forgset the final result
1049         if scanresult.bssid != "00:00:00:00:00:00":
1050             aplist.append(scanresult)
1051         else:
1052             raise RuntimeError, 'Attempting to add an AP without a bssid'
1053         return aplist
1054
1055 class Iwscanresult(object):
1056     """An object to contain all the events associated with a single scanned AP
1057     """
1058     
1059     def __init__(self, data, range):
1060         """Initialize the scan result with the access point data"""
1061         self.iwstruct = Iwstruct()
1062         self.range = range
1063         self.bssid = "%02X:%02X:%02X:%02X:%02X:%02X" % struct.unpack('BBBBBB', data[2:8])
1064         self.essid = None
1065         self.mode = None
1066         self.rate = []
1067         self.quality = Iwquality() 
1068         self.frequency = None
1069         self.encode = None
1070         self.custom = []
1071         self.protocol = None
1072
1073     def addEvent(self, cmd, data):
1074         """Attempts to add the data from an event to a scanresult
1075            Only certain data is accept, in which case the result is True
1076            If the event data is invalid, None is returned
1077            If the data is valid but unused, False is returned
1078         """
1079         if cmd <= SIOCIWLAST:
1080             if cmd < SIOCIWFIRST:
1081                 return None
1082         elif cmd >= IWEVFIRST:
1083             if cmd > IWEVLAST:
1084                 return None
1085         else:
1086             return None
1087             
1088         if cmd == SIOCGIWESSID:
1089             self.essid = data[4:]
1090         elif cmd == SIOCGIWMODE:
1091             self.mode = modes[self.iwstruct.unpack('i', data[:4])[0]]
1092         elif cmd == SIOCGIWRATE:
1093             # TODO, deal with multiple rates, or at least the highest rate
1094             freqsize = struct.calcsize("ihbb")
1095             while len(data) >= freqsize:
1096                 iwfreq = Iwfreq(data)
1097                 self.rate.append(iwfreq.getBitrate())
1098                 data = data[freqsize:]
1099         elif cmd == IWEVQUAL:
1100             self.quality.parse(data)
1101         elif cmd == SIOCGIWFREQ:
1102             self.frequency = Iwfreq(data)
1103         elif cmd == SIOCGIWENCODE:
1104             self.encode = data
1105         elif cmd == IWEVCUSTOM:
1106             self.custom.append(data[1:])
1107         elif cmd == SIOCGIWNAME:
1108             self.protocol = data[:len(data)-2]
1109         else:
1110             #print "Cmd:", cmd
1111             return False
1112         return True