SystemPlugins/WirelessLan: improve wireless device/module detection.
[enigma2.git] / lib / python / Plugins / SystemPlugins / WirelessLan / plugin.py
1 from enigma import eTimer, eTPM, eEnv
2 from Screens.Screen import Screen
3 from Components.ActionMap import ActionMap, NumberActionMap
4 from Components.Pixmap import Pixmap,MultiPixmap
5 from Components.Label import Label
6 from Components.Sources.StaticText import StaticText
7 from Components.Sources.List import List
8 from Components.MenuList import MenuList
9 from Components.config import config, getConfigListEntry, ConfigYesNo, NoSave, ConfigSubsection, ConfigText, ConfigSelection, ConfigPassword
10 from Components.ConfigList import ConfigListScreen
11 from Components.Network import iNetwork
12 from Components.Console import Console
13 from Plugins.Plugin import PluginDescriptor
14 from os import system, path as os_path, listdir
15 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
16 from Tools.LoadPixmap import LoadPixmap
17 from Tools.HardwareInfo import HardwareInfo
18 from Wlan import Wlan, wpaSupplicant, iStatus
19 import sha
20 from time import time
21 from os import urandom
22
23 plugin_path = eEnv.resolve("${libdir}/enigma2/python/Plugins/SystemPlugins/WirelessLan")
24
25 list = []
26 list.append("WEP")
27 list.append("WPA")
28 list.append("WPA2")
29 list.append("WPA/WPA2")
30
31 weplist = []
32 weplist.append("ASCII")
33 weplist.append("HEX")
34
35 config.plugins.wlan = ConfigSubsection()
36 config.plugins.wlan.essid = NoSave(ConfigText(default = "home", fixed_size = False))
37 config.plugins.wlan.hiddenessid = NoSave(ConfigText(default = "home", fixed_size = False))
38
39 config.plugins.wlan.encryption = ConfigSubsection()
40 config.plugins.wlan.encryption.enabled = NoSave(ConfigYesNo(default = False))
41 config.plugins.wlan.encryption.type = NoSave(ConfigSelection(list, default = "WPA/WPA2" ))
42 config.plugins.wlan.encryption.wepkeytype = NoSave(ConfigSelection(weplist, default = "ASCII"))
43 config.plugins.wlan.encryption.psk = NoSave(ConfigPassword(default = "mysecurewlan", fixed_size = False))
44
45
46 class WlanStatus(Screen):
47         skin = """
48                 <screen name="WlanStatus" position="center,center" size="560,400" title="Wireless Network State" >
49                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
50                         <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
51         
52                         <widget source="LabelBSSID" render="Label" position="10,60" size="250,25" valign="left" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
53                         <widget source="LabelESSID" render="Label" position="10,100" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
54                         <widget source="LabelQuality" render="Label" position="10,140" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
55                         <widget source="LabelSignal" render="Label" position="10,180" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
56                         <widget source="LabelBitrate" render="Label" position="10,220" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
57                         <widget source="LabelEnc" render="Label" position="10,260" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
58                         
59                         <widget source="BSSID" render="Label" position="320,60" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
60                         <widget source="ESSID" render="Label" position="320,100" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
61                         <widget source="quality" render="Label" position="320,140" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
62                         <widget source="signal" render="Label" position="320,180" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
63                         <widget source="bitrate" render="Label" position="320,220" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
64                         <widget source="enc" render="Label" position="320,260" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" />
65         
66                         <ePixmap pixmap="skin_default/div-h.png" position="0,350" zPosition="1" size="560,2" />         
67                         <widget source="IFtext" render="Label" position="10,355" size="120,21" zPosition="10" font="Regular;20" halign="left" backgroundColor="#25062748" transparent="1" />
68                         <widget source="IF" render="Label" position="120,355" size="400,21" zPosition="10" font="Regular;20" halign="left" backgroundColor="#25062748" transparent="1" />
69                         <widget source="Statustext" render="Label" position="10,375" size="115,21" zPosition="10" font="Regular;20" halign="left" backgroundColor="#25062748" transparent="1"/>
70                         <widget name="statuspic" pixmaps="skin_default/buttons/button_green.png,skin_default/buttons/button_green_off.png" position="130,380" zPosition="10" size="15,16" transparent="1" alphatest="on"/>
71                 </screen>"""
72         
73         def __init__(self, session, iface):
74                 Screen.__init__(self, session)
75                 self.session = session
76                 self.iface = iface
77                                     
78                 self["LabelBSSID"] = StaticText(_('Accesspoint:'))
79                 self["LabelESSID"] = StaticText(_('SSID:'))
80                 self["LabelQuality"] = StaticText(_('Link Quality:'))
81                 self["LabelSignal"] = StaticText(_('Signal Strength:'))
82                 self["LabelBitrate"] = StaticText(_('Bitrate:'))
83                 self["LabelEnc"] = StaticText(_('Encryption:'))
84                         
85                 self["BSSID"] = StaticText()
86                 self["ESSID"] = StaticText()
87                 self["quality"] = StaticText()
88                 self["signal"] = StaticText()
89                 self["bitrate"] = StaticText()
90                 self["enc"] = StaticText()
91
92                 self["IFtext"] = StaticText()
93                 self["IF"] = StaticText()
94                 self["Statustext"] = StaticText()
95                 self["statuspic"] = MultiPixmap()
96                 self["statuspic"].hide()
97                 self["key_red"] = StaticText(_("Close"))
98
99                 self.resetList()
100                 self.updateStatusbar()
101                 
102                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "EPGSelectActions", "ShortcutActions"],
103                 {
104                         "ok": self.exit,
105                         "back": self.exit,
106                         "red": self.exit,
107                 }, -1)
108                 self.timer = eTimer()
109                 self.timer.timeout.get().append(self.resetList) 
110                 self.onShown.append(lambda: self.timer.start(5000))
111                 self.onLayoutFinish.append(self.layoutFinished)
112                 self.onClose.append(self.cleanup)
113
114         def cleanup(self):
115                 iStatus.stopWlanConsole()
116                 
117         def layoutFinished(self):
118                 self.setTitle(_("Wireless Network State"))
119                 
120         def resetList(self):
121                 iStatus.getDataForInterface(self.iface,self.getInfoCB)
122                 
123         def getInfoCB(self,data,status):
124                 if data is not None:
125                         if data is True:
126                                 if status is not None:
127                                         self["BSSID"].setText(status[self.iface]["acesspoint"])
128                                         self["ESSID"].setText(status[self.iface]["essid"])
129                                         self["quality"].setText(status[self.iface]["quality"])
130                                         self["signal"].setText(status[self.iface]["signal"])
131                                         self["bitrate"].setText(status[self.iface]["bitrate"])
132                                         self["enc"].setText(status[self.iface]["encryption"])
133                                         self.updateStatusLink(status)
134
135         def exit(self):
136                 self.timer.stop()
137                 self.close(True)        
138
139         def updateStatusbar(self):
140                 self["BSSID"].setText(_("Please wait..."))
141                 self["ESSID"].setText(_("Please wait..."))
142                 self["quality"].setText(_("Please wait..."))
143                 self["signal"].setText(_("Please wait..."))
144                 self["bitrate"].setText(_("Please wait..."))
145                 self["enc"].setText(_("Please wait..."))
146                 self["IFtext"].setText(_("Network:"))
147                 self["IF"].setText(iNetwork.getFriendlyAdapterName(self.iface))
148                 self["Statustext"].setText(_("Link:"))
149
150         def updateStatusLink(self,status):
151                 if status is not None:
152                         if status[self.iface]["acesspoint"] == "No Connection" or status[self.iface]["acesspoint"] == "Not-Associated" or status[self.iface]["acesspoint"] == False:
153                                 self["statuspic"].setPixmapNum(1)
154                         else:
155                                 self["statuspic"].setPixmapNum(0)
156                         self["statuspic"].show()                
157
158
159 class WlanScan(Screen):
160         skin = """
161                 <screen name="WlanScan" position="center,center" size="560,400" title="Choose a Wireless Network" >
162                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
163                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
164                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" />
165                         <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
166                         <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
167                         <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" />
168                         <widget source="list" render="Listbox" position="5,40" size="550,300" scrollbarMode="showOnDemand">
169                                 <convert type="TemplatedMultiContent">
170                                         {"template": [
171                                                         MultiContentEntryText(pos = (0, 0), size = (550, 30), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the essid
172                                                         MultiContentEntryText(pos = (0, 30), size = (175, 20), font=1, flags = RT_HALIGN_LEFT, text = 5), # index 5 is the interface
173                                                         MultiContentEntryText(pos = (175, 30), size = (175, 20), font=1, flags = RT_HALIGN_LEFT, text = 4), # index 0 is the encryption
174                                                         MultiContentEntryText(pos = (350, 0), size = (200, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 0 is the signal
175                                                         MultiContentEntryText(pos = (350, 30), size = (200, 20), font=1, flags = RT_HALIGN_LEFT, text = 3), # index 0 is the maxrate
176                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 52), size = (550, 2), png = 6), # index 6 is the div pixmap
177                                                 ],
178                                         "fonts": [gFont("Regular", 28),gFont("Regular", 18)],
179                                         "itemHeight": 54
180                                         }
181                                 </convert>
182                         </widget>
183                         <ePixmap pixmap="skin_default/div-h.png" position="0,340" zPosition="1" size="560,2" />         
184                         <widget source="info" render="Label" position="0,350" size="560,50" font="Regular;24" halign="center" valign="center" backgroundColor="#25062748" transparent="1" />
185                 </screen>"""
186
187         def __init__(self, session, iface):
188                 Screen.__init__(self, session)
189                 self.session = session
190                 self.iface = iface
191                 self.skin_path = plugin_path
192                 self.oldInterfaceState = iNetwork.getAdapterAttribute(self.iface, "up")
193                 self.APList = None
194                 self.newAPList = None
195                 self.WlanList = None
196                 self.cleanList = None
197                 self.oldlist = {}
198                 self.listLength = None
199                 self.rescanTimer = eTimer()
200                 self.rescanTimer.callback.append(self.rescanTimerFired)
201                 
202                 self["info"] = StaticText()
203                 
204                 self.list = []
205                 self["list"] = List(self.list)
206                 
207                 self["key_red"] = StaticText(_("Close"))
208                 self["key_green"] = StaticText(_("Connect"))
209                 self["key_yellow"] = StaticText()
210                         
211                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "EPGSelectActions"],
212                 {
213                         "ok": self.select,
214                         "back": self.cancel,
215                 }, -1)
216                 
217                 self["shortcuts"] = ActionMap(["ShortcutActions"],
218                 {
219                         "red": self.cancel,
220                         "green": self.select,
221                 })
222                 self.onLayoutFinish.append(self.layoutFinished)
223                 self.getAccessPoints(refresh = False)
224                 
225         def layoutFinished(self):
226                 self.setTitle(_("Choose a wireless network"))
227         
228         def select(self):
229                 cur = self["list"].getCurrent()
230                 if cur is not None:
231                         self.rescanTimer.stop()
232                         del self.rescanTimer
233                         if cur[1] is not None:
234                                 if cur[1] == 'hidden...':
235                                         essid = cur[1]
236                                 else:
237                                         essid = cur[0]
238                                 self.close(essid,self.getWlanList())
239                         else:
240                                 self.close(None,None)
241                 else:
242                         self.rescanTimer.stop()
243                         del self.rescanTimer
244                         self.close(None,None)
245         
246         def WlanSetupClosed(self, *ret):
247                 if ret[0] == 2:
248                         self.rescanTimer.stop()
249                         del self.rescanTimer
250                         self.close(None)
251         
252         def cancel(self):
253                 if self.oldInterfaceState is False:
254                         iNetwork.setAdapterAttribute(self.iface, "up", False)
255                         iNetwork.deactivateInterface(self.iface,self.deactivateInterfaceCB)
256                 else:
257                         self.rescanTimer.stop()
258                         del self.rescanTimer
259                         self.close(None)
260
261         def deactivateInterfaceCB(self,data):
262                 if data is not None:
263                         if data is True:
264                                 self.rescanTimer.stop()
265                                 del self.rescanTimer
266                                 self.close(None)
267
268         def rescanTimerFired(self):
269                 self.rescanTimer.stop()
270                 self.updateAPList()
271
272         def buildEntryComponent(self, essid, bssid, encrypted, iface, maxrate, signal):
273                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
274                 encryption = encrypted and _("Yes") or _("No")
275                 if bssid == 'hidden...':
276                         return((essid, bssid, None, None, None, None, divpng))
277                 else:                                   
278                         return((essid, bssid, _("Signal: ") + str(signal), _("Max. Bitrate: ") + str(maxrate), _("Encrypted: ") + encryption, _("Interface: ") + str(iface), divpng))
279
280         def updateAPList(self):
281                 newList = []
282                 newList = self.getAccessPoints(refresh = True)  
283                 self.newAPList = []
284                 tmpList = []
285                 newListIndex = None
286                 currentListEntry = None
287                 currentListIndex = None
288
289                 for ap in self.oldlist.keys():
290                         data = self.oldlist[ap]['data']
291                         if data is not None:
292                                 tmpList.append(data)
293
294                 if len(tmpList):
295                         if "hidden..." not in tmpList:
296                                 tmpList.append( ( _("enter hidden network SSID"), "hidden...", True, self.iface, _("unavailable"), "" ) )
297         
298                         for entry in tmpList:
299                                 self.newAPList.append(self.buildEntryComponent( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ))
300         
301                         currentListEntry = self["list"].getCurrent()
302                         idx = 0
303                         for entry in self.newAPList:
304                                 if entry[0] == currentListEntry[0]:
305                                         newListIndex = idx
306                                 idx +=1
307                         self['list'].setList(self.newAPList)
308                         self["list"].setIndex(newListIndex)
309                         self["list"].updateList(self.newAPList)
310                         self.listLength = len(self.newAPList)
311                         self.buildWlanList()
312                         self.setInfo()
313
314         def getAccessPoints(self, refresh = False):
315                 self.APList = []
316                 self.cleanList = []
317                 self.w = Wlan(self.iface)
318                 aps = self.w.getNetworkList()
319                 if aps is not None:
320                         print "[WirelessLan.py] got Accespoints!"
321                         tmpList = []
322                         compList = []
323                         for ap in aps:
324                                 a = aps[ap]
325                                 if a['active']:
326                                         tmpList.append( (a['essid'], a['bssid']) )
327                                         compList.append( (a['essid'], a['bssid'], a['encrypted'], a['iface'], a['maxrate'], a['signal']) )
328
329                         for entry in tmpList:
330                                 if entry[0] == "":
331                                         for compentry in compList:
332                                                 if compentry[1] == entry[1]:
333                                                         compList.remove(compentry)
334                         for entry in compList:
335                                 self.cleanList.append( ( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ) )
336                                 if not self.oldlist.has_key(entry[0]):
337                                         self.oldlist[entry[0]] = { 'data': entry }
338                                 else:
339                                         self.oldlist[entry[0]]['data'] = entry
340                 
341                 if "hidden..." not in self.cleanList:
342                         self.cleanList.append( ( _("enter hidden network SSID"), "hidden...", True, self.iface, _("unavailable"), "" ) )
343
344                 for entry in self.cleanList:
345                         self.APList.append(self.buildEntryComponent( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ))
346                 
347                 if refresh is False:
348                         self['list'].setList(self.APList)
349                 self.listLength = len(self.APList)
350                 self.setInfo()
351                 self.rescanTimer.start(5000)
352                 return self.cleanList
353
354         def setInfo(self):
355                 length = self.getLength()
356                 if length <= 1:
357                         self["info"].setText(_("No wireless networks found! Please refresh."))
358                 elif length == 2:
359                         self["info"].setText(_("1 wireless network found!"))
360                 else:
361                         self["info"].setText(str(length-1)+_(" wireless networks found!"))
362
363         def buildWlanList(self):
364                 self.WlanList = []
365                 for entry in self['list'].list:
366                         if entry[1] == "hidden...":
367                                 self.WlanList.append(( "hidden...",_("enter hidden network SSID") ))#continue
368                         else:
369                                 self.WlanList.append( (entry[0], entry[0]) )
370
371         def getLength(self):
372                 return self.listLength          
373
374         def getWlanList(self):
375                 if self.WlanList is None:
376                         self.buildWlanList()
377                 return self.WlanList
378
379
380 def bin2long(s):
381         return reduce( lambda x,y:(x<<8L)+y, map(ord, s))
382
383 def long2bin(l):
384         res = ""
385         for byte in range(128):
386                 res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff)
387         return res
388
389 def rsa_pub1024(src, mod):
390         return long2bin(pow(bin2long(src), 65537, bin2long(mod)))
391         
392 def decrypt_block(src, mod):
393         if len(src) != 128 and len(src) != 202:
394                 return None
395         dest = rsa_pub1024(src[:128], mod)
396         hash = sha.new(dest[1:107])
397         if len(src) == 202:
398                 hash.update(src[131:192])       
399         result = hash.digest()
400         if result == dest[107:127]:
401                 return dest
402         return None
403
404 def validate_certificate(cert, key):
405         buf = decrypt_block(cert[8:], key) 
406         if buf is None:
407                 return None
408         return buf[36:107] + cert[139:196]
409
410 def get_random():
411         try:
412                 xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100))
413                 random = urandom(8)
414                 x = str(time())[-8:]
415                 result = xor(random, x)
416                                 
417                 return result
418         except:
419                 return None
420
421 def WlanStatusScreenMain(session, iface):
422         session.open(WlanStatus, iface)
423
424 def callFunction(iface):
425         w = Wlan(iface)
426         i = w.getWirelessInterfaces()
427         if i:
428                 if iface in i:
429                         return WlanStatusScreenMain
430                 else:
431                         if iNetwork.isWirelessInterface(iface):
432                                 return WlanStatusScreenMain
433                         else:
434                                 return None
435         return None
436
437 def configStrings(iface):
438         hardware_info = HardwareInfo()
439         if  hardware_info.device_name != "dm7025":
440                 rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?']
441                 etpm = eTPM()
442                 l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT)
443                 if l2cert is None:
444                         return
445                 l2key = validate_certificate(l2cert, rootkey)
446                 if l2key is None:
447                         return
448                 l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT)
449                 if l3cert is None:
450                         return
451                 l3key = validate_certificate(l3cert, l2key)
452                 if l3key is None:
453                         return
454                 rnd = get_random()
455                 if rnd is None:
456                         return
457                 val = etpm.challenge(rnd)
458                 result = decrypt_block(val, l3key)
459         if hardware_info.device_name == "dm7025" or result[80:88] == rnd:
460                 driver = iNetwork.detectWlanModule(iface)
461         else:
462                 driver = 'dreambox'
463         print 'Using "%s" as wpa-supplicant driver' % (driver)
464         ret = ""
465         if config.plugins.wlan.essid.value == "hidden...":
466                 ret += "\tpre-up iwconfig " + iface + " essid \"" + config.plugins.wlan.hiddenessid.value + "\" || true\n"
467         ret += "\tpre-up wpa_supplicant -i" + iface + " -c/etc/" + iface + "_wpa_supplicant.conf -B -dd -D" + driver + " || true\n"
468         ret += "\tpre-down wpa_cli -i" + iface + " terminate || true\n"
469         return ret
470
471 def Plugins(**kwargs):
472         return PluginDescriptor(name=_("Wireless LAN"), description=_("Connect to a Wireless Network"), where = PluginDescriptor.WHERE_NETWORKSETUP, needsRestart = False, fnc={"ifaceSupported": callFunction, "configStrings": configStrings, "WlanPluginEntry": lambda x: "Wireless Network Configuartion..."})