re-commit new NFIFlash plugin (bug #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.openDeviceBrowser()
519         
520         def openDeviceBrowser(self):
521                 if self.branch != STICK_WIZARD:
522                         self.session.openWithCallback(self.DeviceBrowserClosed, DeviceBrowser, None, showDirectories=True, showMountpoints=True, inhibitMounts=["/autofs/sr0/"])
523                 if self.branch == STICK_WIZARD:
524                         self.session.openWithCallback(self.DeviceBrowserClosed, DeviceBrowser, None, showDirectories=False, showMountpoints=True, inhibitMounts=["/","/autofs/sr0/"])
525
526         def DeviceBrowserClosed(self, path):
527                 print "[DeviceBrowserClosed]", str(path)
528                 self.target_dir = path
529                 if path:
530                         self.ackDestinationDevice()
531                 else:
532                         self.keyRed()
533         
534         def ackDestinationDevice(self, device_description=None):
535                 if device_description == None:
536                         dev = self.target_dir
537                 else:
538                         dev = device_description
539                 message = _("Do you want to download the image to %s ?") % (dev)
540                 choices = [(_("Yes"), self.ackedDestination), (_("List of Storage Devices"),self.openDeviceBrowser), (_("Cancel"),self.keyRed)]
541                 self.session.openWithCallback(self.ackDestination_query, ChoiceBox, title=message, list=choices)
542
543         def ackDestination_query(self, choice):
544                 print "[ackDestination_query]", choice
545                 if isinstance(choice, tuple):
546                         choice[1]()
547                 else:
548                         self.keyRed()
549
550         def ackedDestination(self):
551                 print "[ackedDestination]", self.branch, self.target_dir, self.target_dir[8:]
552                 self.container.setCWD("/mnt")
553                 if self.target_dir[:8] == "/autofs/":
554                         self.target_dir = "/dev/" + self.target_dir[8:-1]
555
556                         if self.branch == STICK_WIZARD:
557                                 job = StickWizardJob(self.target_dir)
558                                 job_manager.AddJob(job)
559                                 self.session.openWithCallback(self.StickWizardCB, JobView, job)
560
561                         elif self.branch != STICK_WIZARD:
562                                 url = self.feedlists[self.branch][self.image_idx][1]
563                                 filename = self.feedlists[self.branch][self.image_idx][0]
564                                 print "[getImage] start downloading %s to %s" % (url, path)
565                                 job = ImageDownloadJob(url, filename, self.target_dir, self.usbmountpoint)
566                                 job_manager.AddJob(job)
567                                 self.session.openWithCallback(self.ImageDownloadCB, JobView, job)
568
569         def StickWizardCB(self, ret=None):
570                 self.session.open(MessageBox, _("The USB stick was prepared to be bootable.\nNow you can download an NFI image file!"), type = MessageBox.TYPE_INFO)
571                 print "[StickWizardCB]", ret
572                 if len(self.feedlists[ALLIMAGES]) == 0:
573                         self.getFeed()
574                 else:
575                         self.setMenu()
576
577         def ImageDownloadCB(self, ret):
578                 print "[ImageDownloadCB]", ret
579                 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)
580
581         def getFeed(self):
582                 self.feedDownloader15 = feedDownloader(self.feed_base, self.box, OE_vers="1.5")
583                 self.feedDownloader16 = feedDownloader(self.feed_base, self.box, OE_vers="1.6")
584                 self.feedlists = [[],[],[]]
585                 self.feedDownloader15.getList(self.gotFeed, self.feed_failed)
586                 self.feedDownloader16.getList(self.gotFeed, self.feed_failed)
587                 
588         def feed_failed(self, message=""):
589                 self["status"].text = _("Could not connect to Dreambox .NFI Image Feed Server:") + "\n" + str(message) + "\n" + _("Please check your network settings!")
590
591         def gotFeed(self, feedlist, OE_vers):
592                 print "[gotFeed]", OE_vers
593                 releaselist = []
594                 experimentallist = []
595                 
596                 for name, url in feedlist:
597                         if name.find("release") > -1:
598                                 releaselist.append((name, url))
599                         if name.find("experimental") > -1:
600                                 experimentallist.append((name, url))
601                         self.feedlists[ALLIMAGES].append((name, url))
602                 
603                 if OE_vers == "1.6":
604                         self.feedlists[RELEASE] = releaselist + self.feedlists[RELEASE]
605                         self.feedlists[EXPERIMENTAL] = experimentallist + self.feedlists[RELEASE]
606                 elif OE_vers == "1.5":
607                         self.feedlists[RELEASE] = self.feedlists[RELEASE] + releaselist
608                         self.feedlists[EXPERIMENTAL] = self.feedlists[EXPERIMENTAL] + experimentallist
609
610                 self.setMenu()
611
612         def checkUSBStick(self):
613                 self.target_dir = None
614                 allpartitions = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = True)]
615                 print "[checkUSBStick] found partitions:", allpartitions
616                 usbpartition = []
617                 for x in allpartitions:
618                         print x, x[1] == '/', x[0].find("USB"), access(x[1], R_OK)
619                         if x[1] != '/' and x[0].find("USB") > -1:  # and access(x[1], R_OK) is True:
620                                 usbpartition.append(x)
621
622                 print usbpartition
623                 if len(usbpartition) == 1:
624                         self.target_dir = usbpartition[0][1]
625                         self.md5_passback = self.getFeed
626                         self.md5_failback = self.askStartWizard
627                         self.md5verify(self.stickimage_md5, self.target_dir)
628                 elif usbpartition == []:
629                         print "[NFIFlash] needs to create usb flasher stick first!"
630                         self.askStartWizard()
631                 else:
632                         self.askStartWizard()
633
634         def askStartWizard(self):
635                 self.branch = STICK_WIZARD
636                 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.
637 First, you need to prepare a USB stick so that it is bootable.
638 In the next step, an NFI image file can be downloaded from the update server and saved on the USB stick.
639 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!""")
640                 self.session.openWithCallback(self.wizardDeviceBrowserClosed, DeviceBrowser, None, message, showDirectories=True, showMountpoints=True, inhibitMounts=["/","/autofs/sr0/"])
641
642         def wizardDeviceBrowserClosed(self, path):
643                 print "[wizardDeviceBrowserClosed]", path
644                 self.target_dir = path
645                 if path:
646                         self.md5_passback = self.getFeed
647                         self.md5_failback = self.wizardQuery
648                         self.md5verify(self.stickimage_md5, self.target_dir)
649                 else:
650                         self.close()
651         
652         def wizardQuery(self):
653                 print "[wizardQuery]"
654                 description = self.target_dir
655                 for name, dev in self.getUSBPartitions():
656                         if dev == self.target_dir:
657                                 description = name
658                 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"
659                 message += _("The following device was found:\n\n%s\n\nDo you want to write the USB flasher to this stick?") % description
660                 choices = [(_("Yes"), self.ackedDestination), (_("List of Storage Devices"),self.askStartWizard), (_("Cancel"),self.close)]
661                 self.session.openWithCallback(self.ackDestination_query, ChoiceBox, title=message, list=choices)
662                         
663         def setMenu(self):
664                 self.menulist = []
665                 try:
666                         latest_release = "Release %s (Opendreambox 1.5)" % self.feedlists[RELEASE][0][0][-9:-4]
667                         self.menulist.append((RELEASE, _("Get latest release image"), _("Download %s from Server" ) % latest_release, None))
668                 except IndexError:
669                         pass
670
671                 try:
672                         dat = self.feedlists[EXPERIMENTAL][0][0][-12:-4]
673                         latest_experimental = "Experimental %s-%s-%s (Opendreambox 1.6)" % (dat[:4], dat[4:6], dat[6:])
674                         self.menulist.append((EXPERIMENTAL, _("Get latest experimental image"), _("Download %s from Server") % latest_experimental, None))
675                 except IndexError:
676                         pass
677
678                 self.menulist.append((ALLIMAGES, _("Choose image to download"), _("Select desired image from feed list" ), None))
679                 self.menulist.append((STICK_WIZARD, _("USB stick wizard"), _("Prepare another USB stick for image flashing" ), None))
680                 self["menu"].setList(self.menulist)
681                 self["status"].text = _("Currently installed image") + ": %s" % (about.getImageVersionString()))
682                 self.branch = START
683                 self.updateButtons()
684
685         def nfo_download(self, branch, idx):
686                 nfourl = (self.feedlists[branch][idx][1])[:-4]+".nfo"
687                 self.nfofilename = (self.feedlists[branch][idx][0])[:-4]+".nfo"
688                 print "[check_for_NFO]", nfourl
689                 client.getPage(nfourl).addCallback(self.nfo_finished).addErrback(self.nfo_failed)
690
691         def nfo_failed(self, failure_instance):
692                 print "[nfo_failed] " + str(failure_instance)
693                 self["key_blue"].text = ""
694                 self.nfofilename = ""
695                 self.nfo = ""
696
697         def nfo_finished(self,nfodata=""):
698                 print "[nfo_finished] " + str(nfodata)
699                 self["key_blue"].text = _("Changelog viewer")
700                 self.nfo = nfodata
701
702         def md5verify(self, md5, path):
703                 cmd = "md5sum -c -s"
704                 print "[verify_md5]", md5, path, cmd
705                 self.container.setCWD(path)
706                 self.container.appClosed.append(self.md5finished)
707                 self.container.execute(cmd)
708                 self.container.write(md5)
709                 self.container.dataSent.append(self.md5ready)
710
711         def md5ready(self, retval):
712                 self.container.sendEOF()
713
714         def md5finished(self, retval):
715                 print "[md5finished]", str(retval)
716                 self.container.appClosed.remove(self.md5finished)
717                 self.container.dataSent.remove(self.md5ready)
718                 if retval==0:
719                         print "check passed! calling", repr(self.md5_passback)
720                         self.md5_passback()
721                 else:
722                         print "check failed! calling", repr(self.md5_failback)
723                         self.md5_failback()
724
725 def main(session, **kwargs):
726         session.open(NFIDownload,"/home/root")
727
728 def filescan_open(list, session, **kwargs):
729         dev = "/dev/" + (list[0].path).rsplit('/',1)[0][7:]
730         print "mounting device " + dev + " to /mnt/usb..."
731         system("mount "+dev+" /mnt/usb/ -o rw,sync")
732         session.open(NFIDownload,"/mnt/usb/")
733
734 def filescan(**kwargs):
735         from Components.Scanner import Scanner, ScanPath
736         return \
737                 Scanner(mimetypes = ["application/x-dream-image"], 
738                         paths_to_scan = 
739                                 [
740                                         ScanPath(path = "", with_subdirs = False),
741                                 ], 
742                         name = "NFI", 
743                         description = (_("Download .NFI-Files for USB-Flasher")+"..."),
744                         openfnc = filescan_open, )