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