Merge remote-tracking branch 'origin/bug_749_ethread_bugfix'
[enigma2.git] / lib / python / Components / DreamInfoHandler.py
1 # -*- coding: iso-8859-1 -*-
2 import xml.sax
3 from Tools.Directories import crawlDirectory, resolveFilename, SCOPE_CONFIG, SCOPE_SKIN, SCOPE_METADIR, copyfile, copytree
4 from Components.NimManager import nimmanager
5 from Components.Ipkg import IpkgComponent
6 from Components.config import config, configfile
7 from Tools.HardwareInfo import HardwareInfo
8 from enigma import eConsoleAppContainer, eDVBDB
9 import os
10 from re import compile as re_compile, search as re_search, IGNORECASE
11
12 class InfoHandlerParseError(Exception):
13         def __init__(self, value):
14                 self.value = value
15         def __str__(self):
16                 return repr(self.value)
17
18 class InfoHandler(xml.sax.ContentHandler):
19         def __init__(self, prerequisiteMet, directory):
20                 self.attributes = {}
21                 self.directory = directory
22                 self.list = []
23                 self.globalprerequisites = {}
24                 self.prerequisites = {}
25                 self.elements = []
26                 self.validFileTypes = ["skin", "config", "services", "favourites", "package"]
27                 self.prerequisitesMet = prerequisiteMet
28                 self.data = ""
29
30         def printError(self, error):
31                 print "Error in defaults xml files:", error
32                 raise InfoHandlerParseError, error
33
34         def startElement(self, name, attrs):
35                 #print name, ":", attrs.items()
36                 self.elements.append(name)
37
38                 if name in ("hardware", "bcastsystem", "satellite", "tag", "flag"):
39                         if not attrs.has_key("type"):
40                                         self.printError(str(name) + " tag with no type attribute")
41                         if self.elements[-3] in ("default", "package"):
42                                 prerequisites = self.globalprerequisites
43                         else:
44                                 prerequisites = self.prerequisites
45                         if not prerequisites.has_key(name):
46                                 prerequisites[name] = []
47                         prerequisites[name].append(str(attrs["type"]))
48
49                 if name == "info":
50                         self.foundTranslation = None
51                         self.data = ""
52
53                 if name == "files":
54                         if attrs.has_key("type"):
55                                 if attrs["type"] == "directories":
56                                         self.attributes["filestype"] = "directories"
57                                 elif attrs["type"] == "package":
58                                         self.attributes["filestype"] = "package"
59                                 # TODO add a compressed archive type
60
61                 if name == "file":
62                         self.prerequisites = {}
63                         if not attrs.has_key("type"):
64                                 self.printError("file tag with no type attribute")
65                         else:
66                                 if not attrs.has_key("name"):
67                                         self.printError("file tag with no name attribute")
68                                 else:   
69                                         if not attrs.has_key("directory"):
70                                                 directory = self.directory
71                                         type = attrs["type"]
72                                         if not type in self.validFileTypes:
73                                                 self.printError("file tag with invalid type attribute")
74                                         else:
75                                                 self.filetype = type
76                                                 self.fileattrs = attrs
77
78                 if name == "package":
79                         if attrs.has_key("details"):
80                                 self.attributes["details"] = str(attrs["details"])
81                         if attrs.has_key("name"):
82                                 self.attributes["name"] = str(attrs["name"])
83                         if attrs.has_key("packagename"):
84                                 self.attributes["packagename"] = str(attrs["packagename"])
85                         if attrs.has_key("packagetype"):
86                                 self.attributes["packagetype"] = str(attrs["packagetype"])
87                         if attrs.has_key("needsRestart"):
88                                 self.attributes["needsRestart"] = str(attrs["needsRestart"])
89                         if attrs.has_key("shortdescription"):
90                                 self.attributes["shortdescription"] = str(attrs["shortdescription"])
91
92                 if name == "screenshot":
93                         if attrs.has_key("src"):
94                                 self.attributes["screenshot"] = str(attrs["src"])
95
96         def endElement(self, name):
97                 #print "endElement", name
98                 #print "self.elements:", self.elements
99                 self.elements.pop()
100                 if name == "file":
101                         #print "prerequisites:", self.prerequisites
102                         if len(self.prerequisites) == 0 or self.prerequisitesMet(self.prerequisites):
103                                 if not self.attributes.has_key(self.filetype):
104                                         self.attributes[self.filetype] = []
105                                 if self.fileattrs.has_key("directory"):
106                                         directory = str(self.fileattrs["directory"])
107                                         if len(directory) < 1 or directory[0] != "/":
108                                                 directory = self.directory + directory
109                                 else:
110                                         directory = self.directory
111                                 self.attributes[self.filetype].append({ "name": str(self.fileattrs["name"]), "directory": directory })
112
113                 if name in ( "default", "package" ):
114                         self.list.append({"attributes": self.attributes, 'prerequisites': self.globalprerequisites})
115                         self.attributes = {}
116                         self.globalprerequisites = {}
117
118         def characters(self, data):
119                 if self.elements[-1] == "author":
120                         self.attributes["author"] = str(data)
121                 if self.elements[-1] == "name":
122                         self.attributes["name"] = str(data)
123                 if self.elements[-1] == "packagename":
124                         self.attributes["packagename"] = str(data)
125                 if self.elements[-1] == "needsRestart":
126                         self.attributes["needsRestart"] = str(data)
127                 if self.elements[-1] == "shortdescription":
128                         self.attributes["shortdescription"] = str(data)
129                 if self.elements[-1] == "description":
130                         self.data += data.strip()
131                         self.attributes["description"] = str(self.data)
132                 #print "characters", data
133
134
135 class DreamInfoHandler:
136         STATUS_WORKING = 0
137         STATUS_DONE = 1
138         STATUS_ERROR = 2
139         STATUS_INIT = 4
140
141         def __init__(self, statusCallback, blocking = False, neededTag = None, neededFlag = None):
142                 self.hardware_info = HardwareInfo()
143                 self.directory = "/"
144
145                 self.neededTag = neededTag
146                 self.neededFlag = neededFlag
147
148                 # caution: blocking should only be used, if further execution in enigma2 depends on the outcome of
149                 # the installer!
150                 self.blocking = blocking
151
152                 self.currentlyInstallingMetaIndex = None
153
154                 self.console = eConsoleAppContainer()
155                 self.console.appClosed.append(self.installNext)
156                 self.reloadFavourites = False
157
158                 self.statusCallback = statusCallback
159                 self.setStatus(self.STATUS_INIT)
160
161                 self.packageslist = []
162                 self.packagesIndexlist = []
163                 self.packageDetails = []
164
165         def readInfo(self, directory, file):
166                 print "Reading .info file", file
167                 handler = InfoHandler(self.prerequisiteMet, directory)
168                 try:
169                         xml.sax.parse(file, handler)
170                         for entry in handler.list:
171                                 self.packageslist.append((entry,file)) 
172                 except InfoHandlerParseError:
173                         print "file", file, "ignored due to errors in the file"
174                 #print handler.list
175
176         def readIndex(self, directory, file):
177                 print "Reading .xml meta index file", directory, file
178                 handler = InfoHandler(self.prerequisiteMet, directory)
179                 try:
180                         xml.sax.parse(file, handler)
181                         for entry in handler.list:
182                                 self.packagesIndexlist.append((entry,file))
183                 except InfoHandlerParseError:
184                         print "file", file, "ignored due to errors in the file"
185                 #print handler.list
186
187         def readDetails(self, directory, file):
188                 self.packageDetails = []
189                 print "Reading .xml meta details file", file
190                 handler = InfoHandler(self.prerequisiteMet, directory)
191                 try:
192                         xml.sax.parse(file, handler)
193                         for entry in handler.list:
194                                 self.packageDetails.append((entry,file))
195                 except InfoHandlerParseError:
196                         print "file", file, "ignored due to errors in the file"
197                 #print handler.list
198
199         # prerequisites = True: give only packages matching the prerequisites
200         def fillPackagesList(self, prerequisites = True):
201                 self.packageslist = []
202                 packages = []
203                 if not isinstance(self.directory, list):
204                         self.directory = [self.directory]
205
206                 for directory in self.directory:
207                         packages += crawlDirectory(directory, ".*\.info$")
208
209                 for package in packages:
210                         self.readInfo(package[0] + "/", package[0] + "/" + package[1])
211
212                 if prerequisites:
213                         for package in self.packageslist[:]:
214                                 if not self.prerequisiteMet(package[0]["prerequisites"]):
215                                         self.packageslist.remove(package)
216                 return self.packageslist
217
218         # prerequisites = True: give only packages matching the prerequisites
219         def fillPackagesIndexList(self, prerequisites = True):
220                 self.packagesIndexlist = []
221                 indexfileList = []
222
223                 if not isinstance(self.directory, list):
224                         self.directory = [self.directory]
225
226                 for indexfile in os.listdir(self.directory[0]):
227                         if indexfile.startswith("index-"):
228                                 if indexfile.endswith(".xml"):
229                                         if indexfile[-7:-6] == "_":
230                                                 continue
231                                         indexfileList.append(indexfile)
232                 if len(indexfileList):
233                         for file in indexfileList:
234                                 neededFile = self.directory[0] + "/" + file
235                                 if os.path.isfile(neededFile):
236                                         self.readIndex(self.directory[0] + "/" , neededFile)
237
238                 if prerequisites:
239                         for package in self.packagesIndexlist[:]:
240                                 if not self.prerequisiteMet(package[0]["prerequisites"]):
241                                         self.packagesIndexlist.remove(package)
242                 return self.packagesIndexlist
243
244         # prerequisites = True: give only packages matching the prerequisites
245         def fillPackageDetails(self, details = None):
246                 self.packageDetails = []
247                 detailsfile = details
248                 if not isinstance(self.directory, list):
249                         self.directory = [self.directory]
250                 self.readDetails(self.directory[0] + "/", self.directory[0] + "/" + detailsfile)
251                 return self.packageDetails
252                         
253         def prerequisiteMet(self, prerequisites):
254                 # TODO: we need to implement a hardware detection here...
255                 print "prerequisites:", prerequisites
256                 met = True
257                 if self.neededTag is None:
258                         if prerequisites.has_key("tag"):
259                                 return False
260                 elif self.neededTag == 'ALL_TAGS':
261                                 return True
262                 else:
263                         if prerequisites.has_key("tag"):
264                                 if not self.neededTag in prerequisites["tag"]:
265                                         return False
266                         else:
267                                 return False
268
269                 if self.neededFlag is None:
270                         if prerequisites.has_key("flag"):
271                                 return False
272                 else:
273                         if prerequisites.has_key("flag"):
274                                 if not self.neededFlag in prerequisites["flag"]:
275                                         return False
276                         else:
277                                 return True # No flag found, assuming all flags valid
278                                 
279                 if prerequisites.has_key("satellite"):
280                         for sat in prerequisites["satellite"]:
281                                 if int(sat) not in nimmanager.getConfiguredSats():
282                                         return False                    
283                 if prerequisites.has_key("bcastsystem"):
284                         has_system = False
285                         for bcastsystem in prerequisites["bcastsystem"]:
286                                 if nimmanager.hasNimType(bcastsystem):
287                                         has_system = True
288                         if not has_system:
289                                 return False
290                 if prerequisites.has_key("hardware"):
291                         hardware_found = False
292                         for hardware in prerequisites["hardware"]:
293                                 if hardware == self.hardware_info.device_name:
294                                         hardware_found = True
295                         if not hardware_found:
296                                 return False
297                 return True
298         
299         def installPackages(self, indexes):
300                 print "installing packages", indexes
301                 if len(indexes) == 0:
302                         self.setStatus(self.STATUS_DONE)
303                         return
304                 self.installIndexes = indexes
305                 print "+++++++++++++++++++++++bla"
306                 self.currentlyInstallingMetaIndex = 0
307                 self.installPackage(self.installIndexes[self.currentlyInstallingMetaIndex])
308
309         def installPackage(self, index):
310                 print "self.packageslist:", self.packageslist
311                 if len(self.packageslist) <= index:
312                         print "no package with index", index, "found... installing nothing"
313                         return
314                 print "installing package with index", index, "and name", self.packageslist[index][0]["attributes"]["name"]
315                 
316                 attributes = self.packageslist[index][0]["attributes"]
317                 self.installingAttributes = attributes
318                 self.attributeNames = ["skin", "config", "favourites", "package", "services"]
319                 self.currentAttributeIndex = 0
320                 self.currentIndex = -1
321                 self.installNext()
322                 
323         def setStatus(self, status):
324                 self.status = status
325                 self.statusCallback(self.status, None)
326                                                 
327         def installNext(self, *args, **kwargs):
328                 if self.reloadFavourites:
329                         self.reloadFavourites = False
330                         db = eDVBDB.getInstance().reloadBouquets()
331
332                 self.currentIndex += 1
333                 attributes = self.installingAttributes
334                 #print "attributes:", attributes
335                 
336                 if self.currentAttributeIndex >= len(self.attributeNames): # end of package reached
337                         print "end of package reached"
338                         if self.currentlyInstallingMetaIndex is None or self.currentlyInstallingMetaIndex >= len(self.installIndexes) - 1:
339                                 print "set status to DONE"
340                                 self.setStatus(self.STATUS_DONE)
341                                 return
342                         else:
343                                 print "increment meta index to install next package"
344                                 self.currentlyInstallingMetaIndex += 1
345                                 self.currentAttributeIndex = 0
346                                 self.installPackage(self.installIndexes[self.currentlyInstallingMetaIndex])
347                                 return
348                 
349                 self.setStatus(self.STATUS_WORKING)             
350                 
351                 print "currentAttributeIndex:", self.currentAttributeIndex
352                 currentAttribute = self.attributeNames[self.currentAttributeIndex]
353                 
354                 print "installing", currentAttribute, "with index", self.currentIndex
355                 
356                 if attributes.has_key(currentAttribute):
357                         if self.currentIndex >= len(attributes[currentAttribute]): # all jobs done for current attribute
358                                 self.currentIndex = -1
359                                 self.currentAttributeIndex += 1
360                                 self.installNext()
361                                 return
362                 else: # nothing to install here
363                         self.currentIndex = -1
364                         self.currentAttributeIndex += 1
365                         self.installNext()
366                         return
367                         
368                 if currentAttribute == "skin":
369                         skin = attributes["skin"][self.currentIndex]
370                         self.installSkin(skin["directory"], skin["name"])
371                 elif currentAttribute == "config":
372                         if self.currentIndex == 0:
373                                 from Components.config import configfile
374                                 configfile.save()
375                         config = attributes["config"][self.currentIndex]
376                         self.mergeConfig(config["directory"], config["name"])
377                 elif currentAttribute == "favourites":
378                         favourite = attributes["favourites"][self.currentIndex]
379                         self.installFavourites(favourite["directory"], favourite["name"])
380                 elif currentAttribute == "package":
381                         package = attributes["package"][self.currentIndex]
382                         self.installIPK(package["directory"], package["name"])
383                 elif currentAttribute == "services":
384                         service = attributes["services"][self.currentIndex]
385                         self.mergeServices(service["directory"], service["name"])
386                                 
387         def readfile(self, filename):
388                 if not os.path.isfile(filename):
389                         return []
390                 fd = open(filename)
391                 lines = fd.readlines()
392                 fd.close()
393                 return lines
394                         
395         def mergeConfig(self, directory, name, merge = True):
396                 print "merging config:", directory, " - ", name
397                 if os.path.isfile(directory + name):
398                         config.loadFromFile(directory + name)
399                         configfile.save()
400                 self.installNext()
401                 
402         def installIPK(self, directory, name):
403                 if self.blocking:
404                         os.system("opkg install " + directory + name)
405                         self.installNext()
406                 else:
407                         self.ipkg = IpkgComponent()
408                         self.ipkg.addCallback(self.ipkgCallback)
409                         self.ipkg.startCmd(IpkgComponent.CMD_INSTALL, {'package': directory + name})
410                 
411         def ipkgCallback(self, event, param):
412                 print "ipkgCallback"
413                 if event == IpkgComponent.EVENT_DONE:
414                         self.installNext()
415                 elif event == IpkgComponent.EVENT_ERROR:
416                         self.installNext()
417         
418         def installSkin(self, directory, name):
419                 print "installing skin:", directory, " - ", name
420                 print "cp -a %s %s" % (directory, resolveFilename(SCOPE_SKIN))
421                 if self.blocking:
422                         copytree(directory, resolveFilename(SCOPE_SKIN))
423                         self.installNext()
424                 else:
425                         if self.console.execute("cp -a %s %s" % (directory, resolveFilename(SCOPE_SKIN))):
426                                 print "execute failed"
427                                 self.installNext()
428
429         def mergeServices(self, directory, name, merge = False):
430                 print "merging services:", directory, " - ", name
431                 if os.path.isfile(directory + name):
432                         db = eDVBDB.getInstance()
433                         db.reloadServicelist()
434                         db.loadServicelist(directory + name)
435                         db.saveServicelist()
436                 self.installNext()
437
438         def installFavourites(self, directory, name):
439                 print "installing favourites:", directory, " - ", name
440                 self.reloadFavourites = True
441
442                 if self.blocking:
443                         copyfile(directory + name, resolveFilename(SCOPE_CONFIG))
444                         self.installNext()
445                 else:
446                         if self.console.execute("cp %s %s" % ((directory + name), resolveFilename(SCOPE_CONFIG))):
447                                 print "execute failed"
448                                 self.installNext()