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