SystemPlugins/WirelessLan: Don't depend on device names where possible. Better wirele...
[vuplus_dvbapp] / 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 iWlan, wpaSupplicant, iStatus, getWlanConfigName
19 import hashlib
20 from time import time
21 from os import urandom, system
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                 iWlan.setInterface(self.iface)
223                 self.w = iWlan.getInterface()
224                 self.onLayoutFinish.append(self.layoutFinished)
225                 self.getAccessPoints(refresh = False)
226                 
227         def layoutFinished(self):
228                 self.setTitle(_("Choose a wireless network"))
229         
230         def select(self):
231                 cur = self["list"].getCurrent()
232                 if cur is not None:
233                         iWlan.stopGetNetworkList()
234                         self.rescanTimer.stop()
235                         del self.rescanTimer
236                         if cur[1] is not None:
237                                 if cur[1] == 'hidden...':
238                                         essid = cur[1]
239                                 else:
240                                         essid = cur[0]
241                                 self.close(essid,self.getWlanList())
242                         else:
243                                 self.close(None,None)
244                 else:
245                         iWlan.stopGetNetworkList()
246                         self.rescanTimer.stop()
247                         del self.rescanTimer
248                         self.close(None,None)
249         
250         def cancel(self):
251                 iWlan.stopGetNetworkList()
252                 self.rescanTimer.stop()
253                 del self.rescanTimer
254                 self.close(None)
255
256         def rescanTimerFired(self):
257                 self.rescanTimer.stop()
258                 self.updateAPList()
259
260         def buildEntryComponent(self, essid, bssid, encrypted, iface, maxrate, signal):
261                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
262                 encryption = encrypted and _("Yes") or _("No")
263                 if bssid == 'hidden...':
264                         return((essid, bssid, None, None, None, None, divpng))
265                 else:                                   
266                         return((essid, bssid, _("Signal: ") + str(signal), _("Max. Bitrate: ") + str(maxrate), _("Encrypted: ") + encryption, _("Interface: ") + str(iface), divpng))
267
268         def updateAPList(self):
269                 newList = []
270                 newList = self.getAccessPoints(refresh = True)  
271                 self.newAPList = []
272                 tmpList = []
273                 newListIndex = None
274                 currentListEntry = None
275                 currentListIndex = None
276
277                 for ap in self.oldlist.keys():
278                         data = self.oldlist[ap]['data']
279                         if data is not None:
280                                 tmpList.append(data)
281
282                 if len(tmpList):
283                         if "hidden..." not in tmpList:
284                                 tmpList.append( ( _("enter hidden network SSID"), "hidden...", True, self.iface, _("unavailable"), "" ) )
285         
286                         for entry in tmpList:
287                                 self.newAPList.append(self.buildEntryComponent( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ))
288         
289                         currentListEntry = self["list"].getCurrent()
290                         idx = 0
291                         for entry in self.newAPList:
292                                 if entry[0] == currentListEntry[0]:
293                                         newListIndex = idx
294                                 idx +=1
295                         self['list'].setList(self.newAPList)
296                         self["list"].setIndex(newListIndex)
297                         self["list"].updateList(self.newAPList)
298                         self.listLength = len(self.newAPList)
299                         self.buildWlanList()
300                         self.setInfo()
301
302         def getAccessPoints(self, refresh = False):
303                 self.APList = []
304                 self.cleanList = []
305                 aps = iWlan.getNetworkList()
306                 if aps is not None:
307                         print "[WirelessLan.py] got Accespoints!"
308                         tmpList = []
309                         compList = []
310                         for ap in aps:
311                                 a = aps[ap]
312                                 if a['active']:
313                                         tmpList.append( (a['essid'], a['bssid']) )
314                                         compList.append( (a['essid'], a['bssid'], a['encrypted'], a['iface'], a['maxrate'], a['signal']) )
315
316                         for entry in tmpList:
317                                 if entry[0] == "":
318                                         for compentry in compList:
319                                                 if compentry[1] == entry[1]:
320                                                         compList.remove(compentry)
321                         for entry in compList:
322                                 self.cleanList.append( ( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ) )
323                                 if not self.oldlist.has_key(entry[0]):
324                                         self.oldlist[entry[0]] = { 'data': entry }
325                                 else:
326                                         self.oldlist[entry[0]]['data'] = entry
327                 
328                 if "hidden..." not in self.cleanList:
329                         self.cleanList.append( ( _("enter hidden network SSID"), "hidden...", True, self.iface, _("unavailable"), "" ) )
330
331                 for entry in self.cleanList:
332                         self.APList.append(self.buildEntryComponent( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ))
333                 
334                 if refresh is False:
335                         self['list'].setList(self.APList)
336                 self.listLength = len(self.APList)
337                 self.setInfo()
338                 self.rescanTimer.start(5000)
339                 return self.cleanList
340
341         def setInfo(self):
342                 length = self.getLength()
343                 if length <= 1:
344                         self["info"].setText(_("No wireless networks found! Please refresh."))
345                 elif length == 2:
346                         self["info"].setText(_("1 wireless network found!"))
347                 else:
348                         self["info"].setText(str(length-1)+_(" wireless networks found!"))
349
350         def buildWlanList(self):
351                 self.WlanList = []
352                 for entry in self['list'].list:
353                         if entry[1] == "hidden...":
354                                 self.WlanList.append(( "hidden...",_("enter hidden network SSID") ))
355                         else:
356                                 self.WlanList.append( (entry[0], entry[0]) )
357
358         def getLength(self):
359                 return self.listLength          
360
361         def getWlanList(self):
362                 if self.WlanList is None:
363                         self.buildWlanList()
364                 return self.WlanList
365
366
367 def bin2long(s):
368         return reduce( lambda x,y:(x<<8L)+y, map(ord, s))
369
370 def long2bin(l):
371         res = ""
372         for byte in range(128):
373                 res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff)
374         return res
375
376 def rsa_pub1024(src, mod):
377         return long2bin(pow(bin2long(src), 65537, bin2long(mod)))
378         
379 def decrypt_block(src, mod):
380         if len(src) != 128 and len(src) != 202:
381                 return None
382         dest = rsa_pub1024(src[:128], mod)
383         hash = hashlib.sha1(dest[1:107])
384         if len(src) == 202:
385                 hash.update(src[131:192])       
386         result = hash.digest()
387         if result == dest[107:127]:
388                 return dest
389         return None
390
391 def validate_certificate(cert, key):
392         buf = decrypt_block(cert[8:], key) 
393         if buf is None:
394                 return None
395         return buf[36:107] + cert[139:196]
396
397 def get_random():
398         try:
399                 xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100))
400                 random = urandom(8)
401                 x = str(time())[-8:]
402                 result = xor(random, x)
403                                 
404                 return result
405         except:
406                 return None
407
408 def WlanStatusScreenMain(session, iface):
409         session.open(WlanStatus, iface)
410
411 def callFunction(iface):
412         iWlan.setInterface(iface)
413         i = iWlan.getWirelessInterfaces()
414         if i:
415                 if iface in i or iNetwork.isWirelessInterface(iface):
416                         return WlanStatusScreenMain
417                 return None
418         return None
419
420 def configStrings(iface):
421         hardware_info = HardwareInfo()
422         if  hardware_info.device_name != "dm7025":
423                 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', ':', '?']
424                 etpm = eTPM()
425                 l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT)
426                 if l2cert is None:
427                         return
428                 l2key = validate_certificate(l2cert, rootkey)
429                 if l2key is None:
430                         return
431                 l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT)
432                 if l3cert is None:
433                         return
434                 l3key = validate_certificate(l3cert, l2key)
435                 if l3key is None:
436                         return
437                 rnd = get_random()
438                 if rnd is None:
439                         return
440                 val = etpm.challenge(rnd)
441                 result = decrypt_block(val, l3key)
442         if hardware_info.device_name == "dm7025" or result[80:88] == rnd:
443                 driver = iNetwork.detectWlanModule(iface)
444         else:
445                 driver = 'dreambox'
446         print 'Using "%s" as wpa-supplicant driver' % (driver)
447         ret = ""
448         if driver == 'madwifi' and config.plugins.wlan.essid.value == "hidden...":
449                 ret += "\tpre-up iwconfig " + iface + " essid \"" + config.plugins.wlan.hiddenessid.value + "\" || true\n"
450         ret += "\tpre-up wpa_supplicant -i" + iface + " -c" + getWlanConfigName(iface) + " -B -dd -D" + driver + " || true\n"
451         ret += "\tpre-down wpa_cli -i" + iface + " terminate || true\n"
452         return ret
453
454 def Plugins(**kwargs):
455         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..."})