NFIFlash: hide hdd and other mounts (#480)
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / NFIFlash / downloader.py
1 # -*- coding: utf-8 -*-
2 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Screens.ChoiceBox import ChoiceBox
6 from Screens.HelpMenu import HelpableScreen
7 from Screens.TaskView import JobView
8 from Components.About import about
9 from Components.ActionMap import ActionMap
10 from Components.Sources.StaticText import StaticText
11 from Components.Sources.List import List
12 from Components.Label import Label
13 from Components.FileList import FileList
14 from Components.MenuList import MenuList
15 from Components.MultiContent import MultiContentEntryText
16 from Components.ScrollLabel import ScrollLabel
17 from Components.Harddisk import harddiskmanager
18 from Components.Task import Task, Job, job_manager, Condition
19 from Tools.Directories import fileExists
20 from Tools.HardwareInfo import HardwareInfo
21 from Tools.Downloader import downloadWithProgress
22 from enigma import eConsoleAppContainer, gFont, RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eTimer
23 from os import system, path, access, stat, remove, W_OK, R_OK
24 from twisted.web import client
25 from twisted.internet import reactor, defer
26 from twisted.python import failure
27 import re
28
29 class ImageDownloadJob(Job):
30         def __init__(self, url, filename, device=None, mountpoint="/"):
31                 Job.__init__(self, _("Download .NFI-Files for USB-Flasher"))
32                 if device:
33                         MountTask(self, device, mountpoint)
34                 ImageDownloadTask(self, url, mountpoint+filename)
35                 ImageDownloadTask(self, url[:-4]+".nfo", mountpoint+filename[:-4]+".nfo")
36                 if device:
37                         UmountTask(self, mountpoint)
38
39         def retry(self):
40                 self.tasks[0].args += self.tasks[0].retryargs
41                 Job.retry(self)
42
43 class MountTask(Task):
44         def __init__(self, job, device, mountpoint):
45                 Task.__init__(self, job, ("mount"))
46                 self.setTool("mount")
47                 self.args += [device, mountpoint, "-orw,sync"]
48                 self.weighting = 1
49
50         def processOutput(self, data):
51                 print "[MountTask] output:", data
52
53 class UmountTask(Task):
54         def __init__(self, job, mountpoint):
55                 Task.__init__(self, job, ("mount"))
56                 self.setTool("umount")
57                 self.args += [mountpoint]
58                 self.weighting = 1
59
60 class DownloaderPostcondition(Condition):
61         def check(self, task):
62                 return task.returncode == 0
63
64         def getErrorMessage(self, task):
65                 return self.error_message
66                 
67 class ImageDownloadTask(Task):
68         def __init__(self, job, url, path):
69                 Task.__init__(self, job, _("Downloading"))
70                 self.postconditions.append(DownloaderPostcondition())
71                 self.job = job
72                 self.url = url
73                 self.path = path
74                 self.error_message = ""
75                 self.last_recvbytes = 0
76                 self.error_message = None
77                 
78         def run(self, callback):
79                 self.callback = callback
80                 self.download = downloadWithProgress(self.url,self.path)
81                 self.download.addProgress(self.download_progress)
82                 self.download.start().addCallback(self.download_finished).addErrback(self.download_failed)
83                 print "[ImageDownloadTask] downloading", self.url, "to", self.path
84
85         def download_progress(self, recvbytes, totalbytes):
86                 #print "[update_progress] recvbytes=%d, totalbytes=%d" % (recvbytes, totalbytes)
87                 if ( recvbytes - self.last_recvbytes  ) > 10000: # anti-flicker
88                         self.progress = int(100*(float(recvbytes)/float(totalbytes)))
89                         self.name = _("Downloading") + ' ' + "%d of %d kBytes" % (recvbytes/1024, totalbytes/1024)
90                         self.last_recvbytes = recvbytes
91                 
92         def download_failed(self, failure_instance=None, error_message=""):
93                 self.error_message = error_message
94                 if error_message == "" and failure_instance is not None:
95                         self.error_message = failure_instance.getErrorMessage()
96                 print "[download_failed]", self.error_message
97                 Task.processFinished(self, 1)
98                 
99         def download_finished(self, string=""):
100                 print "[download_finished]", string
101                 Task.processFinished(self, 0)
102
103 class StickWizardJob(Job):
104         def __init__(self, path):
105                 Job.__init__(self, _("USB stick wizard"))
106                 self.path = path
107                 self.device = path
108                 while self.device[-1:] == "/" or self.device[-1:].isdigit():
109                         self.device = self.device[:-1]
110                                 
111                 box = HardwareInfo().get_device_name()
112                 url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s.tar.bz2" % box
113                 self.downloadfilename = "/tmp/dreambox-nfiflasher-%s.tar.bz2" % box
114                 self.imagefilename = "/tmp/nfiflash_%s.img" % box
115                 #UmountTask(self, device)
116                 PartitionTask(self)
117                 ImageDownloadTask(self, url, self.downloadfilename)
118                 UnpackTask(self)
119                 CopyTask(self)
120
121 class PartitionTaskPostcondition(Condition):
122         def check(self, task):
123                 return task.returncode == 0
124
125         def getErrorMessage(self, task):
126                 return {
127                         task.ERROR_BLKRRPART: ("Device or resource busy"),
128                         task.ERROR_UNKNOWN: (task.errormsg)
129                 }[task.error]
130                 
131 class PartitionTask(Task):
132         ERROR_UNKNOWN, ERROR_BLKRRPART = range(2)
133         def __init__(self, job):
134                 Task.__init__(self, job, ("partitioning"))
135                 self.postconditions.append(PartitionTaskPostcondition())
136                 self.job = job          
137                 self.setTool("sfdisk")
138                 self.args += [self.job.device]
139                 self.weighting = 10
140                 self.initial_input = "0 - 0x6 *\n;\n;\n;\ny"
141                 self.errormsg = ""
142         
143         def run(self, callback):
144                 Task.run(self, callback)
145         
146         def processOutput(self, data):
147                 print "[PartitionTask] output:", data
148                 if data.startswith("BLKRRPART:"):
149                         self.error = self.ERROR_BLKRRPART
150                 else:
151                         self.error = self.ERROR_UNKNOWN
152                         self.errormsg = data
153
154 class UnpackTask(Task):
155         def __init__(self, job):
156                 Task.__init__(self, job, ("Unpacking USB flasher image..."))
157                 self.job = job
158                 self.setTool("tar")
159                 self.args += ["-xjvf", self.job.downloadfilename]
160                 self.weighting = 80
161                 self.end = 80
162                 self.delayTimer = eTimer()
163                 self.delayTimer.callback.append(self.progress_increment)
164         
165         def run(self, callback):
166                 Task.run(self, callback)
167                 self.delayTimer.start(1000, False)
168                 
169         def progress_increment(self):
170                 self.progress += 1
171
172         def processOutput(self, data):
173                 print "[UnpackTask] output: \'%s\'" % data
174                 self.job.imagefilename = data
175         
176         def afterRun(self):
177                 self.delayTimer.callback.remove(self.progress_increment)
178
179 class CopyTask(Task):
180         def __init__(self, job):
181                 Task.__init__(self, job, ("Copying USB flasher boot image to stick..."))
182                 self.job = job
183                 self.setTool("dd")
184                 self.args += ["if=%s" % self.job.imagefilename, "of=%s1" % self.job.device]
185                 self.weighting = 20
186                 self.end = 20
187                 self.delayTimer = eTimer()
188                 self.delayTimer.callback.append(self.progress_increment)
189
190         def run(self, callback):
191                 Task.run(self, callback)
192                 self.delayTimer.start(100, False)
193                 
194         def progress_increment(self):
195                 self.progress += 1
196
197         def processOutput(self, data):
198                 print "[CopyTask] output:", data
199
200         def afterRun(self):
201                 self.delayTimer.callback.remove(self.progress_increment)
202
203 class NFOViewer(Screen):
204         skin = """
205                 <screen name="NFOViewer" position="center,center" size="610,410" title="Changelog viewer" >
206                         <widget name="changelog" position="10,10" size="590,380" font="Regular;16" />
207                 </screen>"""
208
209         def __init__(self, session, nfo):
210                 Screen.__init__(self, session)
211                 self["changelog"] = ScrollLabel(nfo)
212
213                 self["ViewerActions"] = ActionMap(["SetupActions", "ColorActions", "DirectionActions"],
214                         {
215                                 "green": self.exit,
216                                 "red": self.exit,
217                                 "ok": self.exit,
218                                 "cancel": self.exit,
219                                 "down": self.pageDown,
220                                 "up": self.pageUp
221                         })
222         def pageUp(self):
223                 self["changelog"].pageUp()
224
225         def pageDown(self):
226                 self["changelog"].pageDown()
227
228         def exit(self):
229                 self.close(False)
230
231 class feedDownloader:
232         def __init__(self, feed_base, box, OE_vers):
233                 print "[feedDownloader::init] feed_base=%s, box=%s" % (feed_base, box)
234                 self.feed_base = feed_base
235                 self.OE_vers = OE_vers
236                 self.box = box
237         
238         def getList(self, callback, errback):
239                 self.urlbase = "%s/%s/%s/images/" % (self.feed_base, self.OE_vers, self.box)
240                 print "[getList]", self.urlbase
241                 self.callback = callback
242                 self.errback = errback
243                 client.getPage(self.urlbase).addCallback(self.feed_finished).addErrback(self.feed_failed)
244
245         def feed_failed(self, failure_instance):
246                 print "[feed_failed]", str(failure_instance)
247                 self.errback(failure_instance.getErrorMessage())
248
249         def feed_finished(self, feedhtml):
250                 print "[feed_finished]"
251                 fileresultmask = re.compile("<a class=[\'\"]nfi[\'\"] href=[\'\"](?P<url>.*?)[\'\"]>(?P<name>.*?.nfi)</a>", re.DOTALL)
252                 searchresults = fileresultmask.finditer(feedhtml)
253                 fileresultlist = []
254                 if searchresults:
255                         for x in searchresults:
256                                 url = x.group("url")
257                                 if url[0:7] != "http://":
258                                         url = self.urlbase + x.group("url")
259                                 name = x.group("name")
260                                 entry = (name, url)
261                                 fileresultlist.append(entry)
262                 self.callback(fileresultlist, self.OE_vers)
263
264 class DeviceBrowser(Screen, HelpableScreen):
265         skin = """
266                 <screen name="DeviceBrowser" position="center,center" size="520,430" title="Please select target medium" >
267                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
268                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
269                         <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
270                         <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
271                         <widget source="message" render="Label" position="5,50" size="510,150" font="Regular;16" />
272                         <widget name="filelist" position="5,210" size="510,220" scrollbarMode="showOnDemand" />
273                 </screen>"""
274
275         def __init__(self, session, startdir, message="", showDirectories = True, showFiles = True, showMountpoints = True, matchingPattern = "", useServiceRef = False, inhibitDirs = False, inhibitMounts = False, isTop = False, enableWrapAround = False, additionalExtensions = None):
276                 Screen.__init__(self, session)
277
278                 HelpableScreen.__init__(self)
279
280                 self["key_red"] = StaticText(_("Cancel"))
281                 self["key_green"] = StaticText()
282                 self["message"] = StaticText(message)
283
284                 self.filelist = FileList(startdir, showDirectories = showDirectories, showFiles = showFiles, showMountpoints = showMountpoints, matchingPattern = matchingPattern, useServiceRef = useServiceRef, inhibitDirs = inhibitDirs, inhibitMounts = inhibitMounts, isTop = isTop, enableWrapAround = enableWrapAround, additionalExtensions = additionalExtensions)
285                 self["filelist"] = self.filelist
286
287                 self["FilelistActions"] = ActionMap(["SetupActions", "ColorActions"],
288                         {
289                                 "green": self.use,
290                                 "red": self.exit,
291                                 "ok": self.ok,
292                                 "cancel": self.exit
293                         })
294                 
295                 hotplugNotifier.append(self.hotplugCB)
296                 self.onShown.append(self.updateButton)
297                 self.onClose.append(self.removeHotplug)
298
299         def hotplugCB(self, dev, action):
300                 print "[hotplugCB]", dev, action
301                 self.updateButton()
302         
303         def updateButton(self):
304                 
305                 if self["filelist"].getFilename() or self["filelist"].getCurrentDirectory():
306                         self["key_green"].text = _("Use")
307                 else:
308                         self["key_green"].text = ""
309         
310         def removeHotplug(self):
311                 print "[removeHotplug]"
312                 hotplugNotifier.remove(self.hotplugCB)
313
314         def ok(self):
315                 if self.filelist.canDescent():
316                         if self["filelist"].showMountpoints == True and self["filelist"].showDirectories == False:
317                                 self.use()
318                         else:
319                                 self.filelist.descent()
320
321         def use(self):
322                 print "[use]", self["filelist"].getCurrentDirectory(), self["filelist"].getFilename()
323                 if self["filelist"].getCurrentDirectory() is not None:
324                         if self.filelist.canDescent() and self["filelist"].getFilename() and len(self["filelist"].getFilename()) > len(self["filelist"].getCurrentDirectory()):
325                                 self.filelist.descent()
326                         self.close(self["filelist"].getCurrentDirectory())
327                 elif self["filelist"].getFilename():
328                         self.close(self["filelist"].getFilename())
329
330         def exit(self):
331                 self.close(False)
332
333 (ALLIMAGES, RELEASE, EXPERIMENTAL, STICK_WIZARD, START) = range(5)
334
335 class NFIDownload(Screen):
336         skin = """
337         <screen name="NFIDownload" position="center,center" size="610,410" title="NFIDownload" >
338                 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
339                 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
340                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" />
341                 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" />
342                 <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
343                 <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
344                 <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
345                 <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
346                 <ePixmap pixmap="skin_default/border_menu_350.png" position="5,50" zPosition="1" size="350,300" transparent="1" alphatest="on" />
347                 <widget source="menu" render="Listbox" position="15,60" size="330,290" scrollbarMode="showOnDemand">
348                         <convert type="TemplatedMultiContent">
349                                 {"templates": 
350                                         {"default": (25, [
351                                                 MultiContentEntryText(pos = (2, 2), size = (330, 24), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText,
352                                         ], True, "showOnDemand")
353                                         },
354                                 "fonts": [gFont("Regular", 22)],
355                                 "itemHeight": 25
356                                 }
357                         </convert>
358                 </widget>
359                 <widget source="menu" render="Listbox" position="360,50" size="240,300" scrollbarMode="showNever" selectionDisabled="1">
360                         <convert type="TemplatedMultiContent">
361                                 {"templates":
362                                         {"default": (300, [
363                                                 MultiContentEntryText(pos = (2, 2), size = (240, 300), flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text = 2), # index 2 is the Description,
364                                         ], False, "showNever")
365                                         },      
366                                 "fonts": [gFont("Regular", 22)],
367                                 "itemHeight": 300
368                                 }
369                         </convert>
370                 </widget>
371                 <widget source="status" render="Label" position="5,360" zPosition="10" size="600,50" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
372         </screen>"""
373                 
374         def __init__(self, session, destdir=None):
375                 Screen.__init__(self, session)
376                 #self.skin_path = plugin_path
377                 #self.menu = args
378                 
379                 self.box = HardwareInfo().get_device_name()
380                 self.feed_base = "http://www.dreamboxupdate.com/opendreambox" #/1.5/%s/images/" % self.box      
381                 self.usbmountpoint = "/mnt/usb/"
382
383                 self.menulist = []
384
385                 self["menu"] = List(self.menulist)
386                 self["key_red"] = StaticText(_("Close"))
387                 self["key_green"] = StaticText()
388                 self["key_yellow"] = StaticText()
389                 self["key_blue"] = StaticText()
390
391                 self["status"] = StaticText(_("Please wait... Loading list..."))
392
393                 self["shortcuts"] = ActionMap(["OkCancelActions", "ColorActions", "ShortcutActions", "DirectionActions"],
394                 {
395                         "ok": self.keyOk,
396                         "green": self.keyOk,
397                         "red": self.keyRed,
398                         "blue": self.keyBlue,
399                         "up": self.keyUp,
400                         "upRepeated": self.keyUp,
401                         "downRepeated": self.keyDown,
402                         "down": self.keyDown,
403                         "cancel": self.close,
404                 }, -1)
405                 self.onShown.append(self.go)
406                 self.feedlists = [[],[],[]]
407                 self.branch = START
408                 self.container = eConsoleAppContainer()
409                 self.container.dataAvail.append(self.tool_avail)
410                 self.taskstring = ""
411                 self.image_idx = 0
412                 self.nfofilename = ""
413                 self.nfo = ""
414                 self.target_dir = None
415
416         def tool_avail(self, string):
417                 print "[tool_avail]" + string
418                 self.taskstring += string
419
420         def go(self):
421                 self.onShown.remove(self.go)
422                 self["menu"].index = 0
423                 url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s-md5sums" % self.box
424                 client.getPage(url).addCallback(self.md5sums_finished).addErrback(self.feed_failed)
425
426         def md5sums_finished(self, data):
427                 print "[md5sums_finished]", data
428                 self.stickimage_md5 = data
429                 self.checkUSBStick()
430
431         def keyRed(self):
432                 if self.branch == START:
433                         self.close()
434                 else:
435                         self.branch = START
436                         self["menu"].setList(self.menulist)
437                 #elif self.branch == ALLIMAGES or self.branch == STICK_WIZARD:
438
439         def keyBlue(self):
440                 if self.nfo != "":
441                         self.session.open(NFOViewer, self.nfo)
442
443         def keyOk(self):
444                 print "[keyOk]", self["menu"].getCurrent()
445                 current = self["menu"].getCurrent()
446                 if current:
447                         if self.branch == START:
448                                 currentEntry = current[0]
449                                 if currentEntry == RELEASE:
450                                         self.image_idx = 0
451                                         self.branch = RELEASE
452                                         self.askDestination()
453                                 elif currentEntry == EXPERIMENTAL:
454                                         self.image_idx = 0
455                                         self.branch = EXPERIMENTAL
456                                         self.askDestination()
457                                 elif currentEntry == ALLIMAGES:
458                                         self.branch = ALLIMAGES
459                                         self.listImages()
460                                 elif currentEntry == STICK_WIZARD:
461                                         self.askStartWizard()
462                         elif self.branch == ALLIMAGES:
463                                 self.image_idx = self["menu"].getIndex()
464                                 self.askDestination()
465                 self.updateButtons()
466
467         def keyUp(self):
468                 self["menu"].selectPrevious()
469                 self.updateButtons()
470
471         def keyDown(self):
472                 self["menu"].selectNext()
473                 self.updateButtons()
474                 
475         def updateButtons(self):
476                 current = self["menu"].getCurrent()
477                 if current:
478                         if self.branch == START:
479                                 self["key_red"].text = _("Close")
480                                 currentEntry = current[0]
481                                 if currentEntry in (RELEASE, EXPERIMENTAL):
482                                         self.nfo_download(currentEntry, 0)
483                                         self["key_green"].text = _("Download")
484                                 else:
485                                         self.nfofilename = ""
486                                         self.nfo = ""
487                                         self["key_blue"].text = ""
488                                         self["key_green"].text = _("continue")
489
490                         elif self.branch == ALLIMAGES:
491                                 self["key_red"].text = _("Back")
492                                 self["key_green"].text = _("Download")
493                                 self.nfo_download(ALLIMAGES, self["menu"].getIndex())
494
495         def listImages(self):
496                 print "[listImages]"
497                 imagelist = []
498                 for name, url in self.feedlists[ALLIMAGES]:
499                         imagelist.append((url, name, _("Download %s from Server" ) % url, None))
500                 self["menu"].setList(imagelist)
501         
502         def getUSBPartitions(self):
503                 allpartitions = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = True)]
504                 print "[getUSBPartitions]", allpartitions
505                 usbpartition = []
506                 for x in allpartitions:
507                         print x, x[1] == '/', x[0].find("USB"), access(x[1], R_OK)
508                         if x[1] != '/' and x[0].find("USB") > -1:  # and access(x[1], R_OK) is True:
509                                 usbpartition.append(x)
510                 return usbpartition
511                                 
512         def askDestination(self):
513                 usbpartition = self.getUSBPartitions()
514                 if len(usbpartition) == 1:
515                         self.target_dir = usbpartition[0][1]
516                         self.ackDestinationDevice(device_description=usbpartition[0][0])
517                 else:
518                         self.session.openWithCallback(self.DeviceBrowserClosed, DeviceBrowser, None, showDirectories=True, showMountpoints=True, inhibitMounts=["/autofs/sr0/"])
519
520         def DeviceBrowserClosed(self, path):
521                 print "[DeviceBrowserClosed]", str(path)
522                 self.target_dir = path
523                 if path:
524                         self.ackDestinationDevice()
525                 else:
526                         self.keyRed()
527         
528         def ackDestinationDevice(self, device_description=None):
529                 if device_description == None:
530                         dev = self.target_dir
531                 else:
532                         dev = device_description
533                 message = _("Do you want to download the image to %s ?") % (dev)
534                 choices = [(_("Yes"), self.ackedDestination), (_("List of Storage Devices"),self.askDestination), (_("Cancel"),self.keyRed)]
535                 self.session.openWithCallback(self.ackDestination_query, ChoiceBox, title=message, list=choices)
536
537         def ackDestination_query(self, choice):
538                 print "[ackDestination_query]", choice
539                 if isinstance(choice, tuple):
540                         choice[1]()
541                 else:
542                         self.keyRed()
543
544         def ackedDestination(self):
545                 print "[ackedDestination]", self.branch, self.target_dir, self.target_dir[8:]
546                 self.container.setCWD("/mnt")
547                 if self.target_dir[:8] == "/autofs/":
548                         self.target_dir = "/dev/" + self.target_dir[8:-1]
549
550                         if self.branch == STICK_WIZARD:
551                                 job = StickWizardJob(self.target_dir)
552                                 job_manager.AddJob(job)
553                                 self.session.openWithCallback(self.StickWizardCB, JobView, job)
554
555                         elif self.branch != STICK_WIZARD:
556                                 url = self.feedlists[self.branch][self.image_idx][1]
557                                 filename = self.feedlists[self.branch][self.image_idx][0]
558                                 print "[getImage] start downloading %s to %s" % (url, path)
559                                 job = ImageDownloadJob(url, filename, self.target_dir, self.usbmountpoint)
560                                 job_manager.AddJob(job)
561                                 self.session.openWithCallback(self.ImageDownloadCB, JobView, job)
562
563         def StickWizardCB(self, ret=None):
564                 self.session.open(MessageBox, _("The USB stick was prepared to be bootable.\nNow you can download an NFI image file!"), type = MessageBox.TYPE_INFO)
565                 print "[StickWizardCB]", ret
566                 if len(self.feedlists[ALLIMAGES]) == 0:
567                         self.getFeed()
568                 else:
569                         self.setMenu()
570
571         def ImageDownloadCB(self, ret):
572                 print "[ImageDownloadCB]", ret
573                 self.session.open(MessageBox, _("To update your Dreambox firmware, please follow these steps:\n1) Turn off your box with the rear power switch and plug in the bootable USB stick.\n2) Turn mains back on and hold the DOWN button on the front panel pressed for 10 seconds.\n3) Wait for bootup and follow instructions of the wizard."), type = MessageBox.TYPE_INFO)
574
575         def getFeed(self):
576                 self.feedDownloader15 = feedDownloader(self.feed_base, self.box, OE_vers="1.5")
577                 self.feedDownloader16 = feedDownloader(self.feed_base, self.box, OE_vers="1.6")
578                 self.feedlists = [[],[],[]]
579                 self.feedDownloader15.getList(self.gotFeed, self.feed_failed)
580                 self.feedDownloader16.getList(self.gotFeed, self.feed_failed)
581                 
582         def feed_failed(self, message=""):
583                 self["status"].text = _("Could not connect to Dreambox .NFI Image Feed Server:") + "\n" + str(message) + "\n" + _("Please check your network settings!")
584
585         def gotFeed(self, feedlist, OE_vers):
586                 print "[gotFeed]", OE_vers
587                 releaselist = []
588                 experimentallist = []
589                 
590                 for name, url in feedlist:
591                         if name.find("release") > -1:
592                                 releaselist.append((name, url))
593                         if name.find("experimental") > -1:
594                                 experimentallist.append((name, url))
595                         self.feedlists[ALLIMAGES].append((name, url))
596                 
597                 if OE_vers == "1.6":
598                         self.feedlists[RELEASE] = releaselist + self.feedlists[RELEASE]
599                         self.feedlists[EXPERIMENTAL] = experimentallist + self.feedlists[RELEASE]
600                 elif OE_vers == "1.5":
601                         self.feedlists[RELEASE] = self.feedlists[RELEASE] + releaselist
602                         self.feedlists[EXPERIMENTAL] = self.feedlists[EXPERIMENTAL] + experimentallist
603
604                 self.setMenu()
605
606         def checkUSBStick(self):
607                 self.target_dir = None
608                 allpartitions = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = True)]
609                 print "[checkUSBStick] found partitions:", allpartitions
610                 usbpartition = []
611                 for x in allpartitions:
612                         print x, x[1] == '/', x[0].find("USB"), access(x[1], R_OK)
613                         if x[1] != '/' and x[0].find("USB") > -1:  # and access(x[1], R_OK) is True:
614                                 usbpartition.append(x)
615
616                 print usbpartition
617                 if len(usbpartition) == 1:
618                         self.target_dir = usbpartition[0][1]
619                         self.md5_passback = self.getFeed
620                         self.md5_failback = self.askStartWizard
621                         self.md5verify(self.stickimage_md5, self.target_dir)
622                 elif usbpartition == []:
623                         print "[NFIFlash] needs to create usb flasher stick first!"
624                         self.askStartWizard()
625                 else:
626                         self.askStartWizard()
627
628         def askStartWizard(self):
629                 self.branch = STICK_WIZARD
630                 message = _("""This plugin creates a USB stick which can be used to update the firmware of your Dreambox in case it has no network connection or only WLAN access.
631 First, you need to prepare a USB stick so that it is bootable.
632 In the next step, an NFI image file can be downloaded from the update server and saved on the USB stick.
633 If you already have a prepared bootable USB stick, please insert it now. Otherwise plug in a USB stick with a minimum size of 64 MB!""")
634                 self.session.openWithCallback(self.wizardDeviceBrowserClosed, DeviceBrowser, None, message, showDirectories=True, showMountpoints=True, inhibitMounts=["/","/autofs/sr0/","/autofs/sda1/","/media/hdd/","/media/net/","/media/usb/","/media/dvd/"])
635
636         def wizardDeviceBrowserClosed(self, path):
637                 print "[wizardDeviceBrowserClosed]", path
638                 self.target_dir = path
639                 if path:
640                         self.md5_passback = self.getFeed
641                         self.md5_failback = self.wizardQuery
642                         self.md5verify(self.stickimage_md5, self.target_dir)
643                 else:
644                         self.close()
645         
646         def wizardQuery(self):
647                 print "[wizardQuery]"
648                 description = self.target_dir
649                 for name, dev in self.getUSBPartitions():
650                         if dev == self.target_dir:
651                                 description = name
652                 message = _("You have chosen to create a new .NFI flasher bootable USB stick. This will repartition the USB stick and therefore all data on it will be erased.") + "\n"
653                 message += _("The following device was found:\n\n%s\n\nDo you want to write the USB flasher to this stick?") % description
654                 choices = [(_("Yes"), self.ackedDestination), (_("List of Storage Devices"),self.askStartWizard), (_("Cancel"),self.close)]
655                 self.session.openWithCallback(self.ackDestination_query, ChoiceBox, title=message, list=choices)
656                         
657         def setMenu(self):
658                 self.menulist = []
659                 try:
660                         latest_release = "Release %s (Opendreambox 1.5)" % self.feedlists[RELEASE][0][0][-9:-4]
661                         self.menulist.append((RELEASE, _("Get latest release image"), _("Download %s from Server" ) % latest_release, None))
662                 except IndexError:
663                         pass
664
665                 try:
666                         dat = self.feedlists[EXPERIMENTAL][0][0][-12:-4]
667                         latest_experimental = "Experimental %s-%s-%s (Opendreambox 1.6)" % (dat[:4], dat[4:6], dat[6:])
668                         self.menulist.append((EXPERIMENTAL, _("Get latest experimental image"), _("Download %s from Server") % latest_experimental, None))
669                 except IndexError:
670                         pass
671
672                 self.menulist.append((ALLIMAGES, _("Choose image to download"), _("Select desired image from feed list" ), None))
673                 self.menulist.append((STICK_WIZARD, _("USB stick wizard"), _("Prepare another USB stick for image flashing" ), None))
674                 self["menu"].setList(self.menulist)
675                 self["status"].text = _("Currently installed image") + ": %s" % (about.getImageVersionString())
676                 self.branch = START
677                 self.updateButtons()
678
679         def nfo_download(self, branch, idx):
680                 nfourl = (self.feedlists[branch][idx][1])[:-4]+".nfo"
681                 self.nfofilename = (self.feedlists[branch][idx][0])[:-4]+".nfo"
682                 print "[check_for_NFO]", nfourl
683                 client.getPage(nfourl).addCallback(self.nfo_finished).addErrback(self.nfo_failed)
684
685         def nfo_failed(self, failure_instance):
686                 print "[nfo_failed] " + str(failure_instance)
687                 self["key_blue"].text = ""
688                 self.nfofilename = ""
689                 self.nfo = ""
690
691         def nfo_finished(self,nfodata=""):
692                 print "[nfo_finished] " + str(nfodata)
693                 self["key_blue"].text = _("Changelog viewer")
694                 self.nfo = nfodata
695
696         def md5verify(self, md5, path):
697                 cmd = "md5sum -c -s"
698                 print "[verify_md5]", md5, path, cmd
699                 self.container.setCWD(path)
700                 self.container.appClosed.append(self.md5finished)
701                 self.container.execute(cmd)
702                 self.container.write(md5)
703                 self.container.dataSent.append(self.md5ready)
704
705         def md5ready(self, retval):
706                 self.container.sendEOF()
707
708         def md5finished(self, retval):
709                 print "[md5finished]", str(retval)
710                 self.container.appClosed.remove(self.md5finished)
711                 self.container.dataSent.remove(self.md5ready)
712                 if retval==0:
713                         print "check passed! calling", repr(self.md5_passback)
714                         self.md5_passback()
715                 else:
716                         print "check failed! calling", repr(self.md5_failback)
717                         self.md5_failback()
718
719 def main(session, **kwargs):
720         session.open(NFIDownload,"/home/root")
721
722 def filescan_open(list, session, **kwargs):
723         dev = "/dev/" + (list[0].path).rsplit('/',1)[0][7:]
724         print "mounting device " + dev + " to /mnt/usb..."
725         system("mount "+dev+" /mnt/usb/ -o rw,sync")
726         session.open(NFIDownload,"/mnt/usb/")
727
728 def filescan(**kwargs):
729         from Components.Scanner import Scanner, ScanPath
730         return \
731                 Scanner(mimetypes = ["application/x-dream-image"], 
732                         paths_to_scan = 
733                                 [
734                                         ScanPath(path = "", with_subdirs = False),
735                                 ], 
736                         name = "NFI", 
737                         description = (_("Download .NFI-Files for USB-Flasher")+"..."),
738                         openfnc = filescan_open, )