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