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