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