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