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