Merge remote branch 'origin/acid-burn/virtualkeyboard' into experimental
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / SoftwareManager / SoftwareTools.py
1 # -*- coding: iso-8859-1 -*-
2 from enigma import eConsoleAppContainer,eTPM
3 from Components.Console import Console
4 from Components.About import about
5 from Components.DreamInfoHandler import DreamInfoHandler
6 from Components.Language import language
7 from Components.Sources.List import List
8 from Components.Ipkg import IpkgComponent
9 from Components.Network import iNetwork
10 from Tools.Directories import pathExists, fileExists, resolveFilename, SCOPE_METADIR
11 from Tools.HardwareInfo import HardwareInfo
12 import hashlib
13 from time import time
14 from os import urandom
15
16 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', ':', '?']
17
18 def bin2long(s):
19         return reduce( lambda x,y:(x<<8L)+y, map(ord, s))
20
21 def long2bin(l):
22         res = ""
23         for byte in range(128):
24                 res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff)
25         return res
26
27 def rsa_pub1024(src, mod):
28         return long2bin(pow(bin2long(src), 65537, bin2long(mod)))
29         
30 def decrypt_block(src, mod):
31         if len(src) != 128 and len(src) != 202:
32                 return None
33         dest = rsa_pub1024(src[:128], mod)
34         hash = hashlib.sha1(dest[1:107])
35         if len(src) == 202:
36                 hash.update(src[131:192])       
37         result = hash.digest()
38         if result == dest[107:127]:
39                 return dest
40         return None
41
42 def validate_cert(cert, key):
43         buf = decrypt_block(cert[8:], key) 
44         if buf is None:
45                 return None
46         return buf[36:107] + cert[139:196]
47
48 def read_random():
49         try:
50                 xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100))
51                 random = urandom(8)
52                 x = str(time())[-8:]
53                 result = xor(random, x)
54                                 
55                 return result
56         except:
57                 return None
58
59 class SoftwareTools(DreamInfoHandler):
60         lastDownloadDate = None
61         NetworkConnectionAvailable = None
62         list_updating = False
63         available_updates = 0
64         available_updatelist  = []
65         available_packetlist  = []
66         installed_packetlist = {}
67
68         
69         def __init__(self):
70                 aboutInfo = about.getImageVersionString()
71                 if aboutInfo.startswith("dev-"):
72                         self.ImageVersion = 'Experimental'
73                 else:
74                         self.ImageVersion = 'Stable'
75                 self.language = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
76                 DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, neededTag = 'ALL_TAGS', neededFlag = self.ImageVersion)
77                 self.directory = resolveFilename(SCOPE_METADIR)
78                 self.hardware_info = HardwareInfo()
79                 self.list = List([])
80                 self.NotifierCallback = None
81                 self.Console = Console()
82                 self.UpdateConsole = Console()
83                 self.cmdList = []
84                 self.unwanted_extensions = ('-dbg', '-dev', '-doc')
85                 self.ipkg = IpkgComponent()
86                 self.ipkg.addCallback(self.ipkgCallback)                
87
88         def statusCallback(self, status, progress):
89                 pass            
90
91         def startSoftwareTools(self, callback = None):
92                 if callback is not None:
93                         self.NotifierCallback = callback
94                 iNetwork.checkNetworkState(self.checkNetworkCB)
95                 
96         def checkNetworkCB(self,data):
97                 if data is not None:
98                         if data <= 2:
99                                 self.NetworkConnectionAvailable = True
100                                 self.getUpdates()
101                         else:
102                                 self.NetworkConnectionAvailable = False
103                                 self.getUpdates()
104
105         def getUpdates(self, callback = None):
106                 if self.lastDownloadDate is None:
107                         if  self.hardware_info.device_name != "dm7025":
108                                 etpm = eTPM()
109                                 l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT)
110                                 if l2cert is None:
111                                         return
112                                 l2key = validate_cert(l2cert, rootkey)
113                                 if l2key is None:
114                                         return
115                                 l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT)
116                                 if l3cert is None:
117                                         return
118                                 l3key = validate_cert(l3cert, l2key)
119                                 if l3key is None:
120                                         return
121                                 rnd = read_random()
122                                 if rnd is None:
123                                         return
124                                 val = etpm.challenge(rnd)
125                                 result = decrypt_block(val, l3key)
126                         if self.hardware_info.device_name == "dm7025" or result[80:88] == rnd:
127                                 if self.NetworkConnectionAvailable == True:
128                                         self.lastDownloadDate = time()
129                                         if self.list_updating is False and callback is None:
130                                                 self.list_updating = True
131                                                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
132                                         elif self.list_updating is False and callback is not None:
133                                                 self.list_updating = True
134                                                 self.NotifierCallback = callback
135                                                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
136                                         elif self.list_updating is True and callback is not None:
137                                                 self.NotifierCallback = callback
138                                 else:
139                                         self.list_updating = False
140                                         if callback is not None:
141                                                 callback(False)
142                                         elif self.NotifierCallback is not None:
143                                                 self.NotifierCallback(False)
144                         else:
145                                 self.NetworkConnectionAvailable = False
146                                 self.list_updating = False
147                                 if callback is not None:
148                                         callback(False)
149                                 elif self.NotifierCallback is not None:
150                                         self.NotifierCallback(False)            
151                 else:
152                         if self.NetworkConnectionAvailable == True:
153                                 self.lastDownloadDate = time()
154                                 if self.list_updating is False and callback is None:
155                                         self.list_updating = True
156                                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
157                                 elif self.list_updating is False and callback is not None:
158                                         self.list_updating = True
159                                         self.NotifierCallback = callback
160                                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
161                                 elif self.list_updating is True and callback is not None:
162                                         self.NotifierCallback = callback
163                         else:
164                                 if self.list_updating and callback is not None:
165                                         if  self.hardware_info.device_name != "dm7025":
166                                                 etpm = eTPM()
167                                                 l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT)
168                                                 if l2cert is None:
169                                                         return
170                                                 l2key = validate_cert(l2cert, rootkey)
171                                                 if l2key is None:
172                                                         return
173                                                 l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT)
174                                                 if l3cert is None:
175                                                         return
176                                                 l3key = validate_cert(l3cert, l2key)
177                                                 if l3key is None:
178                                                         return
179                                                 rnd = read_random()
180                                                 if rnd is None:
181                                                         return
182                                                 val = etpm.challenge(rnd)
183                                                 result = decrypt_block(val, l3key)
184                                         if self.hardware_info.device_name == "dm7025" or result[80:88] == rnd:
185                                                 self.NotifierCallback = callback
186                                                 self.startIpkgListAvailable()
187                                 else:   
188                                         self.list_updating = False
189                                         if callback is not None:
190                                                 callback(False)
191                                         elif self.NotifierCallback is not None:
192                                                 self.NotifierCallback(False)
193
194         def ipkgCallback(self, event, param):
195                 if event == IpkgComponent.EVENT_ERROR:
196                         self.list_updating = False
197                         if self.NotifierCallback is not None:
198                                 self.NotifierCallback(False)
199                 elif event == IpkgComponent.EVENT_DONE:
200                         if self.list_updating:
201                                 self.startIpkgListAvailable()
202                 #print event, "-", param                
203                 pass
204
205         def startIpkgListAvailable(self, callback = None):
206                 if callback is not None:
207                         self.list_updating = True
208                 if self.list_updating:
209                         if not self.UpdateConsole:
210                                 self.UpdateConsole = Console()
211                         cmd = "opkg list"
212                         self.UpdateConsole.ePopen(cmd, self.IpkgListAvailableCB, callback)
213
214         def IpkgListAvailableCB(self, result, retval, extra_args = None):
215                 (callback) = extra_args
216                 if result:
217                         if self.list_updating:
218                                 self.available_packetlist = []
219                                 for x in result.splitlines():
220                                         tokens = x.split(' - ')
221                                         name = tokens[0].strip()
222                                         if not any(name.endswith(x) for x in self.unwanted_extensions):
223                                                 l = len(tokens)
224                                                 version = l > 1 and tokens[1].strip() or ""
225                                                 descr = l > 2 and tokens[2].strip() or ""
226                                                 self.available_packetlist.append([name, version, descr])
227                                 if callback is None:
228                                         self.startInstallMetaPackage()
229                                 else:
230                                         if self.UpdateConsole:
231                                                 if len(self.UpdateConsole.appContainers) == 0:
232                                                                 callback(True)
233                 else:
234                         self.list_updating = False
235                         if self.UpdateConsole:
236                                 if len(self.UpdateConsole.appContainers) == 0:
237                                         if callback is not None:
238                                                 callback(False)
239
240         def startInstallMetaPackage(self, callback = None):
241                 if callback is not None:
242                         self.list_updating = True
243                 if self.list_updating:
244                         if self.NetworkConnectionAvailable == True:
245                                 if not self.UpdateConsole:
246                                         self.UpdateConsole = Console()
247                                 cmd = "opkg install enigma2-meta enigma2-plugins-meta enigma2-skins-meta enigma2-drivers-meta"
248                                 self.UpdateConsole.ePopen(cmd, self.InstallMetaPackageCB, callback)
249                         else:
250                                 self.InstallMetaPackageCB(True)
251
252         def InstallMetaPackageCB(self, result, retval = None, extra_args = None):
253                 (callback) = extra_args
254                 if result:
255                         self.fillPackagesIndexList()
256                         if callback is None:
257                                 self.startIpkgListInstalled()
258                         else:
259                                 if self.UpdateConsole:
260                                         if len(self.UpdateConsole.appContainers) == 0:
261                                                         callback(True)
262                 else:
263                         self.list_updating = False
264                         if self.UpdateConsole:
265                                 if len(self.UpdateConsole.appContainers) == 0:
266                                         if callback is not None:
267                                                 callback(False)
268
269         def startIpkgListInstalled(self, callback = None):
270                 if callback is not None:
271                         self.list_updating = True
272                 if self.list_updating:
273                         if not self.UpdateConsole:
274                                 self.UpdateConsole = Console()
275                         cmd = "opkg list-installed"
276                         self.UpdateConsole.ePopen(cmd, self.IpkgListInstalledCB, callback)
277
278         def IpkgListInstalledCB(self, result, retval, extra_args = None):
279                 (callback) = extra_args
280                 if result:
281                         self.installed_packetlist = {}
282                         for x in result.splitlines():
283                                 tokens = x.split(' - ')
284                                 name = tokens[0].strip()
285                                 if not any(name.endswith(x) for x in self.unwanted_extensions):
286                                         l = len(tokens)
287                                         version = l > 1 and tokens[1].strip() or ""
288                                         self.installed_packetlist[name] = version
289                         for package in self.packagesIndexlist[:]:
290                                 if not self.verifyPrerequisites(package[0]["prerequisites"]):
291                                         self.packagesIndexlist.remove(package)
292                         for package in self.packagesIndexlist[:]:
293                                 attributes = package[0]["attributes"]
294                                 if attributes.has_key("packagetype"):
295                                         if attributes["packagetype"] == "internal":
296                                                 self.packagesIndexlist.remove(package)
297                         if callback is None:
298                                 self.countUpdates()
299                         else:
300                                 if self.UpdateConsole:
301                                         if len(self.UpdateConsole.appContainers) == 0:
302                                                         callback(True)
303                 else:
304                         self.list_updating = False
305                         if self.UpdateConsole:
306                                 if len(self.UpdateConsole.appContainers) == 0:
307                                         if callback is not None:
308                                                 callback(False)
309
310         def countUpdates(self, callback = None):
311                 self.available_updates = 0
312                 self.available_updatelist  = []
313                 for package in self.packagesIndexlist[:]:
314                         attributes = package[0]["attributes"]
315                         packagename = attributes["packagename"]
316                         for x in self.available_packetlist:
317                                 if x[0] == packagename:
318                                         if self.installed_packetlist.has_key(packagename):
319                                                 if self.installed_packetlist[packagename] != x[1]:
320                                                         self.available_updates +=1
321                                                         self.available_updatelist.append([packagename])
322
323                 self.list_updating = False
324                 if self.UpdateConsole:
325                         if len(self.UpdateConsole.appContainers) == 0:
326                                 if callback is not None:
327                                         callback(True)
328                                         callback = None
329                                 elif self.NotifierCallback is not None:
330                                         self.NotifierCallback(True)
331                                         self.NotifierCallback = None
332
333         def startIpkgUpdate(self, callback = None):
334                 if not self.Console:
335                         self.Console = Console()
336                 cmd = "opkg update"
337                 self.Console.ePopen(cmd, self.IpkgUpdateCB, callback)
338
339         def IpkgUpdateCB(self, result, retval, extra_args = None):
340                 (callback) = extra_args
341                 if result:
342                         if self.Console:
343                                 if len(self.Console.appContainers) == 0:
344                                         if callback is not None:
345                                                 callback(True)
346                                                 callback = None
347
348         def cleanupSoftwareTools(self):
349                 self.list_updating = False
350                 if self.NotifierCallback is not None:
351                         self.NotifierCallback = None
352                 self.ipkg.stop()
353                 if self.Console is not None:
354                         if len(self.Console.appContainers):
355                                 for name in self.Console.appContainers.keys():
356                                         self.Console.kill(name)
357                 if self.UpdateConsole is not None:
358                         if len(self.UpdateConsole.appContainers):
359                                 for name in self.UpdateConsole.appContainers.keys():
360                                         self.UpdateConsole.kill(name)
361
362         def verifyPrerequisites(self, prerequisites):
363                 if prerequisites.has_key("hardware"):
364                         hardware_found = False
365                         for hardware in prerequisites["hardware"]:
366                                 if hardware == self.hardware_info.device_name:
367                                         hardware_found = True
368                         if not hardware_found:
369                                 return False
370                 return True
371
372 iSoftwareTools = SoftwareTools()