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