1df061e2226c971025d72463911c486a8563805b
[enigma2.git] / lib / python / Plugins / SystemPlugins / SoftwareManager / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Screens.Console import Console
3 from Screens.ChoiceBox import ChoiceBox
4 from Screens.MessageBox import MessageBox
5 from Screens.Screen import Screen
6 from Screens.Ipkg import Ipkg
7 from Components.ActionMap import ActionMap, NumberActionMap
8 from Components.Input import Input
9 from Components.Ipkg import IpkgComponent
10 from Components.Label import Label
11 from Components.MenuList import MenuList
12 from Components.Sources.List import List
13 from Components.Slider import Slider
14 from Components.Harddisk import harddiskmanager
15 from Components.config import config,getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations
16 from Components.Console import Console
17 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
18 from Components.SelectionList import SelectionList
19 from Components.PluginComponent import plugins
20 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
21 from Tools.LoadPixmap import LoadPixmap
22 from enigma import eTimer, quitMainloop, RT_HALIGN_LEFT, RT_VALIGN_CENTER, eListboxPythonMultiContent, eListbox, gFont, getDesktop
23 from cPickle import dump, load
24
25 from os import path as os_path, system as os_system, unlink, stat, mkdir, popen, makedirs, listdir, access, rename, remove, W_OK, R_OK, F_OK
26 from time import time, gmtime, strftime, localtime
27 from stat import ST_MTIME
28 from datetime import date
29
30 from ImageWizard import ImageWizard
31 from BackupRestore import BackupSelection, RestoreMenu, BackupScreen, RestoreScreen, getBackupPath, getBackupFilename
32
33 config.plugins.configurationbackup = ConfigSubsection()
34 config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False)
35 config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf'])
36
37 def write_cache(cache_file, cache_data):
38         #Does a cPickle dump
39         if not os_path.isdir( os_path.dirname(cache_file) ):
40                 try:
41                         mkdir( os_path.dirname(cache_file) )
42                 except OSError:
43                             print os_path.dirname(cache_file), 'is a file'
44         fd = open(cache_file, 'w')
45         dump(cache_data, fd, -1)
46         fd.close()
47
48 def valid_cache(cache_file, cache_ttl):
49         #See if the cache file exists and is still living
50         try:
51                 mtime = stat(cache_file)[ST_MTIME]
52         except:
53                 return 0
54         curr_time = time()
55         if (curr_time - mtime) > cache_ttl:
56                 return 0
57         else:
58                 return 1
59
60 def load_cache(cache_file):
61         #Does a cPickle load
62         fd = open(cache_file)
63         cache_data = load(fd)
64         fd.close()
65         return cache_data
66
67
68 class UpdatePluginMenu(Screen):
69         skin = """
70                 <screen name="UpdatePluginMenu" position="90,130" size="550,330" title="Softwaremanager..." >
71                         <ePixmap pixmap="skin_default/border_menu.png" position="10,10" zPosition="1" size="250,300" transparent="1" alphatest="on" />
72                         <widget source="menu" render="Listbox" position="20,20" size="230,260" scrollbarMode="showOnDemand">
73                                 <convert type="TemplatedMultiContent">
74                                         {"template": [
75                                                         MultiContentEntryText(pos = (2, 2), size = (230, 22), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText,
76                                                 ],
77                                         "fonts": [gFont("Regular", 20)],
78                                         "itemHeight": 25
79                                         }
80                                 </convert>
81                         </widget>
82                         <widget source="menu" render="Listbox" position="280,10" size="230,300" scrollbarMode="showNever" selectionDisabled="1">
83                                 <convert type="TemplatedMultiContent">
84                                         {"template": [
85                                                         MultiContentEntryText(pos = (2, 2), size = (230, 300), flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text = 2), # index 2 is the Description,
86                                                 ],
87                                         "fonts": [gFont("Regular", 20)],
88                                         "itemHeight": 230
89                                         }
90                                 </convert>
91                         </widget>
92                 </screen>"""
93                 
94         def __init__(self, session, args = 0):
95                 Screen.__init__(self, session)
96                 self.skin_path = plugin_path
97                 self.menu = args
98                 self.list = []
99                 self.oktext = _("\nPress OK on your remote control to continue.")
100                 self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
101                 if self.menu == 0:
102                         self.list.append(("software-update", _("Software update"), _("\nOnline update of your Dreambox software." ) + self.oktext) )
103                         self.list.append(("software-restore", _("Software restore"), _("\nRestore your Dreambox with a new firmware." ) + self.oktext))
104                         self.list.append(("system-backup", _("Backup system settings"), _("\nBackup your Dreambox settings." ) + self.oktext))
105                         self.list.append(("system-restore",_("Restore system settings"), _("\nRestore your Dreambox settings." ) + self.oktext))
106                         if config.usage.setup_level.index >= 2: # expert+
107                                 self.list.append(("advanced", _("Advanced Options"), _("\nAdvanced options and settings." ) + self.oktext))
108                 elif self.menu == 1:
109                         self.list.append(("ipkg-manager", _("Packet management"),  _("\nView, install and remove available or installed packages." ) + self.oktext))
110                         self.list.append(("ipkg-install", _("Install local IPKG"),  _("\nScan for local packages and install them." ) + self.oktext))
111                         self.list.append(("advancedrestore", _("Advanced restore"), _("\nRestore your backups by date." ) + self.oktext))
112                         self.list.append(("backuplocation", _("Choose backup location"),  _("\nSelect your backup device.\nCurrent device: " ) + config.plugins.configurationbackup.backuplocation.value + self.oktext ))
113                         self.list.append(("backupfiles", _("Choose backup files"),  _("Select files for backup. Currently selected:\n" ) + self.backupdirs + self.oktext))
114                         self.list.append(("ipkg-source",_("Choose upgrade source"), _("\nEdit the upgrade source address." ) + self.oktext))
115
116                 self["menu"] = List(self.list)
117                                 
118                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
119                 {
120                         "ok": self.go,
121                         "back": self.close,
122                         "red": self.close,
123                 }, -1)
124
125                 self.onLayoutFinish.append(self.layoutFinished)
126                 self.backuppath = getBackupPath()
127                 self.backupfile = getBackupFilename()
128                 self.fullbackupfilename = self.backuppath + "/" + self.backupfile
129                 self.onShown.append(self.setWindowTitle)
130                 
131         def layoutFinished(self):
132                 idx = 0
133                 self["menu"].index = idx
134                 
135         def setWindowTitle(self):
136                 self.setTitle(_("Software manager..."))
137                 
138         def go(self):
139                 current = self["menu"].getCurrent()
140                 if current:
141                         current = current[0]
142                         if self.menu == 0:
143                                 if (current == "software-restore"):
144                                         self.session.open(ImageWizard)
145                                 elif (current == "software-update"):
146                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to update your Dreambox?")+"\n"+_("\nAfter pressing OK, please wait!"))
147                                 elif (current == "advanced"):
148                                         self.session.open(UpdatePluginMenu, 1)
149                                 elif (current == "system-backup"):
150                                         self.session.openWithCallback(self.backupDone,BackupScreen, runBackup = True)
151                                 elif (current == "system-restore"):
152                                         if os_path.exists(self.fullbackupfilename):
153                                                 self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore your Enigma2 backup?\nEnigma2 will restart after the restore"))
154                                         else:   
155                                                 self.session.open(MessageBox, _("Sorry no backups found!"), MessageBox.TYPE_INFO)
156                         elif self.menu == 1:
157                                 if (current == "ipkg-manager"):
158                                         self.session.open(PacketManager, self.skin_path)
159                                 elif (current == "ipkg-source"):
160                                         self.session.open(IPKGMenu, self.skin_path)
161                                         #self.session.open(IPKGSource)
162                                 elif (current == "ipkg-install"):
163                                         try:
164                                                 from Plugins.Extensions.MediaScanner.plugin import main
165                                                 main(self.session)
166                                         except:
167                                                 self.session.open(MessageBox, _("Sorry MediaScanner is not installed!"), MessageBox.TYPE_INFO)
168                                 elif (current == "backuplocation"):
169                                         parts = [ (r.description, r.mountpoint, self.session) for r in harddiskmanager.getMountedPartitions(onlyhotplug = False)]
170                                         for x in parts:
171                                                 if not access(x[1], F_OK|R_OK|W_OK) or x[1] == '/':
172                                                         parts.remove(x)
173                                         for x in parts:
174                                                 if x[1].startswith('/autofs/'):
175                                                         parts.remove(x)
176                                         if len(parts):
177                                                 self.session.openWithCallback(self.backuplocation_choosen, ChoiceBox, title = _("Please select medium to use as backup location"), list = parts)
178                                 elif (current == "backupfiles"):
179                                         self.session.openWithCallback(self.backupfiles_choosen,BackupSelection)
180                                 elif (current == "advancedrestore"):
181                                         self.session.open(RestoreMenu, self.skin_path)
182
183         def backupfiles_choosen(self, ret):
184                 self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
185
186         def backuplocation_choosen(self, option):
187                 if option is not None:
188                         config.plugins.configurationbackup.backuplocation.value = str(option[1])
189                 config.plugins.configurationbackup.backuplocation.save()
190                 config.plugins.configurationbackup.save()
191                 config.save()
192                 self.createBackupfolders()
193         
194         def runUpgrade(self, result):
195                 if result:
196                         self.session.open(UpdatePlugin, self.skin_path)
197
198         """def runFinished(self):
199                 self.session.openWithCallback(self.reboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
200                 
201         def reboot(self, result):
202                 if result is None:
203                         return
204                 if result:
205                         quitMainloop(3)"""
206
207         def createBackupfolders(self):
208                 print "Creating backup folder if not already there..."
209                 self.backuppath = getBackupPath()
210                 try:
211                         if (os_path.exists(self.backuppath) == False):
212                                 makedirs(self.backuppath)
213                 except OSError:
214                         self.session.open(MessageBox, _("Sorry, your backup destination is not writeable.\n\nPlease choose another one."), MessageBox.TYPE_INFO)
215
216         def backupDone(self,retval = None):
217                 if retval is True:
218                         self.session.open(MessageBox, _("Backup done."), MessageBox.TYPE_INFO)
219                 else:
220                         self.session.open(MessageBox, _("Backup failed."), MessageBox.TYPE_INFO)
221
222         def startRestore(self, ret = False):
223                 if (ret == True):
224                         self.exe = True
225                         self.session.open(RestoreScreen, runRestore = True)
226
227 class IPKGMenu(Screen):
228         skin = """
229                 <screen name="IPKGMenu" position="135,144" size="450,320" title="Select IPKG source......" >
230                         <widget name="filelist" position="10,10" size="430,240" scrollbarMode="showOnDemand" />
231                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,280" zPosition="2" size="140,40" transparent="1" alphatest="on" />
232                         <widget name="closetext" position="20,290" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
233                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,280" zPosition="2" size="140,40" transparent="1" alphatest="on" />
234                         <widget name="edittext" position="170,290" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
235                 </screen>"""
236
237         def __init__(self, session, plugin_path):
238                 Screen.__init__(self, session)
239                 self.skin_path = plugin_path
240                 
241                 self["closetext"] = Label(_("Close"))
242                 self["edittext"] = Label(_("Edit"))
243
244                 self.sel = []
245                 self.val = []
246                 self.entry = False
247                 self.exe = False
248                 
249                 self.path = ""
250
251                 self["actions"] = NumberActionMap(["SetupActions"],
252                 {
253                         "ok": self.KeyOk,
254                         "cancel": self.keyCancel
255                 }, -1)
256
257                 self["shortcuts"] = ActionMap(["ShortcutActions"],
258                 {
259                         "red": self.keyCancel,
260                         "green": self.KeyOk,
261                 })
262                 self.flist = []
263                 self["filelist"] = MenuList(self.flist)
264                 self.fill_list()
265                 self.onLayoutFinish.append(self.layoutFinished)
266
267         def layoutFinished(self):
268                 self.setWindowTitle()
269
270         def setWindowTitle(self):
271                 self.setTitle(_("Select IPKG source to edit..."))
272
273
274         def fill_list(self):
275                 self.flist = []
276                 self.path = '/etc/ipkg/'
277                 if (os_path.exists(self.path) == False):
278                         self.entry = False
279                         return
280                 for file in listdir(self.path):
281                         if (file.endswith(".conf")):
282                                 if file != 'arch.conf':
283                                         self.flist.append((file))
284                                         self.entry = True
285                                         self["filelist"].l.setList(self.flist)
286
287         def KeyOk(self):
288                 if (self.exe == False) and (self.entry == True):
289                         self.sel = self["filelist"].getCurrent()
290                         self.val = self.path + self.sel
291                         self.session.open(IPKGSource, self.val)
292
293         def keyCancel(self):
294                 self.close()
295
296         def Exit(self):
297                 self.close()
298
299
300 class IPKGSource(Screen):
301         skin = """
302                 <screen name="IPKGSource" position="100,100" size="550,80" title="IPKG source" >
303                         <widget name="text" position="10,10" size="530,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
304                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
305                         <widget name="closetext" position="20,50" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
306                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
307                         <widget name="edittext" position="170,50" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
308                 </screen>"""
309                 
310         def __init__(self, session, configfile = None):
311                 Screen.__init__(self, session)
312                 self.session = session
313                 self.configfile = configfile
314                 text = ""
315                 if self.configfile:
316                         try:
317                                 fp = file(configfile, 'r')
318                                 sources = fp.readlines()
319                                 if sources:
320                                         text = sources[0]
321                                 fp.close()
322                         except IOError:
323                                 pass
324
325                 desk = getDesktop(0)
326                 x= int(desk.size().width())
327                 y= int(desk.size().height())
328                 #print "[IPKGSource] mainscreen: current desktop size: %dx%d" % (x,y)
329
330                 self["closetext"] = Label(_("Cancel"))
331                 self["edittext"] = Label(_("Save"))
332                 
333                 if (y>=720):
334                         self["text"] = Input(text, maxSize=False, type=Input.TEXT)
335                 else:
336                         self["text"] = Input(text, maxSize=False, visible_width = 55, type=Input.TEXT)
337                         
338                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions","ShortcutActions"], 
339                 {
340                         "ok": self.go,
341                         "back": self.close,
342                         "red": self.close,
343                         "green": self.go,
344                         "left": self.keyLeft,
345                         "right": self.keyRight,
346                         "home": self.keyHome,
347                         "end": self.keyEnd,
348                         "deleteForward": self.keyDeleteForward,
349                         "deleteBackward": self.keyDeleteBackward,
350                         "1": self.keyNumberGlobal,
351                         "2": self.keyNumberGlobal,
352                         "3": self.keyNumberGlobal,
353                         "4": self.keyNumberGlobal,
354                         "5": self.keyNumberGlobal,
355                         "6": self.keyNumberGlobal,
356                         "7": self.keyNumberGlobal,
357                         "8": self.keyNumberGlobal,
358                         "9": self.keyNumberGlobal,
359                         "0": self.keyNumberGlobal
360                 }, -1)
361
362                 self.onLayoutFinish.append(self.layoutFinished)
363
364         def layoutFinished(self):
365                 self.setWindowTitle()
366                 self["text"].right()
367
368         def setWindowTitle(self):
369                 self.setTitle(_("Edit IPKG source URL..."))
370                 
371         def go(self):
372                 text = self["text"].getText()
373                 if text:
374                         fp = file(self.configfile, 'w')
375                         fp.write(text)
376                         fp.write("\n")
377                         fp.close()
378                 self.close()
379                 
380         def keyLeft(self):
381                 self["text"].left()
382         
383         def keyRight(self):
384                 self["text"].right()
385         
386         def keyHome(self):
387                 self["text"].home()
388         
389         def keyEnd(self):
390                 self["text"].end()
391         
392         def keyDeleteForward(self):
393                 self["text"].delete()
394         
395         def keyDeleteBackward(self):
396                 self["text"].deleteBackward()
397         
398         def keyNumberGlobal(self, number):
399                 print "pressed", number
400                 self["text"].number(number)
401
402
403 class PacketManager(Screen):
404         skin = """
405                 <screen position="90,80" size="530,420" title="IPKG upgrade..." >
406                         <widget source="list" render="Listbox" position="5,10" size="520,365" scrollbarMode="showOnDemand">
407                                 <convert type="TemplatedMultiContent">
408                                         {"template": [
409                                                         MultiContentEntryText(pos = (5, 1), size = (440, 28), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
410                                                         MultiContentEntryText(pos = (5, 26), size = (440, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description
411                                                         MultiContentEntryPixmapAlphaTest(pos = (445, 2), size = (48, 48), png = 4), # index 4 is the status pixmap
412                                                         MultiContentEntryPixmapAlphaTest(pos = (5, 50), size = (510, 2), png = 5), # index 4 is the div pixmap
413                                                 ],
414                                         "fonts": [gFont("Regular", 22),gFont("Regular", 14)],
415                                         "itemHeight": 52
416                                         }
417                                 </convert>
418                         </widget>
419                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
420                         <widget name="closetext" position="20,390" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
421                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
422                         <widget name="reloadtext" position="170,390" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
423                 </screen>"""
424                 
425         def __init__(self, session, plugin_path, args = None):
426                 Screen.__init__(self, session)
427                 self.session = session
428                 self.skin_path = plugin_path
429
430                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"], 
431                 {
432                         "ok": self.go,
433                         "back": self.exit,
434                         "red": self.exit,
435                         "green": self.reload,
436                 }, -1)
437                 
438                 self.list = []
439                 self.statuslist = []
440                 self["list"] = List(self.list)
441                 self["closetext"] = Label(_("Close"))
442                 self["reloadtext"] = Label(_("Reload"))
443
444                 self.list_updating = True
445                 self.packetlist = []
446                 self.installed_packetlist = {}
447                 self.Console = Console()
448                 self.cmdList = []
449                 self.cachelist = []
450                 self.cache_ttl = 86400  #600 is default, 0 disables, Seconds cache is considered valid (24h should be ok for caching ipkgs)
451                 self.cache_file = '/usr/lib/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache' #Path to cache directory   
452                 self.oktext = _("\nAfter pressing OK, please wait!")
453
454                 self.ipkg = IpkgComponent()
455                 self.ipkg.addCallback(self.ipkgCallback)
456                 self.onShown.append(self.setWindowTitle)
457                 self.onLayoutFinish.append(self.rebuildList)
458
459         def exit(self):
460                 self.ipkg.stop()
461                 if self.Console is not None:
462                         if len(self.Console.appContainers):
463                                 for name in self.Console.appContainers.keys():
464                                         self.Console.kill(name)
465                 self.close()
466
467         def reload(self):
468                 if (os_path.exists(self.cache_file) == True):
469                         remove(self.cache_file)
470                         self.list_updating = True
471                         self.rebuildList()
472                         
473         def setWindowTitle(self):
474                 self.setTitle(_("Packet manager"))
475
476         def setStatus(self,status = None):
477                 if status:
478                         self.statuslist = []
479                         divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
480                         if status == 'update':
481                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
482                                 self.statuslist.append(( _("Package list update"), '', _("Trying to download a new packetlist. Please wait..." ),'',statuspng, divpng ))
483                                 self['list'].setList(self.statuslist)   
484                         elif status == 'error':
485                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
486                                 self.statuslist.append(( _("Error"), '', _("There was an error downloading the packetlist. Please try again." ),'',statuspng, divpng ))
487                                 self['list'].setList(self.statuslist)                           
488
489         def rebuildList(self):
490                 self.setStatus('update')
491                 self.inv_cache = 0
492                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
493                 if self.cache_ttl > 0 and self.vc != 0:
494                         try:
495                                 self.buildPacketList()
496                         except:
497                                 self.inv_cache = 1
498                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
499                         self.run = 0
500                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
501
502         def go(self, returnValue = None):
503                 cur = self["list"].getCurrent()
504                 if cur:
505                         status = cur[3]
506                         package = cur[0]
507                         self.cmdList = []
508                         if status == 'installed':
509                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package }))
510                                 if len(self.cmdList):
511                                         self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n" + package + "\n" + self.oktext))
512                         elif status == 'upgradeable':
513                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package }))
514                                 if len(self.cmdList):
515                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to upgrade the package:\n" + package + "\n" + self.oktext))
516                         elif status == "installable":
517                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package }))
518                                 if len(self.cmdList):
519                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n" + package + "\n" + self.oktext))
520
521         def runRemove(self, result):
522                 if result:
523                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
524
525         def runRemoveFinished(self):
526                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
527
528         def RemoveReboot(self, result):
529                 if result is None:
530                         return
531                 if result is False:
532                         cur = self["list"].getCurrent()
533                         if cur:
534                                 item = self['list'].getIndex()
535                                 self.list[item] = self.buildEntryComponent(cur[0], cur[1], cur[2], 'installable')
536                                 self.cachelist[item] = [cur[0], cur[1], cur[2], 'installable']
537                                 self['list'].setList(self.list)
538                                 write_cache(self.cache_file, self.cachelist)
539                                 self.reloadPluginlist()
540                 if result:
541                         quitMainloop(3)
542
543         def runUpgrade(self, result):
544                 if result:
545                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
546
547         def runUpgradeFinished(self):
548                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
549                 
550         def UpgradeReboot(self, result):
551                 if result is None:
552                         return
553                 if result is False:
554                         cur = self["list"].getCurrent()
555                         if cur:
556                                 item = self['list'].getIndex()
557                                 self.list[item] = self.buildEntryComponent(cur[0], cur[1], cur[2], 'installed')
558                                 self.cachelist[item] = [cur[0], cur[1], cur[2], 'installed']
559                                 self['list'].setList(self.list)
560                                 write_cache(self.cache_file, self.cachelist)
561                                 self.reloadPluginlist()
562                 if result:
563                         quitMainloop(3)
564
565         def ipkgCallback(self, event, param):
566                 if event == IpkgComponent.EVENT_ERROR:
567                         self.list_updating = False
568                         self.setStatus('error')
569                 elif event == IpkgComponent.EVENT_DONE:
570                         if self.list_updating:
571                                 self.list_updating = False
572                                 if not self.Console:
573                                         self.Console = Console()
574                                 cmd = "ipkg list"
575                                 self.Console.ePopen(cmd, self.IpkgList_Finished)
576                 #print event, "-", param
577                 pass
578
579         def IpkgList_Finished(self, result, retval, extra_args = None):
580                 if len(result):
581                         self.packetlist = []
582                         for x in result.splitlines():
583                                 split = x.split(' - ')
584                                 if not (split[0].strip().endswith('-dbg') or split[0].strip().endswith('-dev')):
585                                         self.packetlist.append([split[0].strip(), split[1].strip(),split[2].strip()])
586                 if not self.Console:
587                         self.Console = Console()
588                 cmd = "ipkg list_installed"
589                 self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
590
591         def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
592                 if len(result):
593                         self.installed_packetlist = {}
594                         for x in result.splitlines():
595                                 split = x.split(' - ')
596                                 if not (split[0].strip().endswith('-dbg') or split[0].strip().endswith('-dev')):
597                                         self.installed_packetlist[split[0].strip()] = split[1].strip()
598                 self.buildPacketList()
599
600         def buildEntryComponent(self, name, version, description, state):
601                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
602                 if state == 'installed':
603                         installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
604                         return((name, version, description, state, installedpng, divpng))       
605                 elif state == 'upgradeable':
606                         upgradeablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgradeable.png"))
607                         return((name, version, description, state, upgradeablepng, divpng))     
608                 else:
609                         installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
610                         return((name, version, description, state, installablepng, divpng))
611
612         def buildPacketList(self):
613                 self.list = []
614                 self.cachelist = []
615
616                 if self.cache_ttl > 0 and self.vc != 0:
617                         print 'Loading packagelist cache from ',self.cache_file
618                         try:
619                                 self.cachelist = load_cache(self.cache_file)
620                                 if len(self.cachelist) > 0:
621                                         for x in self.cachelist:
622                                                 self.list.append(self.buildEntryComponent(x[0], x[1], x[2], x[3]))
623                                         self['list'].setList(self.list)
624                         except:
625                                 self.inv_cache = 1
626
627                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
628                         print 'rebuilding fresh package list'
629                         for x in self.packetlist:
630                                 status = ""
631                                 if self.installed_packetlist.has_key(x[0].strip()):
632                                         if self.installed_packetlist[x[0].strip()] == x[1].strip():
633                                                 status = "installed"
634                                                 self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
635                                         else:
636                                                 status = "upgradeable"
637                                                 self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
638                                 else:
639                                         status = "installable"
640                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
641                                 if not (x[0].strip().endswith('-dbg') or x[0].strip().endswith('-dev')):
642                                         self.cachelist.append([x[0].strip(), x[1].strip(), x[2].strip(), status])       
643                         write_cache(self.cache_file, self.cachelist)
644                         self['list'].setList(self.list)
645
646         def reloadPluginlist(self):
647                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
648
649 class UpdatePlugin(Screen):
650         skin = """
651                 <screen position="100,100" size="550,200" title="Software Update..." >
652                         <widget name="activityslider" position="0,0" size="550,5"  />
653                         <widget name="slider" position="0,100" size="550,30"  />
654                         <widget name="package" position="10,30" size="540,20" font="Regular;18"/>
655                         <widget name="status" position="10,60" size="540,45" font="Regular;18"/>
656                 </screen>"""
657                 
658         def __init__(self, session, args = None):
659                 self.skin = UpdatePlugin.skin
660                 Screen.__init__(self, session)
661                 
662                 self.sliderPackages = { "dreambox-dvb-modules": 1, "enigma2": 2, "tuxbox-image-info": 3 }
663                 
664                 self.slider = Slider(0, 4)
665                 self["slider"] = self.slider
666                 self.activityslider = Slider(0, 100)
667                 self["activityslider"] = self.activityslider
668                 self.status = Label(_("Upgrading Dreambox... Please wait"))
669                 self["status"] = self.status
670                 self.package = Label()
671                 self["package"] = self.package
672                 
673                 self.packages = 0
674                 self.error = 0
675                 
676                 self.activity = 0
677                 self.activityTimer = eTimer()
678                 self.activityTimer.callback.append(self.doActivityTimer)
679                 self.activityTimer.start(100, False)
680                                 
681                 self.ipkg = IpkgComponent()
682                 self.ipkg.addCallback(self.ipkgCallback)
683                 
684                 self.updating = True
685                 self.package.setText(_("Package list update"))
686                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
687                         
688                 self["actions"] = ActionMap(["WizardActions"], 
689                 {
690                         "ok": self.exit,
691                         "back": self.exit
692                 }, -1)
693                 
694         def doActivityTimer(self):
695                 self.activity += 1
696                 if self.activity == 100:
697                         self.activity = 0
698                 self.activityslider.setValue(self.activity)
699                 
700         def ipkgCallback(self, event, param):
701                 if event == IpkgComponent.EVENT_DOWNLOAD:
702                         self.status.setText(_("Downloading"))
703                 elif event == IpkgComponent.EVENT_UPGRADE:
704                         if self.sliderPackages.has_key(param):
705                                 self.slider.setValue(self.sliderPackages[param])
706                         self.package.setText(param)
707                         self.status.setText(_("Upgrading"))
708                         self.packages += 1
709                 elif event == IpkgComponent.EVENT_INSTALL:
710                         self.package.setText(param)
711                         self.status.setText(_("Installing"))
712                         self.packages += 1
713                 elif event == IpkgComponent.EVENT_CONFIGURING:
714                         self.package.setText(param)
715                         self.status.setText(_("Configuring"))
716                 elif event == IpkgComponent.EVENT_MODIFIED:
717                         self.session.openWithCallback(
718                                 self.modificationCallback,
719                                 MessageBox,
720                                 _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param)
721                         )
722                 elif event == IpkgComponent.EVENT_ERROR:
723                         self.error += 1
724                 elif event == IpkgComponent.EVENT_DONE:
725                         if self.updating:
726                                 self.updating = False
727                                 self.ipkg.startCmd(IpkgComponent.CMD_UPGRADE, args = {'test_only': False})
728                         elif self.error == 0:
729                                 self.slider.setValue(4)
730                                 
731                                 self.activityTimer.stop()
732                                 self.activityslider.setValue(0)
733                                 
734                                 self.package.setText("")
735                                 self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages)
736                         else:
737                                 self.activityTimer.stop()
738                                 self.activityslider.setValue(0)
739                                 error = _("your dreambox might be unusable now. Please consult the manual for further assistance before rebooting your dreambox.")
740                                 if self.packages == 0:
741                                         error = _("No packages were upgraded yet. So you can check your network and try again.")
742                                 if self.updating:
743                                         error = _("Your dreambox isn't connected to the internet properly. Please check it and try again.")
744                                 self.status.setText(_("Error") +  " - " + error)
745                 #print event, "-", param
746                 pass
747
748         def modificationCallback(self, res):
749                 self.ipkg.write(res and "N" or "Y")
750
751         def exit(self):
752                 if not self.ipkg.isRunning():
753                         if self.packages != 0 and self.error == 0:
754                                 self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"))
755                         else:
756                                 self.close()
757                         
758         def exitAnswer(self, result):
759                 if result is not None and result:
760                         quitMainloop(2)
761                 self.close()
762
763
764
765 class IpkgInstaller(Screen):
766         skin = """
767                 <screen position="100,100" size="550,400" title="..." >
768                         <widget name="red" halign="center" valign="center" position="0,0" size="140,60" backgroundColor="red" font="Regular;21" />
769                         <widget name="green" halign="center" valign="center" position="140,0" text="Install selected" size="140,60" backgroundColor="green" font="Regular;21" />
770                         <widget name="yellow" halign="center" valign="center" position="280,0" size="140,60" backgroundColor="yellow" font="Regular;21" />
771                         <widget name="blue" halign="center" valign="center" position="420,0" size="140,60" backgroundColor="blue" font="Regular;21" />
772                         <widget name="list" position="0,60" size="550,360" />
773                 </screen>
774                 """
775         
776         def __init__(self, session, list):
777                 self.skin = IpkgInstaller.skin
778                 Screen.__init__(self, session)
779
780                 self.list = SelectionList()
781                 self["list"] = self.list
782                 for listindex in range(len(list)):
783                         self.list.addSelection(list[listindex], list[listindex], listindex, True)
784
785                 self["red"] = Label()
786                 self["green"] = Label()
787                 self["yellow"] = Label()
788                 self["blue"] = Label()
789                 
790                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 
791                 {
792                         "ok": self.list.toggleSelection, 
793                         "cancel": self.close, 
794                         "green": self.install
795                 }, -1)
796                 
797         def install(self):
798                 list = self.list.getSelectionsList()
799                 cmdList = []
800                 for item in list:
801                         cmdList.append((IpkgComponent.CMD_INSTALL, { "package": item[1] }))
802                 self.session.open(Ipkg, cmdList = cmdList)
803
804 def filescan_open(list, session, **kwargs):
805         filelist = [x.path for x in list]
806         session.open(IpkgInstaller, filelist) # list
807
808 def filescan(**kwargs):
809         from Components.Scanner import Scanner, ScanPath
810         return \
811                 Scanner(mimetypes = ["application/x-debian-package"], 
812                         paths_to_scan = 
813                                 [
814                                         ScanPath(path = "ipk", with_subdirs = True), 
815                                         ScanPath(path = "", with_subdirs = False), 
816                                 ], 
817                         name = "Ipkg", 
818                         description = "Install software updates...", 
819                         openfnc = filescan_open, )
820
821 def UpgradeMain(session, **kwargs):
822         session.open(UpdatePluginMenu)
823
824 def startSetup(menuid):
825         if menuid != "setup": 
826                 return [ ]
827         return [(_("Software manager") + "...", UpgradeMain, "software_manager", 50)]
828
829 def Plugins(path, **kwargs):
830         global plugin_path
831         plugin_path = path
832         list = [
833                 PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), 
834                 PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
835         ]
836         if config.usage.setup_level.index >= 2: # expert+       
837                 list.append(PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=UpgradeMain))     
838         return list