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